@saltcorn/data 0.6.1-beta.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/base-plugin/actions.js +172 -1
  2. package/base-plugin/fieldviews.js +63 -0
  3. package/base-plugin/fileviews.js +41 -0
  4. package/base-plugin/index.js +35 -0
  5. package/base-plugin/types.js +345 -9
  6. package/base-plugin/viewtemplates/edit.js +107 -0
  7. package/base-plugin/viewtemplates/feed.js +46 -0
  8. package/base-plugin/viewtemplates/filter.js +43 -0
  9. package/base-plugin/viewtemplates/list.js +73 -1
  10. package/base-plugin/viewtemplates/listshowlist.js +54 -3
  11. package/base-plugin/viewtemplates/room.js +100 -0
  12. package/base-plugin/viewtemplates/show.js +86 -0
  13. package/base-plugin/viewtemplates/viewable_fields.js +124 -0
  14. package/contracts.js +58 -0
  15. package/db/connect.js +13 -5
  16. package/db/db.test.js +0 -154
  17. package/db/fixtures.js +13 -1
  18. package/db/index.js +42 -3
  19. package/db/reset_schema.js +11 -0
  20. package/db/state.js +105 -36
  21. package/index.js +13 -0
  22. package/migrate.js +4 -1
  23. package/models/backup.js +78 -0
  24. package/models/config.js +113 -22
  25. package/models/crash.js +44 -0
  26. package/models/discovery.js +13 -11
  27. package/models/email.js +17 -0
  28. package/models/eventlog.js +51 -0
  29. package/models/expression.js +49 -1
  30. package/models/field.js +88 -9
  31. package/models/fieldrepeat.js +33 -0
  32. package/models/file.js +23 -4
  33. package/models/form.js +34 -0
  34. package/models/index.js +42 -0
  35. package/models/layout.js +33 -0
  36. package/models/library.js +44 -0
  37. package/models/pack.js +88 -0
  38. package/models/page.js +13 -3
  39. package/models/plugin.js +9 -2
  40. package/models/random.js +36 -0
  41. package/models/role.js +36 -0
  42. package/models/scheduler.js +28 -0
  43. package/models/table.js +34 -15
  44. package/models/table_constraints.js +44 -0
  45. package/models/tenant.js +46 -9
  46. package/models/trigger.js +24 -11
  47. package/models/user.js +33 -8
  48. package/models/view.js +89 -8
  49. package/models/workflow.js +31 -0
  50. package/package.json +7 -5
  51. package/plugin-helper.js +102 -44
  52. package/plugin-testing.js +4 -0
  53. package/tests/exact_views.test.js +5 -5
  54. package/utils.js +4 -0
  55. package/db/internal.js +0 -229
  56. package/db/multi-tenant.js +0 -24
  57. package/db/pg.js +0 -374
  58. package/db/single-tenant.js +0 -8
  59. package/db/sqlite.js +0 -280
  60. package/db/tenants.js +0 -6
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @category saltcorn-data
3
+ * @module base-plugin/viewtemplates/edit
4
+ * @subcategory base-plugin
5
+ */
1
6
  const Field = require("../../models/field");
2
7
  const File = require("../../models/file");
3
8
  const Table = require("../../models/table");
@@ -37,6 +42,10 @@ const {
37
42
  } = require("../../models/layout");
38
43
  const { asyncMap } = require("../../utils");
39
44
 
45
+ /**
46
+ * @param {object} req
47
+ * @returns {Workflow}
48
+ */
40
49
  const configuration_workflow = (req) =>
41
50
  new Workflow({
42
51
  steps: [
@@ -240,6 +249,14 @@ const configuration_workflow = (req) =>
240
249
  },
241
250
  ],
242
251
  });
252
+
253
+ /**
254
+ * @param {*} table_id
255
+ * @param {*} viewname
256
+ * @param {object} opts
257
+ * @param {*} opts.columns
258
+ * @returns {Promise<object[]>}
259
+ */
243
260
  const get_state_fields = async (table_id, viewname, { columns }) => [
244
261
  {
245
262
  name: "id",
@@ -248,6 +265,10 @@ const get_state_fields = async (table_id, viewname, { columns }) => [
248
265
  },
249
266
  ];
250
267
 
268
+ /**
269
+ * @param {Form} form
270
+ * @param {string} locale
271
+ */
251
272
  const setDateLocales = (form, locale) => {
252
273
  form.fields.forEach((f) => {
253
274
  if (f.type && f.type.name === "Date") {
@@ -256,8 +277,21 @@ const setDateLocales = (form, locale) => {
256
277
  });
257
278
  };
258
279
 
280
+ /** @type {function} */
259
281
  const initial_config = initial_config_all_fields(true);
260
282
 
283
+ /**
284
+ * @param {number} table_id
285
+ * @param {string} viewname
286
+ * @param {object} optsOne
287
+ * @param {*} optsOne.columns
288
+ * @param {*} optsOne.layout
289
+ * @param {string} state
290
+ * @param {object} optsTwo
291
+ * @param {object} optsTwo.req
292
+ * @param {object} optsTwo.res
293
+ * @returns {Promise<Form>}
294
+ */
261
295
  const run = async (
262
296
  table_id,
263
297
  viewname,
@@ -284,6 +318,17 @@ const run = async (
284
318
  state,
285
319
  });
286
320
  };
321
+
322
+ /**
323
+ * @param {number} table_id
324
+ * @param {string} viewname
325
+ * @param {object} opts
326
+ * @param {*} opts.columns
327
+ * @param {*} opts.layout
328
+ * @param {State} state
329
+ * @param {object} extra
330
+ * @returns {Promise<Form[]>}
331
+ */
287
332
  const runMany = async (
288
333
  table_id,
289
334
  viewname,
@@ -323,6 +368,16 @@ const runMany = async (
323
368
  });
324
369
  };
325
370
 
371
+ /**
372
+ * @param {object} opts
373
+ * @param {Form} opts.form
374
+ * @param {Table} opts.table
375
+ * @param {object} opts.req
376
+ * @param {object} opts.row
377
+ * @param {object} opts.res
378
+ * @throws {InvalidConfiguration}
379
+ * @returns {Promise<void>}
380
+ */
326
381
  const transformForm = async ({ form, table, req, row, res }) => {
327
382
  await traverse(form.layout, {
328
383
  action(segment) {
@@ -371,6 +426,19 @@ const transformForm = async ({ form, table, req, row, res }) => {
371
426
  setDateLocales(form, req.getLocale());
372
427
  };
373
428
 
429
+ /**
430
+ * @param {object} opts
431
+ * @param {Table} opts.table
432
+ * @param {Fields[]} opts.fields
433
+ * @param {string} opts.viewname
434
+ * @param {object[]} opts.columns
435
+ * @param {Layout} opts.layout
436
+ * @param {object} opts.row
437
+ * @param {object} opts.req
438
+ * @param {object} opts.state
439
+ * @param {object} opts.res
440
+ * @returns {Promise<Form>}
441
+ */
374
442
  const render = async ({
375
443
  table,
376
444
  fields,
@@ -413,6 +481,23 @@ const render = async ({
413
481
  return renderForm(form, req.csrfToken());
414
482
  };
415
483
 
484
+ /**
485
+ * @param {number} table_id
486
+ * @param {string} viewname
487
+ * @param {object} optsOne
488
+ * @param {object[]} optsOne.columns
489
+ * @param {Layout} optsOne.layout
490
+ * @param {object} optsOne.fixed
491
+ * @param {boolean} optsOne.view_when_done
492
+ * @param {object[]} optsOne.formula_destinations
493
+ * @param {object} state
494
+ * @param {*} body
495
+ * @param {object} optsTwo
496
+ * @param {object} optsTwo.res
497
+ * @param {object} optsTwo.req
498
+ * @param {string} optsTwo.redirect
499
+ * @returns {Promise<void>}
500
+ */
416
501
  const runPost = async (
417
502
  table_id,
418
503
  viewname,
@@ -526,6 +611,14 @@ const runPost = async (
526
611
  }
527
612
  }
528
613
  };
614
+
615
+ /**
616
+ * @param {object} opts
617
+ * @param {object} opts.body
618
+ * @param {string} opts.table_id
619
+ * @param {object} opts.req
620
+ * @returns {Promise<boolean>}
621
+ */
529
622
  const authorise_post = async ({ body, table_id, req }) => {
530
623
  const table = await Table.findOne({ id: table_id });
531
624
  const user_id = req.user ? req.user.id : null;
@@ -540,7 +633,9 @@ const authorise_post = async ({ body, table_id, req }) => {
540
633
  return false;
541
634
  };
542
635
  module.exports = {
636
+ /** @type {string} */
543
637
  name: "Edit",
638
+ /** @type {string} */
544
639
  description: "Form for creating a new row or editing existing rows",
545
640
  configuration_workflow,
546
641
  run,
@@ -548,10 +643,22 @@ module.exports = {
548
643
  runPost,
549
644
  get_state_fields,
550
645
  initial_config,
646
+ /** @type {boolean} */
551
647
  display_state_form: false,
552
648
  authorise_post,
649
+ /**
650
+ * @param {object} opts
651
+ * @param {object} opts.query
652
+ * @param {...*} opts.rest
653
+ * @returns {Promise<boolean>}
654
+ */
553
655
  authorise_get: async ({ query, ...rest }) =>
554
656
  authorise_post({ body: query, ...rest }),
657
+ /**
658
+ * @param {object} opts
659
+ * @param {Layout} opts.layout
660
+ * @returns {string[]}
661
+ */
555
662
  getStringsForI18n({ layout }) {
556
663
  return getStringsForI18n(layout);
557
664
  },
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @category saltcorn-data
3
+ * @module base-plugin/viewtemplates/feed
4
+ * @subcategory base-plugin
5
+ */
1
6
  const Field = require("../../models/field");
2
7
  const Table = require("../../models/table");
3
8
  const Form = require("../../models/form");
@@ -19,6 +24,10 @@ const {
19
24
  const { InvalidConfiguration } = require("../../utils");
20
25
  const { getState } = require("../../db/state");
21
26
 
27
+ /**
28
+ * @param {object} req
29
+ * @returns {Workflow}
30
+ */
22
31
  const configuration_workflow = (req) =>
23
32
  new Workflow({
24
33
  steps: [
@@ -218,6 +227,13 @@ const configuration_workflow = (req) =>
218
227
  ],
219
228
  });
220
229
 
230
+ /**
231
+ * @param {number} table_id
232
+ * @param {*} viewname
233
+ * @param {object} opts
234
+ * @param {*} opts.show_view
235
+ * @returns {Promise<Field>}
236
+ */
221
237
  const get_state_fields = async (table_id, viewname, { show_view }) => {
222
238
  const table_fields = await Field.find({ table_id });
223
239
  return table_fields
@@ -228,6 +244,28 @@ const get_state_fields = async (table_id, viewname, { show_view }) => {
228
244
  return sf;
229
245
  });
230
246
  };
247
+
248
+ /**
249
+ * @param {number} table_id
250
+ * @param {string} viewname
251
+ * @param {object} opts
252
+ * @param {string} opts.show_view
253
+ * @param {name} opts.order_field
254
+ * @param {boolean} opts.descending
255
+ * @param {string} [opts.view_to_create]
256
+ * @param {string} opts.create_view_display
257
+ * @param {boolean} opts.in_card
258
+ * @param {string} opts.masonry_columns
259
+ * @param {number} [opts.rows_per_page = 20]
260
+ * @param {boolean} opts.hide_pagination
261
+ * @param {string} [opts.create_view_label]
262
+ * @param {string} [opts.create_view_location]
263
+ * @param {boolean} opts.always_create_view
264
+ * @param {*} opts.cols
265
+ * @param {object} state
266
+ * @param {*} extraArgs
267
+ * @returns {Promise<div>}
268
+ */
231
269
  const run = async (
232
270
  table_id,
233
271
  viewname,
@@ -374,13 +412,21 @@ const run = async (
374
412
  };
375
413
 
376
414
  module.exports = {
415
+ /** @type {string} */
377
416
  name: "Feed",
417
+ /** @type {string} */
378
418
  description:
379
419
  "Show multiple rows by displaying a chosen view for each row, stacked or in columns",
380
420
  configuration_workflow,
381
421
  run,
382
422
  get_state_fields,
423
+ /** @type {boolean} */
383
424
  display_state_form: false,
425
+ /**
426
+ * @param {object} opts
427
+ * @param {*} opts.create_view_label
428
+ * @returns {string[]|Object[]}
429
+ */
384
430
  getStringsForI18n({ create_view_label }) {
385
431
  if (create_view_label) return [create_view_label];
386
432
  else return [];
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @category saltcorn-data
3
+ * @module base-plugin/viewtemplates/filter
4
+ * @subcategory base-plugin
5
+ */
1
6
  const User = require("../../models/user");
2
7
  const View = require("../../models/view");
3
8
  const Table = require("../../models/table");
@@ -26,6 +31,9 @@ const { InvalidConfiguration } = require("../../utils");
26
31
  const { jsexprToWhere } = require("../../models/expression");
27
32
  const Library = require("../../models/library");
28
33
 
34
+ /**
35
+ * @returns {Workflow}
36
+ */
29
37
  const configuration_workflow = () =>
30
38
  new Workflow({
31
39
  steps: [
@@ -80,10 +88,26 @@ const configuration_workflow = () =>
80
88
  },
81
89
  ],
82
90
  });
91
+
92
+ /** @returns {object[]} */
83
93
  const get_state_fields = () => [];
84
94
 
95
+ /**
96
+ *
97
+ * @returns {Promise<object>}
98
+ */
85
99
  const initial_config = async () => ({ layout: {}, columns: [] });
86
100
 
101
+ /**
102
+ * @param {number} table_id
103
+ * @param {string} viewname
104
+ * @param {object} opts
105
+ * @param {object[]} opts.columns
106
+ * @param {object} opts.layout
107
+ * @param {object} state
108
+ * @param {object} extra
109
+ * @returns {Promise<Layout>}
110
+ */
87
111
  const run = async (table_id, viewname, { columns, layout }, state, extra) => {
88
112
  //console.log(columns);
89
113
  //console.log(layout);
@@ -251,17 +275,36 @@ const run = async (table_id, viewname, { columns, layout }, state, extra) => {
251
275
  return renderLayout({ blockDispatch, layout, role });
252
276
  };
253
277
 
278
+ /**
279
+ * @param {object|undefined} x
280
+ * @param {object|undefined} y
281
+ * @returns {object}
282
+ */
254
283
  const or_if_undef = (x, y) => (typeof x === "undefined" ? y : x);
284
+
285
+ /**
286
+ * @param {string} x
287
+ * @param {string} y
288
+ * @returns {boolean}
289
+ */
255
290
  const eq_string = (x, y) => `${x}` === `${y}`;
256
291
  module.exports = {
292
+ /** @type {string} */
257
293
  name: "Filter",
294
+ /** @type {string} */
258
295
  description:
259
296
  "Elements that limit the rows shown in other views on the same page. Filter views do not show any rows on their own.",
260
297
  get_state_fields,
261
298
  configuration_workflow,
262
299
  run,
263
300
  initial_config,
301
+ /** @type {boolean} */
264
302
  display_state_form: false,
303
+ /**
304
+ * @param {object} opts
305
+ * @param {*} opts.layout
306
+ * @returns {string[]}
307
+ */
265
308
  getStringsForI18n({ layout }) {
266
309
  return getStringsForI18n(layout);
267
310
  },
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @category saltcorn-data
3
+ * @module base-plugin/viewtemplates/list
4
+ * @subcategory base-plugin
5
+ */
1
6
  const Field = require("../../models/field");
2
7
  const FieldRepeat = require("../../models/fieldrepeat");
3
8
  const Table = require("../../models/table");
@@ -31,6 +36,10 @@ const db = require("../../db");
31
36
  const { get_existing_views } = require("../../models/discovery");
32
37
  const { InvalidConfiguration } = require("../../utils");
33
38
 
39
+ /**
40
+ * @param {object} context
41
+ * @returns {Promise<void>}
42
+ */
34
43
  const create_db_view = async (context) => {
35
44
  const table = await Table.findOne({ id: context.table_id });
36
45
  const fields = await table.getFields();
@@ -54,6 +63,13 @@ const create_db_view = async (context) => {
54
63
  await db.query(`create or replace view ${sql_view_name} as ${sql};`);
55
64
  };
56
65
 
66
+ /**
67
+ * @param {*} table_id
68
+ * @param {string} viewname
69
+ * @param {object} opts
70
+ * @param {*} opts.default_state
71
+ * @returns {Promise<void>}
72
+ */
57
73
  const on_delete = async (table_id, viewname, { default_state }) => {
58
74
  if (!db.isSQLite) {
59
75
  const sqlviews = (await get_existing_views()).map((v) => v.table_name);
@@ -66,6 +82,10 @@ const on_delete = async (table_id, viewname, { default_state }) => {
66
82
  }
67
83
  };
68
84
 
85
+ /**
86
+ * @param {object} req
87
+ * @returns {Workflow}
88
+ */
69
89
  const configuration_workflow = (req) =>
70
90
  new Workflow({
71
91
  onDone: async (ctx) => {
@@ -261,6 +281,14 @@ const configuration_workflow = (req) =>
261
281
  },
262
282
  ],
263
283
  });
284
+
285
+ /**
286
+ * @param {string} table_id
287
+ * @param {*} viewname
288
+ * @param {object} opts
289
+ * @param {object[]} opts.columns
290
+ * @returns {function}
291
+ */
264
292
  const get_state_fields = async (table_id, viewname, { columns }) => {
265
293
  const table_fields = await Field.find({ table_id });
266
294
  var state_fields = [];
@@ -281,8 +309,23 @@ const get_state_fields = async (table_id, viewname, { columns }) => {
281
309
  return state_fields;
282
310
  };
283
311
 
312
+ /** @type {function} */
284
313
  const initial_config = initial_config_all_fields(false);
285
314
 
315
+ /**
316
+ * @param {string|number} table_id
317
+ * @param {string} viewname
318
+ * @param {object} opts
319
+ * @param {object[]} opts.columns
320
+ * @param {string} [opts.view_to_create]
321
+ * @param {string} opts.create_view_display
322
+ * @param {string} [opts.create_view_label]
323
+ * @param {object} [opts.default_state]
324
+ * @param {string} [opts.create_view_location]
325
+ * @param {object} [stateWithId]
326
+ * @param {object} extraOpts
327
+ * @returns {Promise<*>}
328
+ */
286
329
  const run = async (
287
330
  table_id,
288
331
  viewname,
@@ -429,6 +472,18 @@ const run = async (
429
472
  return istop ? create_link_div + tableHtml : tableHtml + create_link_div;
430
473
  };
431
474
 
475
+ /**
476
+ * @param {number} table_id
477
+ * @param {*} viewname
478
+ * @param {object} optsOne
479
+ * @param {object[]} optsOne.columns
480
+ * @param {*} optsOne.layout
481
+ * @param {object} body
482
+ * @param {object} optsTwo
483
+ * @param {object} optsTwo.req
484
+ * @param {*} optsTwo.res
485
+ * @returns {Promise<object>}
486
+ */
432
487
  const run_action = async (
433
488
  table_id,
434
489
  viewname,
@@ -468,23 +523,40 @@ const run_action = async (
468
523
  };
469
524
 
470
525
  module.exports = {
526
+ /** @type {string} */
471
527
  name: "List",
528
+ /** @type {string} */
472
529
  description:
473
- "Display multiple rows from a table in a grid with columns you specify",
530
+ "Display multiple rows from a table in a grid with columns you specify",
474
531
  configuration_workflow,
475
532
  run,
533
+ /** @type {string} */
476
534
  view_quantity: "Many",
477
535
  get_state_fields,
478
536
  initial_config,
479
537
  on_delete,
480
538
  routes: { run_action },
539
+ /**
540
+ * @param {object} opts
541
+ * @returns {boolean}
542
+ */
481
543
  display_state_form: (opts) =>
482
544
  !(opts && opts.default_state && opts.default_state._omit_state_form),
545
+ /**
546
+ * @param {object} opts
547
+ * @returns {boolean}
548
+ */
483
549
  default_state_form: ({ default_state }) => {
484
550
  if (!default_state) return default_state;
485
551
  const { _omit_state_form, _create_db_view, ...ds } = default_state;
486
552
  return ds && removeDefaultColor(removeEmptyStrings(ds));
487
553
  },
554
+ /**
555
+ * @param {object} opts
556
+ * @param {*} opts.columns
557
+ * @param {*} opts.create_view_label
558
+ * @returns {string[]}
559
+ */
488
560
  getStringsForI18n({ columns, create_view_label }) {
489
561
  const strings = [];
490
562
  const maybeAdd = (s) => {
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @category saltcorn-data
3
+ * @module base-plugin/viewtemplates/listshowlist
4
+ * @subcategory base-plugin
5
+ */
1
6
  const Table = require("../../models/table");
2
7
  const Form = require("../../models/form");
3
8
  const View = require("../../models/view");
@@ -12,6 +17,10 @@ const {
12
17
  const { splitUniques } = require("./viewable_fields");
13
18
  const { InvalidConfiguration } = require("../../utils");
14
19
 
20
+ /**
21
+ * @param {object} req
22
+ * @returns {Workflow}
23
+ */
15
24
  const configuration_workflow = (req) =>
16
25
  new Workflow({
17
26
  steps: [
@@ -58,6 +67,19 @@ const configuration_workflow = (req) =>
58
67
  options: show_view_opts,
59
68
  },
60
69
  },
70
+ {
71
+ name: "list_width",
72
+ label: req.__("List width"),
73
+ sublabel: req.__(
74
+ "Number of columns (1-12) allocated to the list view"
75
+ ),
76
+ type: "Integer",
77
+ default: 6,
78
+ attributes: {
79
+ min: 1,
80
+ max: 12,
81
+ },
82
+ },
61
83
  {
62
84
  name: "_omit_state_form",
63
85
  label: req.__("Omit search form"),
@@ -106,6 +128,14 @@ const configuration_workflow = (req) =>
106
128
  ],
107
129
  });
108
130
 
131
+ /**
132
+ * @param {*} table_id
133
+ * @param {*} viewname
134
+ * @param {object} opts
135
+ * @param {string} opts.list_view
136
+ * @param {*} opts.show_view
137
+ * @returns {Promise<object[]>}
138
+ */
109
139
  const get_state_fields = async (
110
140
  table_id,
111
141
  viewname,
@@ -125,10 +155,21 @@ const get_state_fields = async (
125
155
  } else return [id];
126
156
  };
127
157
 
158
+ /**
159
+ * @param {string} table_id
160
+ * @param {string} viewname
161
+ * @param {object} opts
162
+ * @param {string} opts.list_view
163
+ * @param {string} opts.show_view
164
+ * @param {object} opts.subtables
165
+ * @param {*} state
166
+ * @param {*} extraArgs
167
+ * @returns {Promise<div>}
168
+ */
128
169
  const run = async (
129
170
  table_id,
130
171
  viewname,
131
- { list_view, show_view, subtables },
172
+ { list_view, show_view, list_width, subtables },
132
173
  state,
133
174
  extraArgs
134
175
  ) => {
@@ -219,10 +260,11 @@ const run = async (
219
260
  ? [h6(Object.keys(reltbls)[0]), reltbls[Object.keys(reltbls)[0]]]
220
261
  : tabs(reltbls);
221
262
  if (lresp) {
263
+ if (list_width === 12) return lresp;
222
264
  return div(
223
265
  { class: "row" },
224
- div({ class: "col-sm-6" }, lresp),
225
- div({ class: "col-sm-6" }, sresp, relTblResp)
266
+ div({ class: `col-sm-${list_width || 6}` }, lresp),
267
+ div({ class: `col-sm-${12 - (list_width || 6)}` }, sresp, relTblResp)
226
268
  );
227
269
  } else {
228
270
  return div(sresp, relTblResp);
@@ -230,12 +272,21 @@ const run = async (
230
272
  };
231
273
 
232
274
  module.exports = {
275
+ /** @type {string} */
233
276
  name: "ListShowList",
277
+ /** @type {string} */
234
278
  description:
235
279
  "Combine an optional list view on the left with displays on the right of a single selected row, with views of related rows from different tables underneath",
236
280
  configuration_workflow,
237
281
  run,
238
282
  get_state_fields,
283
+ /**
284
+
285
+ * @param {object} opts
286
+ * @param {string} opts.list_view
287
+ * @param {boolean} opts._omit_state_form
288
+ * @returns {boolean}
289
+ */
239
290
  display_state_form: ({ list_view, _omit_state_form }) =>
240
291
  !!list_view && !_omit_state_form,
241
292
  };