@overmap-ai/core 1.0.51 → 1.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/forms/builder/constants.d.ts +1 -0
  2. package/dist/forms/builder/utils.d.ts +1 -1
  3. package/dist/forms/fields/QrField/QrField.d.ts +21 -0
  4. package/dist/forms/fields/QrField/QrInput.d.ts +10 -0
  5. package/dist/forms/fields/QrField/index.d.ts +2 -0
  6. package/dist/forms/fields/constants.d.ts +8 -0
  7. package/dist/forms/fields/index.d.ts +1 -0
  8. package/dist/forms/renderer/FormSubmissionBrowser/FormSubmissionBrowser.d.ts +5 -5
  9. package/dist/forms/renderer/FormSubmissionViewer/FormSubmissionViewer.d.ts +3 -3
  10. package/dist/forms/typings.d.ts +5 -2
  11. package/dist/overmap-core.js +1172 -473
  12. package/dist/overmap-core.js.map +1 -1
  13. package/dist/overmap-core.umd.cjs +1174 -476
  14. package/dist/overmap-core.umd.cjs.map +1 -1
  15. package/dist/sdk/sdk.d.ts +2 -1
  16. package/dist/sdk/services/AttachmentService.d.ts +2 -2
  17. package/dist/sdk/services/DocumentService.d.ts +5 -5
  18. package/dist/sdk/services/TeamService.d.ts +12 -0
  19. package/dist/sdk/services/UserFormSubmissionService.d.ts +9 -2
  20. package/dist/sdk/services/index.d.ts +1 -0
  21. package/dist/store/slices/categorySlice.d.ts +5 -1
  22. package/dist/store/slices/documentSlice.d.ts +352 -13
  23. package/dist/store/slices/formRevisionSlice.d.ts +66 -0
  24. package/dist/store/slices/formSlice.d.ts +110 -0
  25. package/dist/store/slices/formSubmissionSlice.d.ts +47 -0
  26. package/dist/store/slices/index.d.ts +4 -1
  27. package/dist/store/slices/issueSlice.d.ts +4 -1
  28. package/dist/store/slices/projectFileSlice.d.ts +4 -1
  29. package/dist/store/slices/teamSlice.d.ts +19 -0
  30. package/dist/store/slices/utils.d.ts +1 -0
  31. package/dist/store/slices/workspaceSlice.d.ts +4 -1
  32. package/dist/store/store.d.ts +13 -4
  33. package/dist/style.css +5 -0
  34. package/dist/typings/files.d.ts +11 -1
  35. package/dist/typings/models/attachments.d.ts +10 -13
  36. package/dist/typings/models/base.d.ts +10 -0
  37. package/dist/typings/models/documents.d.ts +19 -5
  38. package/dist/typings/models/forms.d.ts +8 -12
  39. package/dist/typings/models/index.d.ts +1 -0
  40. package/dist/typings/models/issues.d.ts +3 -2
  41. package/dist/typings/models/teams.d.ts +10 -0
  42. package/dist/utils/file.d.ts +2 -0
  43. package/dist/utils/forms.d.ts +2 -0
  44. package/package.json +2 -1
  45. package/dist/store/slices/userFormSlice.d.ts +0 -145
@@ -8,7 +8,7 @@ var _a;
8
8
  import * as React from "react";
9
9
  import React__default, { useState, useEffect, useRef, memo, useMemo, useCallback, createContext, createElement, useContext, forwardRef, Children, isValidElement, cloneElement, Fragment as Fragment$1, useLayoutEffect, useReducer, lazy, Suspense } from "react";
10
10
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
11
- import { unsafeShowToast, AlertDialogProvider, ToastProvider, DefaultTheme, Flex as Flex$1, IconButton, RiIcon, Text as Text$1, useSeverityColor, Checkbox, TextArea, Select, useToast, Badge, MultiSelect, useViewportSize, Overlay, ButtonGroup, Spinner, IconColorUtility, Tooltip, Popover, useSize, ToggleButton, Separator, OvermapItem, Button, ButtonList, divButtonProps, OvermapDropdownMenu, Input, useAlertDialog } from "@overmap-ai/blocks";
11
+ import { unsafeShowToast, AlertDialogProvider, ToastProvider, DefaultTheme, Flex as Flex$1, IconButton, RiIcon, Text as Text$1, useSeverityColor, Checkbox, TextArea, Select, useToast, Badge, MultiSelect, Overlay, Button, Spinner, useViewportSize, ButtonGroup, IconColorUtility, Tooltip, Popover, useSize, ToggleButton, Separator, OvermapItem, ButtonList, divButtonProps, OvermapDropdownMenu, Input, useAlertDialog } from "@overmap-ai/blocks";
12
12
  import { DepGraph } from "dependency-graph";
13
13
  import { offline as offline$1 } from "@redux-offline/redux-offline";
14
14
  import offlineConfig from "@redux-offline/redux-offline/lib/defaults";
@@ -27,6 +27,7 @@ import { useField, useFormikContext, useFormik, FormikProvider } from "formik";
27
27
  import get from "lodash.get";
28
28
  import Linkify from "linkify-react";
29
29
  import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
30
+ import QrScannerAPI from "qr-scanner";
30
31
  import { read, utils } from "xlsx";
31
32
  import { pdfjs, Document, Page } from "react-pdf";
32
33
  import "react-pdf/dist/Page/AnnotationLayer.css";
@@ -677,15 +678,15 @@ const wrapMigration = (migrator) => (state) => {
677
678
  };
678
679
  const migrations = [initialVersioning, signOut, signOut, createOutboxState];
679
680
  const manifest = Object.fromEntries(migrations.map((migration2, i) => [i, wrapMigration(migration2)]));
680
- const initialState$n = {
681
+ const initialState$q = {
681
682
  accessToken: "",
682
683
  refreshToken: "",
683
684
  isLoggedIn: false
684
685
  };
685
686
  const authSlice = createSlice({
686
687
  name: "auth",
687
- initialState: initialState$n,
688
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$n)),
688
+ initialState: initialState$q,
689
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$q)),
689
690
  reducers: {
690
691
  setTokens: (state, action) => {
691
692
  state.accessToken = action.payload.accessToken;
@@ -850,6 +851,19 @@ function downloadInMemoryFile(filename, text) {
850
851
  element.click();
851
852
  document.body.removeChild(element);
852
853
  }
854
+ const constructUploadedFilePayloads = async (files) => {
855
+ const filePayloads = {};
856
+ for (const file of files) {
857
+ const sha1 = await hashFile(file);
858
+ filePayloads[sha1] = {
859
+ sha1,
860
+ extension: file.name.split(".").pop() || "",
861
+ file_type: file.type,
862
+ size: file.size
863
+ };
864
+ }
865
+ return Object.values(filePayloads);
866
+ };
853
867
  const fileToBlob = async (dataUrl) => {
854
868
  return (await fetch(dataUrl)).blob();
855
869
  };
@@ -1416,7 +1430,7 @@ const getLocalRelativeDateString = memoize((date, min, max) => {
1416
1430
  return getLocalDateString(date);
1417
1431
  return relative.format(days, "days");
1418
1432
  });
1419
- const initialState$m = {
1433
+ const initialState$p = {
1420
1434
  categories: {},
1421
1435
  usedCategoryColors: [],
1422
1436
  categoryVisibility: {
@@ -1426,8 +1440,8 @@ const initialState$m = {
1426
1440
  };
1427
1441
  const categorySlice = createSlice({
1428
1442
  name: "categories",
1429
- initialState: initialState$m,
1430
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$m)),
1443
+ initialState: initialState$p,
1444
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$p)),
1431
1445
  reducers: {
1432
1446
  setCategories: (state, action) => {
1433
1447
  if (!Array.isArray(action.payload))
@@ -1559,6 +1573,9 @@ const selectHiddenCategoryCount = (state) => {
1559
1573
  hiddenCategoryCount++;
1560
1574
  return hiddenCategoryCount;
1561
1575
  };
1576
+ const selectIssueCountOfCategory = (categoryId) => (state) => {
1577
+ return Object.values(state.issueReducer.issues).filter((issue) => issue.category === categoryId).length;
1578
+ };
1562
1579
  const categoryReducer = categorySlice.reducer;
1563
1580
  function setAttachments(state, action) {
1564
1581
  state.attachments = {};
@@ -1596,14 +1613,14 @@ function removeAttachments(state, action) {
1596
1613
  delete state.attachments[attachmentId];
1597
1614
  }
1598
1615
  }
1599
- const initialState$l = {
1616
+ const initialState$o = {
1600
1617
  components: {},
1601
1618
  attachments: {}
1602
1619
  };
1603
1620
  const componentSlice = createSlice({
1604
1621
  name: "components",
1605
- initialState: initialState$l,
1606
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$l)),
1622
+ initialState: initialState$o,
1623
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$o)),
1607
1624
  reducers: {
1608
1625
  addComponent: (state, action) => {
1609
1626
  state.components[action.payload.offline_id] = action.payload;
@@ -1759,13 +1776,13 @@ const {
1759
1776
  removeAllComponentsOfType
1760
1777
  } = componentSlice.actions;
1761
1778
  const componentReducer = componentSlice.reducer;
1762
- const initialState$k = {
1779
+ const initialState$n = {
1763
1780
  completionsByComponentId: {}
1764
1781
  };
1765
1782
  const componentStageCompletionSlice = createSlice({
1766
1783
  name: "componentStageCompletions",
1767
- initialState: initialState$k,
1768
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$k)),
1784
+ initialState: initialState$n,
1785
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$n)),
1769
1786
  reducers: {
1770
1787
  addStageCompletion: (state, action) => {
1771
1788
  let stageToCompletionDateMapping = state.completionsByComponentId[action.payload.component];
@@ -1816,13 +1833,13 @@ const selectCompletedStageIdsForComponent = (component) => (state) => {
1816
1833
  return Object.keys(state.componentStageCompletionReducer.completionsByComponentId[component.offline_id] ?? {});
1817
1834
  };
1818
1835
  const componentStageCompletionReducer = componentStageCompletionSlice.reducer;
1819
- const initialState$j = {
1836
+ const initialState$m = {
1820
1837
  stages: {}
1821
1838
  };
1822
1839
  const componentStageSlice = createSlice({
1823
1840
  name: "componentStages",
1824
- initialState: initialState$j,
1825
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$j)),
1841
+ initialState: initialState$m,
1842
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$m)),
1826
1843
  reducers: {
1827
1844
  addStages: (state, action) => {
1828
1845
  Object.assign(state.stages, toOfflineIdRecord(action.payload));
@@ -1932,15 +1949,15 @@ const selectStageFormIdsFromStageIds = restructureCreateSelectorWithArgs(
1932
1949
  );
1933
1950
  const { addStages, updateStages, removeStages, linkStageToForm, unlinkStageToForm } = componentStageSlice.actions;
1934
1951
  const componentStageReducer = componentStageSlice.reducer;
1935
- const initialState$i = {
1952
+ const initialState$l = {
1936
1953
  componentTypes: {},
1937
1954
  hiddenComponentTypeIds: {},
1938
1955
  attachments: {}
1939
1956
  };
1940
1957
  const componentTypeSlice = createSlice({
1941
1958
  name: "componentTypes",
1942
- initialState: initialState$i,
1943
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$i)),
1959
+ initialState: initialState$l,
1960
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$l)),
1944
1961
  reducers: {
1945
1962
  addComponentType: (state, action) => {
1946
1963
  state.componentTypes[action.payload.offline_id] = action.payload;
@@ -2051,13 +2068,13 @@ const {
2051
2068
  deleteComponentType
2052
2069
  } = componentTypeSlice.actions;
2053
2070
  const componentTypeReducer = componentTypeSlice.reducer;
2054
- const initialState$h = {
2071
+ const initialState$k = {
2055
2072
  workspaces: {},
2056
2073
  activeWorkspaceId: null
2057
2074
  };
2058
2075
  const workspaceSlice = createSlice({
2059
2076
  name: "workspace",
2060
- initialState: initialState$h,
2077
+ initialState: initialState$k,
2061
2078
  // The `reducers` field lets us define reducers and generate associated actions
2062
2079
  reducers: {
2063
2080
  setWorkspaces: (state, action) => {
@@ -2114,7 +2131,7 @@ const selectPermittedWorkspaceIds = createSelector(
2114
2131
  );
2115
2132
  const workspaceReducer = workspaceSlice.reducer;
2116
2133
  const maxRecentIssues = 10;
2117
- const initialState$g = {
2134
+ const initialState$j = {
2118
2135
  issues: {},
2119
2136
  attachments: {},
2120
2137
  comments: {},
@@ -2126,9 +2143,9 @@ const initialState$g = {
2126
2143
  };
2127
2144
  const issueSlice = createSlice({
2128
2145
  name: "issues",
2129
- initialState: initialState$g,
2146
+ initialState: initialState$j,
2130
2147
  extraReducers: (builder) => builder.addCase("RESET", (state) => {
2131
- Object.assign(state, initialState$g);
2148
+ Object.assign(state, initialState$j);
2132
2149
  }),
2133
2150
  reducers: {
2134
2151
  setIssues: (state, action) => {
@@ -2536,15 +2553,15 @@ const selectRecentIssuesAsSearchResults = createSelector(
2536
2553
  }
2537
2554
  );
2538
2555
  const issueReducer = issueSlice.reducer;
2539
- const initialState$f = {
2556
+ const initialState$i = {
2540
2557
  s3Urls: {}
2541
2558
  };
2542
2559
  const msPerHour = 1e3 * 60 * 60;
2543
2560
  const msPerWeek = msPerHour * 24 * 7;
2544
2561
  const fileSlice = createSlice({
2545
2562
  name: "file",
2546
- initialState: initialState$f,
2547
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$f)),
2563
+ initialState: initialState$i,
2564
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$i)),
2548
2565
  reducers: {
2549
2566
  setUploadUrl: (state, action) => {
2550
2567
  const { url, fields, sha1 } = action.payload;
@@ -2571,7 +2588,7 @@ const selectUploadUrl = (sha1) => (state) => {
2571
2588
  return url;
2572
2589
  };
2573
2590
  const fileReducer = fileSlice.reducer;
2574
- const initialState$e = {
2591
+ const initialState$h = {
2575
2592
  // TODO: Change first MapStyle.SATELLITE to MaptStyle.None when project creation map is fixed
2576
2593
  mapStyle: MapStyle.SATELLITE,
2577
2594
  showTooltips: false,
@@ -2579,8 +2596,8 @@ const initialState$e = {
2579
2596
  };
2580
2597
  const mapSlice = createSlice({
2581
2598
  name: "map",
2582
- initialState: initialState$e,
2583
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$e)),
2599
+ initialState: initialState$h,
2600
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$h)),
2584
2601
  reducers: {
2585
2602
  setMapStyle: (state, action) => {
2586
2603
  state.mapStyle = action.payload;
@@ -2649,7 +2666,7 @@ var LicenseStatus = /* @__PURE__ */ ((LicenseStatus2) => {
2649
2666
  LicenseStatus2[LicenseStatus2["PAST_DUE"] = 8] = "PAST_DUE";
2650
2667
  return LicenseStatus2;
2651
2668
  })(LicenseStatus || {});
2652
- const initialState$d = {
2669
+ const initialState$g = {
2653
2670
  users: {},
2654
2671
  currentUser: {
2655
2672
  id: 0,
@@ -2660,8 +2677,8 @@ const initialState$d = {
2660
2677
  };
2661
2678
  const userSlice = createSlice({
2662
2679
  name: "users",
2663
- initialState: initialState$d,
2664
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$d)),
2680
+ initialState: initialState$g,
2681
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$g)),
2665
2682
  reducers: {
2666
2683
  setUsers: (state, action) => {
2667
2684
  const usersMapping = {};
@@ -2723,13 +2740,13 @@ const selectUser = (userId) => (state) => {
2723
2740
  const selectUsersAsMapping = (state) => state.userReducer.users;
2724
2741
  const selectFavouriteProjects = (state) => state.userReducer.currentUser.profile.favourite_project_ids;
2725
2742
  const userReducer = userSlice.reducer;
2726
- const initialState$c = {
2743
+ const initialState$f = {
2727
2744
  organizationAccesses: {}
2728
2745
  };
2729
2746
  const organizationAccessSlice = createSlice({
2730
2747
  name: "organizationAccess",
2731
- initialState: initialState$c,
2732
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$c)),
2748
+ initialState: initialState$f,
2749
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$f)),
2733
2750
  reducers: {
2734
2751
  setOrganizationAccesses: (state, action) => {
2735
2752
  if (!Array.isArray(action.payload))
@@ -2792,13 +2809,13 @@ const selectOrganizationAccessUserMapping = (state) => {
2792
2809
  return organizationAccesses;
2793
2810
  };
2794
2811
  const organizationAccessReducer = organizationAccessSlice.reducer;
2795
- const initialState$b = {
2812
+ const initialState$e = {
2796
2813
  licenses: {}
2797
2814
  };
2798
2815
  const licenseSlice = createSlice({
2799
2816
  name: "license",
2800
- initialState: initialState$b,
2801
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$b)),
2817
+ initialState: initialState$e,
2818
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$e)),
2802
2819
  reducers: {
2803
2820
  setLicenses: (state, action) => {
2804
2821
  if (!Array.isArray(action.payload))
@@ -2843,13 +2860,13 @@ const selectLicensesForProjectsMapping = createSelector(
2843
2860
  (licenses) => Object.values(licenses).filter((license) => license.project).reduce((accum, license) => ({ ...accum, [license.project]: license }), {})
2844
2861
  );
2845
2862
  const licenseReducer = licenseSlice.reducer;
2846
- const initialState$a = {
2863
+ const initialState$d = {
2847
2864
  projectAccesses: {}
2848
2865
  };
2849
2866
  const projectAccessSlice = createSlice({
2850
2867
  name: "projectAccess",
2851
- initialState: initialState$a,
2852
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$a)),
2868
+ initialState: initialState$d,
2869
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$d)),
2853
2870
  reducers: {
2854
2871
  setProjectAccesses: (state, action) => {
2855
2872
  if (!Array.isArray(action.payload))
@@ -2917,7 +2934,7 @@ const selectProjectAccessUserMapping = (state) => {
2917
2934
  return projectAccesses;
2918
2935
  };
2919
2936
  const projectAccessReducer = projectAccessSlice.reducer;
2920
- const initialState$9 = {
2937
+ const initialState$c = {
2921
2938
  projects: {},
2922
2939
  activeProjectId: null,
2923
2940
  recentProjectIds: [],
@@ -2927,7 +2944,7 @@ const initialState$9 = {
2927
2944
  };
2928
2945
  const projectSlice = createSlice({
2929
2946
  name: "projects",
2930
- initialState: initialState$9,
2947
+ initialState: initialState$c,
2931
2948
  reducers: {
2932
2949
  setProjects: (state, action) => {
2933
2950
  const projectsMap = {};
@@ -3114,14 +3131,14 @@ const selectAttachmentsOfProjectByType = restructureCreateSelectorWithArgs(
3114
3131
  }
3115
3132
  )
3116
3133
  );
3117
- const initialState$8 = {
3134
+ const initialState$b = {
3118
3135
  organizations: {},
3119
3136
  activeOrganizationId: null
3120
3137
  };
3121
3138
  const organizationSlice = createSlice({
3122
3139
  name: "organizations",
3123
- initialState: initialState$8,
3124
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$8)),
3140
+ initialState: initialState$b,
3141
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$b)),
3125
3142
  reducers: {
3126
3143
  setOrganizations: (state, action) => {
3127
3144
  for (const org of action.payload) {
@@ -3240,14 +3257,14 @@ const createOfflineAction = (request2, baseUrl) => {
3240
3257
  }
3241
3258
  };
3242
3259
  };
3243
- const initialState$7 = {
3260
+ const initialState$a = {
3244
3261
  deletedRequests: [],
3245
3262
  latestRetryTime: 0
3246
3263
  };
3247
3264
  const outboxSlice = createSlice({
3248
3265
  name: "outbox",
3249
- initialState: initialState$7,
3250
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$7)),
3266
+ initialState: initialState$a,
3267
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$a)),
3251
3268
  reducers: {
3252
3269
  // enqueueActions is a reducer that does nothing but enqueue API request to the Redux Offline outbox
3253
3270
  // Whenever an issue is being created, a reducer addIssue() is responsible for adding it to the offline store
@@ -3279,7 +3296,7 @@ const selectDeletedRequests = (state) => state.outboxReducer.deletedRequests;
3279
3296
  const selectLatestRetryTime = (state) => state.outboxReducer.latestRetryTime;
3280
3297
  const { enqueueRequest, markForDeletion, markAsDeleted, _setLatestRetryTime } = outboxSlice.actions;
3281
3298
  const outboxReducer = outboxSlice.reducer;
3282
- const initialState$6 = {
3299
+ const initialState$9 = {
3283
3300
  projectFiles: {},
3284
3301
  activeProjectFileId: null,
3285
3302
  isImportingProjectFile: false,
@@ -3287,8 +3304,8 @@ const initialState$6 = {
3287
3304
  };
3288
3305
  const projectFileSlice = createSlice({
3289
3306
  name: "projectFiles",
3290
- initialState: initialState$6,
3291
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$6)),
3307
+ initialState: initialState$9,
3308
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$9)),
3292
3309
  reducers: {
3293
3310
  addOrReplaceProjectFiles: (state, action) => {
3294
3311
  for (let fileObj of action.payload) {
@@ -3389,12 +3406,12 @@ const selectProjectFiles = createSelector(
3389
3406
  const selectActiveProjectFileId = (state) => state.projectFileReducer.activeProjectFileId;
3390
3407
  const selectIsImportingProjectFile = (state) => state.projectFileReducer.isImportingProjectFile;
3391
3408
  const projectFileReducer = projectFileSlice.reducer;
3392
- const initialState$5 = {
3409
+ const initialState$8 = {
3393
3410
  isRehydrated: false
3394
3411
  };
3395
3412
  const rehydratedSlice = createSlice({
3396
3413
  name: "rehydrated",
3397
- initialState: initialState$5,
3414
+ initialState: initialState$8,
3398
3415
  // The `reducers` field lets us define reducers and generate associated actions
3399
3416
  reducers: {
3400
3417
  setRehydrated: (state, action) => {
@@ -3404,7 +3421,7 @@ const rehydratedSlice = createSlice({
3404
3421
  });
3405
3422
  const selectRehydrated = (state) => state.rehydratedReducer.isRehydrated;
3406
3423
  const rehydratedReducer = rehydratedSlice.reducer;
3407
- const initialState$4 = {
3424
+ const initialState$7 = {
3408
3425
  useIssueTemplate: false,
3409
3426
  placementMode: false,
3410
3427
  enableClustering: false,
@@ -3421,8 +3438,8 @@ const initialState$4 = {
3421
3438
  };
3422
3439
  const settingSlice = createSlice({
3423
3440
  name: "settings",
3424
- initialState: initialState$4,
3425
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$4)),
3441
+ initialState: initialState$7,
3442
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$7)),
3426
3443
  reducers: {
3427
3444
  setEnableDuplicateIssues: (state, action) => {
3428
3445
  state.useIssueTemplate = action.payload;
@@ -3468,146 +3485,231 @@ const selectAppearance = (state) => state.settingReducer.appearance;
3468
3485
  const settingReducer = settingSlice.reducer;
3469
3486
  const selectIsFetchingInitialData = (state) => state.settingReducer.isFetchingInitialData;
3470
3487
  const selectIsLoading = (state) => state.settingReducer.isLoading;
3471
- const LATEST_REVISION_CACHE = {};
3472
- function considerCachingRevision(revision, formId2, preferPending = false) {
3473
- var _a2;
3474
- if (!revision) {
3475
- if (!formId2) {
3476
- throw new Error("If revision is null, formId is required.");
3477
- }
3478
- const currentLatestRevision = getLatestRevisionFromCache(formId2);
3479
- if (currentLatestRevision)
3480
- return;
3481
- LATEST_REVISION_CACHE[formId2] = null;
3482
- return;
3483
- }
3484
- if (revision.revision === "Pending") {
3485
- if (preferPending) {
3486
- LATEST_REVISION_CACHE[revision.form] = revision;
3487
- }
3488
- return;
3489
- }
3490
- const cachedRevision = (_a2 = LATEST_REVISION_CACHE[revision.form]) == null ? void 0 : _a2.revision;
3491
- if (revision.revision > (typeof cachedRevision === "number" ? cachedRevision : -1)) {
3492
- LATEST_REVISION_CACHE[revision.form] = revision;
3488
+ const formRevisionSortFn = (formRevisionA, formRevisionB) => {
3489
+ const revisionA = formRevisionA.revision;
3490
+ const revisionB = formRevisionB.revision;
3491
+ if (revisionA === "Pending" && revisionB === "Pending") {
3492
+ return formRevisionA.submitted_at < formRevisionB.submitted_at ? -1 : 1;
3493
+ } else if (revisionA === "Pending") {
3494
+ return 1;
3495
+ } else if (revisionB === "Pending") {
3496
+ return -1;
3497
+ } else {
3498
+ return revisionA < revisionB ? -1 : 1;
3493
3499
  }
3494
- }
3495
- function getLatestRevisionFromCache(formId2) {
3496
- return LATEST_REVISION_CACHE[formId2];
3497
- }
3498
- const initialState$3 = {
3499
- userForms: {},
3500
- revisions: {},
3501
- submissions: {},
3502
- submissionAttachments: {},
3503
- revisionAttachments: {}
3504
- };
3505
- const userFormSlice = createSlice({
3506
- name: "userForms",
3507
- initialState: initialState$3,
3508
- extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$3)),
3500
+ };
3501
+ const initialState$6 = {
3502
+ formRevisions: {},
3503
+ attachments: {}
3504
+ };
3505
+ const formRevisionsSlice = createSlice({
3506
+ name: "formRevisions",
3507
+ initialState: initialState$6,
3508
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$6)),
3509
3509
  reducers: {
3510
- setUserForms: (state, action) => {
3511
- state.userForms = {};
3512
- action.payload.forEach((userForm) => {
3513
- state.userForms[userForm.offline_id] = userForm;
3514
- });
3510
+ // revision related actions
3511
+ setFormRevision: (state, action) => {
3512
+ state.formRevisions[action.payload.offline_id] = action.payload;
3515
3513
  },
3516
- addUserForm: (state, action) => {
3517
- state.userForms[action.payload.offline_id] = action.payload;
3518
- },
3519
- addUserForms: (state, action) => {
3520
- action.payload.forEach((userForm) => {
3521
- state.userForms[userForm.offline_id] = userForm;
3522
- });
3523
- },
3524
- addUserFormRevisions: (state, action) => {
3525
- action.payload.forEach((userFormRevision) => {
3526
- state.revisions[userFormRevision.offline_id] = userFormRevision;
3527
- considerCachingRevision(userFormRevision);
3528
- });
3529
- },
3530
- addUserFormRevision: (state, action) => {
3531
- state.revisions[action.payload.offline_id] = action.payload;
3532
- considerCachingRevision(action.payload);
3514
+ setFormRevisions: (state, action) => {
3515
+ state.formRevisions = {};
3516
+ for (const revision of action.payload) {
3517
+ state.formRevisions[revision.offline_id] = revision;
3518
+ }
3533
3519
  },
3534
- deleteUserFormRevision: (state, action) => {
3535
- delete state.revisions[action.payload];
3536
- delete LATEST_REVISION_CACHE[action.payload];
3520
+ addFormRevision: (state, action) => {
3521
+ if (state.formRevisions[action.payload.offline_id] !== void 0) {
3522
+ throw new Error(`Revision with offline_id ${action.payload.offline_id} already exists`);
3523
+ }
3524
+ state.formRevisions[action.payload.offline_id] = action.payload;
3537
3525
  },
3538
- deleteUserFormRevisions: (state, action) => {
3526
+ addFormRevisions: (state, action) => {
3539
3527
  for (const userFormRevision of action.payload) {
3540
- delete state.revisions[userFormRevision.offline_id];
3541
- delete LATEST_REVISION_CACHE[userFormRevision.offline_id];
3528
+ if (state.formRevisions[userFormRevision.offline_id] !== void 0) {
3529
+ throw new Error(`Revision with offline_id ${userFormRevision.offline_id} already exists`);
3530
+ }
3542
3531
  }
3543
- },
3544
- updateOrCreateUserFormSubmission: (state, action) => {
3545
- state.submissions[action.payload.offline_id] = action.payload;
3546
- },
3547
- addUserFormSubmissionAttachment: (state, action) => {
3548
- const submissionId = action.payload.submission;
3549
- const submissionAttachments = state.submissionAttachments[submissionId];
3550
- if (submissionAttachments) {
3551
- submissionAttachments.push(action.payload);
3552
- } else {
3553
- state.submissionAttachments[submissionId] = [action.payload];
3532
+ for (const userFormRevision of action.payload) {
3533
+ state.formRevisions[userFormRevision.offline_id] = userFormRevision;
3554
3534
  }
3555
3535
  },
3556
- addUserFormRevisionAttachment: (state, action) => {
3557
- const revisionId = action.payload.revision;
3558
- const revisionAttachments = state.revisionAttachments[revisionId];
3559
- if (revisionAttachments) {
3560
- revisionAttachments.push(action.payload);
3561
- } else {
3562
- state.revisionAttachments[revisionId] = [action.payload];
3536
+ // UserFormRevisions do not get updated
3537
+ deleteFormRevision: (state, action) => {
3538
+ if (state.formRevisions[action.payload] === void 0) {
3539
+ throw new Error(`Revision with offline_id ${action.payload} does not exist`);
3563
3540
  }
3541
+ delete state.formRevisions[action.payload];
3564
3542
  },
3565
- setUserFormSubmissionAttachments: (state, action) => {
3566
- state.submissionAttachments = {};
3567
- for (const attachment of action.payload) {
3568
- const submissionId = attachment.submission;
3569
- const submissionAttachments = state.submissionAttachments[submissionId];
3570
- if (submissionAttachments) {
3571
- submissionAttachments.push(attachment);
3572
- } else {
3573
- state.submissionAttachments[submissionId] = [attachment];
3543
+ deleteFormRevisions: (state, action) => {
3544
+ for (const offlineId of action.payload) {
3545
+ if (state.formRevisions[offlineId] === void 0) {
3546
+ throw new Error(`Revision with offline_id ${offlineId} does not exist`);
3574
3547
  }
3575
3548
  }
3549
+ for (const offlineId of action.payload) {
3550
+ delete state.formRevisions[offlineId];
3551
+ }
3576
3552
  },
3577
- setUserFormRevisionAttachments: (state, action) => {
3578
- state.revisionAttachments = {};
3553
+ // attachment related actions
3554
+ setFormRevisionAttachments: (state, action) => {
3555
+ state.attachments = {};
3579
3556
  for (const attachment of action.payload) {
3580
- const revisionId = attachment.revision;
3581
- const revisionAttachments = state.revisionAttachments[revisionId];
3582
- if (revisionAttachments) {
3583
- revisionAttachments.push(attachment);
3584
- } else {
3585
- state.revisionAttachments[revisionId] = [attachment];
3586
- }
3557
+ state.attachments[attachment.offline_id] = attachment;
3587
3558
  }
3588
3559
  },
3589
- deleteUserFormSubmission: (state, action) => {
3590
- delete state.submissions[action.payload];
3560
+ addFormRevisionAttachment: (state, action) => {
3561
+ if (state.attachments[action.payload.offline_id] !== void 0) {
3562
+ throw new Error(`Attachment with offline_id ${action.payload.offline_id} already exists`);
3563
+ }
3564
+ state.attachments[action.payload.offline_id] = action.payload;
3591
3565
  },
3592
- deleteUserFormSubmissions: (state, action) => {
3593
- for (const userFormSubmission of action.payload) {
3594
- delete state.submissions[userFormSubmission.offline_id];
3566
+ addFormRevisionAttachments: (state, action) => {
3567
+ for (const attachment of action.payload) {
3568
+ if (state.attachments[attachment.offline_id] !== void 0) {
3569
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} already exists`);
3570
+ }
3571
+ }
3572
+ for (const attachment of action.payload) {
3573
+ state.attachments[attachment.offline_id] = attachment;
3595
3574
  }
3596
3575
  },
3597
- addUserFormSubmissions: (state, action) => {
3598
- for (const submission of action.payload) {
3599
- state.submissions[submission.offline_id] = submission;
3576
+ deleteFormRevisionAttachment: (state, action) => {
3577
+ if (state.attachments[action.payload] === void 0) {
3578
+ throw new Error(`Attachment with offline_id ${action.payload} does not exist`);
3600
3579
  }
3580
+ delete state.attachments[action.payload];
3601
3581
  },
3602
- setUserFormSubmissions: (state, action) => {
3603
- state.submissions = {};
3604
- action.payload.forEach((submission) => {
3605
- state.submissions[submission.offline_id] = submission;
3582
+ deleteFormRevisionAttachments: (state, action) => {
3583
+ for (const offlineId of action.payload) {
3584
+ if (state.attachments[offlineId] === void 0) {
3585
+ throw new Error(`Attachment with offline_id ${offlineId} does not exist`);
3586
+ }
3587
+ }
3588
+ for (const offlineId of action.payload) {
3589
+ delete state.attachments[offlineId];
3590
+ }
3591
+ }
3592
+ }
3593
+ });
3594
+ const {
3595
+ setFormRevision,
3596
+ setFormRevisions,
3597
+ addFormRevision,
3598
+ addFormRevisions,
3599
+ deleteFormRevision,
3600
+ deleteFormRevisions,
3601
+ setFormRevisionAttachments,
3602
+ addFormRevisionAttachment,
3603
+ addFormRevisionAttachments,
3604
+ deleteFormRevisionAttachment,
3605
+ deleteFormRevisionAttachments
3606
+ } = formRevisionsSlice.actions;
3607
+ const selectFormRevisionMapping = (state) => state.formRevisionReducer.formRevisions;
3608
+ const selectFormRevisions = createSelector(
3609
+ [selectFormRevisionMapping],
3610
+ (formRevisions) => Object.values(formRevisions)
3611
+ );
3612
+ const selectFormRevision = (formRevisionId) => (state) => {
3613
+ return state.formRevisionReducer.formRevisions[formRevisionId];
3614
+ };
3615
+ const _selectLatestFormRevision = (formRevisions, formId2) => {
3616
+ let ret = null;
3617
+ for (const candidate of Object.values(formRevisions)) {
3618
+ if (candidate.form === formId2 && (!ret || ret.revision < candidate.revision)) {
3619
+ ret = candidate;
3620
+ }
3621
+ }
3622
+ if (!ret) {
3623
+ throw new Error("No form revision found for form " + formId2);
3624
+ }
3625
+ return ret;
3626
+ };
3627
+ const selectLatestFormRevisionOfForm = restructureCreateSelectorWithArgs(
3628
+ createSelector([selectFormRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3629
+ return revisions.filter((revision) => revision.form === formId2).sort(formRevisionSortFn).pop();
3630
+ })
3631
+ );
3632
+ const selectFormRevisionsOfForm = restructureCreateSelectorWithArgs(
3633
+ createSelector([selectFormRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3634
+ return revisions.filter((revision) => {
3635
+ return revision.form === formId2;
3636
+ });
3637
+ })
3638
+ );
3639
+ const selectLatestFormRevisionsOfComponentTypes = restructureCreateSelectorWithArgs(
3640
+ createSelector(
3641
+ [
3642
+ (state) => state.formReducer.forms,
3643
+ selectFormRevisionMapping,
3644
+ (_state, componentTypeIds) => componentTypeIds
3645
+ ],
3646
+ (userForms, revisions, componentTypeIds) => {
3647
+ const componentTypeIdsSet = new Set(componentTypeIds);
3648
+ const formsOfComponentTypes = {};
3649
+ const ret = {};
3650
+ for (const form of Object.values(userForms)) {
3651
+ if (form.component_type && componentTypeIdsSet.has(form.component_type)) {
3652
+ formsOfComponentTypes[form.offline_id] = form;
3653
+ }
3654
+ }
3655
+ for (const revision of Object.values(revisions)) {
3656
+ const form = formsOfComponentTypes[revision.form];
3657
+ if (!form || !form.component_type || ret[form.component_type] && formRevisionSortFn(ret[form.component_type], revision) > 0)
3658
+ continue;
3659
+ ret[form.component_type] = revision;
3660
+ }
3661
+ return ret;
3662
+ }
3663
+ )
3664
+ );
3665
+ const selectLatestFormRevisionByForm = createSelector([selectFormRevisionMapping], (revisions) => {
3666
+ const latestRevisions = {};
3667
+ for (const revision of Object.values(revisions)) {
3668
+ const formId2 = revision.form;
3669
+ const currentLatestRevision = latestRevisions[formId2];
3670
+ if (!currentLatestRevision || currentLatestRevision.revision < revision.revision) {
3671
+ latestRevisions[formId2] = revision;
3672
+ }
3673
+ }
3674
+ return latestRevisions;
3675
+ });
3676
+ const selectUserFormRevisionAttachmentsMapping = (state) => {
3677
+ return state.formRevisionReducer.attachments;
3678
+ };
3679
+ const selectAttachmentsOfFormRevision = restructureCreateSelectorWithArgs(
3680
+ createSelector(
3681
+ [selectUserFormRevisionAttachmentsMapping, (_state, revisionId) => revisionId],
3682
+ (attachments, revisionId) => {
3683
+ return Object.values(attachments).filter((attachment) => attachment.revision === revisionId);
3684
+ }
3685
+ )
3686
+ );
3687
+ const formRevisionReducer = formRevisionsSlice.reducer;
3688
+ const initialState$5 = {
3689
+ forms: {}
3690
+ };
3691
+ const formSlice = createSlice({
3692
+ name: "forms",
3693
+ initialState: initialState$5,
3694
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$5)),
3695
+ reducers: {
3696
+ setForms: (state, action) => {
3697
+ state.forms = {};
3698
+ action.payload.forEach((userForm) => {
3699
+ state.forms[userForm.offline_id] = userForm;
3606
3700
  });
3607
3701
  },
3702
+ addForm: (state, action) => {
3703
+ state.forms[action.payload.offline_id] = action.payload;
3704
+ },
3705
+ addForms: (state, action) => {
3706
+ for (const userForm of action.payload) {
3707
+ state.forms[userForm.offline_id] = userForm;
3708
+ }
3709
+ },
3608
3710
  favoriteForm: (state, action) => {
3609
3711
  const { formId: formId2 } = action.payload;
3610
- const form = state.userForms[formId2];
3712
+ const form = state.forms[formId2];
3611
3713
  if (!form) {
3612
3714
  throw new Error("No form exists with the id " + formId2);
3613
3715
  }
@@ -3615,48 +3717,23 @@ const userFormSlice = createSlice({
3615
3717
  },
3616
3718
  unfavoriteForm: (state, action) => {
3617
3719
  const { formId: formId2 } = action.payload;
3618
- const form = state.userForms[formId2];
3720
+ const form = state.forms[formId2];
3619
3721
  if (!form) {
3620
3722
  throw new Error("No form exists with the id " + formId2);
3621
3723
  }
3622
3724
  form.favorite = false;
3623
3725
  },
3624
- deleteUserForm: (state, action) => {
3625
- delete state.userForms[action.payload];
3726
+ deleteForm: (state, action) => {
3727
+ delete state.forms[action.payload];
3626
3728
  }
3627
3729
  }
3628
3730
  });
3629
- const {
3630
- addUserForm,
3631
- addUserForms,
3632
- addUserFormRevisions,
3633
- updateOrCreateUserFormSubmission,
3634
- addUserFormSubmissions,
3635
- deleteUserFormSubmission,
3636
- deleteUserFormSubmissions,
3637
- favoriteForm,
3638
- unfavoriteForm,
3639
- deleteUserForm,
3640
- deleteUserFormRevision,
3641
- deleteUserFormRevisions,
3642
- setUserFormSubmissions,
3643
- addUserFormRevision,
3644
- addUserFormSubmissionAttachment,
3645
- addUserFormRevisionAttachment,
3646
- setUserFormSubmissionAttachments,
3647
- setUserFormRevisionAttachments
3648
- } = userFormSlice.actions;
3649
- const selectSubmissionAttachments = (submissionId) => (state) => {
3650
- return state.userFormReducer.submissionAttachments[submissionId] || [];
3651
- };
3652
- const selectRevisionAttachments = (revisionId) => (state) => {
3653
- return state.userFormReducer.revisionAttachments[revisionId] || [];
3654
- };
3655
- const selectFilteredUserForms = restructureCreateSelectorWithArgs(
3731
+ const { setForms, addForm, addForms, favoriteForm, unfavoriteForm, deleteForm } = formSlice.actions;
3732
+ const selectFilteredForms = restructureCreateSelectorWithArgs(
3656
3733
  createSelector(
3657
3734
  [
3658
- (state) => state.userFormReducer.userForms,
3659
- (state) => state.userFormReducer.revisions,
3735
+ (state) => state.formReducer.forms,
3736
+ (state) => state.formRevisionReducer.formRevisions,
3660
3737
  (_state, search) => search
3661
3738
  ],
3662
3739
  (userForms, revisions, search) => {
@@ -3690,63 +3767,188 @@ const selectFilteredUserForms = restructureCreateSelectorWithArgs(
3690
3767
  { memoizeOptions: { equalityCheck: shallowEqual$1 } }
3691
3768
  )
3692
3769
  );
3693
- const selectFormRevision = (revisionId) => (state) => {
3694
- return state.userFormReducer.revisions[revisionId];
3770
+ const selectForm = (formId2) => (state) => {
3771
+ return state.formReducer.forms[formId2];
3695
3772
  };
3696
- const _selectLatestFormRevision = (revisions, formId2) => {
3697
- let ret = null;
3698
- for (const candidate of Object.values(revisions)) {
3699
- if (candidate.form === formId2 && (!ret || ret.revision < candidate.revision)) {
3700
- ret = candidate;
3701
- }
3702
- }
3703
- if (!ret) {
3704
- throw new Error("No revision found for form " + formId2);
3705
- }
3706
- return ret;
3773
+ const selectFormMapping = (state) => {
3774
+ return state.formReducer.forms;
3707
3775
  };
3708
- const selectLatestFormRevision = restructureCreateSelectorWithArgs(
3776
+ const selectFormOfComponentType = restructureCreateSelectorWithArgs(
3709
3777
  createSelector(
3710
- [(state) => state.userFormReducer.revisions, (_state, formId2) => formId2],
3711
- (revisions, formId2) => {
3712
- if (!formId2) {
3713
- throw new Error("formId is required");
3714
- }
3715
- return _selectLatestFormRevision(revisions, formId2);
3778
+ [selectFormMapping, (_state, componentTypeId) => componentTypeId],
3779
+ (userForms, componentTypeId) => {
3780
+ return Object.values(userForms).find((userForm) => userForm.component_type === componentTypeId);
3716
3781
  }
3717
3782
  )
3718
3783
  );
3719
- const selectUserForm = (formId2) => (state) => {
3720
- return state.userFormReducer.userForms[formId2];
3721
- };
3722
- const selectSubmissionMapping = (state) => state.userFormReducer.submissions;
3723
- const selectUserFormSubmission = (submissionId) => (state) => {
3724
- return state.userFormReducer.submissions[submissionId];
3725
- };
3726
- const selectSubmissions = createSelector([selectSubmissionMapping], (submissions) => Object.values(submissions));
3727
- const selectRevisionMapping = (state) => state.userFormReducer.revisions;
3728
- const selectRevisions = createSelector([selectRevisionMapping], (revisions) => Object.values(revisions));
3729
- const selectRevisionsForForm = restructureCreateSelectorWithArgs(
3730
- createSelector([selectRevisions, (_state, formId2) => formId2], (revisions, formId2) => {
3731
- return revisions.filter((revision) => {
3732
- return revision.form === formId2;
3733
- });
3734
- })
3784
+ const selectFormsCount = createSelector([selectFormMapping], (userForms) => {
3785
+ return Object.keys(userForms).length;
3786
+ });
3787
+ const selectGeneralFormCount = createSelector([selectFormMapping], (userForms) => {
3788
+ return Object.values(userForms).filter((form) => !form.component_type).length;
3789
+ });
3790
+ const formReducer = formSlice.reducer;
3791
+ const initialState$4 = {
3792
+ formSubmissions: {},
3793
+ attachments: {}
3794
+ };
3795
+ const formSubmissionSlice = createSlice({
3796
+ name: "formSubmissions",
3797
+ initialState: initialState$4,
3798
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$4)),
3799
+ reducers: {
3800
+ setFormSubmission: (state, action) => {
3801
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3802
+ },
3803
+ setFormSubmissions: (state, action) => {
3804
+ state.formSubmissions = {};
3805
+ for (const submission of action.payload) {
3806
+ state.formSubmissions[submission.offline_id] = submission;
3807
+ }
3808
+ },
3809
+ addFormSubmission: (state, action) => {
3810
+ if (action.payload.offline_id in state.formSubmissions) {
3811
+ throw new Error(`Submission with offline_id ${action.payload.offline_id} already exists`);
3812
+ }
3813
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3814
+ },
3815
+ addFormSubmissions: (state, action) => {
3816
+ for (const submission of action.payload) {
3817
+ if (state.formSubmissions[submission.offline_id] !== void 0) {
3818
+ throw new Error(`Submission with offline_id ${submission.offline_id} already exists`);
3819
+ }
3820
+ }
3821
+ for (const submission of action.payload) {
3822
+ state.formSubmissions[submission.offline_id] = submission;
3823
+ }
3824
+ },
3825
+ updateFormSubmission: (state, action) => {
3826
+ if (state.formSubmissions[action.payload.offline_id] === void 0) {
3827
+ throw new Error(`Submission with offline_id ${action.payload.offline_id} does not exist`);
3828
+ }
3829
+ state.formSubmissions[action.payload.offline_id] = action.payload;
3830
+ },
3831
+ updateFormSubmissions: (state, action) => {
3832
+ for (const submission of action.payload) {
3833
+ if (state.formSubmissions[submission.offline_id] === void 0) {
3834
+ throw new Error(`Submission with offline_id ${submission.offline_id} does not exist`);
3835
+ }
3836
+ }
3837
+ for (const submission of action.payload) {
3838
+ state.formSubmissions[submission.offline_id] = submission;
3839
+ }
3840
+ },
3841
+ deleteFormSubmission: (state, action) => {
3842
+ if (state.formSubmissions[action.payload] === void 0) {
3843
+ throw new Error(`Submission with offline_id ${action.payload} does not exist`);
3844
+ }
3845
+ delete state.formSubmissions[action.payload];
3846
+ },
3847
+ deleteFormSubmissions: (state, action) => {
3848
+ for (const offlineId of action.payload) {
3849
+ if (state.formSubmissions[offlineId] === void 0) {
3850
+ throw new Error(`Submission with offline_id ${offlineId} does not exist`);
3851
+ }
3852
+ delete state.formSubmissions[offlineId];
3853
+ }
3854
+ for (const offlineId of action.payload) {
3855
+ delete state.formSubmissions[offlineId];
3856
+ }
3857
+ },
3858
+ // Attachments
3859
+ addFormSubmissionAttachment: (state, action) => {
3860
+ if (state.attachments[action.payload.offline_id] !== void 0) {
3861
+ throw new Error(`Attachment with offline_id ${action.payload.offline_id} already exists`);
3862
+ }
3863
+ state.attachments[action.payload.offline_id] = action.payload;
3864
+ },
3865
+ addFormSubmissionAttachments: (state, action) => {
3866
+ for (const attachment of action.payload) {
3867
+ if (state.attachments[attachment.offline_id] !== void 0) {
3868
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} already exists`);
3869
+ }
3870
+ }
3871
+ for (const attachment of action.payload) {
3872
+ state.attachments[attachment.offline_id] = attachment;
3873
+ }
3874
+ },
3875
+ // We only need a multi set for attachments because they are not updated, only added and deleted
3876
+ setFormSubmissionAttachments: (state, action) => {
3877
+ state.attachments = {};
3878
+ for (const attachment of action.payload) {
3879
+ state.attachments[attachment.offline_id] = attachment;
3880
+ }
3881
+ },
3882
+ updateFormSubmissionAttachments: (state, action) => {
3883
+ for (const attachment of action.payload) {
3884
+ if (state.attachments[attachment.offline_id] === void 0) {
3885
+ throw new Error(`Attachment with offline_id ${attachment.offline_id} does not exist`);
3886
+ }
3887
+ }
3888
+ for (const attachment of action.payload) {
3889
+ state.attachments[attachment.offline_id] = attachment;
3890
+ }
3891
+ },
3892
+ // The delete actions for UserFormSubmissionAttachments are not used in the app, but are included for completeness
3893
+ // Could be used if editing a submission is ever supported, will be applicable for supporting tip tap content in submissions
3894
+ deleteFormSubmissionAttachment: (state, action) => {
3895
+ if (state.attachments[action.payload] === void 0) {
3896
+ throw new Error(`Attachment with offline_id ${action.payload} does not exist`);
3897
+ }
3898
+ delete state.attachments[action.payload];
3899
+ },
3900
+ deleteFormSubmissionAttachments: (state, action) => {
3901
+ for (const offlineId of action.payload) {
3902
+ if (state.attachments[offlineId] === void 0) {
3903
+ throw new Error(`Attachment with offline_id ${offlineId} does not exist`);
3904
+ }
3905
+ delete state.attachments[offlineId];
3906
+ }
3907
+ }
3908
+ }
3909
+ });
3910
+ const {
3911
+ setFormSubmission,
3912
+ setFormSubmissions,
3913
+ addFormSubmission,
3914
+ addFormSubmissions,
3915
+ updateFormSubmission,
3916
+ updateFormSubmissions,
3917
+ deleteFormSubmission,
3918
+ deleteFormSubmissions,
3919
+ addFormSubmissionAttachment,
3920
+ addFormSubmissionAttachments,
3921
+ setFormSubmissionAttachments,
3922
+ updateFormSubmissionAttachments,
3923
+ deleteFormSubmissionAttachment,
3924
+ deleteFormSubmissionAttachments
3925
+ } = formSubmissionSlice.actions;
3926
+ const selectFormSubmissionsMapping = (state) => {
3927
+ return state.formSubmissionReducer.formSubmissions;
3928
+ };
3929
+ const selectFormSubmissions = createSelector(
3930
+ [selectFormSubmissionsMapping],
3931
+ (submissions) => {
3932
+ return Object.values(submissions);
3933
+ }
3735
3934
  );
3736
- const selectSubmissionsForForm = restructureCreateSelectorWithArgs(
3935
+ const selectFormSubmission = (submissionId) => (state) => {
3936
+ return state.formSubmissionReducer.formSubmissions[submissionId];
3937
+ };
3938
+ const selectFormSubmissionsOfForm = restructureCreateSelectorWithArgs(
3737
3939
  createSelector(
3738
- [selectSubmissions, selectRevisionMapping, (_state, formId2) => formId2],
3940
+ [selectFormSubmissions, selectFormRevisionMapping, (_state, formId2) => formId2],
3739
3941
  (submissions, revisionMapping, formId2) => {
3740
- return Object.values(submissions).filter((submission) => {
3942
+ return submissions.filter((submission) => {
3741
3943
  const revision = revisionMapping[submission.form_revision];
3742
3944
  return (revision == null ? void 0 : revision.form) === formId2;
3743
3945
  });
3744
3946
  }
3745
3947
  )
3746
3948
  );
3747
- const selectSubmissionsForIssue = restructureCreateSelectorWithArgs(
3949
+ const selectFormSubmissionsOfIssue = restructureCreateSelectorWithArgs(
3748
3950
  createSelector(
3749
- [(state) => state.userFormReducer.submissions, (_state, issueId) => issueId],
3951
+ [selectFormSubmissions, (_state, issueId) => issueId],
3750
3952
  (submissions, issueId) => {
3751
3953
  return Object.values(submissions).filter((submission) => {
3752
3954
  return submission.issue === issueId;
@@ -3754,9 +3956,9 @@ const selectSubmissionsForIssue = restructureCreateSelectorWithArgs(
3754
3956
  }
3755
3957
  )
3756
3958
  );
3757
- const selectSubmissionsForComponent = restructureCreateSelectorWithArgs(
3959
+ const selectFormSubmissionsOfComponent = restructureCreateSelectorWithArgs(
3758
3960
  createSelector(
3759
- [selectSubmissions, (_state, componentId) => componentId],
3961
+ [selectFormSubmissions, (_state, componentId) => componentId],
3760
3962
  (submissions, componentId) => {
3761
3963
  return submissions.filter((submission) => {
3762
3964
  return submission.component === componentId;
@@ -3764,8 +3966,8 @@ const selectSubmissionsForComponent = restructureCreateSelectorWithArgs(
3764
3966
  }
3765
3967
  )
3766
3968
  );
3767
- const selectComponentSubmissionMapping = createSelector(
3768
- [selectSubmissionMapping, selectComponentsMapping],
3969
+ const selectFormSubmissionsByComponents = createSelector(
3970
+ [selectFormSubmissionsMapping, selectComponentsMapping],
3769
3971
  (submissions, components) => {
3770
3972
  var _a2;
3771
3973
  const componentSubmissionMapping = {};
@@ -3781,60 +3983,24 @@ const selectComponentSubmissionMapping = createSelector(
3781
3983
  return componentSubmissionMapping;
3782
3984
  }
3783
3985
  );
3784
- const selectUserFormMapping = (state) => {
3785
- return state.userFormReducer.userForms;
3786
- };
3787
- const selectComponentTypeForm = restructureCreateSelectorWithArgs(
3788
- createSelector(
3789
- [selectUserFormMapping, (_state, componentTypeId) => componentTypeId],
3790
- (userForms, componentTypeId) => {
3791
- return Object.values(userForms).find((userForm) => userForm.component_type === componentTypeId);
3792
- }
3793
- )
3794
- );
3795
- const selectLatestRevisionsFromComponentTypeIds = restructureCreateSelectorWithArgs(
3796
- createSelector(
3797
- [
3798
- selectUserFormMapping,
3799
- selectRevisionMapping,
3800
- (_state, componentTypeIds) => componentTypeIds
3801
- ],
3802
- (userForms, revisions, componentTypeIds) => {
3803
- const componentTypeIdsSet = new Set(componentTypeIds);
3804
- const ret = {};
3805
- for (const form of Object.values(userForms)) {
3806
- if (form.component_type && componentTypeIdsSet.has(form.component_type)) {
3807
- ret[form.component_type] = _selectLatestFormRevision(revisions, form.offline_id);
3808
- }
3809
- }
3810
- return ret;
3811
- }
3812
- )
3813
- );
3814
- const selectLatestRevisionByFormId = createSelector([selectRevisionMapping], (revisions) => {
3815
- const latestRevisions = {};
3816
- for (const revision of Object.values(revisions)) {
3817
- const formId2 = revision.form;
3818
- const currentLatestRevision = latestRevisions[formId2];
3819
- if (!currentLatestRevision || currentLatestRevision.revision < revision.revision) {
3820
- latestRevisions[formId2] = revision;
3986
+ const selectFormSubmissionAttachmentsMapping = (state) => {
3987
+ return state.formSubmissionReducer.attachments;
3988
+ };
3989
+ const selectAttachmentsOfFormSubmission = restructureCreateSelectorWithArgs(
3990
+ createSelector(
3991
+ [selectFormSubmissionAttachmentsMapping, (_state, submissionId) => submissionId],
3992
+ (attachmentsMapping, submissionId) => {
3993
+ return Object.values(attachmentsMapping).filter((attachment) => attachment.submission === submissionId);
3821
3994
  }
3822
- }
3823
- return latestRevisions;
3824
- });
3825
- const selectNumberOfUserForms = createSelector([selectUserFormMapping], (userForms) => {
3826
- return Object.keys(userForms).length;
3827
- });
3828
- const selectNumberOfGeneralUserForms = createSelector([selectUserFormMapping], (userForms) => {
3829
- return Object.values(userForms).filter((form) => !form.component_type).length;
3830
- });
3831
- const userFormReducer = userFormSlice.reducer;
3832
- const initialState$2 = {
3995
+ )
3996
+ );
3997
+ const formSubmissionReducer = formSubmissionSlice.reducer;
3998
+ const initialState$3 = {
3833
3999
  emailDomains: {}
3834
4000
  };
3835
4001
  const emailDomainsSlice = createSlice({
3836
4002
  name: "emailDomains",
3837
- initialState: initialState$2,
4003
+ initialState: initialState$3,
3838
4004
  reducers: {
3839
4005
  setEmailDomains: (state, action) => {
3840
4006
  const emailDomains = {};
@@ -3861,15 +4027,15 @@ const selectSortedEmailDomains = (state) => Object.values(state.emailDomainsRedu
3861
4027
  (ed1, ed2) => ed1.domain.localeCompare(ed2.domain)
3862
4028
  );
3863
4029
  const emailDomainsReducer = emailDomainsSlice.reducer;
3864
- const initialState$1 = {
4030
+ const initialState$2 = {
3865
4031
  documents: {},
3866
4032
  attachments: {}
3867
4033
  };
3868
4034
  const documentSlice = createSlice({
3869
4035
  name: "documents",
3870
- initialState: initialState$1,
4036
+ initialState: initialState$2,
3871
4037
  extraReducers: (builder) => builder.addCase("RESET", (state) => {
3872
- Object.assign(state, initialState$1);
4038
+ Object.assign(state, initialState$2);
3873
4039
  }),
3874
4040
  reducers: {
3875
4041
  setDocuments: (state, action) => {
@@ -3906,9 +4072,18 @@ const documentSlice = createSlice({
3906
4072
  }
3907
4073
  }
3908
4074
  for (const document2 of action.payload) {
4075
+ const existingDocument = state.documents[document2.offline_id];
4076
+ if (document2.organization !== void 0 && document2.organization !== existingDocument.organization) {
4077
+ throw new Error("organization cannot be updated");
4078
+ }
4079
+ if (document2.project !== void 0 && document2.project !== existingDocument.project) {
4080
+ throw new Error("project cannot be updated");
4081
+ }
3909
4082
  state.documents[document2.offline_id] = {
3910
- ...state.documents[document2.offline_id],
4083
+ ...existingDocument,
3911
4084
  ...document2
4085
+ // Without the cast, TypeScript doesn't realize that we have guaranteed that the document doesn't
4086
+ // have both a project and an organization.
3912
4087
  };
3913
4088
  }
3914
4089
  },
@@ -4085,6 +4260,62 @@ const selectAttachmentsOfDocumentByType = restructureCreateSelectorWithArgs(
4085
4260
  )
4086
4261
  );
4087
4262
  const documentsReducer = documentSlice.reducer;
4263
+ const initialState$1 = {
4264
+ teams: {}
4265
+ };
4266
+ const teamSlice = createSlice({
4267
+ name: "teams",
4268
+ initialState: initialState$1,
4269
+ extraReducers: (builder) => builder.addCase("RESET", (state) => Object.assign(state, initialState$1)),
4270
+ reducers: {
4271
+ setTeam: (state, action) => {
4272
+ state.teams[action.payload.offline_id] = action.payload;
4273
+ },
4274
+ setTeams: (state, action) => {
4275
+ state.teams = {};
4276
+ for (const team of action.payload) {
4277
+ state.teams[team.offline_id] = team;
4278
+ }
4279
+ },
4280
+ addTeam: (state, action) => {
4281
+ if (state.teams[action.payload.offline_id]) {
4282
+ throw new Error(`Team with offline_id ${action.payload.offline_id} already exists`);
4283
+ }
4284
+ state.teams[action.payload.offline_id] = action.payload;
4285
+ },
4286
+ updateTeam: (state, action) => {
4287
+ if (!state.teams[action.payload.offline_id]) {
4288
+ throw new Error(`Team with offline_id ${action.payload.offline_id} does not exist`);
4289
+ }
4290
+ state.teams[action.payload.offline_id] = action.payload;
4291
+ },
4292
+ deleteTeam: (state, action) => {
4293
+ delete state.teams[action.payload];
4294
+ }
4295
+ }
4296
+ });
4297
+ const { setTeam, setTeams, addTeam, updateTeam, deleteTeam } = teamSlice.actions;
4298
+ const selectTeamsMapping = (state) => state.teamReducer.teams;
4299
+ const selectTeams = createSelector([selectTeamsMapping], (teams) => {
4300
+ return Object.values(teams);
4301
+ });
4302
+ const selectTeam = (teamId) => (state) => {
4303
+ return state.teamReducer.teams[teamId];
4304
+ };
4305
+ const selectTeamsOfOrganization = restructureCreateSelectorWithArgs(
4306
+ createSelector(
4307
+ [selectTeams, (_state, organizationId) => organizationId],
4308
+ (teams, organizationId) => {
4309
+ return teams.filter((team) => team.organization === organizationId);
4310
+ }
4311
+ )
4312
+ );
4313
+ const selectTeamsOfUser = restructureCreateSelectorWithArgs(
4314
+ createSelector([selectTeams, (_state, userId) => userId], (teams, userId) => {
4315
+ return teams.filter((team) => team.members.includes(userId));
4316
+ })
4317
+ );
4318
+ const teamReducer = teamSlice.reducer;
4088
4319
  const initialState = {
4089
4320
  version: 0
4090
4321
  };
@@ -4126,12 +4357,15 @@ const overmapReducers = {
4126
4357
  projectFileReducer,
4127
4358
  rehydratedReducer,
4128
4359
  settingReducer,
4129
- userFormReducer,
4360
+ formReducer,
4361
+ formRevisionReducer,
4362
+ formSubmissionReducer,
4130
4363
  userReducer,
4131
4364
  workspaceReducer,
4132
4365
  emailDomainsReducer,
4133
4366
  licenseReducer,
4134
- documentsReducer
4367
+ documentsReducer,
4368
+ teamReducer
4135
4369
  };
4136
4370
  const overmapReducer = combineReducers(overmapReducers);
4137
4371
  const resetStore = "RESET";
@@ -4179,9 +4413,7 @@ function handleWorkspaceRemoval(draft, action) {
4179
4413
  throw new Error(`Failed to update index_workspace of issue ${issue.offline_id} to main workspace`);
4180
4414
  }
4181
4415
  }
4182
- const indexedForms = Object.values(draft.userFormReducer.userForms).filter(
4183
- (form) => form.index_workspace === workspaceId
4184
- );
4416
+ const indexedForms = Object.values(draft.formReducer.forms).filter((form) => form.index_workspace === workspaceId);
4185
4417
  for (const form of indexedForms) {
4186
4418
  form.index_workspace = mainWorkspace.offline_id;
4187
4419
  }
@@ -6372,6 +6604,7 @@ class MainService extends BaseApiService {
6372
6604
  }
6373
6605
  if (currentOrgId) {
6374
6606
  await this.client.organizations.fetchInitialOrganizationData(currentOrgId, false);
6607
+ void this.client.teams.refreshStore();
6375
6608
  }
6376
6609
  if (!isProjectIdValid) {
6377
6610
  if (validProjects.length !== 0) {
@@ -6809,7 +7042,7 @@ class UserFormService extends BaseApiService {
6809
7042
  ...revisionAttachmentPayload,
6810
7043
  file: URL.createObjectURL(image)
6811
7044
  };
6812
- store.dispatch(addUserFormRevisionAttachment(offlinePayload));
7045
+ store.dispatch(addFormRevisionAttachment(offlinePayload));
6813
7046
  return attach;
6814
7047
  });
6815
7048
  });
@@ -6824,13 +7057,14 @@ class UserFormService extends BaseApiService {
6824
7057
  };
6825
7058
  const currentUser = state.userReducer.currentUser;
6826
7059
  const activeWorkspaceId = state.workspaceReducer.activeWorkspaceId;
7060
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
6827
7061
  const offlineFormPayload = offline({});
6828
- const offlineRevisionPayload = offline(initialRevision);
7062
+ const offlineRevisionPayload = offline({ ...initialRevision, submitted_at: submittedAt });
6829
7063
  const retForm = {
6830
7064
  ...offlineFormPayload,
6831
7065
  index_workspace: activeWorkspaceId,
6832
7066
  favorite: true,
6833
- submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
7067
+ submitted_at: submittedAt,
6834
7068
  created_by: currentUser.id,
6835
7069
  ...componentTypeId && { component_type: componentTypeId },
6836
7070
  ...ownerAttrs
@@ -6840,11 +7074,12 @@ class UserFormService extends BaseApiService {
6840
7074
  ...payloadWithoutImage,
6841
7075
  created_by: currentUser.id,
6842
7076
  form: retForm.offline_id,
6843
- revision: 0
7077
+ revision: 0,
7078
+ submitted_at: submittedAt
6844
7079
  };
6845
7080
  const { store } = this.client;
6846
- store.dispatch(addUserForm(retForm));
6847
- store.dispatch(addUserFormRevision(retRevision));
7081
+ store.dispatch(addForm(retForm));
7082
+ store.dispatch(addFormRevision(retRevision));
6848
7083
  const formPromise = this.enqueueRequest({
6849
7084
  description: "Create form",
6850
7085
  method: HttpMethod.POST,
@@ -6862,8 +7097,8 @@ class UserFormService extends BaseApiService {
6862
7097
  });
6863
7098
  const attachImagesPromises = this.getAttachImagePromises(images, offlineRevisionPayload.offline_id);
6864
7099
  void formPromise.catch((e) => {
6865
- store.dispatch(deleteUserForm(retForm.offline_id));
6866
- store.dispatch(deleteUserFormRevision(retRevision.offline_id));
7100
+ store.dispatch(deleteForm(retForm.offline_id));
7101
+ store.dispatch(deleteFormRevision(retRevision.offline_id));
6867
7102
  throw e;
6868
7103
  });
6869
7104
  const settledPromise = Promise.all([formPromise, ...attachImagesPromises]).then(() => formPromise);
@@ -6903,9 +7138,10 @@ class UserFormService extends BaseApiService {
6903
7138
  ...payloadWithoutImage,
6904
7139
  created_by: currentUserId,
6905
7140
  revision: "Pending",
6906
- form: formId2
7141
+ form: formId2,
7142
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
6907
7143
  };
6908
- store.dispatch(addUserFormRevision(fullRevision));
7144
+ store.dispatch(addFormRevision(fullRevision));
6909
7145
  const promise = this.enqueueRequest({
6910
7146
  description: "Create form revision",
6911
7147
  method: HttpMethod.PATCH,
@@ -6919,9 +7155,9 @@ class UserFormService extends BaseApiService {
6919
7155
  });
6920
7156
  const attachImagesPromises = this.getAttachImagePromises(images, offlineRevision.offline_id);
6921
7157
  void promise.then((result) => {
6922
- store.dispatch(addUserFormRevision(result));
7158
+ store.dispatch(setFormRevision(result));
6923
7159
  }).catch(() => {
6924
- store.dispatch(deleteUserFormRevision(fullRevision.offline_id));
7160
+ store.dispatch(deleteFormRevision(fullRevision.offline_id));
6925
7161
  });
6926
7162
  const settledPromise = Promise.all([promise, ...attachImagesPromises]).then(() => promise);
6927
7163
  return [fullRevision, settledPromise];
@@ -6963,19 +7199,19 @@ class UserFormService extends BaseApiService {
6963
7199
  async delete(formId2) {
6964
7200
  const { store } = this.client;
6965
7201
  const state = store.getState();
6966
- const userForm = selectUserForm(formId2)(state);
7202
+ const userForm = selectForm(formId2)(state);
6967
7203
  if (!userForm) {
6968
7204
  throw new Error("Expected userForm to exist");
6969
7205
  }
6970
- const userFormSubmissions = selectSubmissionsForForm(formId2)(state);
7206
+ const userFormSubmissions = selectFormSubmissionsOfForm(formId2)(state);
6971
7207
  if (userFormSubmissions && userFormSubmissions.length > 0) {
6972
- store.dispatch(deleteUserFormSubmissions(userFormSubmissions));
7208
+ store.dispatch(deleteFormSubmissions(userFormSubmissions.map(({ offline_id }) => offline_id)));
6973
7209
  }
6974
- const userFormRevisions = selectRevisionsForForm(formId2)(state);
7210
+ const userFormRevisions = selectFormRevisionsOfForm(formId2)(state);
6975
7211
  if (userFormRevisions && userFormRevisions.length > 0) {
6976
- store.dispatch(deleteUserFormRevisions(userFormRevisions));
7212
+ store.dispatch(deleteFormRevisions(userFormRevisions.map(({ offline_id }) => offline_id)));
6977
7213
  }
6978
- store.dispatch(deleteUserForm(formId2));
7214
+ store.dispatch(deleteForm(formId2));
6979
7215
  try {
6980
7216
  return await this.enqueueRequest({
6981
7217
  description: "Delete form",
@@ -6985,12 +7221,12 @@ class UserFormService extends BaseApiService {
6985
7221
  blocks: []
6986
7222
  });
6987
7223
  } catch (e) {
6988
- store.dispatch(addUserForm(userForm));
7224
+ store.dispatch(addForm(userForm));
6989
7225
  if (userFormRevisions && userFormRevisions.length > 0) {
6990
- store.dispatch(addUserFormRevisions(userFormRevisions));
7226
+ store.dispatch(addFormRevisions(userFormRevisions));
6991
7227
  }
6992
7228
  if (userFormSubmissions && userFormSubmissions.length > 0) {
6993
- store.dispatch(addUserFormSubmissions(userFormSubmissions));
7229
+ store.dispatch(addFormSubmissions(userFormSubmissions));
6994
7230
  }
6995
7231
  throw e;
6996
7232
  }
@@ -7004,16 +7240,15 @@ class UserFormService extends BaseApiService {
7004
7240
  blockers: [],
7005
7241
  blocks: []
7006
7242
  });
7007
- store.dispatch(addUserForms(Object.values(result.forms)));
7008
- store.dispatch(addUserFormRevisions(Object.values(result.revisions)));
7009
- store.dispatch(setUserFormRevisionAttachments(Object.values(result.attachments)));
7243
+ store.dispatch(setForms(Object.values(result.forms)));
7244
+ store.dispatch(setFormRevisions(Object.values(result.revisions)));
7245
+ store.dispatch(setFormRevisionAttachments(Object.values(result.attachments)));
7010
7246
  }
7011
7247
  }
7012
7248
  const isArrayOfFiles = (value) => {
7013
7249
  return Array.isArray(value) && value[0] instanceof File;
7014
7250
  };
7015
- const separateFilesFromValues = (payload) => {
7016
- const { values } = payload;
7251
+ const separateFilesFromValues = (values) => {
7017
7252
  const files = {};
7018
7253
  const newValues = {};
7019
7254
  for (const key in values) {
@@ -7028,17 +7263,13 @@ const separateFilesFromValues = (payload) => {
7028
7263
  newValues[key] = value;
7029
7264
  }
7030
7265
  }
7031
- const payloadWithoutFiles = {
7032
- ...payload,
7033
- values: newValues
7034
- };
7035
- return { payloadWithoutFiles, files };
7266
+ return { values: newValues, files };
7036
7267
  };
7037
7268
  class UserFormSubmissionService extends BaseApiService {
7038
7269
  constructor() {
7039
7270
  super(...arguments);
7040
7271
  // Attach files to submission, after uploading them to S3
7041
- __publicField(this, "getAttachFilesPromises", (files, payload) => {
7272
+ __publicField(this, "getAttachFilesPromises", (files, submission) => {
7042
7273
  const { store } = this.client;
7043
7274
  return Object.entries(files).map(async ([key, fileArray]) => {
7044
7275
  const attachResults = [];
@@ -7048,24 +7279,27 @@ class UserFormSubmissionService extends BaseApiService {
7048
7279
  const [fileProps] = await this.client.files.uploadFileToS3(sha1);
7049
7280
  const submissionAttachmentPayload = offline({
7050
7281
  ...fileProps,
7051
- submission: payload.offline_id,
7282
+ submission: submission.offline_id,
7052
7283
  field_identifier: key
7053
7284
  });
7054
7285
  const attach = await this.enqueueRequest({
7055
7286
  description: "Attach file to form submission",
7056
7287
  method: HttpMethod.POST,
7057
- url: `/forms/submission/${payload.offline_id}/attachments/`,
7288
+ url: `/forms/submission/${submission.offline_id}/attachments/`,
7058
7289
  payload: submissionAttachmentPayload,
7059
- blockers: [payload.component, payload.component_stage, payload.issue, payload.form_revision].filter(
7060
- (x) => x !== void 0
7061
- ),
7290
+ blockers: [
7291
+ submission.component,
7292
+ submission.component_stage,
7293
+ submission.issue,
7294
+ submission.form_revision
7295
+ ].filter((x) => x !== void 0),
7062
7296
  blocks: [submissionAttachmentPayload.offline_id]
7063
7297
  });
7064
7298
  const offlinePayload = {
7065
7299
  ...submissionAttachmentPayload,
7066
7300
  file: URL.createObjectURL(file)
7067
7301
  };
7068
- store.dispatch(addUserFormSubmissionAttachment(offlinePayload));
7302
+ store.dispatch(addFormSubmissionAttachment(offlinePayload));
7069
7303
  attachResults.push(attach);
7070
7304
  }
7071
7305
  return attachResults;
@@ -7079,71 +7313,168 @@ class UserFormSubmissionService extends BaseApiService {
7079
7313
  if (!activeProjectId) {
7080
7314
  throw new Error("Expected an active project");
7081
7315
  }
7082
- const { payloadWithoutFiles, files } = separateFilesFromValues(payload);
7316
+ const { values, files } = separateFilesFromValues(payload.values);
7317
+ const offlineSubmission = {
7318
+ ...payload,
7319
+ values,
7320
+ created_by: state.userReducer.currentUser.id,
7321
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
7322
+ };
7083
7323
  const promise = this.enqueueRequest({
7084
7324
  description: "Respond to form",
7085
7325
  method: HttpMethod.POST,
7086
7326
  url: `/forms/revisions/${payload.form_revision}/respond/`,
7087
- payload: { ...payloadWithoutFiles, project: activeProjectId },
7327
+ payload: { ...offlineSubmission, project: activeProjectId },
7088
7328
  blockers: [payload.issue, payload.component, payload.component_stage, "add-form-entry"].filter(
7089
7329
  (x) => x !== void 0
7090
7330
  ),
7091
7331
  blocks: [payload.offline_id]
7092
7332
  });
7093
- const attachFilesPromises = this.getAttachFilesPromises(files, payload);
7094
- const now = (/* @__PURE__ */ new Date()).toISOString();
7095
- const fullOfflineResult = {
7096
- ...payload,
7097
- created_by: state.userReducer.currentUser.id,
7098
- created_at: now,
7099
- updated_at: now
7100
- };
7101
- const offlineResultWithoutFiles = {
7102
- ...fullOfflineResult,
7103
- ...payloadWithoutFiles
7104
- };
7105
- store.dispatch(updateOrCreateUserFormSubmission(offlineResultWithoutFiles));
7333
+ const attachFilesPromises = this.getAttachFilesPromises(files, offlineSubmission);
7334
+ store.dispatch(addFormSubmission(offlineSubmission));
7106
7335
  void promise.then((result) => {
7107
7336
  store.dispatch(addActiveProjectFormSubmissionsCount(1));
7108
- store.dispatch(updateOrCreateUserFormSubmission(result));
7337
+ store.dispatch(setFormSubmission(result));
7109
7338
  return result;
7110
7339
  }).catch(() => {
7111
- store.dispatch(deleteUserFormSubmission(payload.offline_id));
7340
+ store.dispatch(deleteFormSubmission(payload.offline_id));
7112
7341
  store.dispatch(addActiveProjectFormSubmissionsCount(-1));
7113
7342
  });
7114
7343
  const settledPromise = Promise.all([promise, ...attachFilesPromises]).then(() => promise);
7115
- return [fullOfflineResult, settledPromise];
7344
+ return [offlineSubmission, settledPromise];
7116
7345
  }
7117
- update(submission) {
7346
+ // Note currently the bulkAdd method is specific to form submissions for components
7347
+ // TODO: adapt the support bulk adding to any model type
7348
+ async bulkAdd(args) {
7349
+ const { formRevision, values: argsValues, componentOfflineIds } = args;
7118
7350
  const { store } = this.client;
7119
- const { payloadWithoutFiles, files } = separateFilesFromValues(submission);
7120
- if (!("created_by" in payloadWithoutFiles) || !("created_at" in payloadWithoutFiles)) {
7121
- throw new Error("Expected payloadWithoutFiles to have created_by and created_at fields.");
7351
+ const offlineSubmissions = [];
7352
+ const offlineAttachments = [];
7353
+ const submissionOfflineIds = [];
7354
+ const submissionsPayload = [];
7355
+ const attachmentsPayload = [];
7356
+ const { values, files } = separateFilesFromValues(argsValues);
7357
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
7358
+ const createdBy = store.getState().userReducer.currentUser.id;
7359
+ for (const component_id of componentOfflineIds) {
7360
+ const submission = offline({
7361
+ form_revision: formRevision,
7362
+ values,
7363
+ created_by: createdBy,
7364
+ submitted_at: submittedAt,
7365
+ component: component_id
7366
+ });
7367
+ submissionOfflineIds.push(submission.offline_id);
7368
+ submissionsPayload.push({ offline_id: submission.offline_id, component_id });
7369
+ offlineSubmissions.push(submission);
7370
+ for (const [fieldIdentifier, fileArray] of Object.entries(files)) {
7371
+ for (const file of fileArray) {
7372
+ const sha1 = await hashFile(file);
7373
+ await this.client.files.addCache(file, sha1);
7374
+ const offlineAttachment = offline({
7375
+ file_name: file.name,
7376
+ file_sha1: sha1,
7377
+ file: URL.createObjectURL(file),
7378
+ submission: submission.offline_id,
7379
+ field_identifier: fieldIdentifier
7380
+ });
7381
+ offlineAttachments.push(offlineAttachment);
7382
+ attachmentsPayload.push({
7383
+ offline_id: offlineAttachment.offline_id,
7384
+ submission_id: submission.offline_id,
7385
+ sha1,
7386
+ name: file.name,
7387
+ field_identifier: fieldIdentifier
7388
+ });
7389
+ }
7390
+ }
7122
7391
  }
7392
+ const filesRecord = {};
7393
+ for (const file of Object.values(files).flat()) {
7394
+ const sha1 = await hashFile(file);
7395
+ filesRecord[sha1] = {
7396
+ sha1,
7397
+ extension: file.name.split(".").pop() || "",
7398
+ file_type: file.type,
7399
+ size: file.size
7400
+ };
7401
+ }
7402
+ store.dispatch(addFormSubmissions(offlineSubmissions));
7403
+ store.dispatch(addFormSubmissionAttachments(offlineAttachments));
7404
+ const promise = this.enqueueRequest({
7405
+ description: "Bulk add form submissions",
7406
+ method: HttpMethod.POST,
7407
+ url: `/forms/revisions/${formRevision}/bulk-respond/`,
7408
+ payload: {
7409
+ form_data: values,
7410
+ submitted_at: submittedAt,
7411
+ submissions: submissionsPayload,
7412
+ attachments: attachmentsPayload,
7413
+ files: Object.values(filesRecord)
7414
+ },
7415
+ blockers: componentOfflineIds,
7416
+ blocks: submissionOfflineIds
7417
+ });
7418
+ promise.then(({ submissions, attachments, presigned_urls }) => {
7419
+ store.dispatch(updateFormSubmissions(submissions));
7420
+ store.dispatch(updateFormSubmissionAttachments(attachments));
7421
+ for (const [sha1, presigned_url] of Object.entries(presigned_urls)) {
7422
+ const file = filesRecord[sha1];
7423
+ if (!file)
7424
+ continue;
7425
+ void this.enqueueRequest({
7426
+ url: presigned_url.url,
7427
+ description: "Upload file",
7428
+ method: HttpMethod.POST,
7429
+ isExternalUrl: true,
7430
+ isAuthNeeded: false,
7431
+ attachmentHash: sha1,
7432
+ blockers: [`s3-${file.sha1}.${file.extension}`],
7433
+ blocks: [sha1],
7434
+ s3url: presigned_url
7435
+ });
7436
+ }
7437
+ }).catch(() => {
7438
+ store.dispatch(deleteFormSubmissions(submissionOfflineIds));
7439
+ store.dispatch(deleteFormSubmissionAttachments(offlineAttachments.map((x) => x.offline_id)));
7440
+ });
7441
+ return [offlineSubmissions, promise.then(({ submissions }) => submissions)];
7442
+ }
7443
+ update(submission) {
7444
+ const { store } = this.client;
7445
+ const { values, files } = separateFilesFromValues(submission.values);
7123
7446
  const attachFilesPromises = this.getAttachFilesPromises(files, submission);
7124
- const fullResult = {
7125
- ...payloadWithoutFiles,
7126
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
7447
+ const offlineSubmission = {
7448
+ ...submission,
7449
+ values
7127
7450
  };
7128
- store.dispatch(updateOrCreateUserFormSubmission(fullResult));
7451
+ const submissionToBeUpdated = store.getState().formSubmissionReducer.formSubmissions[submission.offline_id];
7452
+ store.dispatch(updateFormSubmission(offlineSubmission));
7129
7453
  const promise = this.enqueueRequest({
7130
7454
  description: "Patch form submission",
7131
7455
  method: HttpMethod.PATCH,
7132
7456
  url: `/forms/submissions/${submission.offline_id}/`,
7133
- payload: fullResult,
7134
- blockers: [fullResult.issue, fullResult.component, fullResult.component_stage].filter(
7457
+ payload: offlineSubmission,
7458
+ blockers: [offlineSubmission.issue, offlineSubmission.component, offlineSubmission.component_stage].filter(
7135
7459
  (x) => x !== void 0
7136
7460
  ),
7137
- blocks: [fullResult.offline_id]
7461
+ blocks: [offlineSubmission.offline_id]
7138
7462
  });
7139
- return Promise.all([promise, ...attachFilesPromises]).then(() => promise);
7463
+ promise.then((createdSubmission) => {
7464
+ store.dispatch(setFormSubmission(createdSubmission));
7465
+ }).catch(() => {
7466
+ store.dispatch(setFormSubmission(submissionToBeUpdated));
7467
+ });
7468
+ return [offlineSubmission, Promise.all([promise, ...attachFilesPromises]).then(() => promise)];
7140
7469
  }
7141
7470
  async delete(submissionId) {
7142
7471
  const { store } = this.client;
7143
7472
  const state = store.getState();
7144
- const submission = state.userFormReducer.submissions[submissionId];
7145
- store.dispatch(deleteUserFormSubmission(submissionId));
7473
+ const submission = state.formSubmissionReducer.formSubmissions[submissionId];
7474
+ const submissionAttachments = selectAttachmentsOfFormSubmission(submissionId)(state);
7475
+ store.dispatch(deleteFormSubmission(submissionId));
7146
7476
  store.dispatch(addActiveProjectFormSubmissionsCount(-1));
7477
+ store.dispatch(deleteFormSubmissionAttachments(submissionAttachments.map((x) => x.offline_id)));
7147
7478
  try {
7148
7479
  return await this.enqueueRequest({
7149
7480
  description: "Delete user form submissions",
@@ -7153,10 +7484,9 @@ class UserFormSubmissionService extends BaseApiService {
7153
7484
  blocks: []
7154
7485
  });
7155
7486
  } catch (e) {
7156
- if (submission) {
7157
- store.dispatch(addActiveProjectFormSubmissionsCount(1));
7158
- store.dispatch(updateOrCreateUserFormSubmission(submission));
7159
- }
7487
+ store.dispatch(addActiveProjectFormSubmissionsCount(1));
7488
+ store.dispatch(addFormSubmission(submission));
7489
+ store.dispatch(addFormSubmissionAttachments(submissionAttachments));
7160
7490
  throw e;
7161
7491
  }
7162
7492
  }
@@ -7170,7 +7500,7 @@ class UserFormSubmissionService extends BaseApiService {
7170
7500
  blockers: [],
7171
7501
  blocks: []
7172
7502
  });
7173
- store.dispatch(setUserFormSubmissions(submissions));
7503
+ store.dispatch(setFormSubmissions(submissions));
7174
7504
  const attachments = await this.enqueueRequest({
7175
7505
  description: "Fetch form attachments",
7176
7506
  method: HttpMethod.GET,
@@ -7178,7 +7508,7 @@ class UserFormSubmissionService extends BaseApiService {
7178
7508
  blockers: [],
7179
7509
  blocks: []
7180
7510
  });
7181
- store.dispatch(setUserFormSubmissionAttachments(attachments));
7511
+ store.dispatch(setFormSubmissionAttachments(attachments));
7182
7512
  }
7183
7513
  }
7184
7514
  class WorkspaceService extends BaseApiService {
@@ -7738,17 +8068,28 @@ class LicenseService extends BaseApiService {
7738
8068
  }
7739
8069
  }
7740
8070
  class DocumentService extends BaseApiService {
8071
+ // TODO: Support adding for project or organization
7741
8072
  add(document2) {
7742
8073
  const { store } = this.client;
7743
8074
  const currentUserId = store.getState().userReducer.currentUser.id;
7744
8075
  const activeProjectId = store.getState().projectReducer.activeProjectId;
7745
- const offlineDocument = offline({ ...document2, created_by: currentUserId });
7746
- store.dispatch(addDocuments([offlineDocument]));
8076
+ if (!activeProjectId) {
8077
+ throw new Error("No active project ID while creating document.");
8078
+ }
8079
+ const offlineDocument = offline(document2);
8080
+ const submittedDocument = {
8081
+ ...offlineDocument,
8082
+ created_by: currentUserId,
8083
+ project: activeProjectId,
8084
+ organization: null,
8085
+ children_documents: []
8086
+ };
8087
+ store.dispatch(addDocuments([submittedDocument]));
7747
8088
  const promise = this.enqueueRequest({
7748
8089
  description: "Create Document",
7749
8090
  method: HttpMethod.POST,
7750
- url: `/projects/${activeProjectId}/create-document/`,
7751
- payload: { ...offlineDocument },
8091
+ url: `/projects/${activeProjectId}/documents/`,
8092
+ payload: offlineDocument,
7752
8093
  queryParams: {
7753
8094
  parent_document: offlineDocument.parent_document ?? void 0
7754
8095
  },
@@ -7759,7 +8100,7 @@ class DocumentService extends BaseApiService {
7759
8100
  promise.catch(() => {
7760
8101
  store.dispatch(removeDocuments([offlineDocument.offline_id]));
7761
8102
  });
7762
- return [offlineDocument, promise];
8103
+ return [submittedDocument, promise];
7763
8104
  }
7764
8105
  update(document2) {
7765
8106
  const { store } = this.client;
@@ -7859,15 +8200,25 @@ class DocumentService extends BaseApiService {
7859
8200
  }
7860
8201
  async refreshStore() {
7861
8202
  const { store } = this.client;
7862
- const activeProjectId = store.getState().projectReducer.activeProjectId;
7863
- const result = await this.enqueueRequest({
8203
+ const state = store.getState();
8204
+ const activeProjectId = state.projectReducer.activeProjectId;
8205
+ const projectDocumentsPromise = this.enqueueRequest({
7864
8206
  description: "Get project documents",
7865
8207
  method: HttpMethod.GET,
7866
- url: `/documents/projects/${activeProjectId}/`,
8208
+ url: `/projects/${activeProjectId}/documents/`,
8209
+ blockers: [],
8210
+ blocks: []
8211
+ });
8212
+ const activeOrganizationId = state.organizationReducer.activeOrganizationId;
8213
+ const organizationDocumentsPromise = this.enqueueRequest({
8214
+ description: "Get organization documents",
8215
+ method: HttpMethod.GET,
8216
+ url: `/organizations/${activeOrganizationId}/documents/`,
7867
8217
  blockers: [],
7868
8218
  blocks: []
7869
8219
  });
7870
- store.dispatch(setDocuments(result));
8220
+ store.dispatch(setDocuments(await projectDocumentsPromise));
8221
+ store.dispatch(addDocuments(await organizationDocumentsPromise));
7871
8222
  }
7872
8223
  }
7873
8224
  class AgentService extends BaseApiService {
@@ -7902,6 +8253,142 @@ class AgentService extends BaseApiService {
7902
8253
  });
7903
8254
  }
7904
8255
  }
8256
+ class TeamService extends BaseApiService {
8257
+ add(teamPayload) {
8258
+ const { store } = this.client;
8259
+ const state = store.getState();
8260
+ const activeOrganizationId = state.organizationReducer.activeOrganizationId;
8261
+ if (!activeOrganizationId) {
8262
+ throw new Error(`Expected active organization to be set, got ${activeOrganizationId}`);
8263
+ }
8264
+ const offlineTeam = offline({
8265
+ ...teamPayload,
8266
+ organization: activeOrganizationId,
8267
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString()
8268
+ // TODO: uncomment once supported
8269
+ // created_by: state.userReducer.currentUser.id,
8270
+ });
8271
+ store.dispatch(addTeam(offlineTeam));
8272
+ const promise = this.enqueueRequest({
8273
+ description: "Create team",
8274
+ method: HttpMethod.POST,
8275
+ url: `/organizations/${activeOrganizationId}/teams/`,
8276
+ payload: offlineTeam,
8277
+ // No blocks since users and organizations are not offline
8278
+ blockers: [],
8279
+ blocks: [offlineTeam.offline_id]
8280
+ });
8281
+ promise.then((createdTeam) => {
8282
+ store.dispatch(setTeam(createdTeam));
8283
+ }).catch(() => {
8284
+ store.dispatch(deleteTeam(offlineTeam.offline_id));
8285
+ });
8286
+ return [offlineTeam, promise];
8287
+ }
8288
+ // TODO: @Audiopolis / Magnus - should we pass a offline_id as one arg and a UpdatedTeamProps as a second arg instead of this set up?
8289
+ update(team) {
8290
+ const { store } = this.client;
8291
+ const teamToBeUpdated = store.getState().teamReducer.teams[team.offline_id];
8292
+ const offlineUpdatedTeam = {
8293
+ ...teamToBeUpdated,
8294
+ ...team
8295
+ };
8296
+ store.dispatch(updateTeam(offlineUpdatedTeam));
8297
+ const promise = this.enqueueRequest({
8298
+ description: "Update team",
8299
+ method: HttpMethod.PATCH,
8300
+ url: `/organizations/teams/${team.offline_id}/`,
8301
+ payload: offlineUpdatedTeam,
8302
+ blockers: [team.offline_id],
8303
+ blocks: [team.offline_id]
8304
+ });
8305
+ promise.then((updatedTeam) => {
8306
+ store.dispatch(setTeam(updatedTeam));
8307
+ }).catch(() => {
8308
+ store.dispatch(setTeam(teamToBeUpdated));
8309
+ });
8310
+ return [offlineUpdatedTeam, promise];
8311
+ }
8312
+ async delete(teamId) {
8313
+ const { store } = this.client;
8314
+ const state = store.getState();
8315
+ const team = state.teamReducer.teams[teamId];
8316
+ if (!team) {
8317
+ throw new Error(`Expected team with id ${teamId} to exist`);
8318
+ }
8319
+ store.dispatch(deleteTeam(teamId));
8320
+ try {
8321
+ return await this.enqueueRequest({
8322
+ description: "Delete team",
8323
+ method: HttpMethod.DELETE,
8324
+ url: `/organizations/teams/${teamId}/`,
8325
+ blockers: [teamId],
8326
+ blocks: [teamId]
8327
+ });
8328
+ } catch (e) {
8329
+ store.dispatch(setTeam(team));
8330
+ throw e;
8331
+ }
8332
+ }
8333
+ async setMembers(teamId, members) {
8334
+ const { store } = this.client;
8335
+ const team = store.getState().teamReducer.teams[teamId];
8336
+ if (!team) {
8337
+ throw new Error(`Expected team with id ${teamId} to exist`);
8338
+ }
8339
+ if (members.length !== new Set(members).size) {
8340
+ throw new Error("Duplicate members found in the list");
8341
+ }
8342
+ store.dispatch(updateTeam({ ...team, members }));
8343
+ const promise = this.enqueueRequest({
8344
+ description: "Set team members",
8345
+ method: HttpMethod.PUT,
8346
+ url: `/organizations/teams/${teamId}/set-members/`,
8347
+ payload: {
8348
+ users: members
8349
+ },
8350
+ blockers: [teamId],
8351
+ blocks: [teamId]
8352
+ });
8353
+ promise.catch(() => {
8354
+ store.dispatch(setTeam(team));
8355
+ });
8356
+ return promise;
8357
+ }
8358
+ async addMembers(teamId, members) {
8359
+ const { store } = this.client;
8360
+ const team = store.getState().teamReducer.teams[teamId];
8361
+ if (!team) {
8362
+ throw new Error(`Expected team with id ${teamId} to exist`);
8363
+ }
8364
+ const newMembers = [...team.members, ...members];
8365
+ return this.setMembers(teamId, newMembers);
8366
+ }
8367
+ async removeMembers(teamId, members) {
8368
+ const { store } = this.client;
8369
+ const team = store.getState().teamReducer.teams[teamId];
8370
+ if (!team) {
8371
+ throw new Error(`Expected team with id ${teamId} to exist`);
8372
+ }
8373
+ const newMembers = team.members.filter((member) => !members.includes(member));
8374
+ return this.setMembers(teamId, newMembers);
8375
+ }
8376
+ async refreshStore() {
8377
+ const { store } = this.client;
8378
+ const activeOrganizationId = store.getState().organizationReducer.activeOrganizationId;
8379
+ if (!activeOrganizationId) {
8380
+ throw new Error(`Expected active organization to be set, got ${activeOrganizationId}`);
8381
+ }
8382
+ const result = await this.enqueueRequest({
8383
+ description: "Fetch teams",
8384
+ method: HttpMethod.GET,
8385
+ url: `/organizations/${activeOrganizationId}/teams/`,
8386
+ blockers: [],
8387
+ blocks: []
8388
+ });
8389
+ store.dispatch(setTeams(result));
8390
+ }
8391
+ }
7905
8392
  class OvermapSDK {
7906
8393
  constructor(apiUrl, store) {
7907
8394
  __publicField(this, "API_URL");
@@ -7931,6 +8418,7 @@ class OvermapSDK {
7931
8418
  __publicField(this, "emailDomains", new EmailDomainsService(this));
7932
8419
  __publicField(this, "licenses", new LicenseService(this));
7933
8420
  __publicField(this, "documents", new DocumentService(this));
8421
+ __publicField(this, "teams", new TeamService(this));
7934
8422
  this.API_URL = apiUrl;
7935
8423
  this.store = store;
7936
8424
  }
@@ -7972,7 +8460,7 @@ const tabTrigger = "_tabTrigger_1w0fq_69";
7972
8460
  const patchfieldBorder = "_patchfieldBorder_1w0fq_73";
7973
8461
  const title = "_title_1w0fq_73";
7974
8462
  const error = "_error_1w0fq_89";
7975
- const styles$c = {
8463
+ const styles$d = {
7976
8464
  description: description$2,
7977
8465
  floatingButtonContainer: floatingButtonContainer$2,
7978
8466
  FullScreenImageContainer: FullScreenImageContainer$2,
@@ -8093,7 +8581,7 @@ const fileName$1 = "_fileName_10o76_31";
8093
8581
  const longIconButton$1 = "_longIconButton_10o76_36";
8094
8582
  const previewImage$1 = "_previewImage_10o76_42";
8095
8583
  const FullScreenImage$1 = "_FullScreenImage_10o76_12";
8096
- const styles$b = {
8584
+ const styles$c = {
8097
8585
  description: description$1,
8098
8586
  floatingButtonContainer: floatingButtonContainer$1,
8099
8587
  FullScreenImageContainer: FullScreenImageContainer$1,
@@ -8117,7 +8605,7 @@ const FullScreenImagePreview = memo((props) => {
8117
8605
  /* @__PURE__ */ jsx(
8118
8606
  "button",
8119
8607
  {
8120
- className: styles$b.FullScreenImageContainer,
8608
+ className: styles$c.FullScreenImageContainer,
8121
8609
  type: "button",
8122
8610
  onClick: () => {
8123
8611
  setShowPreview(false);
@@ -8125,7 +8613,7 @@ const FullScreenImagePreview = memo((props) => {
8125
8613
  children: /* @__PURE__ */ jsx(
8126
8614
  "img",
8127
8615
  {
8128
- className: styles$b.FullScreenImage,
8616
+ className: styles$c.FullScreenImage,
8129
8617
  src: url,
8130
8618
  alt: name,
8131
8619
  onClick: (e) => {
@@ -8135,11 +8623,11 @@ const FullScreenImagePreview = memo((props) => {
8135
8623
  )
8136
8624
  }
8137
8625
  ),
8138
- /* @__PURE__ */ jsxs(Flex$1, { className: styles$b.TopBarContainer, align: "center", children: [
8626
+ /* @__PURE__ */ jsxs(Flex$1, { className: styles$c.TopBarContainer, align: "center", children: [
8139
8627
  /* @__PURE__ */ jsx(
8140
8628
  IconButton,
8141
8629
  {
8142
- className: styles$b.longIconButton,
8630
+ className: styles$c.longIconButton,
8143
8631
  variant: "soft",
8144
8632
  "aria-label": "Exit preview",
8145
8633
  onClick: () => {
@@ -8148,11 +8636,11 @@ const FullScreenImagePreview = memo((props) => {
8148
8636
  children: /* @__PURE__ */ jsx(RiIcon, { icon: "RiArrowLeftLine" })
8149
8637
  }
8150
8638
  ),
8151
- /* @__PURE__ */ jsx(Text$1, { className: styles$b.fileName, children: name }),
8639
+ /* @__PURE__ */ jsx(Text$1, { className: styles$c.fileName, children: name }),
8152
8640
  /* @__PURE__ */ jsx(
8153
8641
  IconButton,
8154
8642
  {
8155
- className: styles$b.longIconButton,
8643
+ className: styles$c.longIconButton,
8156
8644
  variant: "soft",
8157
8645
  "aria-label": `Download ${name}`,
8158
8646
  onClick: handleDownload,
@@ -8180,7 +8668,7 @@ const InputWithLabel = (props) => {
8180
8668
  /* @__PURE__ */ jsx(
8181
8669
  "img",
8182
8670
  {
8183
- className: styles$b.previewImage,
8671
+ className: styles$c.previewImage,
8184
8672
  src: resolvedImageURL,
8185
8673
  alt: resolvedImage.name,
8186
8674
  onClick: () => {
@@ -8208,7 +8696,7 @@ const InputWithHelpText = (props) => {
8208
8696
  const { helpText, children, severity } = props;
8209
8697
  return /* @__PURE__ */ jsxs(Flex$1, { direction: "column", gap: "1", children: [
8210
8698
  children,
8211
- /* @__PURE__ */ jsx(Flex$1, { direction: "column", children: /* @__PURE__ */ jsx(Text$1, { size: "1", severity, className: styles$b.description, children: helpText }) })
8699
+ /* @__PURE__ */ jsx(Flex$1, { direction: "column", children: /* @__PURE__ */ jsx(Text$1, { size: "1", severity, className: styles$c.description, children: helpText }) })
8212
8700
  ] });
8213
8701
  };
8214
8702
  const InputWithLabelAndHelpText = (props) => {
@@ -8442,6 +8930,9 @@ function RiArrowUpLine(props) {
8442
8930
  function RiCalendarLine(props) {
8443
8931
  return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 24 24", "fill": "currentColor" }, "child": [{ "tag": "path", "attr": { "d": "M9 1V3H15V1H17V3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3H7V1H9ZM20 11H4V19H20V11ZM7 5H4V9H20V5H17V7H15V5H9V7H7V5Z" }, "child": [] }] })(props);
8444
8932
  }
8933
+ function RiQrCodeLine(props) {
8934
+ return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 24 24", "fill": "currentColor" }, "child": [{ "tag": "path", "attr": { "d": "M16 17V16H13V13H16V15H18V17H17V19H15V21H13V18H15V17H16ZM21 21H17V19H19V17H21V21ZM3 3H11V11H3V3ZM5 5V9H9V5H5ZM13 3H21V11H13V3ZM15 5V9H19V5H15ZM3 13H11V21H3V13ZM5 15V19H9V15H5ZM18 13H21V15H18V13ZM6 6H8V8H6V6ZM6 16H8V18H6V16ZM16 6H18V8H16V6Z" }, "child": [] }] })(props);
8935
+ }
8445
8936
  function RiFileCopyLine(props) {
8446
8937
  return GenIcon({ "tag": "svg", "attr": { "viewBox": "0 0 24 24", "fill": "currentColor" }, "child": [{ "tag": "path", "attr": { "d": "M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM5.00242 8L5.00019 20H14.9998V8H5.00242ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z" }, "child": [] }] })(props);
8447
8938
  }
@@ -9345,9 +9836,9 @@ const Inset = React.forwardRef((props, forwardedRef) => {
9345
9836
  return React.createElement("div", { ...insetProps, ref: forwardedRef, className: classNames("rt-Inset", className, withBreakpoints(side, "rt-r-side"), withBreakpoints(clip, "rt-r-clip"), withBreakpoints(p, "rt-r-p"), withBreakpoints(px, "rt-r-px"), withBreakpoints(py, "rt-r-py"), withBreakpoints(pt, "rt-r-pt"), withBreakpoints(pr, "rt-r-pr"), withBreakpoints(pb, "rt-r-pb"), withBreakpoints(pl, "rt-r-pl"), withMarginProps(marginProps)) });
9346
9837
  });
9347
9838
  Inset.displayName = "Inset";
9348
- const sizes$7 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
9839
+ const sizes$8 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
9349
9840
  const headingPropDefs = {
9350
- size: { type: "enum", values: sizes$7, default: "6", responsive: true },
9841
+ size: { type: "enum", values: sizes$8, default: "6", responsive: true },
9351
9842
  weight: { ...weightProp, default: "bold" },
9352
9843
  align: alignProp,
9353
9844
  trim: trimProp,
@@ -9360,9 +9851,9 @@ const Heading = React.forwardRef((props, forwardedRef) => {
9360
9851
  return React.createElement($5e63c961fc1ce211$export$8c6ed5c666ac1360, { "data-accent-color": color, ...headingProps, ref: forwardedRef, className: classNames("rt-Heading", className, withBreakpoints(size, "rt-r-size"), withBreakpoints(weight, "rt-r-weight"), withBreakpoints(align, "rt-r-ta"), withBreakpoints(trim, "rt-r-lt"), { "rt-high-contrast": highContrast }, withMarginProps(marginProps)) }, asChild ? children : React.createElement(Tag, null, children));
9361
9852
  });
9362
9853
  Heading.displayName = "Heading";
9363
- const sizes$6 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
9854
+ const sizes$7 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
9364
9855
  const textPropDefs = {
9365
- size: { type: "enum", values: sizes$6, default: void 0, responsive: true },
9856
+ size: { type: "enum", values: sizes$7, default: void 0, responsive: true },
9366
9857
  weight: weightProp,
9367
9858
  align: alignProp,
9368
9859
  trim: trimProp,
@@ -9375,6 +9866,21 @@ const Text = React.forwardRef((props, forwardedRef) => {
9375
9866
  return React.createElement($5e63c961fc1ce211$export$8c6ed5c666ac1360, { "data-accent-color": color, ...textProps, ref: forwardedRef, className: classNames("rt-Text", className, withBreakpoints(size, "rt-r-size"), withBreakpoints(weight, "rt-r-weight"), withBreakpoints(align, "rt-r-ta"), withBreakpoints(trim, "rt-r-lt"), { "rt-high-contrast": highContrast }, withMarginProps(marginProps)) }, asChild ? children : React.createElement(Tag, null, children));
9376
9867
  });
9377
9868
  Text.displayName = "Text";
9869
+ const sizes$6 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
9870
+ const variants$4 = ["solid", "soft", "outline", "ghost"];
9871
+ const codePropDefs = {
9872
+ size: { type: "enum", values: sizes$6, default: void 0, responsive: true },
9873
+ variant: { type: "enum", values: variants$4, default: "soft" },
9874
+ weight: weightProp,
9875
+ color: colorProp,
9876
+ highContrast: highContrastProp
9877
+ };
9878
+ const Code = React.forwardRef((props, forwardedRef) => {
9879
+ const { rest: marginRest, ...marginProps } = extractMarginProps(props);
9880
+ const { className, size = codePropDefs.size.default, variant = codePropDefs.variant.default, weight = codePropDefs.weight.default, color = codePropDefs.color.default, highContrast = codePropDefs.highContrast.default, ...codeProps } = marginRest;
9881
+ return React.createElement("code", { "data-accent-color": color, ...codeProps, ref: forwardedRef, className: classNames("rt-Code", className, withBreakpoints(size, "rt-r-size"), `rt-variant-${variant}`, withBreakpoints(weight, "rt-r-weight"), { "rt-high-contrast": highContrast }, withMarginProps(marginProps)) });
9882
+ });
9883
+ Code.displayName = "Code";
9378
9884
  const Em = React.forwardRef((props, forwardedRef) => React.createElement("em", { ...props, ref: forwardedRef, className: classNames("rt-Em", props.className) }));
9379
9885
  Em.displayName = "Em";
9380
9886
  const Strong = React.forwardRef((props, forwardedRef) => React.createElement("strong", { ...props, ref: forwardedRef, className: classNames("rt-Strong", props.className) }));
@@ -11765,7 +12271,7 @@ __publicField(StringOrTextField, "_validateMax", (path) => (value, allValues) =>
11765
12271
  });
11766
12272
  const clickableLinkContainer = "_clickableLinkContainer_1ace7_1";
11767
12273
  const TextFieldInputCopy = "_TextFieldInputCopy_1ace7_5";
11768
- const styles$a = {
12274
+ const styles$b = {
11769
12275
  clickableLinkContainer,
11770
12276
  TextFieldInputCopy
11771
12277
  };
@@ -11794,13 +12300,13 @@ const StringInput = memo((props) => {
11794
12300
  placeholder: field.placeholder,
11795
12301
  color
11796
12302
  }
11797
- ) : /* @__PURE__ */ jsxs(TextField$1.Root, { className: styles$a.clickableLinkContainer, children: [
12303
+ ) : /* @__PURE__ */ jsxs(TextField$1.Root, { className: styles$b.clickableLinkContainer, children: [
11798
12304
  /* @__PURE__ */ jsx(
11799
12305
  "div",
11800
12306
  {
11801
12307
  className: classNames$1(
11802
12308
  "rt-TextFieldInput rt-r-size-2 rt-variant-surface",
11803
- styles$a.TextFieldInputCopy
12309
+ styles$b.TextFieldInputCopy
11804
12310
  ),
11805
12311
  children: /* @__PURE__ */ jsx(
11806
12312
  Linkify,
@@ -12372,8 +12878,7 @@ class BaseSelectField extends BaseField {
12372
12878
  description: "List possible options for the user to select from.",
12373
12879
  required: true,
12374
12880
  identifier: `${path}options`,
12375
- minimum_length: 2,
12376
- maximum_length: 20
12881
+ minimum_length: 2
12377
12882
  }),
12378
12883
  showDirectly: true
12379
12884
  }
@@ -12492,6 +12997,158 @@ __publicField(_MultiSelectField, "fieldTypeName", "Multi-select");
12492
12997
  __publicField(_MultiSelectField, "fieldTypeDescription", "Allows the user to select a multiple options from a list of options.");
12493
12998
  __publicField(_MultiSelectField, "Icon", RiCheckboxLine);
12494
12999
  let MultiSelectField = _MultiSelectField;
13000
+ const QrScannerWrapper = "_QrScannerWrapper_1puz3_1";
13001
+ const styles$a = {
13002
+ QrScannerWrapper
13003
+ };
13004
+ const QrInput = memo((props) => {
13005
+ const [{ inputId, labelId, label, helpText, size, severity, showInputOnly, field, fieldProps }, rest] = useFormikInput(props);
13006
+ const [showQrScanner, setShowQrScanner] = useState(false);
13007
+ const value = fieldProps.value;
13008
+ const handleQrScan = useCallback(
13009
+ (data) => {
13010
+ fieldProps.onChange({ target: { value: data } });
13011
+ setShowQrScanner(false);
13012
+ },
13013
+ [fieldProps]
13014
+ );
13015
+ const handleClearScanResult = useCallback(() => {
13016
+ fieldProps.onChange({ target: { value: "" } });
13017
+ }, [fieldProps]);
13018
+ const handleScanButtonClicked = useCallback(() => {
13019
+ setShowQrScanner(true);
13020
+ }, []);
13021
+ const handleQrScannerClose = useCallback(() => {
13022
+ setShowQrScanner(false);
13023
+ }, []);
13024
+ return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText, severity, children: /* @__PURE__ */ jsxs(
13025
+ InputWithLabel,
13026
+ {
13027
+ size,
13028
+ severity,
13029
+ inputId,
13030
+ labelId,
13031
+ label: showInputOnly ? label : "",
13032
+ image: showInputOnly ? void 0 : field.image,
13033
+ flexProps: { direction: "column", justify: "start", align: "start", gap: "1" },
13034
+ children: [
13035
+ /* @__PURE__ */ jsx(
13036
+ Overlay,
13037
+ {
13038
+ open: showQrScanner,
13039
+ content: () => /* @__PURE__ */ jsx(QrScanner, { onQrScan: handleQrScan, onClose: handleQrScannerClose }),
13040
+ onOpenChange: setShowQrScanner
13041
+ }
13042
+ ),
13043
+ /* @__PURE__ */ jsxs(Flex, { width: "max-content", gap: "1", align: "center", children: [
13044
+ /* @__PURE__ */ jsxs(Button, { ...rest, variant: "soft", onClick: handleScanButtonClicked, children: [
13045
+ /* @__PURE__ */ jsx(RiIcon, { icon: "RiQrCodeLine" }),
13046
+ "Scan"
13047
+ ] }),
13048
+ value && /* @__PURE__ */ jsx(Text, { color: "jade", size: "1", children: /* @__PURE__ */ jsx(RiIcon, { icon: "RiCheckLine", style: { verticalAlign: "bottom" } }) })
13049
+ ] }),
13050
+ value && /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex, { width: "max-content", gap: "2", align: "center", children: [
13051
+ /* @__PURE__ */ jsx(Code, { color: "gray", highContrast: true, children: value }),
13052
+ /* @__PURE__ */ jsx(
13053
+ IconButton,
13054
+ {
13055
+ severity: "info",
13056
+ variant: "ghost",
13057
+ "aria-label": "delete",
13058
+ size: "small",
13059
+ onClick: handleClearScanResult,
13060
+ children: /* @__PURE__ */ jsx(RiIcon, { icon: "RiCloseLine" })
13061
+ }
13062
+ )
13063
+ ] }) })
13064
+ ]
13065
+ }
13066
+ ) });
13067
+ });
13068
+ QrInput.displayName = "QrInput";
13069
+ const QrScanner = memo((props) => {
13070
+ const { onQrScan, onClose } = props;
13071
+ const videoRef = useRef(null);
13072
+ const [isScannerLoading, setIsScannerLoading] = useState(false);
13073
+ useEffect(() => {
13074
+ if (!videoRef.current)
13075
+ return;
13076
+ const qrScanner = new QrScannerAPI(
13077
+ videoRef.current,
13078
+ (result) => {
13079
+ const data = result.data;
13080
+ onQrScan(data);
13081
+ qrScanner.destroy();
13082
+ },
13083
+ {
13084
+ highlightCodeOutline: true,
13085
+ highlightScanRegion: true,
13086
+ maxScansPerSecond: 1
13087
+ }
13088
+ );
13089
+ setIsScannerLoading(true);
13090
+ qrScanner.start().then(() => {
13091
+ setIsScannerLoading(false);
13092
+ }).catch(() => {
13093
+ setIsScannerLoading(false);
13094
+ });
13095
+ }, [onQrScan]);
13096
+ return /* @__PURE__ */ jsxs(
13097
+ Flex,
13098
+ {
13099
+ className: styles$a.QrScannerWrapper,
13100
+ width: "100%",
13101
+ height: "100%",
13102
+ direction: "column",
13103
+ gap: "2",
13104
+ justify: "center",
13105
+ position: "relative",
13106
+ children: [
13107
+ /* @__PURE__ */ jsx(Flex, { width: "100%", position: "absolute", top: "0", p: "2", children: /* @__PURE__ */ jsx(IconButton, { "aria-label": "close", variant: "soft", severity: "info", highContrast: true, onClick: onClose, children: /* @__PURE__ */ jsx(RiIcon, { icon: "RiCloseLine" }) }) }),
13108
+ /* @__PURE__ */ jsxs(Box, { style: { maxWidth: "100%", maxHeight: "100%" }, position: "relative", children: [
13109
+ /* @__PURE__ */ jsx("video", { ref: videoRef, style: { width: "100%", height: "100%" } }),
13110
+ isScannerLoading && /* @__PURE__ */ jsx(
13111
+ Flex,
13112
+ {
13113
+ position: "absolute",
13114
+ inset: "0",
13115
+ style: { background: "var(--color-background)" },
13116
+ justify: "center",
13117
+ align: "center",
13118
+ children: /* @__PURE__ */ jsx(Spinner, {})
13119
+ }
13120
+ )
13121
+ ] })
13122
+ ]
13123
+ }
13124
+ );
13125
+ });
13126
+ QrScanner.displayName = "QrScanner";
13127
+ const emptyQrField = {
13128
+ ...emptyBaseField,
13129
+ type: "qr"
13130
+ };
13131
+ const _QrField = class _QrField extends BaseField {
13132
+ constructor(options) {
13133
+ super({ ...options, type: "qr" });
13134
+ __publicField(this, "onlyValidateAfterTouched", false);
13135
+ }
13136
+ serialize() {
13137
+ return super._serialize();
13138
+ }
13139
+ static deserialize(data) {
13140
+ if (data.type !== "qr")
13141
+ throw new Error("Type mismatch.");
13142
+ return new _QrField(data);
13143
+ }
13144
+ getInput(props) {
13145
+ return /* @__PURE__ */ jsx(QrInput, { ...props, field: this });
13146
+ }
13147
+ };
13148
+ __publicField(_QrField, "fieldTypeName", "QR");
13149
+ __publicField(_QrField, "fieldTypeDescription", "Used for scanning/reading QR codes.");
13150
+ __publicField(_QrField, "Icon", RiQrCodeLine);
13151
+ let QrField = _QrField;
12495
13152
  const FieldInputCloner = memo((props) => {
12496
13153
  const { field, ...rest } = props;
12497
13154
  const [{ value: identifier }] = useField(field.options.clonedFieldIdentifier);
@@ -13749,6 +14406,7 @@ const FieldTypeToClsMapping = {
13749
14406
  text: TextField,
13750
14407
  custom: CustomField,
13751
14408
  upload: UploadField,
14409
+ qr: QrField,
13752
14410
  // TODO: Underscore
13753
14411
  "multi-string": MultiStringField,
13754
14412
  "multi-select": MultiSelectField
@@ -13762,6 +14420,7 @@ const FieldTypeToEmptyFieldMapping = {
13762
14420
  text: emptyTextField,
13763
14421
  custom: emptyCustomField,
13764
14422
  upload: emptyUploadField,
14423
+ qr: emptyQrField,
13765
14424
  // TODO: Underscore
13766
14425
  "multi-string": emptyMultiStringField,
13767
14426
  "multi-select": emptyMultiSelectField
@@ -13849,7 +14508,7 @@ const FieldSectionLayout = memo((props) => {
13849
14508
  return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(Flex$1, { direction: "column", gap: "3", children: [
13850
14509
  /* @__PURE__ */ jsxs(Flex$1, { direction: "column", children: [
13851
14510
  /* @__PURE__ */ jsx(Heading, { as: "h3", size: "3", children: label }),
13852
- /* @__PURE__ */ jsx(Text$1, { className: styles$b.description, children: description2 })
14511
+ /* @__PURE__ */ jsx(Text$1, { className: styles$c.description, children: description2 })
13853
14512
  ] }),
13854
14513
  inputs
13855
14514
  ] }) });
@@ -14036,7 +14695,7 @@ const initialFormValues = (fields, values) => {
14036
14695
  };
14037
14696
  const useAttachImagesToFormRevisionFields = (revision) => {
14038
14697
  const { sdk } = useSDK();
14039
- const attachments = useAppSelector(selectRevisionAttachments((revision == null ? void 0 : revision.offline_id) ?? ""));
14698
+ const attachments = useAppSelector(selectAttachmentsOfFormRevision((revision == null ? void 0 : revision.offline_id) ?? ""));
14040
14699
  return useMemo(() => {
14041
14700
  if (!revision || !attachments)
14042
14701
  return revision;
@@ -14092,7 +14751,7 @@ const FormRenderer = memo(
14092
14751
  [schema.title]
14093
14752
  );
14094
14753
  const Description = useMemo(
14095
- () => typeof schema.description === "string" ? /* @__PURE__ */ jsx(Text$1, { className: styles$b.description, children: schema.description }) : schema.description,
14754
+ () => typeof schema.description === "string" ? /* @__PURE__ */ jsx(Text$1, { className: styles$c.description, children: schema.description }) : schema.description,
14096
14755
  [schema.description]
14097
14756
  );
14098
14757
  const inputs = useFieldInputs(schema.fields, { formId: formId2, disabled: readonly });
@@ -14108,7 +14767,7 @@ const FormRenderer = memo(
14108
14767
  !hideDescription && Description
14109
14768
  ] }) }),
14110
14769
  inputs,
14111
- !readonly && /* @__PURE__ */ jsxs(Flex$1, { className: styles$b.floatingButtonContainer, align: "center", justify: "end", gap: "2", children: [
14770
+ !readonly && /* @__PURE__ */ jsxs(Flex$1, { className: styles$c.floatingButtonContainer, align: "center", justify: "end", gap: "2", children: [
14112
14771
  cancelText && /* @__PURE__ */ jsx(Button, { severity: "info", ...buttonProps, type: "button", onClick: onCancel, children: cancelText }),
14113
14772
  /* @__PURE__ */ jsx(Button, { ...buttonProps, type: "submit", disabled: !formik.isValid, children: submitText })
14114
14773
  ] })
@@ -14133,7 +14792,7 @@ const FormSubmissionViewer = memo(
14133
14792
  return formRevisionToSchema(revisionWithImages, { readonly: true });
14134
14793
  }, [revisionWithImages]);
14135
14794
  const submissionValuesWithAttachments = useMemo(() => {
14136
- const attachments = selectSubmissionAttachments(submission.offline_id)(sdk.store.getState()) ?? [];
14795
+ const attachments = selectAttachmentsOfFormSubmission(submission.offline_id)(sdk.store.getState()) ?? [];
14137
14796
  const downloadedAttachments = {};
14138
14797
  for (const attachment of attachments) {
14139
14798
  const promise = sdk.files.fetchFileFromUrl(attachment.file, attachment.file_sha1, attachment.file_name);
@@ -14183,8 +14842,8 @@ const FormBrowser = memo(
14183
14842
  }
14184
14843
  return ret;
14185
14844
  }, [filter, maxResults, ownerFilter]);
14186
- const userForms = useAppSelector(selectFilteredUserForms(ownerFilterOptions)) ?? [];
14187
- const userFormMapping = useAppSelector(selectUserFormMapping);
14845
+ const userForms = useAppSelector(selectFilteredForms(ownerFilterOptions)) ?? [];
14846
+ const userFormMapping = useAppSelector(selectFormMapping);
14188
14847
  const attachableUserForms = userForms.filter((form) => !form.component_type);
14189
14848
  const attachableUserFormMapping = Object.values(userFormMapping).filter(
14190
14849
  (form) => !form.component_type
@@ -14217,7 +14876,7 @@ const FormBrowser = memo(
14217
14876
  const handleChange = useCallback((e) => {
14218
14877
  setFilter(e.currentTarget.value);
14219
14878
  }, []);
14220
- const numberOfForms = useAppSelector(selectNumberOfGeneralUserForms) || 0;
14879
+ const numberOfForms = useAppSelector(selectGeneralFormCount) || 0;
14221
14880
  const numberOfHiddenForms = numberOfForms - attachableUserForms.length;
14222
14881
  const overflowMessage = attachableUserForms.length == maxResults && numberOfHiddenForms > 0 ? `Only the first ${maxResults} results are shown (${numberOfHiddenForms} hidden)` : numberOfHiddenForms > 0 && `${numberOfHiddenForms} hidden forms`;
14223
14882
  return /* @__PURE__ */ jsxs(Flex$1, { ref, direction: "column", gap: "2", children: [
@@ -14311,16 +14970,13 @@ const FormSubmissionBrowserEntry = memo((props) => {
14311
14970
  const { submission, onSubmissionClick, compact, labelType, rowDecorator } = props;
14312
14971
  const currentUser = useAppSelector(selectCurrentUser);
14313
14972
  const createdBy = useAppSelector(selectUser("created_by" in submission ? submission.created_by : currentUser.id));
14314
- const dateToUse = getCreatedAtOrSubmittedAtDate(submission);
14315
- const formattedDateTime = isToday(dateToUse) ? dateToUse.toLocaleTimeString([], {
14316
- hour: "2-digit",
14317
- minute: "2-digit"
14318
- }) : getLocalDateString(dateToUse);
14973
+ const dateToUse = submission.submitted_at;
14974
+ const formattedDateTime = getLocalDateString(dateToUse);
14319
14975
  const revision = useAppSelector(selectFormRevision(submission.form_revision));
14320
14976
  if (!revision) {
14321
14977
  throw new Error(`Could not find revision ${submission.form_revision} for submission ${submission.offline_id}.`);
14322
14978
  }
14323
- const latestRevisionNumber = (_a2 = useAppSelector(selectLatestFormRevision(revision.form))) == null ? void 0 : _a2.revision;
14979
+ const latestRevisionNumber = (_a2 = useAppSelector(selectLatestFormRevisionOfForm(revision.form))) == null ? void 0 : _a2.revision;
14324
14980
  const creatorProfileSrc = useFileSrc({
14325
14981
  file: (createdBy == null ? void 0 : createdBy.profile.file) ?? null,
14326
14982
  fileSha1: (createdBy == null ? void 0 : createdBy.profile.file_sha1) ?? null
@@ -14351,10 +15007,6 @@ const FormSubmissionBrowserEntry = memo((props) => {
14351
15007
  return row;
14352
15008
  });
14353
15009
  FormSubmissionBrowserEntry.displayName = "FormSubmissionBrowserEntry";
14354
- const getCreatedAtOrSubmittedAtDate = (submission) => {
14355
- const date = "created_at" in submission ? submission.created_at : submission.submitted_at;
14356
- return new Date(date);
14357
- };
14358
15010
  const FormSubmissionBrowser = memo((props) => {
14359
15011
  const {
14360
15012
  formId: formId2,
@@ -14368,10 +15020,10 @@ const FormSubmissionBrowser = memo((props) => {
14368
15020
  if (!!formId2 === !!propSubmissions) {
14369
15021
  throw new Error("Either formId or submissions must be provided, but not both.");
14370
15022
  }
14371
- const submissions = useAppSelector(propSubmissions ? () => propSubmissions : selectSubmissionsForForm(formId2));
15023
+ const submissions = useAppSelector(propSubmissions ? () => propSubmissions : selectFormSubmissionsOfForm(formId2));
14372
15024
  const sortedSubmissions = useMemo(
14373
15025
  () => submissions == null ? void 0 : submissions.sort((a, b) => {
14374
- return getCreatedAtOrSubmittedAtDate(b).getTime() - getCreatedAtOrSubmittedAtDate(a).getTime();
15026
+ return a.submitted_at.localeCompare(b.submitted_at);
14375
15027
  }),
14376
15028
  [submissions]
14377
15029
  );
@@ -15463,12 +16115,12 @@ const FormBuilder = memo(
15463
16115
  });
15464
16116
  const previewSchema = useMemo(() => formRevisionToSchema(formik.values), [formik.values]);
15465
16117
  return /* @__PURE__ */ jsx(Tabs.Root, { ref, defaultValue: "edit", children: /* @__PURE__ */ jsxs(Flex$1, { direction: "column", gap: "2", children: [
15466
- showTabs && /* @__PURE__ */ jsxs(Tabs.List, { className: classNames$1(styles$c.tabsList, tabsListClassName), children: [
15467
- /* @__PURE__ */ jsx(Tabs.Trigger, { className: styles$c.tabTrigger, value: "edit", children: /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
16118
+ showTabs && /* @__PURE__ */ jsxs(Tabs.List, { className: classNames$1(styles$d.tabsList, tabsListClassName), children: [
16119
+ /* @__PURE__ */ jsx(Tabs.Trigger, { className: styles$d.tabTrigger, value: "edit", children: /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
15468
16120
  /* @__PURE__ */ jsx(RiIcon, { icon: "RiPencilLine" }),
15469
16121
  "Edit"
15470
16122
  ] }) }),
15471
- /* @__PURE__ */ jsx(Tabs.Trigger, { className: styles$c.tabTrigger, value: "preview", children: /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
16123
+ /* @__PURE__ */ jsx(Tabs.Trigger, { className: styles$d.tabTrigger, value: "preview", children: /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
15472
16124
  /* @__PURE__ */ jsx(RiIcon, { icon: "RiEyeLine" }),
15473
16125
  "Preview"
15474
16126
  ] }) })
@@ -15492,8 +16144,8 @@ const FormBuilder = memo(
15492
16144
  render: ({ setValue, value, meta }) => /* @__PURE__ */ jsx(InputWithHelpText, { severity: "danger", helpText: meta.error ?? null, children: /* @__PURE__ */ jsx(
15493
16145
  Input,
15494
16146
  {
15495
- className: classNames$1(styles$c.title, {
15496
- [styles$c.error]: meta.error
16147
+ className: classNames$1(styles$d.title, {
16148
+ [styles$d.error]: meta.error
15497
16149
  }),
15498
16150
  placeholder: "Form title",
15499
16151
  value,
@@ -15515,7 +16167,7 @@ const FormBuilder = memo(
15515
16167
  render: ({ setValue, value }) => /* @__PURE__ */ jsx(
15516
16168
  TextArea,
15517
16169
  {
15518
- className: styles$c.description,
16170
+ className: styles$d.description,
15519
16171
  placeholder: "Explain the purpose of this form",
15520
16172
  value,
15521
16173
  onChange: (event) => {
@@ -15533,7 +16185,7 @@ const FormBuilder = memo(
15533
16185
  /* @__PURE__ */ jsx(FieldsEditor, { fieldsOnly }),
15534
16186
  /* @__PURE__ */ jsx(Text$1, { severity: "danger", size: "1", children: typeof formik.errors.fields === "string" && formik.errors.fields })
15535
16187
  ] }),
15536
- /* @__PURE__ */ jsxs(Flex$1, { className: styles$c.floatingButtonContainer, align: "center", justify: "end", gap: "2", children: [
16188
+ /* @__PURE__ */ jsxs(Flex$1, { className: styles$d.floatingButtonContainer, align: "center", justify: "end", gap: "2", children: [
15537
16189
  onCancel && /* @__PURE__ */ jsx(Button, { type: "button", variant: "solid", severity: "info", onClick: onCancel, children: "Cancel" }),
15538
16190
  /* @__PURE__ */ jsx(Button, { type: "submit", children: "Save form" })
15539
16191
  ] })
@@ -15568,6 +16220,9 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
15568
16220
  NumberInput,
15569
16221
  PatchField,
15570
16222
  PatchFormProvider,
16223
+ QrField,
16224
+ QrInput,
16225
+ QrScanner,
15571
16226
  SelectField,
15572
16227
  SelectInput,
15573
16228
  StringField,
@@ -15582,6 +16237,7 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
15582
16237
  emptyMultiSelectField,
15583
16238
  emptyMultiStringField,
15584
16239
  emptyNumberField,
16240
+ emptyQrField,
15585
16241
  emptySelectField,
15586
16242
  emptyStringField,
15587
16243
  emptyTextField,
@@ -15672,6 +16328,9 @@ export {
15672
16328
  ProjectFileService,
15673
16329
  ProjectService,
15674
16330
  ProjectType,
16331
+ QrField,
16332
+ QrInput,
16333
+ QrScanner,
15675
16334
  SDKContext,
15676
16335
  SDKProvider,
15677
16336
  SUPPORTED_IMAGE_FILE_TYPES,
@@ -15682,6 +16341,7 @@ export {
15682
16341
  SpreadsheetViewer,
15683
16342
  StringField,
15684
16343
  StringInput,
16344
+ TeamService,
15685
16345
  TextField,
15686
16346
  TextInput,
15687
16347
  UserFormService,
@@ -15689,6 +16349,7 @@ export {
15689
16349
  VerificationCodeType,
15690
16350
  WorkspaceService,
15691
16351
  YELLOW,
16352
+ _selectLatestFormRevision,
15692
16353
  _setLatestRetryTime,
15693
16354
  acceptProjectInvite,
15694
16355
  addActiveProjectFormSubmissionsCount,
@@ -15706,6 +16367,16 @@ export {
15706
16367
  addDocuments,
15707
16368
  addEmailDomain,
15708
16369
  addFavouriteProjectId,
16370
+ addForm,
16371
+ addFormRevision,
16372
+ addFormRevisionAttachment,
16373
+ addFormRevisionAttachments,
16374
+ addFormRevisions,
16375
+ addFormSubmission,
16376
+ addFormSubmissionAttachment,
16377
+ addFormSubmissionAttachments,
16378
+ addFormSubmissions,
16379
+ addForms,
15709
16380
  addIssue,
15710
16381
  addIssueAttachment,
15711
16382
  addIssueAttachments,
@@ -15725,14 +16396,8 @@ export {
15725
16396
  addStageCompletion,
15726
16397
  addStageCompletions,
15727
16398
  addStages,
16399
+ addTeam,
15728
16400
  addToRecentIssues,
15729
- addUserForm,
15730
- addUserFormRevision,
15731
- addUserFormRevisionAttachment,
15732
- addUserFormRevisions,
15733
- addUserFormSubmissionAttachment,
15734
- addUserFormSubmissions,
15735
- addUserForms,
15736
16401
  addUsers,
15737
16402
  addWorkspace,
15738
16403
  areArraysEqual,
@@ -15753,6 +16418,7 @@ export {
15753
16418
  componentStageSlice,
15754
16419
  componentTypeReducer,
15755
16420
  componentTypeSlice,
16421
+ constructUploadedFilePayloads,
15756
16422
  coordinatesAreEqual,
15757
16423
  coordinatesToLiteral,
15758
16424
  coordinatesToPointGeometry,
@@ -15763,12 +16429,17 @@ export {
15763
16429
  defaultBadgeColor,
15764
16430
  defaultStore,
15765
16431
  deleteComponentType,
16432
+ deleteForm,
16433
+ deleteFormRevision,
16434
+ deleteFormRevisionAttachment,
16435
+ deleteFormRevisionAttachments,
16436
+ deleteFormRevisions,
16437
+ deleteFormSubmission,
16438
+ deleteFormSubmissionAttachment,
16439
+ deleteFormSubmissionAttachments,
16440
+ deleteFormSubmissions,
15766
16441
  deleteProject,
15767
- deleteUserForm,
15768
- deleteUserFormRevision,
15769
- deleteUserFormRevisions,
15770
- deleteUserFormSubmission,
15771
- deleteUserFormSubmissions,
16442
+ deleteTeam,
15772
16443
  dequeue,
15773
16444
  deserialize,
15774
16445
  deserializeField,
@@ -15786,6 +16457,7 @@ export {
15786
16457
  emptyMultiSelectField,
15787
16458
  emptyMultiStringField,
15788
16459
  emptyNumberField,
16460
+ emptyQrField,
15789
16461
  emptySelectField,
15790
16462
  emptyStringField,
15791
16463
  emptyTextField,
@@ -15797,7 +16469,13 @@ export {
15797
16469
  fileSlice,
15798
16470
  fileToBlob,
15799
16471
  flipCoordinates,
16472
+ formReducer,
16473
+ formRevisionReducer,
15800
16474
  formRevisionToSchema,
16475
+ formRevisionsSlice,
16476
+ formSlice,
16477
+ formSubmissionReducer,
16478
+ formSubmissionSlice,
15801
16479
  index as forms,
15802
16480
  fullComponentMarkerSize,
15803
16481
  generateBadgeColors,
@@ -15925,6 +16603,8 @@ export {
15925
16603
  selectAttachmentsOfComponentTypeByType,
15926
16604
  selectAttachmentsOfDocument,
15927
16605
  selectAttachmentsOfDocumentByType,
16606
+ selectAttachmentsOfFormRevision,
16607
+ selectAttachmentsOfFormSubmission,
15928
16608
  selectAttachmentsOfIssue,
15929
16609
  selectAttachmentsOfIssueByType,
15930
16610
  selectAttachmentsOfProject,
@@ -15942,11 +16622,9 @@ export {
15942
16622
  selectComponent,
15943
16623
  selectComponentAttachment,
15944
16624
  selectComponentAttachmentMapping,
15945
- selectComponentSubmissionMapping,
15946
16625
  selectComponentType,
15947
16626
  selectComponentTypeAttachment,
15948
16627
  selectComponentTypeAttachmentMapping,
15949
- selectComponentTypeForm,
15950
16628
  selectComponentTypeFromComponent,
15951
16629
  selectComponentTypeFromComponents,
15952
16630
  selectComponentTypeStagesMapping,
@@ -15976,8 +16654,24 @@ export {
15976
16654
  selectExpandedSections,
15977
16655
  selectFavouriteProjects,
15978
16656
  selectFileAttachmentsOfIssue,
15979
- selectFilteredUserForms,
16657
+ selectFilteredForms,
16658
+ selectForm,
16659
+ selectFormMapping,
16660
+ selectFormOfComponentType,
15980
16661
  selectFormRevision,
16662
+ selectFormRevisionMapping,
16663
+ selectFormRevisions,
16664
+ selectFormRevisionsOfForm,
16665
+ selectFormSubmission,
16666
+ selectFormSubmissionAttachmentsMapping,
16667
+ selectFormSubmissions,
16668
+ selectFormSubmissionsByComponents,
16669
+ selectFormSubmissionsMapping,
16670
+ selectFormSubmissionsOfComponent,
16671
+ selectFormSubmissionsOfForm,
16672
+ selectFormSubmissionsOfIssue,
16673
+ selectFormsCount,
16674
+ selectGeneralFormCount,
15981
16675
  selectHiddenCategoryCount,
15982
16676
  selectHiddenComponentTypeIds,
15983
16677
  selectIsFetchingInitialData,
@@ -15988,14 +16682,15 @@ export {
15988
16682
  selectIssueAttachment,
15989
16683
  selectIssueAttachmentMapping,
15990
16684
  selectIssueAttachments,
16685
+ selectIssueCountOfCategory,
15991
16686
  selectIssueMapping,
15992
16687
  selectIssueUpdateMapping,
15993
16688
  selectIssueUpdatesOfIssue,
15994
16689
  selectIssues,
15995
- selectLatestFormRevision,
16690
+ selectLatestFormRevisionByForm,
16691
+ selectLatestFormRevisionOfForm,
16692
+ selectLatestFormRevisionsOfComponentTypes,
15996
16693
  selectLatestRetryTime,
15997
- selectLatestRevisionByFormId,
15998
- selectLatestRevisionsFromComponentTypeIds,
15999
16694
  selectLicense,
16000
16695
  selectLicenseForProject,
16001
16696
  selectLicenses,
@@ -16004,8 +16699,6 @@ export {
16004
16699
  selectMapStyle,
16005
16700
  selectNumberOfComponentTypesMatchingCaseInsensitiveName,
16006
16701
  selectNumberOfComponentsOfComponentType,
16007
- selectNumberOfGeneralUserForms,
16008
- selectNumberOfUserForms,
16009
16702
  selectOrganization,
16010
16703
  selectOrganizationAccess,
16011
16704
  selectOrganizationAccessForUser,
@@ -16033,8 +16726,6 @@ export {
16033
16726
  selectRecentIssuesAsSearchResults,
16034
16727
  selectRecentProjects,
16035
16728
  selectRehydrated,
16036
- selectRevisionAttachments,
16037
- selectRevisionsForForm,
16038
16729
  selectRootDocuments,
16039
16730
  selectShowTooltips,
16040
16731
  selectSortedEmailDomains,
@@ -16049,16 +16740,15 @@ export {
16049
16740
  selectStagesFromComponentType,
16050
16741
  selectStagesFromComponentTypeIds,
16051
16742
  selectStagesFromStageIds,
16052
- selectSubmissionAttachments,
16053
- selectSubmissionsForComponent,
16054
- selectSubmissionsForForm,
16055
- selectSubmissionsForIssue,
16743
+ selectTeam,
16744
+ selectTeams,
16745
+ selectTeamsMapping,
16746
+ selectTeamsOfOrganization,
16747
+ selectTeamsOfUser,
16056
16748
  selectUploadUrl,
16057
16749
  selectUsedColors,
16058
16750
  selectUser,
16059
- selectUserForm,
16060
- selectUserFormMapping,
16061
- selectUserFormSubmission,
16751
+ selectUserFormRevisionAttachmentsMapping,
16062
16752
  selectUsersAsMapping,
16063
16753
  selectVisibleStatuses,
16064
16754
  selectVisibleUserIds,
@@ -16085,6 +16775,13 @@ export {
16085
16775
  setEnableClustering,
16086
16776
  setEnableDuplicateIssues,
16087
16777
  setEnablePlacementMode,
16778
+ setFormRevision,
16779
+ setFormRevisionAttachments,
16780
+ setFormRevisions,
16781
+ setFormSubmission,
16782
+ setFormSubmissionAttachments,
16783
+ setFormSubmissions,
16784
+ setForms,
16088
16785
  setIsFetchingInitialData,
16089
16786
  setIsImportingProjectFile,
16090
16787
  setIsLoading,
@@ -16106,12 +16803,11 @@ export {
16106
16803
  setSectionExpanded,
16107
16804
  setShowTooltips,
16108
16805
  setStageCompletions,
16806
+ setTeam,
16807
+ setTeams,
16109
16808
  setTokens,
16110
16809
  setTourStep,
16111
16810
  setUploadUrl,
16112
- setUserFormRevisionAttachments,
16113
- setUserFormSubmissionAttachments,
16114
- setUserFormSubmissions,
16115
16811
  setUsers,
16116
16812
  setVisibleStatuses,
16117
16813
  setVisibleUserIds,
@@ -16122,6 +16818,8 @@ export {
16122
16818
  slugify,
16123
16819
  spacesToDashesLower,
16124
16820
  successColor,
16821
+ teamReducer,
16822
+ teamSlice,
16125
16823
  toFileNameSafeString,
16126
16824
  toOfflineIdRecord,
16127
16825
  toggleComponentTypeVisibility,
@@ -16136,15 +16834,18 @@ export {
16136
16834
  updateComponentTypeAttachment,
16137
16835
  updateDocumentAttachment,
16138
16836
  updateDocuments,
16837
+ updateFormSubmission,
16838
+ updateFormSubmissionAttachments,
16839
+ updateFormSubmissions,
16139
16840
  updateIssue,
16140
16841
  updateIssueAttachment,
16141
16842
  updateLicense,
16142
16843
  updateOrCreateProject,
16143
- updateOrCreateUserFormSubmission,
16144
16844
  updateOrganizationAccess,
16145
16845
  updateProjectAccess,
16146
16846
  updateProjectAttachment,
16147
16847
  updateStages,
16848
+ updateTeam,
16148
16849
  useAppDispatch,
16149
16850
  useAppSelector,
16150
16851
  useFieldInput,
@@ -16154,8 +16855,6 @@ export {
16154
16855
  useFormikInput,
16155
16856
  useMemoCompare,
16156
16857
  useSDK,
16157
- userFormReducer,
16158
- userFormSlice,
16159
16858
  userReducer,
16160
16859
  userSlice,
16161
16860
  valueIsFile,