tempest-express-sdk 0.27.0 → 0.28.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.
- package/README.md +1 -1
- package/dist/{chunk-ADF7OHRH.js → chunk-3NS5KVHT.js} +3 -3
- package/dist/{chunk-ADF7OHRH.js.map → chunk-3NS5KVHT.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +324 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -2
- package/dist/index.d.ts +126 -2
- package/dist/index.js +324 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9461,6 +9461,23 @@ function partitionTotal(partition) {
|
|
|
9461
9461
|
return partition.segments.reduce((total, segment) => total + segment.value, 0);
|
|
9462
9462
|
}
|
|
9463
9463
|
|
|
9464
|
+
// src/admin/inlines.ts
|
|
9465
|
+
function adminInline(options) {
|
|
9466
|
+
const slug = options.model.tablename;
|
|
9467
|
+
if (typeof slug !== "string" || slug === "") {
|
|
9468
|
+
throw new Error("adminInline requires a concrete model with a tablename");
|
|
9469
|
+
}
|
|
9470
|
+
return {
|
|
9471
|
+
model: options.model,
|
|
9472
|
+
slug,
|
|
9473
|
+
fkField: options.fkField,
|
|
9474
|
+
listDisplay: options.listDisplay === void 0 ? null : [...options.listDisplay],
|
|
9475
|
+
label: options.label ?? null,
|
|
9476
|
+
editable: options.editable ?? false,
|
|
9477
|
+
canDelete: options.canDelete ?? false
|
|
9478
|
+
};
|
|
9479
|
+
}
|
|
9480
|
+
|
|
9464
9481
|
// src/admin/lenses.ts
|
|
9465
9482
|
function slugify(name) {
|
|
9466
9483
|
return name.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "lens";
|
|
@@ -9716,6 +9733,8 @@ var AdminModel = class {
|
|
|
9716
9733
|
canImport;
|
|
9717
9734
|
/** Foreign-key columns rendered as a typed search box. */
|
|
9718
9735
|
autocompleteFields;
|
|
9736
|
+
/** Related child models listed on the detail view. */
|
|
9737
|
+
inlines;
|
|
9719
9738
|
actions = /* @__PURE__ */ new Map();
|
|
9720
9739
|
slugOverride;
|
|
9721
9740
|
listDisplayOverride;
|
|
@@ -9747,6 +9766,7 @@ var AdminModel = class {
|
|
|
9747
9766
|
this.uploadStorage = options.uploadStorage ?? null;
|
|
9748
9767
|
this.canImport = options.canImport ?? false;
|
|
9749
9768
|
this.autocompleteFields = [...options.autocompleteFields ?? []];
|
|
9769
|
+
this.inlines = [...options.inlines ?? []];
|
|
9750
9770
|
if (this.uploadFields.length > 0 && this.uploadStorage === null) {
|
|
9751
9771
|
throw new Error(
|
|
9752
9772
|
`AdminModel(${this.model.tablename}).uploadFields requires an uploadStorage (e.g. LocalUploadStorage / S3UploadStorage) \u2014 without one there is nowhere to write the file.`
|
|
@@ -10025,7 +10045,9 @@ function parseFormBody(admin, body, options = {}) {
|
|
|
10025
10045
|
const data = {};
|
|
10026
10046
|
const errors = {};
|
|
10027
10047
|
const uploads = options.uploadsAsText === true ? /* @__PURE__ */ new Set() : new Set(admin.uploadFields);
|
|
10048
|
+
const only = options.only === void 0 ? null : new Set(options.only);
|
|
10028
10049
|
for (const name of admin.editableFieldNames()) {
|
|
10050
|
+
if (only !== null && !only.has(name)) continue;
|
|
10029
10051
|
const column6 = columns[name];
|
|
10030
10052
|
if (column6 === void 0) continue;
|
|
10031
10053
|
if (uploads.has(name)) continue;
|
|
@@ -12359,10 +12381,97 @@ function renderDetailPage(context, view) {
|
|
|
12359
12381
|
</div>
|
|
12360
12382
|
</header>
|
|
12361
12383
|
<dl class="tempest-admin-detail__fields">${fields}</dl>
|
|
12384
|
+
${view.inlines.map((inline) => renderInline(inline, context.session?.csrfToken ?? "", view.inlineError)).join("")}
|
|
12362
12385
|
${auditPanel}
|
|
12363
12386
|
</section>`;
|
|
12364
12387
|
return renderLayout(context, `${view.title} \xB7 ${view.identity}`, body);
|
|
12365
12388
|
}
|
|
12389
|
+
function renderInlineCell(field) {
|
|
12390
|
+
const required = field.required ? " required" : "";
|
|
12391
|
+
const name = escapeHtml(field.name);
|
|
12392
|
+
const value = escapeHtml(field.value);
|
|
12393
|
+
let control;
|
|
12394
|
+
switch (field.widget) {
|
|
12395
|
+
case "checkbox":
|
|
12396
|
+
control = `<input type="checkbox" name="${name}" value="true"${field.checked ? " checked" : ""}>`;
|
|
12397
|
+
break;
|
|
12398
|
+
case "textarea":
|
|
12399
|
+
case "json":
|
|
12400
|
+
control = `<textarea name="${name}" rows="2"${required}>${value}</textarea>`;
|
|
12401
|
+
break;
|
|
12402
|
+
case "select": {
|
|
12403
|
+
const blank = field.required ? "" : '<option value="">\u2014 none \u2014</option>';
|
|
12404
|
+
const options = field.options.map(
|
|
12405
|
+
(option) => `<option value="${escapeHtml(option.value)}"${option.value === field.value ? " selected" : ""}>${escapeHtml(option.label)}</option>`
|
|
12406
|
+
).join("");
|
|
12407
|
+
control = `<select name="${name}"${required}>${blank}${options}</select>`;
|
|
12408
|
+
break;
|
|
12409
|
+
}
|
|
12410
|
+
case "number":
|
|
12411
|
+
control = `<input type="number" name="${name}" value="${value}"${field.step !== null ? ` step="${escapeHtml(field.step)}"` : ""}${required}>`;
|
|
12412
|
+
break;
|
|
12413
|
+
case "datetime":
|
|
12414
|
+
control = `<input type="datetime-local" name="${name}" value="${value}"${required}>`;
|
|
12415
|
+
break;
|
|
12416
|
+
case "date":
|
|
12417
|
+
control = `<input type="date" name="${name}" value="${value}"${required}>`;
|
|
12418
|
+
break;
|
|
12419
|
+
case "time":
|
|
12420
|
+
control = `<input type="time" name="${name}" value="${value}"${required}>`;
|
|
12421
|
+
break;
|
|
12422
|
+
default:
|
|
12423
|
+
control = `<input type="text" name="${name}" value="${value}"${required}>`;
|
|
12424
|
+
}
|
|
12425
|
+
const error = field.error !== null ? `<small class="tempest-admin-form__field-error">${escapeHtml(field.error)}</small>` : "";
|
|
12426
|
+
return `${control}${error}`;
|
|
12427
|
+
}
|
|
12428
|
+
function renderInline(inline, csrfToken, error) {
|
|
12429
|
+
const heading = `<header class="tempest-admin-inline__header">
|
|
12430
|
+
<h2>${escapeHtml(inline.label)}${inline.total > 0 ? ` <span class="tempest-admin-inline__count">(${escapeHtml(inline.total)})</span>` : ""}</h2>
|
|
12431
|
+
${inline.addUrl !== null ? `<a class="tempest-admin-btn" href="${escapeHtml(inline.addUrl)}">Add</a>` : ""}
|
|
12432
|
+
</header>`;
|
|
12433
|
+
const more = inline.truncated ? `<p class="tempest-admin-inline__more"><em>Showing the first ${escapeHtml(inline.rows.length)} of ${escapeHtml(inline.total)}.</em></p>` : "";
|
|
12434
|
+
if (!inline.editable) {
|
|
12435
|
+
if (inline.rows.length === 0) {
|
|
12436
|
+
return `<section class="tempest-admin-inline">${heading}<p><em>No related records.</em></p></section>`;
|
|
12437
|
+
}
|
|
12438
|
+
const head2 = `<tr>${inline.columns.map((column6) => `<th>${escapeHtml(column6)}</th>`).join("")}<th></th></tr>`;
|
|
12439
|
+
const body = inline.rows.map(
|
|
12440
|
+
(row) => `<tr>${row.cells.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}<td>${row.url === null ? "" : `<a href="${escapeHtml(row.url)}">View</a>`}</td></tr>`
|
|
12441
|
+
).join("");
|
|
12442
|
+
return `<section class="tempest-admin-inline">
|
|
12443
|
+
${heading}
|
|
12444
|
+
<div class="tempest-admin-inline__scroll">
|
|
12445
|
+
<table class="tempest-admin-inline__table">
|
|
12446
|
+
<thead>${head2}</thead>
|
|
12447
|
+
<tbody>${body}</tbody>
|
|
12448
|
+
</table>
|
|
12449
|
+
</div>
|
|
12450
|
+
${more}
|
|
12451
|
+
</section>`;
|
|
12452
|
+
}
|
|
12453
|
+
const head = `<tr>${inline.columns.map((column6) => `<th>${escapeHtml(column6)}</th>`).join("")}${inline.canDelete ? "<th>Delete</th>" : ""}</tr>`;
|
|
12454
|
+
const renderRow = (row, isNew) => `<tr${isNew ? ' class="tempest-admin-inline__new"' : ""}>${row.fields.map((field) => `<td>${renderInlineCell(field)}</td>`).join("")}${inline.canDelete ? `<td class="tempest-admin-inline__del">${isNew ? "" : `<input type="checkbox" name="row.${escapeHtml(row.key)}.__delete" value="true">`}</td>` : ""}</tr>`;
|
|
12455
|
+
const rows = inline.rows.map((row) => renderRow(row, false)).join("");
|
|
12456
|
+
const blank = inline.newRow === null ? "" : renderRow(inline.newRow, true);
|
|
12457
|
+
return `<section class="tempest-admin-inline">
|
|
12458
|
+
${heading}
|
|
12459
|
+
${error !== null ? `<p class="tempest-admin-form__error">${escapeHtml(error)}</p>` : ""}
|
|
12460
|
+
<form method="post" action="${escapeHtml(inline.formAction)}" class="tempest-admin-inline__form">
|
|
12461
|
+
<input type="hidden" name="csrf_token" value="${escapeHtml(csrfToken)}">
|
|
12462
|
+
<div class="tempest-admin-inline__scroll">
|
|
12463
|
+
<table class="tempest-admin-inline__table">
|
|
12464
|
+
<thead>${head}</thead>
|
|
12465
|
+
<tbody>${rows}${blank}</tbody>
|
|
12466
|
+
</table>
|
|
12467
|
+
</div>
|
|
12468
|
+
<div class="tempest-admin-form__actions">
|
|
12469
|
+
<button type="submit">Save ${escapeHtml(inline.label)}</button>
|
|
12470
|
+
</div>
|
|
12471
|
+
</form>
|
|
12472
|
+
${more}
|
|
12473
|
+
</section>`;
|
|
12474
|
+
}
|
|
12366
12475
|
function renderAuditPanel(audit) {
|
|
12367
12476
|
const rows = audit.fields.map(
|
|
12368
12477
|
(field) => `<dt>${escapeHtml(field.label)}</dt><dd>${field.value === "" ? "<em>\u2014</em>" : escapeHtml(field.value)}</dd>`
|
|
@@ -12551,6 +12660,7 @@ var AUTOCOMPLETE_SCRIPT = `<script>
|
|
|
12551
12660
|
})();
|
|
12552
12661
|
</script>`;
|
|
12553
12662
|
var logger2 = new JSONLogger("tempest_express_sdk.admin.router");
|
|
12663
|
+
var INLINE_ROW_LIMIT = 50;
|
|
12554
12664
|
var AUTOCOMPLETE_LIMIT = 20;
|
|
12555
12665
|
var AUDIT_HISTORY_LIMIT = 50;
|
|
12556
12666
|
var FK_OPTION_CAP = 1e3;
|
|
@@ -13167,6 +13277,8 @@ function makeAdminRouter(site, options) {
|
|
|
13167
13277
|
title: admin.verboseName(),
|
|
13168
13278
|
identity,
|
|
13169
13279
|
audit: await buildAuditView(admin, row, state.dbSession),
|
|
13280
|
+
inlines: await buildInlines(admin, row, state, identity),
|
|
13281
|
+
inlineError: null,
|
|
13170
13282
|
fields: admin.detailFieldNames().map((name) => ({ label: name, value: formatCellValue(row[name]) })),
|
|
13171
13283
|
backUrl: `${prefix}/m/${admin.slug()}`,
|
|
13172
13284
|
editUrl: await allows(state.principal, admin, AdminPermission.EDIT) ? `${prefix}/m/${admin.slug()}/${identity}/edit` : null,
|
|
@@ -13287,6 +13399,103 @@ function makeAdminRouter(site, options) {
|
|
|
13287
13399
|
res.redirect(`${prefix}/m/${admin.slug()}/${identity}?ok=updated`);
|
|
13288
13400
|
})
|
|
13289
13401
|
);
|
|
13402
|
+
router.post(
|
|
13403
|
+
`${prefix}/m/:slug/:identity/inlines/:child`,
|
|
13404
|
+
guarded(async (req, res) => {
|
|
13405
|
+
const state = await authenticate(req, res);
|
|
13406
|
+
if (state === null) return;
|
|
13407
|
+
const admin = await resolveAdmin(req, res, state);
|
|
13408
|
+
if (admin === null) return;
|
|
13409
|
+
if (!checkCsrf(req, res, state)) return;
|
|
13410
|
+
const childSlug = String(req.params.child);
|
|
13411
|
+
const inline = admin.inlines.find(
|
|
13412
|
+
(entry) => entry.slug === childSlug && entry.editable
|
|
13413
|
+
);
|
|
13414
|
+
const childAdmin = inline === void 0 ? null : site.get(childSlug);
|
|
13415
|
+
if (inline === void 0 || childAdmin === null || !await allows(state.principal, childAdmin, AdminPermission.EDIT)) {
|
|
13416
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 404);
|
|
13417
|
+
return;
|
|
13418
|
+
}
|
|
13419
|
+
const identity = String(req.params.identity);
|
|
13420
|
+
const parent = await findRow(admin, state.dbSession, identity);
|
|
13421
|
+
if (parent === null) {
|
|
13422
|
+
html(res, renderNotFound(context(req, state.session, state.visible)), 404);
|
|
13423
|
+
return;
|
|
13424
|
+
}
|
|
13425
|
+
const parentId = parent[admin.identityField];
|
|
13426
|
+
const { rows: grouped, deletions } = groupInlineSubmission(
|
|
13427
|
+
req.body
|
|
13428
|
+
);
|
|
13429
|
+
const names = inlineFieldNames(childAdmin, inline);
|
|
13430
|
+
const childRepo = childAdmin.repository(state.dbSession);
|
|
13431
|
+
const actorId = backend.principalId(state.principal);
|
|
13432
|
+
const canDelete = inline.canDelete && await allows(state.principal, childAdmin, AdminPermission.DELETE);
|
|
13433
|
+
const failed = [];
|
|
13434
|
+
let formError = null;
|
|
13435
|
+
const owned = async (key) => await childRepo.first({
|
|
13436
|
+
[childAdmin.identityField]: key,
|
|
13437
|
+
[inline.fkField]: parentId
|
|
13438
|
+
});
|
|
13439
|
+
for (const [key, values] of Object.entries(grouped)) {
|
|
13440
|
+
const isNew = key.startsWith("new");
|
|
13441
|
+
if (!isNew && canDelete && deletions.has(key)) {
|
|
13442
|
+
if (await owned(key) !== null) {
|
|
13443
|
+
await childRepo.delete({ [childAdmin.identityField]: key });
|
|
13444
|
+
}
|
|
13445
|
+
continue;
|
|
13446
|
+
}
|
|
13447
|
+
if (isNew && Object.values(values).every((value) => value.trim() === "")) {
|
|
13448
|
+
continue;
|
|
13449
|
+
}
|
|
13450
|
+
const parsed = parseFormBody(childAdmin, values, { only: names });
|
|
13451
|
+
if (Object.keys(parsed.errors).length > 0) {
|
|
13452
|
+
failed.push({ key, values, errors: parsed.errors });
|
|
13453
|
+
continue;
|
|
13454
|
+
}
|
|
13455
|
+
try {
|
|
13456
|
+
if (isNew) {
|
|
13457
|
+
stampActor(childAdmin, parsed.data, actorId, true);
|
|
13458
|
+
await childRepo.create({
|
|
13459
|
+
...parsed.data,
|
|
13460
|
+
[inline.fkField]: parentId
|
|
13461
|
+
});
|
|
13462
|
+
} else {
|
|
13463
|
+
if (await owned(key) === null) continue;
|
|
13464
|
+
stampActor(childAdmin, parsed.data, actorId, false);
|
|
13465
|
+
await childRepo.update(
|
|
13466
|
+
{ [childAdmin.identityField]: key },
|
|
13467
|
+
parsed.data
|
|
13468
|
+
);
|
|
13469
|
+
}
|
|
13470
|
+
} catch (error) {
|
|
13471
|
+
formError = describeWriteFailure(childAdmin, error);
|
|
13472
|
+
failed.push({ key, values, errors: {} });
|
|
13473
|
+
}
|
|
13474
|
+
}
|
|
13475
|
+
if (failed.length > 0) {
|
|
13476
|
+
const fresh = await findRow(admin, state.dbSession, identity) ?? parent;
|
|
13477
|
+
html(
|
|
13478
|
+
res,
|
|
13479
|
+
renderDetailPage(context(req, state.session, state.visible), {
|
|
13480
|
+
title: admin.verboseName(),
|
|
13481
|
+
identity,
|
|
13482
|
+
audit: await buildAuditView(admin, fresh, state.dbSession),
|
|
13483
|
+
inlines: await buildInlines(admin, fresh, state, identity, {
|
|
13484
|
+
[childSlug]: failed
|
|
13485
|
+
}),
|
|
13486
|
+
inlineError: formError ?? "Some inline rows could not be saved.",
|
|
13487
|
+
fields: admin.detailFieldNames().map((name) => ({ label: name, value: formatCellValue(fresh[name]) })),
|
|
13488
|
+
backUrl: `${prefix}/m/${admin.slug()}`,
|
|
13489
|
+
editUrl: await allows(state.principal, admin, AdminPermission.EDIT) ? `${prefix}/m/${admin.slug()}/${identity}/edit` : null,
|
|
13490
|
+
deleteUrl: await allows(state.principal, admin, AdminPermission.DELETE) ? `${prefix}/m/${admin.slug()}/${identity}/delete` : null
|
|
13491
|
+
}),
|
|
13492
|
+
400
|
|
13493
|
+
);
|
|
13494
|
+
return;
|
|
13495
|
+
}
|
|
13496
|
+
res.redirect(`${prefix}/m/${admin.slug()}/${identity}?ok=updated`);
|
|
13497
|
+
})
|
|
13498
|
+
);
|
|
13290
13499
|
router.post(
|
|
13291
13500
|
`${prefix}/m/:slug/:identity/delete`,
|
|
13292
13501
|
guarded(async (req, res) => {
|
|
@@ -13308,6 +13517,89 @@ function makeAdminRouter(site, options) {
|
|
|
13308
13517
|
const principal = await backend.loadPrincipal(dbSession, String(actor));
|
|
13309
13518
|
return principal === null ? String(actor) : backend.displayName(principal);
|
|
13310
13519
|
}
|
|
13520
|
+
async function buildInlines(admin, parent, state, identity, overrides = {}) {
|
|
13521
|
+
const parentId = parent[admin.identityField];
|
|
13522
|
+
const blocks = [];
|
|
13523
|
+
for (const inline of admin.inlines) {
|
|
13524
|
+
const childAdmin = site.get(inline.slug);
|
|
13525
|
+
const repository = childAdmin === null ? new tempestDbJs.BaseRepository(inline.model, state.dbSession) : childAdmin.repository(state.dbSession);
|
|
13526
|
+
const children = await repository.list({
|
|
13527
|
+
[inline.fkField]: parentId
|
|
13528
|
+
});
|
|
13529
|
+
const columns = inline.listDisplay ?? childAdmin?.listDisplayNames() ?? Object.keys(adminColumns(inline.model));
|
|
13530
|
+
const label = inline.label ?? childAdmin?.verboseNamePlural() ?? humanizeField(inline.slug);
|
|
13531
|
+
const visible = children.slice(0, INLINE_ROW_LIMIT);
|
|
13532
|
+
const editable = inline.editable && childAdmin !== null && await allows(state.principal, childAdmin, AdminPermission.EDIT);
|
|
13533
|
+
const addUrl = childAdmin !== null && await allows(state.principal, childAdmin, AdminPermission.CREATE) ? `${prefix}/m/${inline.slug}/new` : null;
|
|
13534
|
+
if (!editable || childAdmin === null) {
|
|
13535
|
+
blocks.push({
|
|
13536
|
+
label,
|
|
13537
|
+
total: children.length,
|
|
13538
|
+
columns,
|
|
13539
|
+
editable: false,
|
|
13540
|
+
canDelete: false,
|
|
13541
|
+
addUrl,
|
|
13542
|
+
formAction: "",
|
|
13543
|
+
rows: visible.map((child) => ({
|
|
13544
|
+
key: String(child[childAdmin?.identityField ?? "id"]),
|
|
13545
|
+
cells: columns.map((column6) => formatCellValue(child[column6])),
|
|
13546
|
+
fields: [],
|
|
13547
|
+
url: childAdmin === null ? null : `${prefix}/m/${inline.slug}/${String(child[childAdmin.identityField])}`
|
|
13548
|
+
})),
|
|
13549
|
+
newRow: null,
|
|
13550
|
+
truncated: children.length > visible.length
|
|
13551
|
+
});
|
|
13552
|
+
continue;
|
|
13553
|
+
}
|
|
13554
|
+
const names = inlineFieldNames(childAdmin, inline);
|
|
13555
|
+
const submitted = overrides[inline.slug];
|
|
13556
|
+
const rows = [];
|
|
13557
|
+
if (submitted !== void 0) {
|
|
13558
|
+
for (const entry of submitted) {
|
|
13559
|
+
rows.push({
|
|
13560
|
+
key: entry.key,
|
|
13561
|
+
cells: [],
|
|
13562
|
+
fields: inlineFields(
|
|
13563
|
+
childAdmin,
|
|
13564
|
+
names,
|
|
13565
|
+
entry.key,
|
|
13566
|
+
entry.values,
|
|
13567
|
+
entry.errors
|
|
13568
|
+
),
|
|
13569
|
+
url: null
|
|
13570
|
+
});
|
|
13571
|
+
}
|
|
13572
|
+
} else {
|
|
13573
|
+
for (const child of visible) {
|
|
13574
|
+
const key = String(child[childAdmin.identityField]);
|
|
13575
|
+
rows.push({
|
|
13576
|
+
key,
|
|
13577
|
+
cells: [],
|
|
13578
|
+
fields: inlineFields(childAdmin, names, key, child, {}),
|
|
13579
|
+
url: `${prefix}/m/${inline.slug}/${key}`
|
|
13580
|
+
});
|
|
13581
|
+
}
|
|
13582
|
+
}
|
|
13583
|
+
blocks.push({
|
|
13584
|
+
label,
|
|
13585
|
+
total: children.length,
|
|
13586
|
+
columns: names.map(humanizeField),
|
|
13587
|
+
editable: true,
|
|
13588
|
+
canDelete: inline.canDelete && childAdmin.canDelete,
|
|
13589
|
+
addUrl,
|
|
13590
|
+
formAction: `${prefix}/m/${admin.slug()}/${identity}/inlines/${inline.slug}`,
|
|
13591
|
+
rows,
|
|
13592
|
+
newRow: {
|
|
13593
|
+
key: "new1",
|
|
13594
|
+
cells: [],
|
|
13595
|
+
fields: inlineFields(childAdmin, names, "new1", {}, {}),
|
|
13596
|
+
url: null
|
|
13597
|
+
},
|
|
13598
|
+
truncated: children.length > visible.length
|
|
13599
|
+
});
|
|
13600
|
+
}
|
|
13601
|
+
return blocks;
|
|
13602
|
+
}
|
|
13311
13603
|
async function buildAuditView(admin, row, dbSession) {
|
|
13312
13604
|
const fields = [];
|
|
13313
13605
|
for (const name of admin.auditFieldNames()) {
|
|
@@ -13606,6 +13898,35 @@ function makeAdminRouter(site, options) {
|
|
|
13606
13898
|
}
|
|
13607
13899
|
return router;
|
|
13608
13900
|
}
|
|
13901
|
+
function groupInlineSubmission(body) {
|
|
13902
|
+
const rows = {};
|
|
13903
|
+
const deletions = /* @__PURE__ */ new Set();
|
|
13904
|
+
for (const [key, raw] of Object.entries(body)) {
|
|
13905
|
+
if (!key.startsWith("row.")) continue;
|
|
13906
|
+
const parts = key.split(".");
|
|
13907
|
+
if (parts.length !== 3) continue;
|
|
13908
|
+
const [, rowKey, field] = parts;
|
|
13909
|
+
const value = typeof raw === "string" ? raw : "";
|
|
13910
|
+
if (field === "__delete") {
|
|
13911
|
+
if (!["", "false", "off", "0", "no"].includes(value.trim().toLowerCase())) {
|
|
13912
|
+
deletions.add(rowKey);
|
|
13913
|
+
}
|
|
13914
|
+
continue;
|
|
13915
|
+
}
|
|
13916
|
+
const row = rows[rowKey] ?? {};
|
|
13917
|
+
row[field] = value;
|
|
13918
|
+
rows[rowKey] = row;
|
|
13919
|
+
}
|
|
13920
|
+
return { rows, deletions };
|
|
13921
|
+
}
|
|
13922
|
+
function inlineFieldNames(childAdmin, inline) {
|
|
13923
|
+
return childAdmin.editableFieldNames().filter(
|
|
13924
|
+
(name) => name !== inline.fkField && !childAdmin.uploadFields.includes(name) && !childAdmin.autocompleteFields.includes(name)
|
|
13925
|
+
);
|
|
13926
|
+
}
|
|
13927
|
+
function inlineFields(childAdmin, names, key, values, errors) {
|
|
13928
|
+
return buildFormFields(childAdmin, { values, errors }).filter((field) => names.includes(field.name)).map((field) => ({ ...field, name: `row.${key}.${field.name}` }));
|
|
13929
|
+
}
|
|
13609
13930
|
function flagAllows(admin, action) {
|
|
13610
13931
|
if (action === AdminPermission.CREATE) return admin.canCreate;
|
|
13611
13932
|
if (action === AdminPermission.EDIT) return admin.canEdit;
|
|
@@ -15777,7 +16098,7 @@ async function withTestDatabase(models, fn) {
|
|
|
15777
16098
|
}
|
|
15778
16099
|
|
|
15779
16100
|
// src/version.ts
|
|
15780
|
-
var VERSION = "0.
|
|
16101
|
+
var VERSION = "0.28.0";
|
|
15781
16102
|
|
|
15782
16103
|
Object.defineProperty(exports, "OpenAPIRegistry", {
|
|
15783
16104
|
enumerable: true,
|
|
@@ -16037,6 +16358,7 @@ exports.activationSchema = activationSchema;
|
|
|
16037
16358
|
exports.addLogSink = addLogSink;
|
|
16038
16359
|
exports.adminAction = adminAction;
|
|
16039
16360
|
exports.adminColumns = adminColumns;
|
|
16361
|
+
exports.adminInline = adminInline;
|
|
16040
16362
|
exports.adminLens = adminLens;
|
|
16041
16363
|
exports.adminThemeCss = adminThemeCss;
|
|
16042
16364
|
exports.attachWebSocketHub = attachWebSocketHub;
|
|
@@ -16098,6 +16420,7 @@ exports.getConditions = getConditions;
|
|
|
16098
16420
|
exports.getPaginationConditions = getPaginationConditions;
|
|
16099
16421
|
exports.getRequestId = getRequestId;
|
|
16100
16422
|
exports.getState = getState;
|
|
16423
|
+
exports.groupInlineSubmission = groupInlineSubmission;
|
|
16101
16424
|
exports.hashOpaqueToken = hashOpaqueToken;
|
|
16102
16425
|
exports.hexColorField = hexColorField;
|
|
16103
16426
|
exports.humanizeField = humanizeField;
|