@overmap-ai/core 1.0.58-no-hardcoding-api-urls.0 → 1.0.58-sign-up-hotfix.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1738,6 +1738,16 @@ const assetSlice = createSlice({
1738
1738
  }
1739
1739
  prevAssets = null;
1740
1740
  },
1741
+ updateAssets: (state, action) => {
1742
+ for (const asset of action.payload) {
1743
+ if (asset.offline_id in state.assets) {
1744
+ state.assets[asset.offline_id] = asset;
1745
+ } else {
1746
+ throw new Error(`Tried to update asset with ID that doesn't exist: ${asset.offline_id}`);
1747
+ }
1748
+ }
1749
+ prevAssets = null;
1750
+ },
1741
1751
  removeAsset: (state, action) => {
1742
1752
  if (action.payload in state.assets) {
1743
1753
  delete state.assets[action.payload];
@@ -1746,6 +1756,16 @@ const assetSlice = createSlice({
1746
1756
  }
1747
1757
  prevAssets = null;
1748
1758
  },
1759
+ removeAssets: (state, action) => {
1760
+ for (const assetId of action.payload) {
1761
+ if (assetId in state.assets) {
1762
+ delete state.assets[assetId];
1763
+ } else {
1764
+ throw new Error(`Failed to remove asset because ID doesn't exist: ${assetId}`);
1765
+ }
1766
+ }
1767
+ prevAssets = null;
1768
+ },
1749
1769
  removeAllAssetsOfType: (state, action) => {
1750
1770
  var _a2;
1751
1771
  for (const componentId in state.assets) {
@@ -1769,7 +1789,9 @@ const assetSlice = createSlice({
1769
1789
  const {
1770
1790
  addAsset,
1771
1791
  updateAsset,
1792
+ updateAssets,
1772
1793
  removeAsset,
1794
+ removeAssets,
1773
1795
  addAssetsInBatches,
1774
1796
  setAssets,
1775
1797
  removeAllAssetsOfType,
@@ -3810,17 +3832,15 @@ const selectFilteredForms = restructureCreateSelectorWithArgs(
3810
3832
  (_state, search) => search
3811
3833
  ],
3812
3834
  (userForms, revisions, search) => {
3813
- const { searchTerm, maxResults, favorites, owner_organization, owner_user } = search;
3835
+ const { searchTerm, maxResults, favorites, organization } = search;
3814
3836
  const favoriteMatches = [];
3815
3837
  const regularMatches = [];
3816
3838
  for (const [userFormId, userForm] of Object.entries(userForms)) {
3817
3839
  if (favorites !== void 0 && userForm.favorite != favorites)
3818
3840
  continue;
3819
- if (Number.isInteger(owner_organization) && owner_organization !== userForm.owner_organization) {
3841
+ if (Number.isInteger(organization) && organization !== userForm.organization) {
3820
3842
  continue;
3821
3843
  }
3822
- if (Number.isInteger(owner_user) && owner_user !== userForm.owner_user)
3823
- continue;
3824
3844
  const latestRevision = _selectLatestFormRevision(revisions, userFormId);
3825
3845
  if (latestRevision.title.toLowerCase().includes(searchTerm.toLowerCase())) {
3826
3846
  if (userForm.favorite) {
@@ -4605,10 +4625,6 @@ function handleWorkspaceRemoval(draft, action) {
4605
4625
  throw new Error(`Failed to update index_workspace of issue ${issue.offline_id} to main workspace`);
4606
4626
  }
4607
4627
  }
4608
- const indexedForms = Object.values(draft.formReducer.forms).filter((form) => form.index_workspace === workspaceId);
4609
- for (const form of indexedForms) {
4610
- form.index_workspace = mainWorkspace.offline_id;
4611
- }
4612
4628
  }
4613
4629
  const rootReducer = (state, action) => {
4614
4630
  if (action.type === "auth/setLoggedIn" && !action.payload) {
@@ -5378,6 +5394,15 @@ class CategoryService extends BaseApiService {
5378
5394
  store.dispatch(setCategories(result));
5379
5395
  }
5380
5396
  }
5397
+ function chunkArray(arr, chunkSize) {
5398
+ const chunks = [];
5399
+ let index2 = 0;
5400
+ const arrLength = arr.length;
5401
+ while (index2 < arrLength) {
5402
+ chunks.push(arr.slice(index2, index2 += chunkSize));
5403
+ }
5404
+ return chunks;
5405
+ }
5381
5406
  class AssetService extends BaseApiService {
5382
5407
  // Basic CRUD functions
5383
5408
  add(asset, workspaceId) {
@@ -5454,36 +5479,55 @@ class AssetService extends BaseApiService {
5454
5479
  throw err;
5455
5480
  });
5456
5481
  }
5457
- async addBatch(assetsToCreate, workspaceId, assetTypeId) {
5458
- const fullAssets = assetsToCreate.map((asset) => {
5459
- return { ...offline(asset), submitted_at: (/* @__PURE__ */ new Date()).toISOString() };
5460
- });
5482
+ // TODO: payload does not require asset_type
5483
+ bulkAdd(assetsToCreate, workspaceId, assetTypeId, batchSize) {
5461
5484
  const { store } = this.client;
5462
- store.dispatch(addAssetsInBatches(fullAssets));
5463
- const promise = this.client.enqueueRequest({
5464
- description: "Batch create assets",
5465
- method: HttpMethod.POST,
5466
- url: `/assets/types/${assetTypeId}/add-assets/`,
5467
- queryParams: {
5468
- workspace_id: workspaceId.toString()
5469
- },
5470
- payload: {
5471
- assets: fullAssets
5472
- },
5473
- blockers: [assetTypeId],
5474
- blocks: fullAssets.map((c) => c.offline_id)
5485
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
5486
+ const transactionId = v4();
5487
+ const assetBatches = chunkArray(assetsToCreate, batchSize).map((assetBatch) => {
5488
+ const assetPayloads = assetBatch.map((assetPayload) => {
5489
+ return offline({
5490
+ ...assetPayload,
5491
+ submitted_at: submittedAt
5492
+ });
5493
+ });
5494
+ return {
5495
+ batchId: v4(),
5496
+ payload: {
5497
+ transaction_id: transactionId,
5498
+ assets: assetPayloads
5499
+ }
5500
+ };
5475
5501
  });
5476
- void promise.then((result) => {
5477
- for (const assets of Object.values(result)) {
5478
- store.dispatch(updateAsset(assets));
5479
- }
5480
- }).catch((e) => {
5481
- for (const asset of fullAssets) {
5482
- store.dispatch(removeAsset(asset.offline_id));
5483
- }
5484
- throw e;
5502
+ const batchPromises = [];
5503
+ let prevBatchId = null;
5504
+ for (const assetBatch of assetBatches) {
5505
+ const { batchId, payload } = assetBatch;
5506
+ const batchAssetOfflineIds = payload.assets.map((c) => c.offline_id);
5507
+ const blockers = [assetTypeId];
5508
+ if (prevBatchId)
5509
+ blockers.push(prevBatchId);
5510
+ const blocks = batchAssetOfflineIds;
5511
+ blocks.push(batchId);
5512
+ const promise = this.client.enqueueRequest({
5513
+ description: "Batch create assets",
5514
+ method: HttpMethod.POST,
5515
+ url: `/assets/types/${assetTypeId}/add-assets/`,
5516
+ queryParams: {
5517
+ workspace_id: workspaceId.toString()
5518
+ },
5519
+ payload,
5520
+ blockers,
5521
+ blocks
5522
+ });
5523
+ prevBatchId = assetBatch.batchId;
5524
+ batchPromises.push(promise);
5525
+ }
5526
+ void Promise.all(batchPromises).then((result) => {
5527
+ const allCreatedAssets = result.flat();
5528
+ store.dispatch(addAssetsInBatches(allCreatedAssets));
5485
5529
  });
5486
- return promise;
5530
+ return batchPromises;
5487
5531
  }
5488
5532
  async refreshStore() {
5489
5533
  const { store } = this.client;
@@ -6617,6 +6661,7 @@ class MainService extends BaseApiService {
6617
6661
  store.dispatch(addOrReplaceProjects(projects));
6618
6662
  store.dispatch(addOrReplaceWorkspaces(workspaces));
6619
6663
  }
6664
+ console.debug("currentProjectId", currentProjectId);
6620
6665
  if (!currentProjectId) {
6621
6666
  store.dispatch(setIsFetchingInitialData(false));
6622
6667
  } else {
@@ -6975,8 +7020,7 @@ class ProjectService extends BaseApiService {
6975
7020
  });
6976
7021
  }
6977
7022
  }
6978
- const separateImageFromFields = async (payload) => {
6979
- const { fields } = payload;
7023
+ const separateImageFromFields = async (fields) => {
6980
7024
  const images = {};
6981
7025
  const newFields = [];
6982
7026
  for (const section of fields) {
@@ -7002,11 +7046,7 @@ const separateImageFromFields = async (payload) => {
7002
7046
  }
7003
7047
  newFields.push({ ...section, fields: newSectionFields });
7004
7048
  }
7005
- const payloadWithoutImage = {
7006
- ...payload,
7007
- fields: newFields
7008
- };
7009
- return { payloadWithoutImage, images };
7049
+ return { fields: newFields, images };
7010
7050
  };
7011
7051
  class UserFormService extends BaseApiService {
7012
7052
  constructor() {
@@ -7040,93 +7080,90 @@ class UserFormService extends BaseApiService {
7040
7080
  });
7041
7081
  });
7042
7082
  }
7043
- async add(state, initialRevision, url, ownerUser, ownerOrganization, assetTypeId, issueTypeId) {
7044
- if (!!ownerUser === !!ownerOrganization) {
7045
- throw new Error("Exactly one of ownerUser and ownerOrganization must be defined.");
7046
- }
7047
- const ownerAttrs = {
7048
- owner_user: ownerUser,
7049
- owner_organization: ownerOrganization
7050
- };
7051
- const currentUser = state.userReducer.currentUser;
7052
- const activeWorkspaceId = state.workspaceReducer.activeWorkspaceId;
7053
- const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
7054
- const offlineFormPayload = offline({ ...ownerAttrs });
7055
- const offlineRevisionPayload = offline({ ...initialRevision, submitted_at: submittedAt });
7056
- const retForm = {
7057
- ...offlineFormPayload,
7058
- index_workspace: activeWorkspaceId,
7059
- favorite: true,
7060
- submitted_at: submittedAt,
7061
- created_by: currentUser.id,
7062
- ...assetTypeId && { asset_type: assetTypeId },
7063
- ...issueTypeId && { issue_type: issueTypeId },
7064
- ...ownerAttrs
7065
- };
7066
- const { payloadWithoutImage, images } = await separateImageFromFields(offlineRevisionPayload);
7067
- const retRevision = {
7068
- ...payloadWithoutImage,
7069
- created_by: currentUser.id,
7070
- form: retForm.offline_id,
7071
- revision: 0,
7072
- submitted_at: submittedAt
7073
- };
7083
+ async add(ownerId, form, initialRevision, urlPrefix) {
7074
7084
  const { store } = this.client;
7075
- store.dispatch(addForm(retForm));
7076
- store.dispatch(addFormRevision(retRevision));
7085
+ const { fields, images } = await separateImageFromFields(initialRevision.fields);
7086
+ const offlineFormRevision = offline({
7087
+ ...initialRevision,
7088
+ fields,
7089
+ created_by: form.created_by,
7090
+ form: form.offline_id,
7091
+ submitted_at: form.submitted_at,
7092
+ revision: "Pending"
7093
+ });
7094
+ store.dispatch(addForm(form));
7095
+ store.dispatch(addFormRevision(offlineFormRevision));
7077
7096
  const formPromise = this.client.enqueueRequest({
7078
7097
  description: "Create form",
7079
7098
  method: HttpMethod.POST,
7080
- url,
7081
- queryParams: activeWorkspaceId ? {
7082
- workspace_id: activeWorkspaceId
7083
- } : void 0,
7099
+ url: urlPrefix,
7084
7100
  payload: {
7085
- ...offlineFormPayload,
7086
- ...assetTypeId && { asset_type: assetTypeId },
7087
- ...issueTypeId && { issue_type: issueTypeId },
7088
- initial_revision: payloadWithoutImage
7101
+ // Sending exactly what is currently needed for the endpoint
7102
+ offline_id: form.offline_id,
7103
+ initial_revision: {
7104
+ offline_id: offlineFormRevision.offline_id,
7105
+ submitted_at: offlineFormRevision.submitted_at,
7106
+ title: offlineFormRevision.title,
7107
+ description: offlineFormRevision.description,
7108
+ fields: offlineFormRevision.fields
7109
+ }
7089
7110
  },
7090
- blockers: assetTypeId ? [assetTypeId] : issueTypeId ? [issueTypeId] : [],
7091
- blocks: [offlineFormPayload.offline_id, payloadWithoutImage.offline_id]
7111
+ blockers: [ownerId],
7112
+ blocks: [form.offline_id, offlineFormRevision.offline_id]
7092
7113
  });
7093
- const attachImagesPromises = this.getAttachImagePromises(images, offlineRevisionPayload.offline_id);
7114
+ const attachImagesPromises = this.getAttachImagePromises(images, offlineFormRevision.offline_id);
7094
7115
  void formPromise.catch((e) => {
7095
- store.dispatch(deleteForm(retForm.offline_id));
7096
- store.dispatch(deleteFormRevision(retRevision.offline_id));
7116
+ store.dispatch(deleteForm(form.offline_id));
7117
+ store.dispatch(deleteFormRevision(offlineFormRevision.offline_id));
7097
7118
  throw e;
7098
7119
  });
7099
7120
  const settledPromise = Promise.all([formPromise, ...attachImagesPromises]).then(() => formPromise);
7100
- return [retForm, retRevision, formPromise, settledPromise];
7121
+ return [form, offlineFormRevision, formPromise, settledPromise];
7101
7122
  }
7102
- async addForOrganization(initialRevision, attachedTo) {
7123
+ addForOrganization(organizationId, initialRevision) {
7103
7124
  const state = this.client.store.getState();
7104
- const activeOrganizationId = state.organizationReducer.activeOrganizationId;
7105
- if (!activeOrganizationId) {
7106
- throw new Error("Cannot add forms for organization when there is no active organization.");
7107
- }
7108
- return await this.add(
7109
- state,
7125
+ const offlineForm = offline({
7126
+ favorite: false,
7127
+ created_by: state.userReducer.currentUser.id,
7128
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
7129
+ organization: organizationId
7130
+ });
7131
+ return this.add(
7132
+ organizationId.toString(),
7133
+ offlineForm,
7110
7134
  initialRevision,
7111
- `/forms/in-organization/${activeOrganizationId}/`,
7112
- void 0,
7113
- activeOrganizationId,
7114
- attachedTo && "assetTypeId" in attachedTo ? attachedTo.assetTypeId : void 0,
7115
- attachedTo && "issueTypeId" in attachedTo ? attachedTo.issueTypeId : void 0
7135
+ `/organizations/${organizationId}/create-form/`
7116
7136
  );
7117
7137
  }
7118
- async addForCurrentUser(initialRevision, attachedTo) {
7138
+ addForProject(projectId, initialRevision) {
7119
7139
  const state = this.client.store.getState();
7120
- const currentUser = state.userReducer.currentUser;
7121
- return await this.add(
7122
- state,
7123
- initialRevision,
7124
- "/forms/my-forms/",
7125
- currentUser.id,
7126
- void 0,
7127
- attachedTo && "assetTypeId" in attachedTo ? attachedTo.assetTypeId : void 0,
7128
- attachedTo && "issueTypeId" in attachedTo ? attachedTo.issueTypeId : void 0
7129
- );
7140
+ const offlineForm = offline({
7141
+ favorite: false,
7142
+ created_by: state.userReducer.currentUser.id,
7143
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
7144
+ project: projectId
7145
+ });
7146
+ return this.add(projectId.toString(), offlineForm, initialRevision, `/projects/${projectId}/create-form/`);
7147
+ }
7148
+ addForIssueType(issueTypeId, initialRevision) {
7149
+ const state = this.client.store.getState();
7150
+ const offlineForm = offline({
7151
+ favorite: false,
7152
+ created_by: state.userReducer.currentUser.id,
7153
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
7154
+ issue_type: issueTypeId
7155
+ });
7156
+ return this.add(issueTypeId, offlineForm, initialRevision, `/issues/types/${issueTypeId}/create-form/`);
7157
+ }
7158
+ addForAssetType(assetTypeId, initialRevision) {
7159
+ const state = this.client.store.getState();
7160
+ const offlineForm = offline({
7161
+ favorite: false,
7162
+ created_by: state.userReducer.currentUser.id,
7163
+ submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
7164
+ asset_type: assetTypeId
7165
+ });
7166
+ return this.add(assetTypeId, offlineForm, initialRevision, `/assets/types/${assetTypeId}/create-form/`);
7130
7167
  }
7131
7168
  async createRevision(formId2, revision) {
7132
7169
  const offlineRevision = offline(revision);
@@ -7137,9 +7174,10 @@ class UserFormService extends BaseApiService {
7137
7174
  throw new Error("Cannot create form revision when there is no active project.");
7138
7175
  }
7139
7176
  const currentUserId = state.userReducer.currentUser.id;
7140
- const { payloadWithoutImage, images } = await separateImageFromFields(offlineRevision);
7177
+ const { fields, images } = await separateImageFromFields(offlineRevision.fields);
7141
7178
  const fullRevision = {
7142
- ...payloadWithoutImage,
7179
+ ...offlineRevision,
7180
+ fields,
7143
7181
  created_by: currentUserId,
7144
7182
  revision: "Pending",
7145
7183
  form: formId2,
@@ -7150,9 +7188,14 @@ class UserFormService extends BaseApiService {
7150
7188
  description: "Create form revision",
7151
7189
  method: HttpMethod.PATCH,
7152
7190
  url: `/forms/${formId2}/`,
7153
- payload: { initial_revision: payloadWithoutImage },
7154
- queryParams: {
7155
- project_id: activeProjectId.toString()
7191
+ payload: {
7192
+ initial_revision: {
7193
+ offline_id: fullRevision.offline_id,
7194
+ submitted_at: fullRevision.submitted_at,
7195
+ title: fullRevision.title,
7196
+ description: fullRevision.description,
7197
+ fields: fullRevision.fields
7198
+ }
7156
7199
  },
7157
7200
  blockers: [formId2],
7158
7201
  blocks: [offlineRevision.offline_id]
@@ -7237,16 +7280,68 @@ class UserFormService extends BaseApiService {
7237
7280
  }
7238
7281
  async refreshStore() {
7239
7282
  const { store } = this.client;
7240
- const result = await this.client.enqueueRequest({
7241
- description: "Fetch user forms",
7283
+ const activeProjectId = store.getState().projectReducer.activeProjectId;
7284
+ if (!activeProjectId) {
7285
+ throw new Error("No active project");
7286
+ }
7287
+ const forms = [];
7288
+ const revisions = [];
7289
+ const attachments = [];
7290
+ const projectFormsResult = await this.client.enqueueRequest({
7291
+ description: "Fetch project forms",
7242
7292
  method: HttpMethod.GET,
7243
- url: `/forms/in-project/${store.getState().projectReducer.activeProjectId}/forms/`,
7244
- blockers: [],
7293
+ url: `/projects/${activeProjectId}/forms/`,
7294
+ blockers: [activeProjectId.toString()],
7295
+ blocks: []
7296
+ });
7297
+ for (const form of projectFormsResult.forms)
7298
+ forms.push(form);
7299
+ for (const revision of projectFormsResult.revisions)
7300
+ revisions.push(revision);
7301
+ for (const attachment of projectFormsResult.attachments)
7302
+ attachments.push(attachment);
7303
+ const organizationFormsResult = await this.client.enqueueRequest({
7304
+ description: "Fetch organization forms",
7305
+ method: HttpMethod.GET,
7306
+ url: `/projects/${activeProjectId}/organizations/forms/`,
7307
+ blockers: [activeProjectId.toString()],
7308
+ blocks: []
7309
+ });
7310
+ for (const form of organizationFormsResult.forms)
7311
+ forms.push(form);
7312
+ for (const revision of organizationFormsResult.revisions)
7313
+ revisions.push(revision);
7314
+ for (const attachment of organizationFormsResult.attachments)
7315
+ attachments.push(attachment);
7316
+ const assetTypeFormsResult = await this.client.enqueueRequest({
7317
+ description: "Fetch asset type forms",
7318
+ method: HttpMethod.GET,
7319
+ url: `/projects/${activeProjectId}/asset-types/forms/`,
7320
+ blockers: [activeProjectId.toString()],
7321
+ blocks: []
7322
+ });
7323
+ for (const form of assetTypeFormsResult.forms)
7324
+ forms.push(form);
7325
+ for (const revision of assetTypeFormsResult.latest_revisions)
7326
+ revisions.push(revision);
7327
+ for (const attachment of assetTypeFormsResult.attachments)
7328
+ attachments.push(attachment);
7329
+ const issueTypeFormsResult = await this.client.enqueueRequest({
7330
+ description: "Fetch issue type forms",
7331
+ method: HttpMethod.GET,
7332
+ url: `/projects/${activeProjectId}/issue-types/forms/`,
7333
+ blockers: [activeProjectId.toString()],
7245
7334
  blocks: []
7246
7335
  });
7247
- store.dispatch(setForms(Object.values(result.forms)));
7248
- store.dispatch(setFormRevisions(Object.values(result.revisions)));
7249
- store.dispatch(setFormRevisionAttachments(Object.values(result.attachments)));
7336
+ for (const form of issueTypeFormsResult.forms)
7337
+ forms.push(form);
7338
+ for (const revision of issueTypeFormsResult.latest_revisions)
7339
+ revisions.push(revision);
7340
+ for (const attachment of issueTypeFormsResult.attachments)
7341
+ attachments.push(attachment);
7342
+ store.dispatch(setForms(forms));
7343
+ store.dispatch(setFormRevisions(revisions));
7344
+ store.dispatch(setFormRevisionAttachments(attachments));
7250
7345
  }
7251
7346
  }
7252
7347
  const isArrayOfFiles = (value) => {
@@ -7349,100 +7444,120 @@ class UserFormSubmissionService extends BaseApiService {
7349
7444
  }
7350
7445
  // Note currently the bulkAdd method is specific to form submissions for assets
7351
7446
  // TODO: adapt the support bulk adding to any model type
7352
- async bulkAdd(args) {
7353
- const { formRevision, values: argsValues, assetOfflineIds } = args;
7447
+ async bulkAdd(args, batchSize) {
7354
7448
  const { store } = this.client;
7355
- const offlineSubmissions = [];
7356
- const offlineAttachments = [];
7357
- const submissionOfflineIds = [];
7358
- const submissionsPayload = [];
7359
- const attachmentsPayload = [];
7360
- const { values, files } = separateFilesFromValues(argsValues);
7449
+ const { formRevision, commonFieldValues, fieldValuesByAsset } = args;
7450
+ const allFilesRecord = {};
7451
+ const { values: fileSeperatedCommonFieldValues, files: commonFiles } = separateFilesFromValues(commonFieldValues);
7361
7452
  const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
7362
- const createdBy = store.getState().userReducer.currentUser.id;
7363
- for (const assetId of assetOfflineIds) {
7364
- const submission = offline({
7365
- form_revision: formRevision,
7366
- values,
7367
- created_by: createdBy,
7368
- submitted_at: submittedAt,
7369
- asset: assetId
7370
- });
7371
- submissionOfflineIds.push(submission.offline_id);
7372
- submissionsPayload.push({ offline_id: submission.offline_id, asset_id: assetId });
7373
- offlineSubmissions.push(submission);
7374
- for (const [fieldIdentifier, fileArray] of Object.entries(files)) {
7375
- for (const file of fileArray) {
7376
- const sha1 = await hashFile(file);
7377
- await this.client.files.addCache(file, sha1);
7378
- const offlineAttachment = offline({
7379
- file_name: file.name,
7380
- file_sha1: sha1,
7381
- file: URL.createObjectURL(file),
7382
- submission: submission.offline_id,
7383
- field_identifier: fieldIdentifier
7453
+ const transactionId = v4();
7454
+ const assetIdBatches = chunkArray(Object.keys(fieldValuesByAsset), batchSize);
7455
+ const bulkAddBatches = await Promise.all(
7456
+ assetIdBatches.map(async (assetIdBatch) => {
7457
+ const batchId = v4();
7458
+ const submissionPayloads = [];
7459
+ const attachmentPayloads = [];
7460
+ const files = { ...commonFiles };
7461
+ for (const assetId of assetIdBatch) {
7462
+ const { values: fileSeperatedSubmissionSpecificValues, files: submissionSpecificFiles } = separateFilesFromValues(fieldValuesByAsset[assetId] ?? {});
7463
+ Object.assign(files, submissionSpecificFiles);
7464
+ const submissionPayload = offline({
7465
+ asset_id: assetId,
7466
+ form_data: fileSeperatedSubmissionSpecificValues
7384
7467
  });
7385
- offlineAttachments.push(offlineAttachment);
7386
- attachmentsPayload.push({
7387
- offline_id: offlineAttachment.offline_id,
7388
- submission_id: submission.offline_id,
7468
+ submissionPayloads.push(submissionPayload);
7469
+ for (const [fieldIdentifier, fileArray] of Object.entries(files)) {
7470
+ for (const file of fileArray) {
7471
+ const sha1 = await hashFile(file);
7472
+ await this.client.files.addCache(file, sha1);
7473
+ const attachmentPayload = offline({
7474
+ submission_id: submissionPayload.offline_id,
7475
+ sha1,
7476
+ name: file.name,
7477
+ field_identifier: fieldIdentifier
7478
+ });
7479
+ attachmentPayloads.push(attachmentPayload);
7480
+ }
7481
+ }
7482
+ }
7483
+ const filePaylods = [];
7484
+ for (const file of Object.values(files).flat()) {
7485
+ const sha1 = await hashFile(file);
7486
+ const filePayload = {
7389
7487
  sha1,
7390
- name: file.name,
7391
- field_identifier: fieldIdentifier
7488
+ extension: file.name.split(".").pop() || "",
7489
+ file_type: file.type,
7490
+ size: file.size
7491
+ };
7492
+ allFilesRecord[sha1] = filePayload;
7493
+ filePaylods.push(filePayload);
7494
+ }
7495
+ return {
7496
+ batchId,
7497
+ payload: {
7498
+ transaction_id: transactionId,
7499
+ form_data: fileSeperatedCommonFieldValues,
7500
+ submitted_at: submittedAt,
7501
+ submissions: submissionPayloads,
7502
+ attachments: attachmentPayloads,
7503
+ files: filePaylods
7504
+ }
7505
+ };
7506
+ })
7507
+ );
7508
+ const batchPromises = [];
7509
+ let prevBatchId = null;
7510
+ for (const batch of bulkAddBatches) {
7511
+ const { payload, batchId } = batch;
7512
+ const batchAssetIds = payload.submissions.map((x) => x.asset_id);
7513
+ const batchSubmissionOfflineIds = payload.submissions.map((x) => x.offline_id);
7514
+ const batchAttachmentsOfflineIds = payload.attachments.map((x) => x.offline_id);
7515
+ const blockers = batchAssetIds;
7516
+ if (prevBatchId)
7517
+ blockers.push(prevBatchId);
7518
+ const blocks = [...batchSubmissionOfflineIds, ...batchAttachmentsOfflineIds, batchId];
7519
+ const promise = this.client.enqueueRequest({
7520
+ description: "Bulk add form submissions",
7521
+ method: HttpMethod.POST,
7522
+ url: `/forms/revisions/${formRevision}/bulk-respond/`,
7523
+ payload,
7524
+ blockers,
7525
+ blocks
7526
+ });
7527
+ void promise.then(({ presigned_urls }) => {
7528
+ for (const [sha1, presignedUrl] of Object.entries(presigned_urls)) {
7529
+ const file = allFilesRecord[sha1];
7530
+ if (!file)
7531
+ continue;
7532
+ void this.client.enqueueRequest({
7533
+ url: presignedUrl.url,
7534
+ description: "Upload file",
7535
+ method: HttpMethod.POST,
7536
+ isExternalUrl: true,
7537
+ isAuthNeeded: false,
7538
+ attachmentHash: sha1,
7539
+ blockers: [`s3-${file.sha1}.${file.extension}`],
7540
+ blocks: [sha1],
7541
+ s3url: presignedUrl
7392
7542
  });
7393
7543
  }
7394
- }
7395
- }
7396
- const filesRecord = {};
7397
- for (const file of Object.values(files).flat()) {
7398
- const sha1 = await hashFile(file);
7399
- filesRecord[sha1] = {
7400
- sha1,
7401
- extension: file.name.split(".").pop() || "",
7402
- file_type: file.type,
7403
- size: file.size
7404
- };
7405
- }
7406
- store.dispatch(addFormSubmissions(offlineSubmissions));
7407
- store.dispatch(addFormSubmissionAttachments(offlineAttachments));
7408
- const promise = this.client.enqueueRequest({
7409
- description: "Bulk add form submissions",
7410
- method: HttpMethod.POST,
7411
- url: `/forms/revisions/${formRevision}/bulk-respond/`,
7412
- payload: {
7413
- form_data: values,
7414
- submitted_at: submittedAt,
7415
- submissions: submissionsPayload,
7416
- attachments: attachmentsPayload,
7417
- files: Object.values(filesRecord)
7418
- },
7419
- blockers: assetOfflineIds,
7420
- blocks: submissionOfflineIds
7421
- });
7422
- promise.then(({ submissions, attachments, presigned_urls }) => {
7423
- store.dispatch(updateFormSubmissions(submissions));
7424
- store.dispatch(updateFormSubmissionAttachments(attachments));
7425
- for (const [sha1, presigned_url] of Object.entries(presigned_urls)) {
7426
- const file = filesRecord[sha1];
7427
- if (!file)
7428
- continue;
7429
- void this.client.enqueueRequest({
7430
- url: presigned_url.url,
7431
- description: "Upload file",
7432
- method: HttpMethod.POST,
7433
- isExternalUrl: true,
7434
- isAuthNeeded: false,
7435
- attachmentHash: sha1,
7436
- blockers: [`s3-${file.sha1}.${file.extension}`],
7437
- blocks: [sha1],
7438
- s3url: presigned_url
7439
- });
7440
- }
7441
- }).catch(() => {
7442
- store.dispatch(deleteFormSubmissions(submissionOfflineIds));
7443
- store.dispatch(deleteFormSubmissionAttachments(offlineAttachments.map((x) => x.offline_id)));
7544
+ });
7545
+ prevBatchId = batchId;
7546
+ batchPromises.push(promise);
7547
+ }
7548
+ void Promise.all(batchPromises).then((results) => {
7549
+ const createdSubmissions = [];
7550
+ const createdAttachments = [];
7551
+ for (const result of results) {
7552
+ for (const createdSubmission of result.submissions)
7553
+ createdSubmissions.push(createdSubmission);
7554
+ for (const createdAttachment of result.attachments)
7555
+ createdAttachments.push(createdAttachment);
7556
+ }
7557
+ store.dispatch(addFormSubmissions(createdSubmissions));
7558
+ store.dispatch(addFormSubmissionAttachments(createdAttachments));
7444
7559
  });
7445
- return [offlineSubmissions, promise.then(({ submissions }) => submissions)];
7560
+ return batchPromises;
7446
7561
  }
7447
7562
  update(submission) {
7448
7563
  const { store } = this.client;
@@ -8652,8 +8767,8 @@ class OvermapSDK {
8652
8767
  }
8653
8768
  const makeClient = (apiUrl, store) => new OvermapSDK(apiUrl, store);
8654
8769
  const SDKContext = React__default.createContext({});
8655
- const SDKProvider = memo(({ children, apiUrl, store }) => {
8656
- const client = useMemo(() => makeClient(apiUrl, store), [apiUrl, store]);
8770
+ const SDKProvider = memo(({ children, API_URL, store }) => {
8771
+ const client = useMemo(() => makeClient(API_URL, store), [API_URL, store]);
8657
8772
  useEffect(() => {
8658
8773
  setClientStore(store);
8659
8774
  }, [store]);
@@ -8664,9 +8779,11 @@ const useSDK = () => {
8664
8779
  return React__default.useContext(SDKContext);
8665
8780
  };
8666
8781
  const OvermapContext = React__default.createContext(null);
8782
+ const PRODUCTION_URL = "https://api.wordn.io";
8783
+ const STAGING_URL = "https://test-api.hemora.ca";
8667
8784
  const OvermapProvider = (props) => {
8668
- const { children, disableDefaultTheme = false, store, apiUrl } = props;
8669
- let ret = /* @__PURE__ */ jsx(AlertDialogProvider, { children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsx(SDKProvider, { store, apiUrl, children }) }) });
8785
+ const { children, production, disableDefaultTheme = false, store } = props;
8786
+ let ret = /* @__PURE__ */ jsx(AlertDialogProvider, { children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsx(SDKProvider, { API_URL: production ? PRODUCTION_URL : STAGING_URL, store, children }) }) });
8670
8787
  if (!disableDefaultTheme) {
8671
8788
  ret = /* @__PURE__ */ jsx(DefaultTheme, { children: ret });
8672
8789
  }
@@ -8795,6 +8912,12 @@ class BaseField extends BaseFormElement {
8795
8912
  getFormValidators() {
8796
8913
  return [...this.formValidators];
8797
8914
  }
8915
+ encodeValueToJson(value) {
8916
+ return JSON.stringify(value);
8917
+ }
8918
+ decodeJsonToValue(json) {
8919
+ return JSON.parse(json);
8920
+ }
8798
8921
  }
8799
8922
  __publicField(BaseField, "fieldTypeName");
8800
8923
  __publicField(BaseField, "fieldTypeDescription");
@@ -13048,10 +13171,7 @@ const MultiStringInput = memo((props) => {
13048
13171
  helpText = showInputOnly ? null : helpText;
13049
13172
  label = showInputOnly ? "" : label;
13050
13173
  const color = useSeverityColor(severity);
13051
- const value = useMemo(
13052
- () => Array.isArray(fieldProps.value) ? fieldProps.value : [],
13053
- [fieldProps.value]
13054
- );
13174
+ const value = useMemo(() => Array.isArray(fieldProps.value) ? fieldProps.value : [], [fieldProps.value]);
13055
13175
  const { onChange, onBlur } = fieldProps;
13056
13176
  const droppableId = `${inputId}-droppable`;
13057
13177
  const { disabled } = rest;
@@ -13068,7 +13188,7 @@ const MultiStringInput = memo((props) => {
13068
13188
  );
13069
13189
  const handleChange = useCallback(
13070
13190
  (e) => {
13071
- if (value.findIndex((option) => option.value === e.target.value.trim()) >= 0) {
13191
+ if (value.findIndex((option) => option === e.target.value.trim()) >= 0) {
13072
13192
  setInternalError("All options must be unique");
13073
13193
  } else if (!e.target.value) {
13074
13194
  setInternalError("Option cannot be empty");
@@ -13087,7 +13207,7 @@ const MultiStringInput = memo((props) => {
13087
13207
  return;
13088
13208
  }
13089
13209
  const trimmedValue = intermediateValue.trim();
13090
- setValueAndTouched([...value, { value: trimmedValue, label: trimmedValue }]);
13210
+ setValueAndTouched([...value, trimmedValue]);
13091
13211
  setIntermediateValue("");
13092
13212
  }, [intermediateValue, internalError, setValueAndTouched, value]);
13093
13213
  const handleKeyDown = useCallback(
@@ -13157,7 +13277,7 @@ const MultiStringInput = memo((props) => {
13157
13277
  value.map((option, index2) => /* @__PURE__ */ jsx(
13158
13278
  Draggable,
13159
13279
  {
13160
- draggableId: `${option.value}-draggable`,
13280
+ draggableId: `${option}-draggable`,
13161
13281
  index: index2,
13162
13282
  isDragDisabled: disabled,
13163
13283
  children: ({ draggableProps, dragHandleProps, innerRef }) => /* @__PURE__ */ jsx(
@@ -13172,7 +13292,10 @@ const MultiStringInput = memo((props) => {
13172
13292
  mb: "1",
13173
13293
  asChild: true,
13174
13294
  children: /* @__PURE__ */ jsxs(Badge, { color: "gray", size: "2", children: [
13175
- /* @__PURE__ */ jsx("span", { children: option.label }),
13295
+ /* @__PURE__ */ jsx("span", {
13296
+ // TODO: remove this, its just a saftey check for old compatibility of what was acceptable as a value for multi string
13297
+ children: typeof option === "object" && "label" in option ? option.label : option
13298
+ }),
13176
13299
  /* @__PURE__ */ jsx(
13177
13300
  IconButton,
13178
13301
  {
@@ -13192,7 +13315,7 @@ const MultiStringInput = memo((props) => {
13192
13315
  }
13193
13316
  )
13194
13317
  },
13195
- option.value
13318
+ option
13196
13319
  )),
13197
13320
  droppableProvided.placeholder
13198
13321
  ] }) })
@@ -14867,6 +14990,44 @@ function formRevisionToSchema(formRevision, meta = {}) {
14867
14990
  meta: { readonly }
14868
14991
  };
14869
14992
  }
14993
+ function flattenFields(schema) {
14994
+ const allFields = [];
14995
+ for (const field of schema.fields) {
14996
+ if (field instanceof FieldSection) {
14997
+ for (const subField of field.fields) {
14998
+ allFields.push(subField);
14999
+ }
15000
+ } else {
15001
+ if (!(field instanceof BaseField)) {
15002
+ throw new Error(`Invalid field type: ${field.type}`);
15003
+ }
15004
+ allFields.push(field);
15005
+ }
15006
+ }
15007
+ return allFields;
15008
+ }
15009
+ function decodeFormValues(schema, values) {
15010
+ const allFields = flattenFields(schema);
15011
+ const result = {};
15012
+ for (const field of allFields) {
15013
+ const value = values[field.identifier] ?? null;
15014
+ if (value !== null) {
15015
+ result[field.identifier] = field.decodeJsonToValue(value);
15016
+ } else {
15017
+ result[field.identifier] = value;
15018
+ }
15019
+ }
15020
+ return result;
15021
+ }
15022
+ function encodeFormValues(schema, values) {
15023
+ const allFields = flattenFields(schema);
15024
+ const result = {};
15025
+ for (const field of allFields) {
15026
+ const value = values[field.identifier];
15027
+ result[field.identifier] = field.encodeValueToJson(value);
15028
+ }
15029
+ return result;
15030
+ }
14870
15031
  function valueIsFile(v) {
14871
15032
  return Array.isArray(v) && v.some((v2) => v2 instanceof File || v2 instanceof Promise);
14872
15033
  }
@@ -15243,7 +15404,6 @@ const styles$3 = {
15243
15404
  regularIcon
15244
15405
  };
15245
15406
  const orgOptionPrefix = "organization:";
15246
- const userOptionPrefix = "user:";
15247
15407
  const FormBrowser = memo(
15248
15408
  forwardRef((props, ref) => {
15249
15409
  const { maxResults = 20, ...entryProps } = props;
@@ -15254,9 +15414,7 @@ const FormBrowser = memo(
15254
15414
  const ret = { maxResults, searchTerm: filter };
15255
15415
  if (ownerFilter) {
15256
15416
  if (ownerFilter.startsWith(orgOptionPrefix)) {
15257
- ret.owner_organization = parseInt(ownerFilter.slice(orgOptionPrefix.length));
15258
- } else if (ownerFilter.startsWith(userOptionPrefix)) {
15259
- ret.owner_user = parseInt(ownerFilter.slice(userOptionPrefix.length));
15417
+ ret.organization = parseInt(ownerFilter.slice(orgOptionPrefix.length));
15260
15418
  }
15261
15419
  }
15262
15420
  return ret;
@@ -15281,14 +15439,10 @@ const FormBrowser = memo(
15281
15439
  const state = sdk.store.getState();
15282
15440
  const accumulator = {};
15283
15441
  for (const form of attachableUserFormMapping) {
15284
- const organization = selectOrganization(form.owner_organization || -1)(state);
15442
+ const organization = selectOrganization(form.organization || -1)(state);
15285
15443
  if (organization) {
15286
15444
  accumulator[`${orgOptionPrefix}${organization.id}`] = organization.name;
15287
15445
  }
15288
- const user = selectUser(form.owner_user || -1)(state);
15289
- if (user) {
15290
- accumulator[`${userOptionPrefix}${user.id}`] = user.username;
15291
- }
15292
15446
  }
15293
15447
  return Object.entries(accumulator).map(([value, label]) => ({ itemContent: label, value }));
15294
15448
  }, [sdk.store, attachableUserFormMapping]);
@@ -15330,11 +15484,7 @@ const FormBrowser = memo(
15330
15484
  const FormBrowserEntry = (props) => {
15331
15485
  var _a2;
15332
15486
  const { form, onSelectForm, isFavoriteEditable, handleToggleFavorite } = props;
15333
- const ownerOrganization = (_a2 = useAppSelector(selectOrganization(form.owner_organization || -1))) == null ? void 0 : _a2.name;
15334
- const ownerUser = useAppSelector(selectUser(form.owner_user || -1));
15335
- const currentUserId = useAppSelector(selectCurrentUser).id;
15336
- const ownedByCurrentUser = !!ownerUser && ownerUser.id === currentUserId;
15337
- const owner = ownerOrganization ?? (ownedByCurrentUser ? "You" : ownerUser == null ? void 0 : ownerUser.username) ?? "Unknown";
15487
+ const ownerOrganization = (_a2 = useAppSelector(selectOrganization(form.organization || -1))) == null ? void 0 : _a2.name;
15338
15488
  const handleFavoriteClick = useCallback(
15339
15489
  (e) => {
15340
15490
  e.stopPropagation();
@@ -15365,10 +15515,10 @@ const FormBrowserEntry = (props) => {
15365
15515
  /* @__PURE__ */ jsx(Text$1, { noWrap: true, children: form.latestRevision.title }),
15366
15516
  form.latestRevision.description && /* @__PURE__ */ jsx(RiIcon, { icon: "RiQuestionLine" })
15367
15517
  ] }),
15368
- owner && /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
15518
+ ownerOrganization && /* @__PURE__ */ jsxs(Flex$1, { align: "center", gap: "2", children: [
15369
15519
  /* @__PURE__ */ jsx(RiIcon, { icon: "RiUserLine" }),
15370
15520
  " ",
15371
- owner
15521
+ ownerOrganization
15372
15522
  ] })
15373
15523
  ] })
15374
15524
  }
@@ -16648,6 +16798,7 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
16648
16798
  StringInput,
16649
16799
  TextField,
16650
16800
  TextInput,
16801
+ decodeFormValues,
16651
16802
  deserialize,
16652
16803
  deserializeField,
16653
16804
  emptyBaseField,
@@ -16660,11 +16811,15 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
16660
16811
  emptySelectField,
16661
16812
  emptyStringField,
16662
16813
  emptyTextField,
16814
+ encodeFormValues,
16815
+ flattenFields,
16663
16816
  formRevisionToSchema,
16817
+ initialFormValues,
16664
16818
  isConditionMet,
16665
16819
  useFieldInput,
16666
16820
  useFieldInputs,
16667
16821
  useFormikInput,
16822
+ validateForm,
16668
16823
  valueIsFile
16669
16824
  }, Symbol.toStringTag, { value: "Module" }));
16670
16825
  export {
@@ -16856,6 +17011,7 @@ export {
16856
17011
  coordinatesToUrlText,
16857
17012
  createOfflineAction,
16858
17013
  createPointMarker,
17014
+ decodeFormValues,
16859
17015
  defaultBadgeColor,
16860
17016
  defaultStore,
16861
17017
  deleteAssetType,
@@ -16891,6 +17047,7 @@ export {
16891
17047
  emptySelectField,
16892
17048
  emptyStringField,
16893
17049
  emptyTextField,
17050
+ encodeFormValues,
16894
17051
  enqueue,
16895
17052
  enqueueRequest,
16896
17053
  errorColor,
@@ -16898,6 +17055,7 @@ export {
16898
17055
  fileReducer,
16899
17056
  fileSlice,
16900
17057
  fileToBlob,
17058
+ flattenFields,
16901
17059
  flipCoordinates,
16902
17060
  formReducer,
16903
17061
  formRevisionReducer,
@@ -16921,6 +17079,7 @@ export {
16921
17079
  hashFile,
16922
17080
  hideAllCategories,
16923
17081
  hideCategory,
17082
+ initialFormValues,
16924
17083
  isConditionMet,
16925
17084
  isToday,
16926
17085
  issueReducer,
@@ -16974,6 +17133,7 @@ export {
16974
17133
  removeAssetAttachments,
16975
17134
  removeAssetTypeAttachment,
16976
17135
  removeAssetTypeAttachments,
17136
+ removeAssets,
16977
17137
  removeAttachmentsOfIssue,
16978
17138
  removeCategory,
16979
17139
  removeColor,
@@ -17291,6 +17451,7 @@ export {
17291
17451
  updateAssetAttachments,
17292
17452
  updateAssetTypeAttachment,
17293
17453
  updateAssetTypeAttachments,
17454
+ updateAssets,
17294
17455
  updateConversation,
17295
17456
  updateDocumentAttachment,
17296
17457
  updateDocumentAttachments,
@@ -17321,6 +17482,7 @@ export {
17321
17482
  useSDK,
17322
17483
  userReducer,
17323
17484
  userSlice,
17485
+ validateForm,
17324
17486
  valueIsFile,
17325
17487
  warningColor,
17326
17488
  workspaceReducer,