filter enhancements
This commit is contained in:
@@ -236,12 +236,23 @@
|
||||
|
||||
result.push(key);
|
||||
|
||||
if (field.type === "relation" && field.options.collectionId) {
|
||||
// add relation fields
|
||||
if (field.type === "relation" && field.options?.collectionId) {
|
||||
const subKeys = getCollectionFieldKeys(field.options.collectionId, key + ".", level + 1);
|
||||
if (subKeys.length) {
|
||||
result = result.concat(subKeys);
|
||||
}
|
||||
}
|
||||
|
||||
// add ":each" field modifier
|
||||
if (field.type === "select" && field.options?.maxSelect != 1) {
|
||||
result.push(key + ":each");
|
||||
}
|
||||
|
||||
// add ":length" field modifier to arrayble fields
|
||||
if (field.options?.maxSelect != 1 && ["select", "file", "relation"].includes(field.type)) {
|
||||
result.push(key + ":length");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -259,7 +270,6 @@
|
||||
result.push("@request.method");
|
||||
result.push("@request.query.");
|
||||
result.push("@request.data.");
|
||||
result.push("@request.auth.");
|
||||
result.push("@request.auth.id");
|
||||
result.push("@request.auth.collectionId");
|
||||
result.push("@request.auth.collectionName");
|
||||
@@ -279,6 +289,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
// load base collection fields into @request.data.*
|
||||
const issetExcludeList = ["created", "updated"];
|
||||
if (baseCollection?.id) {
|
||||
const keys = getCollectionFieldKeys(baseCollection.name, "@request.data.");
|
||||
for (const key of keys) {
|
||||
result.push(key);
|
||||
|
||||
// add ":isset" modifier to non-base keys
|
||||
const parts = key.split(".");
|
||||
if (
|
||||
parts.length === 3 &&
|
||||
// doesn't contain another modifier
|
||||
parts[2].indexOf(":") === -1 &&
|
||||
// is not from the exclude list
|
||||
!issetExcludeList.includes(parts[2])
|
||||
) {
|
||||
result.push(key + ":isset");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</script>
|
||||
|
||||
<div class="block m-b-base">
|
||||
<div class="flex txt-sm m-b-5">
|
||||
<div class="flex txt-sm txt-hint m-b-5">
|
||||
<p>
|
||||
All rules follow the
|
||||
<a href={import.meta.env.PB_RULES_SYNTAX_DOCS} target="_blank" rel="noopener noreferrer">
|
||||
|
||||
@@ -108,6 +108,17 @@
|
||||
return CommonHelper.slugify(name);
|
||||
}
|
||||
|
||||
function requiredLabel(field) {
|
||||
switch (field?.type) {
|
||||
case "bool":
|
||||
return "Nonfalsey";
|
||||
case "number":
|
||||
return "Nonzero";
|
||||
default:
|
||||
return "Nonempty";
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// auto expand new fields
|
||||
if (!field.id) {
|
||||
@@ -150,7 +161,7 @@
|
||||
<span class="label" class:label-warning={interactive && !field.toDelete}>New</span>
|
||||
{/if}
|
||||
{#if field.required}
|
||||
<span class="label label-success">Nonempty</span>
|
||||
<span class="label label-success">{requiredLabel(field)}</span>
|
||||
{/if}
|
||||
{#if field.unique}
|
||||
<span class="label label-success">Unique</span>
|
||||
@@ -270,13 +281,13 @@
|
||||
<Field class="form-field form-field-toggle m-0" name="requried" let:uniqueId>
|
||||
<input type="checkbox" id={uniqueId} bind:checked={field.required} />
|
||||
<label for={uniqueId}>
|
||||
<span class="txt">Nonempty</span>
|
||||
<span class="txt">{requiredLabel(field)}</span>
|
||||
<i
|
||||
class="ri-information-line link-hint"
|
||||
use:tooltip={{
|
||||
text: `Requires the field value to be nonempty\n(aka. not ${CommonHelper.zeroDefaultStr(
|
||||
text: `Requires the field value to be ${requiredLabel(
|
||||
field
|
||||
)}).`,
|
||||
)}\n(aka. not ${CommonHelper.zeroDefaultStr(field)}).`,
|
||||
position: "right",
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
<script>
|
||||
import { tick } from "svelte";
|
||||
import tooltip from "@/actions/tooltip";
|
||||
import Field from "@/components/base/Field.svelte";
|
||||
|
||||
export let collection = null;
|
||||
@@ -20,6 +19,8 @@
|
||||
|
||||
$: isAdminOnly = rule === null;
|
||||
|
||||
loadEditorComponent();
|
||||
|
||||
async function loadEditorComponent() {
|
||||
if (ruleInputComponent || isRuleComponentLoading) {
|
||||
return; // already loaded or in the process
|
||||
@@ -34,7 +35,16 @@
|
||||
isRuleComponentLoading = false;
|
||||
}
|
||||
|
||||
loadEditorComponent();
|
||||
async function unlock() {
|
||||
rule = tempValue || "";
|
||||
await tick();
|
||||
editorRef?.focus();
|
||||
}
|
||||
|
||||
async function lock() {
|
||||
tempValue = rule;
|
||||
rule = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if isRuleComponentLoading}
|
||||
@@ -42,80 +52,60 @@
|
||||
<span class="loader" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="rule-block">
|
||||
{#if isAdminOnly}
|
||||
<button
|
||||
type="button"
|
||||
class="rule-toggle-btn btn btn-circle btn-outline btn-success"
|
||||
use:tooltip={{
|
||||
text: "Unlock and set custom rule",
|
||||
position: "left",
|
||||
}}
|
||||
on:click={async () => {
|
||||
rule = tempValue || "";
|
||||
await tick();
|
||||
editorRef?.focus();
|
||||
}}
|
||||
>
|
||||
<i class="ri-lock-unlock-line" />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="rule-toggle-btn btn btn-circle btn-outline"
|
||||
use:tooltip={{
|
||||
text: "Lock and set to Admins only",
|
||||
position: "left",
|
||||
}}
|
||||
on:click={() => {
|
||||
tempValue = rule;
|
||||
rule = null;
|
||||
}}
|
||||
>
|
||||
<i class="ri-lock-line" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<Field
|
||||
class="form-field rule-field m-0 {required ? 'requied' : ''} {isAdminOnly ? 'disabled' : ''}"
|
||||
name={formKey}
|
||||
let:uniqueId
|
||||
>
|
||||
<label for={uniqueId}>
|
||||
<Field
|
||||
class="form-field rule-field m-0 {required ? 'requied' : ''} {isAdminOnly ? 'disabled' : ''}"
|
||||
name={formKey}
|
||||
let:uniqueId
|
||||
>
|
||||
<label for={uniqueId}>
|
||||
<span class="txt">
|
||||
{label} - {isAdminOnly ? "Admins only" : "Custom rule"}
|
||||
</label>
|
||||
</span>
|
||||
|
||||
<svelte:component
|
||||
this={ruleInputComponent}
|
||||
id={uniqueId}
|
||||
bind:this={editorRef}
|
||||
bind:value={rule}
|
||||
baseCollection={collection}
|
||||
disabled={isAdminOnly}
|
||||
/>
|
||||
{#if isAdminOnly}
|
||||
<button type="button" class="lock-toggle link-success" on:click={unlock}>
|
||||
<i class="ri-lock-unlock-line" />
|
||||
<span class="txt">Set custom rule</span>
|
||||
</button>
|
||||
{:else}
|
||||
<button type="button" class="lock-toggle link-hint" on:click={lock}>
|
||||
<i class="ri-lock-line" />
|
||||
<span class="txt">Set Admins only</span>
|
||||
</button>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<div class="help-block">
|
||||
<slot {isAdminOnly}>
|
||||
<p>
|
||||
{#if isAdminOnly}
|
||||
Only admins will be able to perform this action (unlock to change)
|
||||
{:else}
|
||||
Leave empty to grant everyone access
|
||||
{/if}
|
||||
</p>
|
||||
</slot>
|
||||
</div>
|
||||
</Field>
|
||||
</div>
|
||||
<svelte:component
|
||||
this={ruleInputComponent}
|
||||
id={uniqueId}
|
||||
bind:this={editorRef}
|
||||
bind:value={rule}
|
||||
baseCollection={collection}
|
||||
disabled={isAdminOnly}
|
||||
/>
|
||||
|
||||
<div class="help-block">
|
||||
<slot {isAdminOnly}>
|
||||
<p>
|
||||
{#if isAdminOnly}
|
||||
Only admins will be able to perform this action (
|
||||
<button type="button" class="link-hint" on:click={unlock}>unlock to change</button>
|
||||
).
|
||||
{:else}
|
||||
Leave empty to grant everyone access.
|
||||
{/if}
|
||||
</p>
|
||||
</slot>
|
||||
</div>
|
||||
</Field>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.rule-block {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--xsSpacing);
|
||||
}
|
||||
.rule-toggle-btn {
|
||||
margin-top: 15px;
|
||||
.lock-toggle {
|
||||
margin-left: auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: var(--smFontSize);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
// fetch a paginated records list
|
||||
const resultList = await pb.collection('${collection?.name}').getList(1, 50, {
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
});
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
@@ -101,7 +101,7 @@
|
||||
final resultList = await pb.collection('${collection?.name}').getList(
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
);
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
{#if hasErrors}
|
||||
<i
|
||||
class="ri-error-warning-fill txt-danger"
|
||||
transition:scale={{ duration: 150, start: 0.7 }}
|
||||
transition:scale|local={{ duration: 150, start: 0.7 }}
|
||||
use:tooltip={{ text: "Has errors", position: "left" }}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
{#if hasErrors}
|
||||
<i
|
||||
class="ri-error-warning-fill txt-danger"
|
||||
transition:scale={{ duration: 150, start: 0.7 }}
|
||||
transition:scale|local={{ duration: 150, start: 0.7 }}
|
||||
use:tooltip={{ text: "Has errors", position: "left" }}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -346,10 +346,6 @@ button {
|
||||
$thumbActiveColor: var(--baseAlt4Color)
|
||||
);
|
||||
}
|
||||
&:focus,
|
||||
&.active {
|
||||
border-color: var(--primaryColor);
|
||||
}
|
||||
&[readonly],
|
||||
&.readonly {
|
||||
cursor: default;
|
||||
@@ -359,7 +355,6 @@ button {
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
color: var(--txtDisabledColor);
|
||||
border-color: var(--baseAlt2Color);
|
||||
}
|
||||
&.txt-mono {
|
||||
line-height: var(--smLineHeight);
|
||||
@@ -536,9 +531,6 @@ select {
|
||||
> label {
|
||||
color: var(--txtDisabledColor);
|
||||
}
|
||||
label, %input {
|
||||
border-color: var(--baseAlt2Color);
|
||||
}
|
||||
&.required > label:after {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user