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": { "main.js": {
"file": "assets/main.28b0c00f.js", "file": "assets/main.a696aa4b.js",
"src": "main.js", "src": "main.js",
"isEntry": true, "isEntry": true,
"css": [ "css": [
"assets/main.04114af1.css" "assets/main.66909535.css"
] ]
}, },
"main.css": { "main.css": {
"file": "assets/main.04114af1.css", "file": "assets/main.66909535.css",
"src": "main.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} {sortField}
{operators} {operators}
{filter} {filter}
{graph}
{inModal} {inModal}
{modalUrl} {modalUrl}
{isWritable} {isWritable}
@@ -1,6 +1,8 @@
<script> <script>
import {createEventDispatcher} from "svelte"; import {createEventDispatcher, getContext} from "svelte";
import {previewTitle} from "../../records/Preview";
const channel = getContext("channel");
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
export let schema; export let schema;
export let operators; export let operators;
@@ -8,6 +10,8 @@
export let value; export let value;
export let inModal; export let inModal;
export let modalUrl; export let modalUrl;
export let records;
export let graph;
// export let systemFields; // export let systemFields;
let filterSplit = key.split("_"); let filterSplit = key.split("_");
@@ -16,6 +20,12 @@
let fieldName = key.replace("_" + operator, ""); let fieldName = key.replace("_" + operator, "");
let filterField = schema.fields.find((f) => f.name === fieldName); let filterField = schema.fields.find((f) => f.name === fieldName);
let filterLabel = filterField?.label ?? 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) { function removeFilter(e, k) {
e.preventDefault(); e.preventDefault();
@@ -36,9 +46,17 @@
style="line-height:22px ;" style="line-height:22px ;"
> >
<div class="d-flex align-items-center justify-content-center"> <div class="d-flex align-items-center justify-content-center">
{#if isReference && filterRecord}
{previewTitle(channel.schemas, filterRecord)}
{:else}
{filterLabel} {filterLabel}
{operators.find((o) => o.name === operator)?.symbol ?? ""} {operators.find((o) => o.name === operator)?.symbol ?? ""}
{value} {value}
{/if}
<button <button
on:click={(e) => removeFilter(e, key)} on:click={(e) => removeFilter(e, key)}
type="button" type="button"
@@ -1,6 +1,8 @@
<script> <script>
import Icon from "../../common/Icon.svelte"; import Icon from "../../common/Icon.svelte";
import {createEventDispatcher} from "svelte"; import {createEventDispatcher} from "svelte";
import FilterReferenceInput from "./FilterReferenceInput.svelte";
import Dropdown from "../../common/Dropdown.svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
export let schema; export let schema;
@@ -8,6 +10,8 @@
export let operators; export let operators;
export let inModal; export let inModal;
export let modalUrl; export let modalUrl;
let dropdown;
let search = ""; let search = "";
let systemFieldsFiltered = systemFields; let systemFieldsFiltered = systemFields;
if (schema.type == "collection") { if (schema.type == "collection") {
@@ -15,14 +19,13 @@
} }
let filterableFields = [...schema.fields, ...systemFieldsFiltered].filter( 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 selectedField;
let selectedInput = ""; let selectedInput = "";
$: operatorsFiltered = operators.filter( $: 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]; $: selectedOperator = operatorsFiltered[0];
@@ -30,16 +33,26 @@
function addFilter(e) { function addFilter(e) {
e.preventDefault(); e.preventDefault();
let filterPrefix = ""; let filterPrefix = "";
let filterKey;
if (schema.fields.find(f => f.name === selectedField.name)) { if (schema.fields.find(f => f.name === selectedField.name)) {
if (selectedField.info.name == "reference" && selectedOperator.name == "eq") {
filterPrefix = "children." + selectedField.name + ".id";
filterKey = `filter[${filterPrefix}]`;
} else {
filterPrefix = "data."; 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); const url = new URL(modalUrl ?? window.location.href);
url.searchParams.set("skip", "0"); url.searchParams.set("skip", "0");
url.searchParams.set(filterKey, selectedInput); url.searchParams.set(filterKey, selectedInput);
if (inModal) { if (inModal) {
dispatch("refresh", url); dispatch("refresh", url);
dropdown.hide()
} else { } else {
window.location = url; window.location = url;
} }
@@ -70,18 +83,12 @@
</script> </script>
<div class="mx-2 d-flex align-items-center"> <div class="mx-2 d-flex align-items-center">
<div class="btn-group"> <Dropdown bind:this={dropdown} width="300">
<button <div slot="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"/> <Icon icon="filter"/>
<span class="ms-1">Filter</span> <span class="ms-1">Filter</span>
</button> </div>
<div class="dropdown-menu" style="width:300px;">
<div class="px-3 py-1 d-flex align-items-center"> <div class="px-3 py-1 d-flex align-items-center">
<select bind:value={selectedField} class="form-select"> <select bind:value={selectedField} class="form-select">
{#each filterableFields as field} {#each filterableFields as field}
@@ -97,11 +104,15 @@
</select> </select>
</div> </div>
<div class="px-3 py-1 d-flex align-items-center"> <div class="px-3 py-1 d-flex align-items-center">
{#if selectedField?.info?.name === "reference" && selectedOperator.name === "eq"}
<FilterReferenceInput field={selectedField} bind:value={selectedInput} on:addFilter={addFilter}/>
{:else}
<input <input
type="text" type="text"
class="form-control" class="form-control"
bind:value={selectedInput} bind:value={selectedInput}
/> />
{/if}
</div> </div>
<div class="px-3 py-1 d-flex align-items-center"> <div class="px-3 py-1 d-flex align-items-center">
<button <button
@@ -126,6 +137,5 @@
/> />
</div> </div>
</form> </form>
</div> </Dropdown>
</div>
</div> </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 modalUrl;
export let isWritable; export let isWritable;
export let records; export let records;
export let graph;
export let systemFields = []; export let systemFields = [];
// export let visibleFields = []; // export let visibleFields = [];
@@ -151,6 +152,8 @@
value={v} value={v}
{inModal} {inModal}
{modalUrl} {modalUrl}
{records}
{graph}
{systemFields} {systemFields}
on:refresh on:refresh
/> />
@@ -103,10 +103,12 @@
.modal-dialog { .modal-dialog {
width: auto; width: auto;
max-width: 100%; max-width: 100%;
} }
.modal-content { .modal-content {
margin: 40px auto; margin: 40px auto;
width: auto; width: auto;
height: 100%;
} }
</style> </style>
@@ -36,7 +36,6 @@
} }
async function reorder(e) { async function reorder(e) {
console.log(e.detail)
graph.edges = await sortByField(e.detail.source, e.detail.target, graph.edges, field.name); 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; break;
} }
if(str_contains($data["logs"],"Exception")){
break;
}
// Break the loop if the client aborted the connection (closed the page) // Break the loop if the client aborted the connection (closed the page)
if (connection_aborted()) { if (connection_aborted()) {
break; break;
+2
View File
@@ -294,6 +294,8 @@ class RecordController extends Controller
} elseif ($request->input("ui") == "number") { } elseif ($request->input("ui") == "number") {
$arguments["data." . $request->input("field") . "_eqnum"] = floatval($request->input("value")); $arguments["data." . $request->input("field") . "_eqnum"] = floatval($request->input("value"));
} elseif ($request->input("ui") == "date") { } 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); $operator = $this->detectOperator($filter);
$field = $this->detectField($filter, $operator); $field = $this->detectField($filter, $operator);
$formattedValue = match ($operator) { $formattedValue = match ($operator) {
"eq" => $this->formatText($value), "eq" => $this->formatText($value),
@@ -61,7 +62,6 @@ final class FilterParser
default => $value, default => $value,
}; };
$matchedOperator = Operator::list()[$operator]; $matchedOperator = Operator::list()[$operator];
return new Argument( return new Argument(
field: str_replace(".", "->", $field), field: str_replace(".", "->", $field),
@@ -134,6 +134,7 @@ final class FilterParser
return $c; return $c;
}, []); }, []);
$sourceIds = collect($subqueries)->reduce(function ($c, $subquery, $k) { $sourceIds = collect($subqueries)->reduce(function ($c, $subquery, $k) {
$query = $this->app->make(Query::class); $query = $this->app->make(Query::class);
@@ -197,7 +198,6 @@ final class FilterParser
*/ */
private function parseAnd(Builder $builder, array $arguments): Builder private function parseAnd(Builder $builder, array $arguments): Builder
{ {
foreach ($arguments as $argument) { foreach ($arguments as $argument) {
if ($argument->operator == "in") { if ($argument->operator == "in") {
$builder->whereIn($argument->field, $argument->value); $builder->whereIn($argument->field, $argument->value);
+2 -2
View File
@@ -37,7 +37,7 @@ final class Operator
label: "Equals", label: "Equals",
symbol: "is", symbol: "is",
db: '=', db: '=',
uis: ["id", "text", "textarea", "url", "color", "date", "datetime"], uis: ["id", "text", "textarea", "url", "color", "date", "datetime", "reference"],
), ),
"ne" => new Operator( "ne" => new Operator(
name: "ne", name: "ne",
@@ -61,7 +61,7 @@ final class Operator
uis: ["number"], uis: ["number"],
), ),
"filter" => new Operator( "filter" => new Operator(
name: "object", name: "filter",
label: "Equals Object", label: "Equals Object",
symbol: "is", symbol: "is",
db: 'filter', db: 'filter',
+6
View File
@@ -6,6 +6,7 @@ use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Throwable;
class StaticGenerator class StaticGenerator
{ {
@@ -23,7 +24,12 @@ class StaticGenerator
echo "Start ".Carbon::now()->format("Y-m-d H:i:s").PHP_EOL; echo "Start ".Carbon::now()->format("Y-m-d H:i:s").PHP_EOL;
$this->removeBuildDirectory(); $this->removeBuildDirectory();
echo "Removing previous data".PHP_EOL; echo "Removing previous data".PHP_EOL;
try {
$callback($this->writer); $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(); $this->copyBuildDirectory();
echo "Finito ".Carbon::now()->format("Y-m-d H:i:s").PHP_EOL; echo "Finito ".Carbon::now()->format("Y-m-d H:i:s").PHP_EOL;
} }