filters improvements

This commit is contained in:
2023-11-17 20:21:45 +02:00
parent cfde3bf501
commit 794916b178
17 changed files with 387 additions and 241 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -1,14 +1,14 @@
{
"main.js": {
"file": "assets/main.28b0c00f.js",
"file": "assets/main.a696aa4b.js",
"src": "main.js",
"isEntry": true,
"css": [
"assets/main.04114af1.css"
"assets/main.66909535.css"
]
},
"main.css": {
"file": "assets/main.04114af1.css",
"file": "assets/main.66909535.css",
"src": "main.css"
}
}
+23
View File
@@ -0,0 +1,23 @@
<script>
export let width = "300";
let dropdownMenu;
export function hide(){
dropdownMenu.classList.remove("show")
}
</script>
<button
class="btn btn-sm btn-outline-primary dropdown-toggle d-flex align-items-center"
type="button"
data-bs-toggle="dropdown"
data-bs-auto-close="outside"
aria-expanded="false"
>
<slot name="button">Dropdown</slot>
</button>
<div bind:this={dropdownMenu} class="dropdown-menu" style="width:{width}px;">
<slot/>
</div>
+1
View File
@@ -76,6 +76,7 @@
{sortField}
{operators}
{filter}
{graph}
{inModal}
{modalUrl}
{isWritable}
@@ -1,6 +1,8 @@
<script>
import {createEventDispatcher} from "svelte";
import {createEventDispatcher, getContext} from "svelte";
import {previewTitle} from "../../records/Preview";
const channel = getContext("channel");
const dispatch = createEventDispatcher();
export let schema;
export let operators;
@@ -8,14 +10,22 @@
export let value;
export let inModal;
export let modalUrl;
export let records;
export let graph;
// export let systemFields;
let filterSplit = key.split("_");
let operator = filterSplit[filterSplit.length - 1] ?? "eq";
let fieldName = key.replace("_" + operator, "");
let filterField = schema.fields.find((f) => f.name === fieldName);
let filterLabel = filterField?.label ?? fieldName;
let filterRecord = null;
let isReference = key.startsWith("children")
if (isReference) {
filterRecord = graph.records.find(r => r.id === value)
}
function removeFilter(e, k) {
e.preventDefault();
@@ -32,20 +42,28 @@
</script>
<span
class="applied-filter d-inline-block border border-primary rounded lx-small-text me-1 px-2 py-1"
style="line-height:22px ;"
class="applied-filter d-inline-block border border-primary rounded lx-small-text me-1 px-2 py-1"
style="line-height:22px ;"
>
<div class="d-flex align-items-center justify-content-center">
{filterLabel}
{#if isReference && filterRecord}
{previewTitle(channel.schemas, filterRecord)}
{:else}
{filterLabel}
{operators.find((o) => o.name === operator)?.symbol ?? ""}
{value}
{/if}
<button
on:click={(e) => removeFilter(e, key)}
type="button"
class="btn-close btn-close ms-1"
aria-label="Close"
on:click={(e) => removeFilter(e, key)}
type="button"
class="btn-close btn-close ms-1"
aria-label="Close"
/>
</div>
</div>
</span>
<style>
@@ -1,6 +1,8 @@
<script>
import Icon from "../../common/Icon.svelte";
import {createEventDispatcher} from "svelte";
import FilterReferenceInput from "./FilterReferenceInput.svelte";
import Dropdown from "../../common/Dropdown.svelte";
const dispatch = createEventDispatcher();
export let schema;
@@ -8,6 +10,8 @@
export let operators;
export let inModal;
export let modalUrl;
let dropdown;
let search = "";
let systemFieldsFiltered = systemFields;
if (schema.type == "collection") {
@@ -15,14 +19,13 @@
}
let filterableFields = [...schema.fields, ...systemFieldsFiltered].filter(
(f) => !["file", "json", "tab"].includes(f.ui)
(f) => !["file", "json"].includes(f.info?.name ?? f.ui)
);
let selectedField;
let selectedInput = "";
$: operatorsFiltered = operators.filter(
(o) => o.uis.includes(selectedField?.ui) || o.uis[0] == "*"
(o) => o.uis.includes(selectedField?.info?.name) || o.uis[0] == "*"
);
$: selectedOperator = operatorsFiltered[0];
@@ -30,16 +33,26 @@
function addFilter(e) {
e.preventDefault();
let filterPrefix = "";
let filterKey;
if (schema.fields.find(f => f.name === selectedField.name)) {
filterPrefix = "data.";
if (selectedField.info.name == "reference" && selectedOperator.name == "eq") {
filterPrefix = "children." + selectedField.name + ".id";
filterKey = `filter[${filterPrefix}]`;
} else {
filterPrefix = "data.";
filterKey = `filter[${filterPrefix + selectedField.name}_${selectedOperator.name}]`;
}
}
let filterKey = `filter[${filterPrefix + selectedField.name}_${selectedOperator.name}]`;
const url = new URL(modalUrl ?? window.location.href);
url.searchParams.set("skip", "0");
url.searchParams.set(filterKey, selectedInput);
if (inModal) {
dispatch("refresh", url);
dropdown.hide()
} else {
window.location = url;
}
@@ -70,18 +83,12 @@
</script>
<div class="mx-2 d-flex align-items-center">
<div class="btn-group">
<button
class="btn btn-sm btn-outline-primary dropdown-toggle d-flex align-items-center"
type="button"
data-bs-toggle="dropdown"
data-bs-auto-close="outside"
aria-expanded="false"
>
<Icon icon="filter"/>
<span class="ms-1">Filter</span>
</button>
<div class="dropdown-menu" style="width:300px;">
<Dropdown bind:this={dropdown} width="300">
<div slot="button">
<Icon icon="filter"/>
<span class="ms-1">Filter</span>
</div>
<div class="px-3 py-1 d-flex align-items-center">
<select bind:value={selectedField} class="form-select">
{#each filterableFields as field}
@@ -97,17 +104,21 @@
</select>
</div>
<div class="px-3 py-1 d-flex align-items-center">
<input
type="text"
class="form-control"
bind:value={selectedInput}
/>
{#if selectedField?.info?.name === "reference" && selectedOperator.name === "eq"}
<FilterReferenceInput field={selectedField} bind:value={selectedInput} on:addFilter={addFilter}/>
{:else}
<input
type="text"
class="form-control"
bind:value={selectedInput}
/>
{/if}
</div>
<div class="px-3 py-1 d-flex align-items-center">
<button
on:click={addFilter}
class="btn btn-outline-primary"
type="button"
on:click={addFilter}
class="btn btn-outline-primary"
type="button"
>
Add filter
</button>
@@ -118,14 +129,13 @@
<form on:submit={submitSearch}>
<div class="px-3 py-1 d-flex align-items-center">
<input
bind:value={search}
type="search"
class="form-control"
placeholder="Advanced filters"
required
bind:value={search}
type="search"
class="form-control"
placeholder="Advanced filters"
required
/>
</div>
</form>
</div>
</div>
</Dropdown>
</div>
@@ -0,0 +1,78 @@
<script>
import {createEventDispatcher, getContext} from "svelte";
import {debounce} from "lodash";
import {previewTitle} from "../../records/Preview";
const channel = getContext("channel");
const dispatch = createEventDispatcher();
export let value = "";
export let field;
let search = ""
$: searchOptions = []
const updateResults = debounce((e) => {
axios
.get(channel.lucentUrl + "/records/suggestions", {
params: {
schema: field.collections[0],
field: "search",
value: search,
ui: "search",
},
})
.then((response) => {
searchOptions = response.data;
})
.catch((error) => {
searchOptions = [];
console.log(error);
});
}, 500);
function apply(e, newOption) {
e.preventDefault();
value = newOption.id
dispatch("addFilter");
value = ""
}
</script>
<input
type="search"
on:keyup={updateResults}
class="form-control dropdown-toggle"
bind:value={search}
placeholder={"Search for "+field.label}
data-bs-toggle="dropdown"
autocomplete="off"
/>
<div class="dropdown-menu w-100">
{#if searchOptions}
{#each searchOptions as option (option.id)}
<div
on:click={(e) => apply(e, option)}
on:keypress={(e) => apply(e, option)}
>
<span class="dropdown-item">
{previewTitle(channel.schemas, option)}
</span>
</div>
{:else}
Start typing...
{/each}
{/if}
</div>
@@ -18,6 +18,7 @@
export let modalUrl;
export let isWritable;
export let records;
export let graph;
export let systemFields = [];
// export let visibleFields = [];
@@ -151,6 +152,8 @@
value={v}
{inModal}
{modalUrl}
{records}
{graph}
{systemFields}
on:refresh
/>
@@ -103,10 +103,12 @@
.modal-dialog {
width: auto;
max-width: 100%;
}
.modal-content {
margin: 40px auto;
width: auto;
height: 100%;
}
</style>
@@ -36,7 +36,6 @@
}
async function reorder(e) {
console.log(e.detail)
graph.edges = await sortByField(e.detail.source, e.detail.target, graph.edges, field.name);
}
+4
View File
@@ -62,6 +62,10 @@ class BuildController extends Controller
break;
}
if(str_contains($data["logs"],"Exception")){
break;
}
// Break the loop if the client aborted the connection (closed the page)
if (connection_aborted()) {
break;
+2
View File
@@ -294,6 +294,8 @@ class RecordController extends Controller
} elseif ($request->input("ui") == "number") {
$arguments["data." . $request->input("field") . "_eqnum"] = floatval($request->input("value"));
} elseif ($request->input("ui") == "date") {
} elseif ($request->input("ui") == "search") {
$arguments["search_regex"] = $request->input("value");
}
}
+2 -2
View File
@@ -34,6 +34,7 @@ final class FilterParser
{
$operator = $this->detectOperator($filter);
$field = $this->detectField($filter, $operator);
$formattedValue = match ($operator) {
"eq" => $this->formatText($value),
@@ -61,7 +62,6 @@ final class FilterParser
default => $value,
};
$matchedOperator = Operator::list()[$operator];
return new Argument(
field: str_replace(".", "->", $field),
@@ -134,6 +134,7 @@ final class FilterParser
return $c;
}, []);
$sourceIds = collect($subqueries)->reduce(function ($c, $subquery, $k) {
$query = $this->app->make(Query::class);
@@ -197,7 +198,6 @@ final class FilterParser
*/
private function parseAnd(Builder $builder, array $arguments): Builder
{
foreach ($arguments as $argument) {
if ($argument->operator == "in") {
$builder->whereIn($argument->field, $argument->value);
+2 -2
View File
@@ -37,7 +37,7 @@ final class Operator
label: "Equals",
symbol: "is",
db: '=',
uis: ["id", "text", "textarea", "url", "color", "date", "datetime"],
uis: ["id", "text", "textarea", "url", "color", "date", "datetime", "reference"],
),
"ne" => new Operator(
name: "ne",
@@ -61,7 +61,7 @@ final class Operator
uis: ["number"],
),
"filter" => new Operator(
name: "object",
name: "filter",
label: "Equals Object",
symbol: "is",
db: 'filter',
+7 -1
View File
@@ -6,6 +6,7 @@ use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Throwable;
class StaticGenerator
{
@@ -23,7 +24,12 @@ class StaticGenerator
echo "Start ".Carbon::now()->format("Y-m-d H:i:s").PHP_EOL;
$this->removeBuildDirectory();
echo "Removing previous data".PHP_EOL;
$callback($this->writer);
try {
$callback($this->writer);
}catch (Throwable $th){
echo "Finito with errors".Carbon::now()->format("Y-m-d H:i:s")." ".$th->getMessage().PHP_EOL;
}
$this->copyBuildDirectory();
echo "Finito ".Carbon::now()->format("Y-m-d H:i:s").PHP_EOL;
}