filters improvements
This commit is contained in:
Vendored
-188
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+188
File diff suppressed because one or more lines are too long
Vendored
+3
-3
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
|
|
||||||
@@ -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,14 +10,22 @@
|
|||||||
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("_");
|
||||||
let operator = filterSplit[filterSplit.length - 1] ?? "eq";
|
let operator = filterSplit[filterSplit.length - 1] ?? "eq";
|
||||||
|
|
||||||
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();
|
||||||
@@ -32,20 +42,28 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="applied-filter d-inline-block border border-primary rounded lx-small-text me-1 px-2 py-1"
|
class="applied-filter d-inline-block border border-primary rounded lx-small-text me-1 px-2 py-1"
|
||||||
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">
|
||||||
{filterLabel}
|
{#if isReference && filterRecord}
|
||||||
|
{previewTitle(channel.schemas, filterRecord)}
|
||||||
|
|
||||||
|
{:else}
|
||||||
|
|
||||||
|
|
||||||
|
{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"
|
||||||
class="btn-close btn-close ms-1"
|
class="btn-close btn-close ms-1"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -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)) {
|
||||||
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);
|
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"
|
<Icon icon="filter"/>
|
||||||
type="button"
|
<span class="ms-1">Filter</span>
|
||||||
data-bs-toggle="dropdown"
|
</div>
|
||||||
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;">
|
|
||||||
<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,17 +104,21 @@
|
|||||||
</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">
|
||||||
<input
|
{#if selectedField?.info?.name === "reference" && selectedOperator.name === "eq"}
|
||||||
type="text"
|
<FilterReferenceInput field={selectedField} bind:value={selectedInput} on:addFilter={addFilter}/>
|
||||||
class="form-control"
|
{:else}
|
||||||
bind:value={selectedInput}
|
<input
|
||||||
/>
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
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
|
||||||
on:click={addFilter}
|
on:click={addFilter}
|
||||||
class="btn btn-outline-primary"
|
class="btn btn-outline-primary"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
Add filter
|
Add filter
|
||||||
</button>
|
</button>
|
||||||
@@ -118,14 +129,13 @@
|
|||||||
<form on:submit={submitSearch}>
|
<form on:submit={submitSearch}>
|
||||||
<div class="px-3 py-1 d-flex align-items-center">
|
<div class="px-3 py-1 d-flex align-items-center">
|
||||||
<input
|
<input
|
||||||
bind:value={search}
|
bind:value={search}
|
||||||
type="search"
|
type="search"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="Advanced filters"
|
placeholder="Advanced filters"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,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;
|
||||||
$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();
|
$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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user