@stoker-platform/web-app 0.5.182 → 0.5.184

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @stoker-platform/web-app
2
2
 
3
+ ## 0.5.184
4
+
5
+ ### Patch Changes
6
+
7
+ - fix: perform bulk write in batches
8
+
9
+ ## 0.5.183
10
+
11
+ ### Patch Changes
12
+
13
+ - fix: improve compact list metric transitions
14
+
3
15
  ## 0.5.182
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoker-platform/web-app",
3
- "version": "0.5.182",
3
+ "version": "0.5.184",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "scripts": {
package/src/Form.tsx CHANGED
@@ -4323,49 +4323,58 @@ function RecordForm({
4323
4323
  return
4324
4324
  }
4325
4325
 
4326
- const updatePromises: Promise<StokerRecord>[] = []
4326
+ const recordsToUpdate = rowSelection.filter((selectedRecord) => selectedRecord.id)
4327
4327
 
4328
- rowSelection.forEach((selectedRecord) => {
4329
- if (selectedRecord.id) {
4330
- setOptimisticUpdate(labels.collection, {
4331
- ...selectedRecord,
4332
- ...cloneDeep(recordToSave),
4333
- } as StokerRecord)
4334
- }
4328
+ recordsToUpdate.forEach((selectedRecord) => {
4329
+ setOptimisticUpdate(labels.collection, {
4330
+ ...selectedRecord,
4331
+ ...cloneDeep(recordToSave),
4332
+ } as StokerRecord)
4335
4333
  })
4336
4334
 
4337
- rowSelection.forEach((selectedRecord) => {
4338
- if (!selectedRecord.id) return
4339
-
4340
- const serverWrite = isServerUpdate(collection, { ...recordToSave, id: selectedRecord.id })
4341
- setGlobalLoading("+", selectedRecord.id, serverWrite, !(serverWrite || isServerReadOnly))
4335
+ const BATCH_SIZE = 5
4336
+ const runUpdates = async () => {
4337
+ for (let i = 0; i < recordsToUpdate.length; i += BATCH_SIZE) {
4338
+ const batch = recordsToUpdate.slice(i, i + BATCH_SIZE)
4339
+ const batchPromises = batch.map((selectedRecord) => {
4340
+ const serverWrite = isServerUpdate(collection, {
4341
+ ...recordToSave,
4342
+ id: selectedRecord.id,
4343
+ })
4344
+ setGlobalLoading("+", selectedRecord.id, serverWrite, !(serverWrite || isServerReadOnly))
4342
4345
 
4343
- const updatePromise = updateRecord(path, selectedRecord.id, recordToSave, {
4344
- originalRecord: selectedRecord,
4345
- })
4346
- .catch((error) => {
4347
- console.error(`Failed to update record ${selectedRecord.id}:`, error)
4348
- toast({
4349
- // eslint-disable-next-line security/detect-object-injection
4350
- description: `${recordTitle} ${recordTitleField ? selectedRecord[recordTitleField] : selectedRecord.id} failed to update.`,
4351
- variant: "destructive",
4346
+ return updateRecord(path, selectedRecord.id, recordToSave, {
4347
+ originalRecord: selectedRecord,
4352
4348
  })
4349
+ .catch((error) => {
4350
+ console.error(`Failed to update record ${selectedRecord.id}:`, error)
4351
+ toast({
4352
+ // eslint-disable-next-line security/detect-object-injection
4353
+ description: `${recordTitle} ${recordTitleField ? selectedRecord[recordTitleField] : selectedRecord.id} failed to update.`,
4354
+ variant: "destructive",
4355
+ })
4356
+ })
4357
+ .finally(() => {
4358
+ removeOptimisticUpdate(labels.collection, selectedRecord.id)
4359
+ setGlobalLoading(
4360
+ "-",
4361
+ selectedRecord.id,
4362
+ undefined,
4363
+ !(serverWrite || isServerReadOnly),
4364
+ )
4365
+ if (serverWrite || isServerReadOnly) {
4366
+ if (onSaveRecord) onSaveRecord()
4367
+ }
4368
+ })
4353
4369
  })
4354
- .finally(() => {
4355
- removeOptimisticUpdate(labels.collection, selectedRecord.id)
4356
- setGlobalLoading("-", selectedRecord.id, undefined, !(serverWrite || isServerReadOnly))
4357
- if (serverWrite || isServerReadOnly) {
4358
- if (onSaveRecord) onSaveRecord()
4359
- }
4360
- })
4361
-
4362
- updatePromises.push(updatePromise as Promise<StokerRecord>)
4363
- })
4370
+ await Promise.all(batchPromises)
4371
+ }
4372
+ }
4364
4373
 
4365
4374
  if (isServerReadOnly) {
4366
- await Promise.all(updatePromises)
4375
+ await runUpdates()
4367
4376
  } else {
4368
- Promise.all(updatePromises)
4377
+ runUpdates()
4369
4378
  }
4370
4379
 
4371
4380
  rowSelection.forEach((selectedRecord) => {
package/src/List.tsx CHANGED
@@ -1232,46 +1232,68 @@ export function List({
1232
1232
  const titles = await getCachedConfigValue(customization, ["collections", labels.collection, "admin", "titles"])
1233
1233
  const recordTitle = titles?.record || labels.record
1234
1234
 
1235
- selectedRecords.forEach((record) => {
1235
+ const recordsToDelete = [...selectedRecords]
1236
+
1237
+ for (const record of recordsToDelete) {
1236
1238
  if (isGlobalLoading.get(record.id)?.server) {
1237
1239
  alert(
1238
1240
  `Record ${record.id} is currently being written to the server. Please wait for it to finish before deleting.`,
1239
1241
  )
1240
1242
  return
1241
1243
  }
1242
- const serverWrite = isServerDelete(collection, record)
1244
+ }
1243
1245
 
1246
+ recordsToDelete.forEach((record) => {
1244
1247
  setOptimisticDelete(labels.collection, record.id)
1248
+ })
1245
1249
 
1246
- setGlobalLoading("+", record.id, serverWrite, !(serverWrite || isServerReadOnly))
1250
+ const BATCH_SIZE = 5
1251
+ const runDeletes = async () => {
1252
+ for (let i = 0; i < recordsToDelete.length; i += BATCH_SIZE) {
1253
+ const batch = recordsToDelete.slice(i, i + BATCH_SIZE)
1254
+ const batchPromises = batch.map((record) => {
1255
+ const serverWrite = isServerDelete(collection, record)
1247
1256
 
1248
- deleteRecord(record.Collection_Path, record.id)
1249
- .then(() => {
1250
- if (isServerReadOnly) {
1251
- backToStart()
1252
- }
1253
- })
1254
- .catch((error) => {
1255
- console.error(error)
1256
- toast({
1257
- // eslint-disable-next-line security/detect-object-injection
1258
- description: `${recordTitle} ${recordTitleField ? record[recordTitleField] : record.id} failed to delete.`,
1259
- variant: "destructive",
1260
- })
1261
- })
1262
- .finally(() => {
1263
- if (serverWrite || isServerReadOnly) {
1257
+ setGlobalLoading("+", record.id, serverWrite, !(serverWrite || isServerReadOnly))
1258
+
1259
+ const deletePromise = deleteRecord(record.Collection_Path, record.id)
1260
+ .then(() => {
1261
+ if (isServerReadOnly) {
1262
+ backToStart()
1263
+ }
1264
+ })
1265
+ .catch((error) => {
1266
+ console.error(error)
1267
+ toast({
1268
+ // eslint-disable-next-line security/detect-object-injection
1269
+ description: `${recordTitle} ${recordTitleField ? record[recordTitleField] : record.id} failed to delete.`,
1270
+ variant: "destructive",
1271
+ })
1272
+ })
1273
+ .finally(() => {
1274
+ if (serverWrite || isServerReadOnly) {
1275
+ removeOptimisticDelete(labels.collection, record.id)
1276
+ }
1277
+ setGlobalLoading("-", record.id, undefined, !(serverWrite || isServerReadOnly))
1278
+ })
1279
+ if (!serverWrite && !isServerReadOnly) {
1264
1280
  removeOptimisticDelete(labels.collection, record.id)
1265
1281
  }
1266
- setGlobalLoading("-", record.id, undefined, !(serverWrite || isServerReadOnly))
1282
+ return deletePromise
1267
1283
  })
1268
- if (!serverWrite && !isServerReadOnly) {
1269
- removeOptimisticDelete(labels.collection, record.id)
1284
+ await Promise.all(batchPromises)
1270
1285
  }
1271
- })
1286
+ }
1287
+
1288
+ if (isServerReadOnly) {
1289
+ await runDeletes()
1290
+ } else {
1291
+ runDeletes()
1292
+ }
1293
+
1272
1294
  setRowSelection({})
1273
1295
  toast({
1274
- description: `Deleting ${selectedRecords.length} ${selectedRecords.length > 1 ? "records" : "record"}.`,
1296
+ description: `Deleting ${recordsToDelete.length} ${recordsToDelete.length > 1 ? "records" : "record"}.`,
1275
1297
  })
1276
1298
  }, [
1277
1299
  collection,
@@ -1513,6 +1535,8 @@ export function List({
1513
1535
  ) {
1514
1536
  const metricTitle = metric.title || `Total ${collectionTitle}`
1515
1537
  if (metric.compact) {
1538
+ // eslint-disable-next-line security/detect-object-injection
1539
+ if (!metricsValues[index]) return null
1516
1540
  return (
1517
1541
  <div
1518
1542
  key={`metric-${index}`}