Page MenuHomeSealhub

deep-reverse-single-reference.ts
No OneTemporary

deep-reverse-single-reference.ts

import { toKebabCase, toPascalCase } from "js-convert-case";
import extract_fields_from_collection from "../../../utils/extract-fields-from-collection.js";
import {
FieldHandler,
FieldHandlerResult,
} from "../shared-crud-form-fields.js";
export const deep_reverse_single_reference_handler: FieldHandler<
FieldHandlerResult,
Partial<FieldHandlerResult>
> = async (
collection_name,
{
name,
target,
referencing_collection,
intermediary_field_that_points_there,
referencing_field,
},
___,
{ is_required_expr, fields_var }
) => {
if (
!referencing_collection ||
!target ||
!intermediary_field_that_points_there ||
!referencing_field
) {
console.warn(
"missing referencing_collection, target, intermediary_field_that_points_there, or referencing_field member in extracted field info"
);
return { field: "" };
}
const referencing_fields = await extract_fields_from_collection(
referencing_collection
);
if (referencing_fields.length == 2) {
// just a regular n-to-n intermediary collection
const post_create = (get_id: string) => `{
const item_id = ${get_id};
// First, we remove links refering to current item
const {items: existing_links } = (await ctx.$app.collections["${referencing_collection}"]
.list(ctx.$context)
.filter({"${referencing_field}": item_id})
.fetch()
)
const should_exist_ids = Object.entries(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
(data["${name}"] as unknown as Record<string, string>) || {}
)
.filter(([, value]) => value === "on")
.map(([key]) => key);
const to_delete = existing_links.filter(
(link) => !should_exist_ids.includes(link.get("${intermediary_field_that_points_there}"))
);
const to_create_ids = should_exist_ids.filter(
(id) => !existing_links.map((link) => link.get("${intermediary_field_that_points_there}")).includes(id)
);
await Promise.all(to_delete.map((item) => item.remove(ctx.$context)));
// Then, we iterate over all selected values and insert appropriate missing links
const promises = to_create_ids.map(async (id) => {
await ctx.$app.collections["${toKebabCase(referencing_collection)}"].create(
ctx.$context,
{
"${referencing_field}": item_id,
"${intermediary_field_that_points_there}": id,
}
);
});
await Promise.all(promises);
}`;
return {
field: `${name}: new Fields.CheckboxedListField(${is_required_expr}, async (ctx) => {
const { items } = await ctx.$app.collections["${target}"]
.list(ctx.$context)
.fetch();
return items.map((s) => ({
label: s.get("name"),
value: s.id,
}));
})`,
controls: `new Controls.CheckboxedListInput(${fields_var}.${name}, {
label: "${name}",
})`,
post_edit: post_create(`await this.getID(ctx)`),
post_create: post_create(`created.id`),
initial_value_edit: `Object.fromEntries((item.get("${name}") || []).map(id=>[id, "on"]))`,
sealious_value: () => "",
};
} else {
// probably an n-to-n intermediary collection with an additional attribute
const initial_value_function_name = `get${toPascalCase(
name
)}TableInitialValues`;
const post_create = (get_id: string) => `{
const item_id = ${get_id};
// First, we remove links refering to current item
const { items: to_delete } = await ctx.$app.collections[
"${toKebabCase(referencing_collection)}"
]
.list(ctx.$context)
.filter({ "${referencing_field}": item_id })
.fetch();
await Promise.all(to_delete.map((item) => item.remove(ctx.$context)));
// Then, we iterate over all selected values and insert appropriate entries for the subcategories relation
const promises = (data["${name}"] || [])
.filter((s) => s.selected == true)
.map(async (link_info) => {
await ctx.$app.collections["${toKebabCase(referencing_collection)}"].create(
ctx.$context,
{
"${referencing_field}": item_id,
"${intermediary_field_that_points_there}": String(link_info["${intermediary_field_that_points_there}"]),
${referencing_fields
.filter(
({ type }) => type !== "single-reference"
)
.map(
(field) =>
`"${field.name}": link_info["${field.name}"]`
)
.join(",\n")}
}
);
});
await Promise.all(promises);
}`;
const initial_value = `(await ${initial_value_function_name}(ctx,item)) as unknown as FormDataValue`;
return {
imports: {
shared: `import type { CollectionItem } from "sealious"; import type { Context } from "koa";`,
edit: `import {${initial_value_function_name}} from "../shared.js"; import type { FormDataValue } from "@sealcode/sealgen";`,
},
field: `
${name}: new Fields.Table({
selected: new Fields.Boolean(false),
"${referencing_field}": new Fields.SimpleFormField(true),
"${intermediary_field_that_points_there}": new Fields.SimpleFormField(true),
${referencing_fields
.filter(({ type }) => type !== "single-reference")
.map(
(field) =>
`${field.name}: new Fields.SimpleFormField(true)`
)
.join(",\n")}
})`,
controls: `
new Controls.Table(${fields_var}.${name}, {
render_fields: {
selected: (fctx, name, value) => /* HTML */ \`<input
type="checkbox"
name=\${name}
\${value?.toString() == "true" ? "checked" : ""}
form="\${fctx.form_id}"
autocomplete="off"
/>\`,
"${referencing_field}": (fctx, name, value) => /* HTML */ \`<input
type="hidden"
name=\${name}
value="\${value}"
form="\${fctx.form_id}"
/>\`,
"${intermediary_field_that_points_there}": (fctx, name, value) => /* HTML */ \`<input
type="hidden"
name=\${name}
value="\${value}"
form="\${fctx.form_id}"
/>\`,
${referencing_fields
.filter(
({ type }) => type !== "single-reference"
)
.map(
(field) =>
`${field.name}: (fctx, name, value) => /* HTML */ \`<input
name=\${name}
value="\${value}"
form="\${fctx.form_id}"
autocomplete="off"
/>\``
)
.join(",\n")}
},
allow_adding: false,
})`,
top_level_shared: `export async function ${initial_value_function_name}(
ctx: Context,
item: CollectionItem<typeof ${toPascalCase(
collection_name
)}>
) {
// convert from an array to array-like object
return Object.fromEntries(
await Promise.all([
ctx.$app.collections["${toKebabCase(target)}"].list(ctx.$context).fetch(),
ctx.$app.collections["${toKebabCase(
referencing_collection
)}"].list(ctx.$context).fetch(),
]).then(([{ items: targets }, { items: links }]) =>
targets
.map((target) => {
return {
selected: (item.get("${name}") || []).includes(
target.id
)
? "on"
: "off",
"${referencing_field}": item.id,
"${intermediary_field_that_points_there}": target.id,
${referencing_fields
.filter(
({ type }) =>
type !== "single-reference"
)
.map(
(
field
) => `"${field.name}": links
.find(
(e) =>
e.get("${intermediary_field_that_points_there}") == target.id &&
e.get("${referencing_field}") == item.id
)
?.get("rank") || 0,`
)
.join(",\n")}
};
})
.entries()
)
);
}`,
initial_value_edit: initial_value,
initial_value_create: initial_value,
post_edit: post_create(`await this.getID(ctx)`),
post_create: post_create(`created.id`),
};
}
};

File Metadata

Mime Type
text/x-java
Expires
Tue, Jul 8, 08:20 (21 h, 21 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
799162
Default Alt Text
deep-reverse-single-reference.ts (7 KB)

Event Timeline