export function collectionRulesTab(upsertData) { const local = store({ showRulesInfo: false, showAuthRules: false, }); const systemRuleTooltip = () => app.attrs.tooltip( upsertData.originalCollection?.system ? "System collection rule cannot be changed." : null, "top-left", ); function autocomplete(word) { return app.utils.collectionAutocompleteKeys(upsertData.collection, word); } return t.div( { className: "collection-tab-content collection-rules-tab-content" }, t.div( { className: "grid" }, t.div( { className: "col-12" }, t.div( { className: "flex txt-hint txt-sm" }, t.span( { className: "txt" }, "All rules follow the ", t.a({ target: "_blank", rel: "noopener noreferrer", href: import.meta.env.PB_RULES_SYNTAX_DOCS, textContent: "PocketBase filter syntax and operators", }), ".", ), t.strong({ tabIndex: -1, className: "m-l-auto link-hint", textContent: () => (local.showRulesInfo ? "Hide available fields" : "Show available fields"), onclick: () => (local.showRulesInfo = !local.showRulesInfo), }), ), app.components.slide( () => local.showRulesInfo, t.div( { className: "alert warning m-t-sm" }, t.div( { className: "content" }, t.p(null, "The following record fields are available:"), t.div({ className: "flex flex-wrap gap-5" }, () => { const identifiers = app.utils.getAllCollectionIdentifiers(upsertData.collection); return identifiers.map((f) => { return t.code(null, f); }); }), t.hr({ className: "m-t-10 m-b-10" }), t.p( null, "The request fields could be accessed with the special ", t.strong(null, "@request"), " fields:", ), t.div( { className: "flex flex-wrap gap-5" }, t.code(null, "@request.headers.*"), t.code(null, "@request.query.*"), t.code(null, "@request.body.*"), t.code(null, "@request.auth.*"), ), t.hr({ className: "m-t-10 m-b-10" }), t.p( null, "You could also add constraints and query other collections using the ", t.strong(null, "@collection"), " field:", ), t.div( { className: "flex flex-wrap gap-5" }, t.code(null, "@collection.ANY_COLLECTION_NAME.*"), ), t.hr({ className: "m-t-10 m-b-10" }), t.p(null, "Example rule:"), () => { const dateField = upsertData.collection.fields?.find( (f) => f.type == "date" || f.type == "autodate", ); if (dateField) { return t.code( null, `@request.auth.id != "" && ${dateField.name} > "2022-01-01 00:00:00.000Z"`, ); } return t.code(null, `@request.auth.id != ""`); }, ), ), ), ), t.div( { className: "col-12", ariaDescription: systemRuleTooltip() }, app.components.ruleField({ label: "List/Search rule", name: "listRule", autocomplete: autocomplete, disabled: () => upsertData.originalCollection?.system, value: () => upsertData.collection.listRule, oninput: (val) => (upsertData.collection.listRule = val), }), ), t.div( { className: "col-12", ariaDescription: systemRuleTooltip() }, app.components.ruleField({ label: "View rule", name: "viewRule", autocomplete: autocomplete, disabled: () => upsertData.originalCollection?.system, value: () => upsertData.collection.viewRule, oninput: (val) => (upsertData.collection.viewRule = val), }), ), () => { // view collections has only List and View API rules if (upsertData.collection.type == "view") { return; } return [ t.div( { className: "col-12", ariaDescription: systemRuleTooltip() }, app.components.ruleField({ label: [ t.span({ className: "txt", textContent: "Create rule" }), t.i({ hidden: () => upsertData.collection.createRule == null, className: "ri-information-line link-hint", ariaDescription: app.attrs.tooltip( "The main record fields hold the values that are going to be inserted in the database.", ), }), ], name: "createRule", autocomplete: autocomplete, disabled: () => upsertData.originalCollection?.system, value: () => upsertData.collection.createRule, oninput: (val) => (upsertData.collection.createRule = val), }), ), t.div( { className: "col-12", ariaDescription: systemRuleTooltip() }, app.components.ruleField({ label: [ t.span({ className: "txt", textContent: "Update rule" }), t.i({ hidden: () => upsertData.collection.updateRule == null, className: "ri-information-line link-hint", ariaDescription: app.attrs.tooltip( "The main record fields hold the old/existing record field values.\nTo target the newly submitted ones you can use @request.body.*.", ), }), ], name: "updateRule", autocomplete: autocomplete, disabled: () => upsertData.originalCollection?.system, value: () => upsertData.collection.updateRule, oninput: (val) => (upsertData.collection.updateRule = val), }), ), t.div( { className: "col-12", ariaDescription: systemRuleTooltip() }, app.components.ruleField({ label: "Delete rule", name: "deleteRule", autocomplete: autocomplete, disabled: () => upsertData.originalCollection?.system, value: () => upsertData.collection.deleteRule, oninput: (val) => (upsertData.collection.deleteRule = val), }), ), ]; }, ), // auth specific fields () => { if (upsertData.collection.type != "auth") { return; } return [ t.hr({ className: "m-t-base m-b-base" }), t.button( { type: "button", onmount: () => { local.showAuthRules = upsertData.collection.manageRule !== null || upsertData.collection.authRule !== ""; }, className: () => `btn secondary sm ${local.showAuthRules ? "" : "transparent"}`, onclick: () => { local.showAuthRules = !local.showAuthRules; }, }, t.span({ className: "txt" }, "Additional auth collection rules"), t.i({ ariaHidden: true, className: () => (local.showAuthRules ? "ri-arrow-drop-up-line" : "ri-arrow-drop-down-line"), }), ), app.components.slide( () => local.showAuthRules, t.div( { className: "grid sm m-t-sm" }, t.div( { className: "col-12", ariaDescription: systemRuleTooltip() }, app.components.ruleField({ label: "Authentication rule", name: "authRule", placeholder: "", autocomplete: autocomplete, disabled: () => upsertData.originalCollection?.system, value: () => upsertData.collection.authRule, oninput: (val) => (upsertData.collection.authRule = val), }), t.div( { className: "field-help" }, t.p( null, "This rule is executed every time ", t.strong(null, "before authentication"), " allowing you to restrict who can authenticate.", ), t.p( null, "For example, to allow only verified users you can set it to ", t.code(null, "verified = true"), ".", ), t.p(null, "Leave it empty to allow anyone with an account to authenticate."), t.p( null, `To disable authentication entirely you can change it to "Set superusers only".`, ), ), ), t.div( { className: "col-12", ariaDescription: systemRuleTooltip() }, app.components.ruleField({ label: "Manage rule", name: "manageRule", autocomplete: autocomplete, disabled: () => upsertData.originalCollection?.system, value: () => upsertData.collection.manageRule, oninput: (val) => (upsertData.collection.manageRule = val), }), t.div( { className: "field-help" }, t.p( null, "This rule is executed in addition to the ", t.strong(null, "create"), " and ", t.strong(null, "update"), " API rules.", ), t.p( null, "It enables superuser-like permissions to allow fully managing the auth record(s), eg. changing the password without requiring to enter the old one, directly updating the verified state or email, etc.", ), ), ), ), ), ]; }, ); }