Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F7188604
deep-reverse-single-reference.ts
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
deep-reverse-single-reference.ts
View Options
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
Details
Attached
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)
Attached To
Mode
rSGEN sealgen
Attached
Detach File
Event Timeline
Log In to Comment