mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #609 from appwrite/feat-generic-filters
Refactor filters to be generic
This commit is contained in:
+24
-13
@@ -4,8 +4,11 @@
|
||||
import InputText from '$lib/elements/forms/inputText.svelte';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { columns } from '../store';
|
||||
import { tags, type Column, type Operator, queries } from './store';
|
||||
import { tags, type Operator, queries } from './store';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let columns: Writable<Column[]>;
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
clear: void;
|
||||
@@ -31,22 +34,22 @@
|
||||
'greater than': {
|
||||
toQuery: (attr, input) => Query.greaterThan(attr, Number(input)),
|
||||
toTag: (attribute, input) => `**${attribute}** greater than **${input}**`,
|
||||
types: ['integer', 'double']
|
||||
types: ['integer', 'double', 'datetime']
|
||||
},
|
||||
'greater than or equal to': {
|
||||
toQuery: (attr, input) => Query.greaterThanEqual(attr, Number(input)),
|
||||
toTag: (attribute, input) => `**${attribute}** greater than or equal to **${input}**`,
|
||||
types: ['integer', 'double']
|
||||
types: ['integer', 'double', 'datetime']
|
||||
},
|
||||
'less than': {
|
||||
toQuery: Query.lessThan,
|
||||
toTag: (attribute, input) => `**${attribute}** less than **${input}**`,
|
||||
types: ['integer', 'double']
|
||||
types: ['integer', 'double', 'datetime']
|
||||
},
|
||||
'less than or equal to': {
|
||||
toQuery: Query.lessThanEqual,
|
||||
toTag: (attribute, input) => `**${attribute}** less than or equal to **${input}**`,
|
||||
types: ['integer', 'double']
|
||||
types: ['integer', 'double', 'datetime']
|
||||
},
|
||||
equal: {
|
||||
toQuery: Query.equal,
|
||||
@@ -97,19 +100,17 @@
|
||||
|
||||
// This Map is keyed by tags, and has a query as the value
|
||||
function addFilter() {
|
||||
if (column && operator) {
|
||||
queries.addFilter({ column, operator, value });
|
||||
value = null;
|
||||
}
|
||||
if (!column || !operator) return;
|
||||
|
||||
queries.addFilter({ column, operator, value: value ?? '' });
|
||||
value = null;
|
||||
}
|
||||
|
||||
function tagFormat(node: HTMLElement) {
|
||||
node.innerHTML = node.innerHTML.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
|
||||
}
|
||||
|
||||
$: isDisabled = (function getDisabled() {
|
||||
return !operator || (!operator?.hideInput && !value);
|
||||
})();
|
||||
$: isDisabled = !operator;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@@ -134,6 +135,16 @@
|
||||
<div class="u-margin-block-start-8">
|
||||
{#if column.type === 'integer' || column.type === 'double'}
|
||||
<InputNumber id="value" bind:value placeholder="Enter value" />
|
||||
{:else if column.type === 'boolean'}
|
||||
<InputSelect
|
||||
id="value"
|
||||
placeholder="Select a value"
|
||||
required={true}
|
||||
options={[
|
||||
{ label: 'True', value: true },
|
||||
{ label: 'False', value: false }
|
||||
].filter(Boolean)}
|
||||
bind:value />
|
||||
{:else}
|
||||
<InputText id="value" bind:value placeholder="Enter value" />
|
||||
{/if}
|
||||
+14
-2
@@ -2,8 +2,16 @@
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import { Drop, Modal } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import Content from './content.svelte';
|
||||
import { queries, queriesAreDirty, tags } from './store';
|
||||
import { queries, queriesAreDirty, queryParamToMap, tags } from './store';
|
||||
|
||||
export let query = '[]';
|
||||
export let columns: Writable<Column[]>;
|
||||
|
||||
const parsedQueries = queryParamToMap(query);
|
||||
queries.set(parsedQueries);
|
||||
|
||||
// We need to separate them so we don't trigger Drop's handlers
|
||||
let showFiltersDesktop = false;
|
||||
@@ -33,6 +41,7 @@
|
||||
<div class="dropped card">
|
||||
<p>Apply filter rules to refine the table view</p>
|
||||
<Content
|
||||
{columns}
|
||||
on:apply={(e) => (applied = e.detail.applied)}
|
||||
on:clear={() => (applied = 0)} />
|
||||
<hr />
|
||||
@@ -61,7 +70,10 @@
|
||||
description="Apply filter rules to refine the table view"
|
||||
bind:show={showFiltersMobile}
|
||||
size="big">
|
||||
<Content on:apply={(e) => (applied = e.detail.applied)} on:clear={() => (applied = 0)} />
|
||||
<Content
|
||||
{columns}
|
||||
on:apply={(e) => (applied = e.detail.applied)}
|
||||
on:clear={() => (applied = 0)} />
|
||||
<svelte:fragment slot="footer">
|
||||
<Button text on:click={queries.clearAll}>Clear all</Button>
|
||||
<Button on:click={queries.apply} disabled={!$queriesAreDirty}>Apply</Button
|
||||
@@ -0,0 +1 @@
|
||||
export { default as filters } from './filters.svelte';
|
||||
+2
-10
@@ -1,17 +1,9 @@
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { derived, get, writable, type Writable } from 'svelte/store';
|
||||
import type { columns } from '../store';
|
||||
import { derived, get, writable } from 'svelte/store';
|
||||
import { page } from '$app/stores';
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
const columnTypes = ['string', 'integer', 'double', 'boolean', 'datetime', 'relationship'] as const;
|
||||
type ColumnType = (typeof columnTypes)[number];
|
||||
|
||||
type StoreValues<Store> = Store extends Writable<infer T> ? T : never;
|
||||
export type Column = Omit<StoreValues<typeof columns>[number], 'type'> & {
|
||||
type: ColumnType;
|
||||
};
|
||||
import type { Column, ColumnType } from '$lib/helpers/types';
|
||||
|
||||
export type Operator = {
|
||||
toTag: (attribute: string, input?: string | number) => string;
|
||||
@@ -1,12 +1,3 @@
|
||||
<script context="module" lang="ts">
|
||||
export type Column = {
|
||||
id: string;
|
||||
title: string;
|
||||
show: boolean;
|
||||
width?: number;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Button, InputChoice } from '$lib/elements/forms';
|
||||
import { DropList } from '.';
|
||||
@@ -16,6 +7,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import { tooltip } from '$lib/actions/tooltip';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
|
||||
export let columns: Writable<Column[]>;
|
||||
export let view: View;
|
||||
|
||||
@@ -32,6 +32,10 @@ export function getSearch(url: URL): string | undefined {
|
||||
return url.searchParams.get('search') ?? undefined;
|
||||
}
|
||||
|
||||
export function getQuery(url: URL): string | undefined {
|
||||
return url.searchParams.get('query') ?? undefined;
|
||||
}
|
||||
|
||||
type TabElement = { href: string; title: string; hasChildren?: boolean };
|
||||
|
||||
export function isTabSelected(
|
||||
|
||||
@@ -12,3 +12,13 @@ export function isHTMLInputElement(el: unknown): el is HTMLInputElement {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type Prettify<T> = T & {};
|
||||
|
||||
const columnTypes = ['string', 'integer', 'double', 'boolean', 'datetime', 'relationship'] as const;
|
||||
export type ColumnType = (typeof columnTypes)[number];
|
||||
export type Column = {
|
||||
id: string;
|
||||
title: string;
|
||||
type: ColumnType;
|
||||
show: boolean;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Heading, ViewSelector } from '$lib/components';
|
||||
import type { View } from '$lib/helpers/load';
|
||||
import type { Column } from '$lib/components/viewSelector.svelte';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let title: string;
|
||||
|
||||
+5
-11
@@ -1,15 +1,9 @@
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export type Column = {
|
||||
id: string;
|
||||
title: string;
|
||||
show: boolean;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
export const columns = writable<Column[]>([
|
||||
{ id: '$id', title: 'Database ID', show: true, width: 150 },
|
||||
{ id: 'name', title: 'Name', show: true, width: 120 },
|
||||
{ id: '$createdAt', title: 'Created', show: true, width: 120 },
|
||||
{ id: '$updatedAt', title: 'Updated', show: true, width: 120 }
|
||||
{ id: '$id', title: 'Database ID', type: 'string', show: true, width: 150 },
|
||||
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
|
||||
{ id: '$createdAt', title: 'Created', type: 'string', show: true, width: 120 },
|
||||
{ id: '$updatedAt', title: 'Updated', type: 'string', show: true, width: 120 }
|
||||
]);
|
||||
|
||||
+5
-4
@@ -1,14 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { Empty, EmptySearch, Heading, PaginationWithLimit } from '$lib/components';
|
||||
import Filters from '$lib/components/filters/filters.svelte';
|
||||
import { hasPageQueries, queries } from '$lib/components/filters/store';
|
||||
import ViewSelector from '$lib/components/viewSelector.svelte';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import type { ColumnType } from '$lib/helpers/types';
|
||||
import { Container } from '$lib/layout';
|
||||
import { preferences } from '$lib/stores/preferences';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import type { PageData } from './$types';
|
||||
import Filters from './(filters)/filters.svelte';
|
||||
import { hasPageQueries, queries } from './(filters)/store';
|
||||
import CreateAttributeDropdown from './attributes/createAttributeDropdown.svelte';
|
||||
import type { Option } from './attributes/store';
|
||||
import CreateAttribute from './createAttribute.svelte';
|
||||
@@ -27,7 +28,7 @@
|
||||
$collection.attributes.map((attribute) => ({
|
||||
id: attribute.key,
|
||||
title: attribute.key,
|
||||
type: attribute.type,
|
||||
type: attribute.type as ColumnType,
|
||||
show: selected?.includes(attribute.key) ?? true
|
||||
}))
|
||||
);
|
||||
@@ -54,7 +55,7 @@
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Filters />
|
||||
<Filters query={data.query} {columns} />
|
||||
|
||||
<div class="u-flex u-main-end u-gap-16">
|
||||
<ViewSelector
|
||||
|
||||
+4
-3
@@ -1,10 +1,9 @@
|
||||
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
|
||||
import { getLimit, getPage, getView, pageToOffset, View } from '$lib/helpers/load';
|
||||
import { getLimit, getPage, getQuery, getView, pageToOffset, View } from '$lib/helpers/load';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
import { queries, queryParamToMap } from './(filters)/store';
|
||||
import { queries, queryParamToMap } from '$lib/components/filters/store';
|
||||
|
||||
export const load: PageLoad = async ({ params, depends, url, route }) => {
|
||||
depends(Dependencies.DOCUMENTS);
|
||||
@@ -12,6 +11,7 @@ export const load: PageLoad = async ({ params, depends, url, route }) => {
|
||||
const limit = getLimit(url, route, PAGE_LIMIT);
|
||||
const view = getView(url, route, View.Grid);
|
||||
const offset = pageToOffset(page, limit);
|
||||
const query = getQuery(url);
|
||||
|
||||
const paramQueries = url.searchParams.get('query');
|
||||
const parsedQueries = queryParamToMap(paramQueries || '[]');
|
||||
@@ -21,6 +21,7 @@ export const load: PageLoad = async ({ params, depends, url, route }) => {
|
||||
offset,
|
||||
limit,
|
||||
view,
|
||||
query,
|
||||
documents: await sdk.forProject.databases.listDocuments(
|
||||
params.database,
|
||||
params.collection,
|
||||
|
||||
+1
-8
@@ -1,4 +1,5 @@
|
||||
import { page } from '$app/stores';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
|
||||
@@ -16,14 +17,6 @@ export type Attributes =
|
||||
type Collection = Omit<Models.Collection, 'attributes'> & {
|
||||
attributes: Array<Attributes>;
|
||||
};
|
||||
export type Column = {
|
||||
id: string;
|
||||
title: string;
|
||||
show: boolean;
|
||||
type?: string;
|
||||
twoWay?: string;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
export const collection = derived(page, ($page) => $page.data.collection as Collection);
|
||||
export const attributes = derived(
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@
|
||||
import RelationshipsModal from './relationshipsModal.svelte';
|
||||
import { attributes, collection, columns } from './store';
|
||||
import { clickOnEnter } from '$lib/helpers/a11y';
|
||||
import type { ColumnType } from '$lib/helpers/types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -89,7 +90,7 @@
|
||||
$collection.attributes.map((attribute) => ({
|
||||
id: attribute.key,
|
||||
title: attribute.key,
|
||||
type: attribute.type,
|
||||
type: attribute.type as ColumnType,
|
||||
show: selected?.includes(attribute.key) ?? true
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { page } from '$app/stores';
|
||||
import type { Column } from '$lib/components/viewSelector.svelte';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { derived, writable } from 'svelte/store';
|
||||
|
||||
@@ -7,8 +7,8 @@ export const database = derived(page, ($page) => $page.data.database as Models.D
|
||||
export const showCreate = writable(false);
|
||||
|
||||
export const columns = writable<Column[]>([
|
||||
{ id: '$id', title: 'Collection ID', show: true, width: 150 },
|
||||
{ id: 'name', title: 'Name', show: true, width: 120 },
|
||||
{ id: '$createdAt', title: 'Created', show: true, width: 120 },
|
||||
{ id: '$updatedAt', title: 'Updated', show: true, width: 120 }
|
||||
{ id: '$id', title: 'Collection ID', type: 'string', show: true, width: 150 },
|
||||
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
|
||||
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 120 },
|
||||
{ id: '$updatedAt', title: 'Updated', type: 'datetime', show: true, width: 120 }
|
||||
]);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Column } from '$lib/components/viewSelector.svelte';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const columns = writable<Column[]>([
|
||||
{ id: '$id', title: 'Database ID', show: true, width: 150 },
|
||||
{ id: 'name', title: 'Name', show: true, width: 120 },
|
||||
{ id: '$createdAt', title: 'Created', show: true, width: 120 },
|
||||
{ id: '$updatedAt', title: 'Updated', show: true, width: 120 }
|
||||
{ id: '$id', title: 'Database ID', type: 'string', show: true, width: 150 },
|
||||
{ id: 'name', title: 'Name', type: 'string', show: true, width: 120 },
|
||||
{ id: '$createdAt', title: 'Created', type: 'datetime', show: true, width: 120 },
|
||||
{ id: '$updatedAt', title: 'Updated', type: 'datetime', show: true, width: 120 }
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user