mirror of
https://github.com/appwrite/console.git
synced 2026-04-07 19:17:46 +00:00
refactor: quick filters, centrilize logic, remove duplicated code
This commit is contained in:
@@ -3,4 +3,5 @@ export { default as TagList } from './tagList.svelte';
|
||||
export { default as FilterMenu } from './menu.svelte';
|
||||
export { default as FilterSubMenu } from './subMenu.svelte';
|
||||
export { default as CustomFilters } from './customFilters.svelte';
|
||||
export { default as QuickFilters } from './quickFilters.svelte';
|
||||
export { hasPageQueries, queryParamToMap, queries } from '$lib/components/filters/store';
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
import MenuItem from '$lib/components/filters/subMenu.svelte';
|
||||
import { queryParamToMap } from '$lib/components/filters/store';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { type Writable } from 'svelte/store';
|
||||
import Menu from '$lib/components/filters/menu.svelte';
|
||||
import { CustomFilters } from '$lib/components/filters';
|
||||
import { setFiltersOnNavigate } from '$lib/components/filters/setFilters';
|
||||
import { addFilterAndApply, buildFilterCol } from '$lib/components/filters/quickFilters';
|
||||
|
||||
export let columns: Writable<Column[]>;
|
||||
export let analyticsSource: string;
|
||||
|
||||
let filterCols = $columns
|
||||
.map((col) => (col.filter !== false ? buildFilterCol(col) : null))
|
||||
.filter((f) => f?.options);
|
||||
|
||||
afterNavigate((p) => {
|
||||
const paramQueries = p.to.url.searchParams.get('query');
|
||||
const localQueries = queryParamToMap(paramQueries || '[]');
|
||||
const localTags = Array.from(localQueries.keys());
|
||||
|
||||
setFiltersOnNavigate(localTags, filterCols, $columns);
|
||||
});
|
||||
</script>
|
||||
|
||||
<Menu>
|
||||
{#each filterCols as filter}
|
||||
{#if filter.options}
|
||||
<MenuItem
|
||||
{filter}
|
||||
variant={filter?.array ? 'checkbox' : 'radio'}
|
||||
on:add={(e) => {
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
e.detail.value,
|
||||
filter?.array
|
||||
? (filter.options
|
||||
.filter((opt) => opt.checked)
|
||||
.map((opt) => opt.value) ?? [])
|
||||
: [],
|
||||
$columns,
|
||||
analyticsSource
|
||||
);
|
||||
}}
|
||||
on:clear={() => {
|
||||
filter.tag = null;
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
null,
|
||||
[],
|
||||
$columns,
|
||||
analyticsSource
|
||||
);
|
||||
}} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<svelte:fragment slot="end">
|
||||
<CustomFilters {columns} />
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { get } from 'svelte/store';
|
||||
import { addFilter, queries, tags, ValidOperators } from './store';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export type FilterData = {
|
||||
title: string;
|
||||
id: string;
|
||||
array: boolean;
|
||||
show: boolean;
|
||||
tag: string;
|
||||
operator: ValidOperators;
|
||||
options: { value: string; label: string; checked: boolean }[];
|
||||
};
|
||||
|
||||
export function buildFilterCol(col: Column, customOperator = null): FilterData {
|
||||
return {
|
||||
title: col.title,
|
||||
id: col.id,
|
||||
show: false,
|
||||
array: col?.array,
|
||||
tag: null,
|
||||
operator: customOperator ?? ValidOperators.Equal,
|
||||
options: col?.elements?.map((element) => {
|
||||
return {
|
||||
value: (element?.value ?? element) as string,
|
||||
label: (element?.label ?? element) as string,
|
||||
checked: false
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
export function addFilterAndApply(
|
||||
colId: string,
|
||||
colTitle: string,
|
||||
operator: ValidOperators,
|
||||
value: string,
|
||||
arrayValues: string[] = [],
|
||||
columns: Column[],
|
||||
analyticsSource: string
|
||||
) {
|
||||
const tagList = get(tags).filter((tag) => tag.tag.includes(colTitle));
|
||||
tagList.forEach((tag) => queries.removeFilter(tag));
|
||||
if (value || arrayValues?.length) {
|
||||
if (colId === 'sourceSize' || colId === 'buildSize') {
|
||||
addSizeFilter(value, colId, columns);
|
||||
} else if (colId === 'statusCode') {
|
||||
addStatusCodeFilter(value, colId, columns);
|
||||
} else if (colId === '$createdAt' || colId === '$updatedAt' || colId === 'buildDuration') {
|
||||
addDateFilter(value, colId, columns);
|
||||
} else {
|
||||
addFilter(columns, colId, operator, value, arrayValues);
|
||||
}
|
||||
}
|
||||
queries.apply();
|
||||
trackEvent(Submit.ApplyQuickFilter, {
|
||||
source: analyticsSource,
|
||||
column: colId,
|
||||
value: value || arrayValues.join(', ')
|
||||
});
|
||||
}
|
||||
|
||||
export function resetOptions(filter: FilterData) {
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
export function addStatusCodeFilter(value: string, colId: string, columns: Column[]) {
|
||||
addFilter(columns, colId, ValidOperators.LessThanOrEqual, parseInt(value));
|
||||
addFilter(columns, colId, ValidOperators.GreaterThanOrEqual, parseInt(value) - 99);
|
||||
}
|
||||
export function addDateFilter(value: string, colId: string, columns: Column[]) {
|
||||
const now = new Date();
|
||||
const isoValue = new Date(now.getTime() - parseInt(value));
|
||||
addFilter(columns, colId, ValidOperators.GreaterThanOrEqual, isoValue.toISOString());
|
||||
addFilter(columns, colId, ValidOperators.LessThanOrEqual, now.toISOString());
|
||||
}
|
||||
|
||||
export function addSizeFilter(value: string, colId: string, columns: Column[]) {
|
||||
addFilter(columns, colId, ValidOperators.GreaterThanOrEqual, value);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { resetOptions, type FilterData } from './quickFilters';
|
||||
import type { TagValue } from './store';
|
||||
|
||||
export function setFiltersOnNavigate(
|
||||
tags: TagValue[],
|
||||
filterCols: FilterData[],
|
||||
$columns: Column[]
|
||||
) {
|
||||
if (!tags?.length) {
|
||||
filterCols.forEach((filter) => {
|
||||
resetOptions(filter);
|
||||
});
|
||||
} else {
|
||||
filterCols.forEach((filter) => {
|
||||
if (filter.id === 'buildDuration') {
|
||||
setTimeFilter(tags, filter, $columns);
|
||||
} else if (filter.id.includes('size')) {
|
||||
setSizeFilter(tags, filter, $columns);
|
||||
} else if (filter.id === 'statusCode') {
|
||||
setStatusCodeFilter(tags, filter, $columns);
|
||||
} else if (filter.id === '$createdAt' || filter.id === '$updatedAt') {
|
||||
setDateFilter(tags, filter, $columns);
|
||||
} else {
|
||||
setFilterData(filter, tags);
|
||||
}
|
||||
});
|
||||
|
||||
// Reasinging the filters to trigger reactivity
|
||||
filterCols = filterCols;
|
||||
}
|
||||
}
|
||||
|
||||
export function setFilterData(filter: FilterData, list: TagValue[]) {
|
||||
const tagData = list.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
if (tagData) {
|
||||
filter.tag = tagData.tag;
|
||||
if (Array.isArray(tagData.value) && tagData.value?.length) {
|
||||
const values = tagData.value as string[];
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = values.includes(option.value);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
resetOptions(filter);
|
||||
}
|
||||
}
|
||||
|
||||
export function setTimeFilter(localTags: TagValue[], filter: FilterData, columns: Column[]) {
|
||||
const col = columns.find((c) => c.id === filter.id);
|
||||
const timeTag = localTags.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
if (timeTag) {
|
||||
const now = new Date();
|
||||
|
||||
const diff = now.getTime() - new Date(timeTag.value as string).getTime();
|
||||
const ranges = col.elements as { value: string; label: string }[];
|
||||
const dateRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(curr.value) < diff && curr.value > prev.value) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (dateRange) {
|
||||
filter.tag = `**${filter.title}** is **${dateRange.label}**`;
|
||||
filter = filter;
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function setSizeFilter(localTags: TagValue[], filter: FilterData, columns: Column[]) {
|
||||
const sizeTag = localTags.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
const col = columns.find((c) => c.id === filter.id);
|
||||
if (sizeTag) {
|
||||
const size = sizeTag.value as string;
|
||||
const ranges = col.elements as { value: string; label: string }[];
|
||||
// find smallest range that is bigger than size
|
||||
const sizeRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(size) >= parseInt(curr.value)) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (sizeRange) {
|
||||
filter.tag = `**${filter.title}** is **${sizeRange.label}**`;
|
||||
filter = filter;
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function setStatusCodeFilter(localTags: TagValue[], filter: FilterData, columns: Column[]) {
|
||||
const statusCodeTag = localTags.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
const col = columns.find((c) => c.id === filter.id);
|
||||
|
||||
if (statusCodeTag) {
|
||||
const ranges = col.elements as { value: number; label: string }[];
|
||||
|
||||
const codeRange = ranges.find((c) => c?.value && c.value === statusCodeTag.value);
|
||||
if (codeRange) {
|
||||
filter.tag = `**${filter.title}** is **${codeRange.label}**`;
|
||||
filter = filter;
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function setDateFilter(localTags: TagValue[], filter: FilterData, columns: Column[]) {
|
||||
const dateTeag = localTags.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
const col = columns.find((c) => c.id === filter.id);
|
||||
if (dateTeag) {
|
||||
const now = new Date();
|
||||
|
||||
const diff = now.getTime() - new Date(dateTeag.value as string).getTime();
|
||||
const ranges = col.elements as { value: string; label: string }[];
|
||||
const dateRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(curr.value) < diff && curr.value > prev.value) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (dateRange) {
|
||||
filter.tag = `**${filter.title}** is **${dateRange.label}**`;
|
||||
filter = filter;
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { derived, get, writable } from 'svelte/store';
|
||||
import { page } from '$app/stores';
|
||||
import deepEqual from 'deep-equal';
|
||||
|
||||
@@ -39,6 +39,9 @@ export type Column = {
|
||||
array?: boolean;
|
||||
format?: string;
|
||||
elements?: string[] | { value: string | number; label: string }[];
|
||||
/**
|
||||
* Set to true to hide this column by default
|
||||
*/
|
||||
hide?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,12 +9,9 @@
|
||||
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
|
||||
import { readOnly } from '$lib/stores/billing';
|
||||
import Table from './table.svelte';
|
||||
import { Filters } from '$lib/components/filters';
|
||||
import { queries, tags } from '$lib/components/filters/store';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import DeploymentCard from './(components)/deploymentCard.svelte';
|
||||
import RedeployModal from './(modals)/redeployModal.svelte';
|
||||
import QuickFilters from './quickFilters.svelte';
|
||||
import { canWriteFunctions } from '$lib/stores/roles';
|
||||
import {
|
||||
ActionMenu,
|
||||
@@ -28,13 +25,13 @@
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
IconFilterLine,
|
||||
IconPlus,
|
||||
IconRefresh,
|
||||
IconTerminal
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { app } from '$lib/stores/app';
|
||||
import CreateActionMenu from './(components)/createActionMenu.svelte';
|
||||
import { QuickFilters } from '$lib/components/filters';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -42,11 +39,6 @@
|
||||
let showAlert = true;
|
||||
|
||||
let selectedDeployment: Models.Deployment = null;
|
||||
|
||||
function clearAll() {
|
||||
queries.clearAll();
|
||||
queries.apply();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
@@ -182,7 +174,7 @@
|
||||
<Layout.Stack direction="row" alignItems="center">
|
||||
<Layout.Stack direction="row" gap="s" wrap="wrap">
|
||||
{#if data.deploymentList.total}
|
||||
<QuickFilters {columns} />
|
||||
<QuickFilters {columns} analyticsSource="function_deployments" />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" gap="s" inline>
|
||||
|
||||
+2
-2
@@ -9,11 +9,11 @@
|
||||
import { project } from '$routes/(console)/project-[project]/store';
|
||||
import { base } from '$app/paths';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import QuickFilters from './quickFilters.svelte';
|
||||
import { Icon, Layout } from '@appwrite.io/pink-svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import Table from './table.svelte';
|
||||
import { columns } from './store';
|
||||
import { QuickFilters } from '$lib/components/filters';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<Container>
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="space-between">
|
||||
<QuickFilters {columns} />
|
||||
<QuickFilters {columns} analyticsSource="function_executions" />
|
||||
|
||||
<Layout.Stack gap="s" inline direction="row" alignItems="center">
|
||||
{#if data?.executions?.total}
|
||||
|
||||
-233
@@ -1,233 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
import MenuItem from '$lib/components/filters/subMenu.svelte';
|
||||
import {
|
||||
addFilter,
|
||||
queries,
|
||||
queryParamToMap,
|
||||
tags,
|
||||
ValidOperators,
|
||||
type TagValue
|
||||
} from '$lib/components/filters/store';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { type Writable } from 'svelte/store';
|
||||
import Menu from '$lib/components/filters/menu.svelte';
|
||||
import { CustomFilters } from '$lib/components/filters';
|
||||
|
||||
export let columns: Writable<Column[]>;
|
||||
|
||||
type FilterData = {
|
||||
title: string;
|
||||
id: string;
|
||||
array: boolean;
|
||||
show: boolean;
|
||||
tag: string;
|
||||
operator: ValidOperators;
|
||||
options: { value: string; label: string; checked: boolean }[];
|
||||
};
|
||||
|
||||
function buildFilterCol(col: Column, customOperator = null): FilterData {
|
||||
return {
|
||||
title: col.title,
|
||||
id: col.id,
|
||||
show: false,
|
||||
array: col?.array,
|
||||
tag: null,
|
||||
operator: customOperator ?? ValidOperators.Equal,
|
||||
options: col?.elements?.map((element) => {
|
||||
return {
|
||||
value: (element?.value ?? element) as string,
|
||||
label: (element?.label ?? element) as string,
|
||||
checked: false
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const statusCol = $columns.find((col) => col.id === 'status');
|
||||
let statusFilter = buildFilterCol(statusCol);
|
||||
const triggerCol = $columns.find((col) => col.id === 'trigger');
|
||||
let triggerFilter = buildFilterCol(triggerCol);
|
||||
const methodCol = $columns.find((col) => col.id === 'requestMethod');
|
||||
let methodFilter = buildFilterCol(methodCol);
|
||||
const statusCodeCol = $columns.find((col) => col.id === 'responseStatusCode');
|
||||
let statusCodeFilter = buildFilterCol(statusCodeCol);
|
||||
const createdAtCol = $columns.find((col) => col.id === '$createdAt');
|
||||
let createdAtFilter = buildFilterCol(createdAtCol);
|
||||
|
||||
let localQueries = new Map<TagValue, string>();
|
||||
|
||||
afterNavigate((p) => {
|
||||
const paramQueries = p.to.url.searchParams.get('query');
|
||||
localQueries = queryParamToMap(paramQueries || '[]');
|
||||
const localTags = Array.from(localQueries.keys());
|
||||
|
||||
if (!localTags?.length) {
|
||||
statusFilter.tag = null;
|
||||
triggerFilter.tag = null;
|
||||
methodFilter.tag = null;
|
||||
statusCodeFilter.tag = null;
|
||||
createdAtFilter.tag = null;
|
||||
[statusFilter, triggerFilter, methodFilter].forEach((filter) => {
|
||||
resetOptions(filter);
|
||||
});
|
||||
} else {
|
||||
[statusFilter, triggerFilter, methodFilter].forEach((filter) => {
|
||||
setFilterData(filter, localTags);
|
||||
});
|
||||
|
||||
const statusCodeTag = localTags.find((tag) =>
|
||||
tag.tag.includes(`**${statusCodeFilter.title}**`)
|
||||
);
|
||||
if (statusCodeTag) {
|
||||
const ranges = statusCodeCol.elements as { value: number; label: string }[];
|
||||
|
||||
const codeRange = ranges.find((c) => c?.value && c.value === statusCodeTag.value);
|
||||
if (codeRange) {
|
||||
statusCodeFilter.tag = `**${statusCodeFilter.title}** is **${codeRange.label}**`;
|
||||
statusCodeFilter = statusCodeFilter;
|
||||
}
|
||||
} else {
|
||||
statusCodeFilter.tag = null;
|
||||
}
|
||||
|
||||
const createdAtTag = localTags.find((tag) =>
|
||||
tag.tag.includes(`**${createdAtFilter.title}**`)
|
||||
);
|
||||
if (createdAtTag) {
|
||||
const now = new Date();
|
||||
|
||||
const diff = now.getTime() - new Date(createdAtTag.value as string).getTime();
|
||||
const ranges = createdAtCol.elements as { value: string; label: string }[];
|
||||
const dateRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(curr.value) < diff && curr.value > prev.value) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (dateRange) {
|
||||
createdAtFilter.tag = `**${createdAtFilter.title}** is **${dateRange.label}**`;
|
||||
createdAtFilter = createdAtFilter;
|
||||
}
|
||||
} else {
|
||||
createdAtFilter.tag = null;
|
||||
}
|
||||
|
||||
// Reasinging the filters to trigger reactivity
|
||||
statusFilter = statusFilter;
|
||||
triggerFilter = triggerFilter;
|
||||
methodFilter = methodFilter;
|
||||
statusCodeFilter = statusCodeFilter;
|
||||
createdAtFilter = createdAtFilter;
|
||||
}
|
||||
});
|
||||
|
||||
function setFilterData(filter: FilterData, list: TagValue[]) {
|
||||
const tagData = list.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
if (tagData) {
|
||||
filter.tag = tagData.tag;
|
||||
if (Array.isArray(tagData.value) && tagData.value?.length) {
|
||||
const values = tagData.value as string[];
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = values.includes(option.value);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
resetOptions(filter);
|
||||
}
|
||||
}
|
||||
|
||||
function resetOptions(filter: FilterData) {
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
function addFilterAndApply(
|
||||
colId: string,
|
||||
colTitle: string,
|
||||
operator: ValidOperators,
|
||||
value: string,
|
||||
arrayValues: string[] = []
|
||||
) {
|
||||
const tagList = $tags.filter((tag) => tag.tag.includes(colTitle));
|
||||
tagList.forEach((tag) => queries.removeFilter(tag));
|
||||
if (value || arrayValues?.length) {
|
||||
if (colId === statusCodeFilter.id) {
|
||||
addStatusCodeFilter(value, colId);
|
||||
} else if (colId === createdAtFilter.id) {
|
||||
addCreatedAtFilter(value, colId);
|
||||
} else {
|
||||
addFilter($columns, colId, operator, value, arrayValues);
|
||||
}
|
||||
}
|
||||
queries.apply();
|
||||
trackEvent(Submit.ApplyQuickFilter, {
|
||||
source: 'function_executions',
|
||||
column: colId,
|
||||
value: value || arrayValues.join(', ')
|
||||
});
|
||||
}
|
||||
|
||||
function addStatusCodeFilter(value: string, colId: string) {
|
||||
addFilter($columns, colId, ValidOperators.LessThanOrEqual, parseInt(value));
|
||||
addFilter($columns, colId, ValidOperators.GreaterThanOrEqual, parseInt(value) - 99);
|
||||
}
|
||||
function addCreatedAtFilter(value: string, colId: string) {
|
||||
const now = new Date();
|
||||
const isoValue = new Date(now.getTime() - parseInt(value));
|
||||
addFilter($columns, colId, ValidOperators.GreaterThanOrEqual, isoValue.toISOString());
|
||||
addFilter($columns, colId, ValidOperators.LessThanOrEqual, now.toISOString());
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu>
|
||||
{#each [statusFilter, triggerFilter, methodFilter] as filter}
|
||||
<MenuItem
|
||||
{filter}
|
||||
on:add={(e) => {
|
||||
console.log('test');
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
e.detail.value,
|
||||
filter?.array
|
||||
? (filter.options.filter((opt) => opt.checked).map((opt) => opt.value) ??
|
||||
[])
|
||||
: []
|
||||
);
|
||||
}}
|
||||
on:clear={() => {
|
||||
filter.tag = null;
|
||||
addFilterAndApply(filter.id, filter.title, filter.operator, null, []);
|
||||
}} />
|
||||
{/each}
|
||||
{#each [statusCodeFilter, createdAtFilter] as filter}
|
||||
<MenuItem
|
||||
variant="radio"
|
||||
{filter}
|
||||
on:add={(e) => {
|
||||
console.log('test');
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
e.detail.value,
|
||||
filter?.array
|
||||
? (filter.options.filter((opt) => opt.checked).map((opt) => opt.value) ??
|
||||
[])
|
||||
: []
|
||||
);
|
||||
}}
|
||||
on:clear={() => {
|
||||
filter.tag = null;
|
||||
addFilterAndApply(filter.id, filter.title, filter.operator, null, []);
|
||||
}} />
|
||||
{/each}
|
||||
<svelte:fragment slot="end">
|
||||
<CustomFilters {columns} />
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
-231
@@ -1,231 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
import { CustomFilters } from '$lib/components/filters';
|
||||
import Menu from '$lib/components/filters/menu.svelte';
|
||||
import {
|
||||
addFilter,
|
||||
queries,
|
||||
queryParamToMap,
|
||||
tags,
|
||||
ValidOperators,
|
||||
type TagValue
|
||||
} from '$lib/components/filters/store';
|
||||
import SubMenu from '$lib/components/filters/subMenu.svelte';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { type Writable } from 'svelte/store';
|
||||
|
||||
export let columns: Writable<Column[]>;
|
||||
|
||||
type FilterData = {
|
||||
title: string;
|
||||
id: string;
|
||||
array: boolean;
|
||||
show: boolean;
|
||||
tag: string;
|
||||
operator: ValidOperators;
|
||||
options: { value: string; label: string; checked: boolean }[];
|
||||
};
|
||||
|
||||
function buildFilterCol(col: Column, customOperator = null): FilterData {
|
||||
return {
|
||||
title: col.title,
|
||||
id: col.id,
|
||||
show: false,
|
||||
array: col?.array,
|
||||
tag: null,
|
||||
operator: customOperator ?? ValidOperators.Equal,
|
||||
options: col?.elements?.map((element) => {
|
||||
return {
|
||||
value: (element?.value ?? element) as string,
|
||||
label: (element?.label ?? element) as string,
|
||||
checked: false
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const statusCol = $columns.find((col) => col.id === 'status');
|
||||
let statusFilter = buildFilterCol(statusCol);
|
||||
const typeCol = $columns.find((col) => col.id === 'type');
|
||||
let typeFilter = buildFilterCol(typeCol);
|
||||
const sizeCol = $columns.find((col) => col.id === 'sourceSize');
|
||||
let sizeFilter = buildFilterCol(sizeCol);
|
||||
const buildTimeCol = $columns.find((col) => col.id === 'buildDuration');
|
||||
let buildTimeFilter = buildFilterCol(buildTimeCol);
|
||||
|
||||
let localQueries = new Map<TagValue, string>();
|
||||
|
||||
afterNavigate((p) => {
|
||||
const paramQueries = p.to.url.searchParams.get('query');
|
||||
localQueries = queryParamToMap(paramQueries || '[]');
|
||||
const localTags = Array.from(localQueries.keys());
|
||||
|
||||
if (!localTags?.length) {
|
||||
statusFilter.tag = null;
|
||||
typeFilter.tag = null;
|
||||
buildTimeFilter.tag = null;
|
||||
[statusFilter, typeFilter].forEach((filter) => {
|
||||
resetOptions(filter);
|
||||
});
|
||||
} else {
|
||||
[statusFilter, typeFilter].forEach((filter) => {
|
||||
setFilterData(filter, localTags);
|
||||
});
|
||||
|
||||
const buildTimeTag = localTags.find((tag) =>
|
||||
tag.tag.includes(`**${buildTimeFilter.title}**`)
|
||||
);
|
||||
if (buildTimeTag) {
|
||||
const now = new Date();
|
||||
|
||||
const diff = now.getTime() - new Date(buildTimeTag.value as string).getTime();
|
||||
const ranges = buildTimeCol.elements as { value: string; label: string }[];
|
||||
const dateRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(curr.value) < diff && curr.value > prev.value) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (dateRange) {
|
||||
buildTimeFilter.tag = `**${buildTimeFilter.title}** is **${dateRange.label}**`;
|
||||
buildTimeFilter = buildTimeFilter;
|
||||
}
|
||||
} else {
|
||||
buildTimeFilter.tag = null;
|
||||
}
|
||||
|
||||
const sizeTag = localTags.find((tag) => tag.tag.includes(`**${sizeFilter.title}**`));
|
||||
if (sizeTag) {
|
||||
const size = sizeTag.value as string;
|
||||
const ranges = sizeCol.elements as { value: string; label: string }[];
|
||||
// find smallest range that is bigger than size
|
||||
const sizeRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(size) >= parseInt(curr.value)) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (sizeRange) {
|
||||
sizeFilter.tag = `**${sizeFilter.title}** is **${sizeRange.label}**`;
|
||||
sizeFilter = sizeFilter;
|
||||
}
|
||||
} else {
|
||||
sizeFilter.tag = null;
|
||||
}
|
||||
|
||||
// Reasinging the filters to trigger reactivity
|
||||
statusFilter = statusFilter;
|
||||
typeFilter = typeFilter;
|
||||
sizeFilter = sizeFilter;
|
||||
buildTimeFilter = buildTimeFilter;
|
||||
}
|
||||
});
|
||||
|
||||
function setFilterData(filter: FilterData, list: TagValue[]) {
|
||||
const tagData = list.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
if (tagData) {
|
||||
filter.tag = tagData.tag;
|
||||
if (Array.isArray(tagData.value) && tagData.value?.length) {
|
||||
const values = tagData.value as string[];
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = values.includes(option.value);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
resetOptions(filter);
|
||||
}
|
||||
}
|
||||
|
||||
function resetOptions(filter: FilterData) {
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
function addFilterAndApply(
|
||||
colId: string,
|
||||
colTitle: string,
|
||||
operator: ValidOperators,
|
||||
value: string,
|
||||
arrayValues: string[] = []
|
||||
) {
|
||||
const tagList = $tags.filter((tag) => tag.tag.includes(colTitle));
|
||||
tagList.forEach((tag) => queries.removeFilter(tag));
|
||||
if (value || arrayValues?.length) {
|
||||
if (colId === buildTimeFilter.id) {
|
||||
addBuildTimeFilter(value, colId);
|
||||
} else if (colId === sizeFilter.id) {
|
||||
addSizeFilter(value, colId);
|
||||
} else {
|
||||
addFilter($columns, colId, operator, value, arrayValues);
|
||||
}
|
||||
}
|
||||
queries.apply();
|
||||
trackEvent(Submit.ApplyQuickFilter, {
|
||||
source: 'function_deployments',
|
||||
column: colId,
|
||||
value: value || arrayValues.join(', ')
|
||||
});
|
||||
}
|
||||
|
||||
function addSizeFilter(value: string, colId: string) {
|
||||
addFilter($columns, colId, ValidOperators.GreaterThanOrEqual, value);
|
||||
}
|
||||
function addBuildTimeFilter(value: string, colId: string) {
|
||||
const now = new Date();
|
||||
const isoValue = new Date(now.getTime() - parseInt(value));
|
||||
addFilter($columns, colId, ValidOperators.GreaterThanOrEqual, isoValue.toISOString());
|
||||
addFilter($columns, colId, ValidOperators.LessThanOrEqual, now.toISOString());
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu>
|
||||
{#each [typeFilter] as filter}
|
||||
<SubMenu
|
||||
{filter}
|
||||
on:add={(e) => {
|
||||
console.log('test');
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
e.detail.value,
|
||||
filter?.array
|
||||
? (filter.options.filter((opt) => opt.checked).map((opt) => opt.value) ??
|
||||
[])
|
||||
: []
|
||||
);
|
||||
}}
|
||||
on:clear={() => {
|
||||
filter.tag = null;
|
||||
addFilterAndApply(filter.id, filter.title, filter.operator, null, []);
|
||||
}} />
|
||||
{/each}
|
||||
{#each [sizeFilter] as filter}
|
||||
<SubMenu
|
||||
variant="radio"
|
||||
{filter}
|
||||
on:add={(e) => {
|
||||
console.log('test');
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
e.detail.value,
|
||||
filter?.array
|
||||
? (filter.options.filter((opt) => opt.checked).map((opt) => opt.value) ??
|
||||
[])
|
||||
: []
|
||||
);
|
||||
}}
|
||||
on:clear={() => {
|
||||
filter.tag = null;
|
||||
addFilterAndApply(filter.id, filter.title, filter.operator, null, []);
|
||||
}} />
|
||||
{/each}
|
||||
<svelte:fragment slot="end">
|
||||
<CustomFilters {columns} />
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
+49
-109
@@ -3,24 +3,22 @@
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { Container } from '$lib/layout';
|
||||
import { type Models } from '@appwrite.io/console';
|
||||
import { Filters } from '$lib/components/filters';
|
||||
import { queries, tags } from '$lib/components/filters/store';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import { ActionMenu, Icon, Layout, Popover, Tag } from '@appwrite.io/pink-svelte';
|
||||
import { ActionMenu, Icon, Layout, Popover } from '@appwrite.io/pink-svelte';
|
||||
import Table from './table.svelte';
|
||||
import QuickFilters from './quickFilters.svelte';
|
||||
import RedeployModal from '../../redeployModal.svelte';
|
||||
import CreateGitDeploymentModal from './createGitDeploymentModal.svelte';
|
||||
import ConnectRepoModal from '../../(components)/connectRepoModal.svelte';
|
||||
import { columns } from './store';
|
||||
import CreateManualDeploymentModal from './createManualDeploymentModal.svelte';
|
||||
import DeploymentMetrics from './deploymentMetrics.svelte';
|
||||
import { IconFilterLine, IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import CreateCliModal from './createCliModal.svelte';
|
||||
import { QuickFilters } from '$lib/components/filters';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -33,11 +31,6 @@
|
||||
let showConnectManual = false;
|
||||
let showMobileFilters = false;
|
||||
|
||||
function clearAll() {
|
||||
queries.clearAll();
|
||||
queries.apply();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
data?.query ? (showMobileFilters = true) : (showMobileFilters = false);
|
||||
return sdk.forConsole.client.subscribe('console', (response) => {
|
||||
@@ -53,106 +46,53 @@
|
||||
<DeploymentMetrics deploymentList={data.deploymentList} />
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack justifyContent="space-between" direction="row">
|
||||
<div class="is-not-mobile">
|
||||
<Layout.Stack alignItems="center" direction="row">
|
||||
{#if data.deploymentList.total}
|
||||
<QuickFilters {columns} />
|
||||
<Filters
|
||||
query={data.query}
|
||||
{columns}
|
||||
let:disabled
|
||||
let:toggle
|
||||
singleCondition>
|
||||
<Layout.Stack alignItems="center" direction="row" gap="xs">
|
||||
<Button
|
||||
compact
|
||||
on:click={toggle}
|
||||
{disabled}
|
||||
size="s"
|
||||
ariaLabel="open filter">
|
||||
<Icon icon={IconFilterLine} size="s" slot="start" />
|
||||
More filters
|
||||
</Button>
|
||||
{#if $tags?.length}
|
||||
<!-- TODO: add vertical divider to pink 2 -->
|
||||
<div
|
||||
style="flex-basis:1px; background-color:hsl(var(--border)); width: 1px">
|
||||
</div>
|
||||
<Button text on:click={clearAll} size="s">Clear all</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Filters>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
<div class="is-only-mobile">
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:click={() => (showMobileFilters = !showMobileFilters)}
|
||||
ariaLabel="toggle filters">
|
||||
<span class="icon-filter-line" />
|
||||
<span class="text">Filters</span>
|
||||
</Button>
|
||||
<div
|
||||
class:u-hide={!showMobileFilters}
|
||||
class:u-flex={showMobileFilters}
|
||||
class=" u-gap-8 u-flex-wrap u-margin-block-start-16">
|
||||
<QuickFilters {columns} />
|
||||
<Layout.Stack alignItems="center" direction="row">
|
||||
{#if data.deploymentList.total || data?.query}
|
||||
<QuickFilters {columns} analyticsSource="site_deployments" />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
<Filters query={data.query} {columns} clearOnClick>
|
||||
<svelte:fragment slot="mobile" let:disabled let:toggle>
|
||||
<Tag size="m" on:click={toggle} {disabled}>
|
||||
<span class="text">More filters</span>
|
||||
<span class="icon-cheveron-down" />
|
||||
</Tag>
|
||||
</svelte:fragment>
|
||||
</Filters>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Layout.Stack direction="row" inline>
|
||||
{#if data.deploymentList.total}
|
||||
<ViewSelector view={View.Table} {columns} hideView allowNoColumns />
|
||||
{/if}
|
||||
<Popover padding="none" let:toggle>
|
||||
<Button size="s" on:click={toggle}>
|
||||
<Icon size="s" icon={IconPlus} />
|
||||
Create deployment
|
||||
</Button>
|
||||
<svelte:fragment slot="tooltip" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
badge="Recommended"
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
if (!hasInstallation) {
|
||||
showConnectRepo = true;
|
||||
} else {
|
||||
showCreateDeployment = true;
|
||||
}
|
||||
}}>
|
||||
Git
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
showConnectCLI = true;
|
||||
}}>
|
||||
CLI
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
showConnectManual = true;
|
||||
}}>
|
||||
Manual
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
<Layout.Stack direction="row" inline>
|
||||
{#if data.deploymentList.total}
|
||||
<ViewSelector view={View.Table} {columns} hideView allowNoColumns />
|
||||
{/if}
|
||||
<Popover padding="none" let:toggle>
|
||||
<Button size="s" on:click={toggle}>
|
||||
<Icon size="s" icon={IconPlus} />
|
||||
Create deployment
|
||||
</Button>
|
||||
<svelte:fragment slot="tooltip" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Button
|
||||
badge="Recommended"
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
if (!hasInstallation) {
|
||||
showConnectRepo = true;
|
||||
} else {
|
||||
showCreateDeployment = true;
|
||||
}
|
||||
}}>
|
||||
Git
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
showConnectCLI = true;
|
||||
}}>
|
||||
CLI
|
||||
</ActionMenu.Item.Button>
|
||||
<ActionMenu.Item.Button
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
showConnectManual = true;
|
||||
}}>
|
||||
Manual
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
|
||||
{#if data.deploymentList.total}
|
||||
|
||||
-276
@@ -1,276 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
import {
|
||||
addFilter,
|
||||
queries,
|
||||
queryParamToMap,
|
||||
tagFormat,
|
||||
tags,
|
||||
ValidOperators,
|
||||
type TagValue
|
||||
} from '$lib/components/filters/store';
|
||||
import { SelectSearchCheckbox } from '$lib/elements';
|
||||
import type { Column } from '$lib/helpers/types';
|
||||
import { IconChevronDown, IconChevronUp } from '@appwrite.io/pink-icons-svelte';
|
||||
import { ActionMenu, Icon, Popover, Tag } from '@appwrite.io/pink-svelte';
|
||||
import { type Writable } from 'svelte/store';
|
||||
|
||||
export let columns: Writable<Column[]>;
|
||||
|
||||
type FilterData = {
|
||||
title: string;
|
||||
id: string;
|
||||
array: boolean;
|
||||
show: boolean;
|
||||
tag: string;
|
||||
operator: ValidOperators;
|
||||
options: { value: string; label: string; checked: boolean }[];
|
||||
};
|
||||
|
||||
function buildFilterCol(col: Column, customOperator = null): FilterData {
|
||||
return {
|
||||
title: col.title,
|
||||
id: col.id,
|
||||
show: false,
|
||||
array: col?.array,
|
||||
tag: null,
|
||||
operator: customOperator ?? ValidOperators.Equal,
|
||||
options: col?.elements?.map((element) => {
|
||||
return {
|
||||
value: (element?.value ?? element) as string,
|
||||
label: (element?.label ?? element) as string,
|
||||
checked: false
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const statusCol = $columns.find((col) => col.id === 'status');
|
||||
let statusFilter = buildFilterCol(statusCol);
|
||||
const typeCol = $columns.find((col) => col.id === 'type');
|
||||
let typeFilter = buildFilterCol(typeCol);
|
||||
const sizeCol = $columns.find((col) => col.id === 'sourceSize');
|
||||
let sizeFilter = buildFilterCol(sizeCol);
|
||||
const buildTimeCol = $columns.find((col) => col.id === 'buildDuration');
|
||||
let buildTimeFilter = buildFilterCol(buildTimeCol);
|
||||
|
||||
let localQueries = new Map<TagValue, string>();
|
||||
|
||||
afterNavigate((p) => {
|
||||
const paramQueries = p.to.url.searchParams.get('query');
|
||||
localQueries = queryParamToMap(paramQueries || '[]');
|
||||
const localTags = Array.from(localQueries.keys());
|
||||
|
||||
if (!localTags?.length) {
|
||||
statusFilter.tag = null;
|
||||
typeFilter.tag = null;
|
||||
buildTimeFilter.tag = null;
|
||||
[statusFilter, typeFilter].forEach((filter) => {
|
||||
resetOptions(filter);
|
||||
});
|
||||
} else {
|
||||
[statusFilter, typeFilter].forEach((filter) => {
|
||||
setFilterData(filter, localTags);
|
||||
});
|
||||
|
||||
const buildTimeTag = localTags.find((tag) =>
|
||||
tag.tag.includes(`**${buildTimeFilter.title}**`)
|
||||
);
|
||||
if (buildTimeTag) {
|
||||
const now = new Date();
|
||||
|
||||
const diff = now.getTime() - new Date(buildTimeTag.value as string).getTime();
|
||||
const ranges = buildTimeCol.elements as { value: string; label: string }[];
|
||||
const dateRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(curr.value) < diff && curr.value > prev.value) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (dateRange) {
|
||||
buildTimeFilter.tag = `**${buildTimeFilter.title}** is **${dateRange.label}**`;
|
||||
buildTimeFilter = buildTimeFilter;
|
||||
}
|
||||
} else {
|
||||
buildTimeFilter.tag = null;
|
||||
}
|
||||
|
||||
const sizeTag = localTags.find((tag) => tag.tag.includes(`**${sizeFilter.title}**`));
|
||||
if (sizeTag) {
|
||||
const size = sizeTag.value as string;
|
||||
const ranges = sizeCol.elements as { value: string; label: string }[];
|
||||
// find smallest range that is bigger than size
|
||||
const sizeRange = ranges.reduce((prev, curr) => {
|
||||
if (parseInt(size) >= parseInt(curr.value)) {
|
||||
return curr;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
if (sizeRange) {
|
||||
sizeFilter.tag = `**${sizeFilter.title}** is **${sizeRange.label}**`;
|
||||
sizeFilter = sizeFilter;
|
||||
}
|
||||
} else {
|
||||
sizeFilter.tag = null;
|
||||
}
|
||||
|
||||
// Reasinging the filters to trigger reactivity
|
||||
statusFilter = statusFilter;
|
||||
typeFilter = typeFilter;
|
||||
sizeFilter = sizeFilter;
|
||||
buildTimeFilter = buildTimeFilter;
|
||||
}
|
||||
});
|
||||
|
||||
function setFilterData(filter: FilterData, list: TagValue[]) {
|
||||
const tagData = list.find((tag) => tag.tag.includes(`**${filter.title}**`));
|
||||
if (tagData) {
|
||||
filter.tag = tagData.tag;
|
||||
if (Array.isArray(tagData.value) && tagData.value?.length) {
|
||||
const values = tagData.value as string[];
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = values.includes(option.value);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
filter.tag = null;
|
||||
resetOptions(filter);
|
||||
}
|
||||
}
|
||||
|
||||
function resetOptions(filter: FilterData) {
|
||||
filter.options.forEach((option) => {
|
||||
option.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
function addFilterAndApply(
|
||||
colId: string,
|
||||
colTitle: string,
|
||||
operator: ValidOperators,
|
||||
value: string,
|
||||
arrayValues: string[] = []
|
||||
) {
|
||||
const tagList = $tags.filter((tag) => tag.tag.includes(colTitle));
|
||||
tagList.forEach((tag) => queries.removeFilter(tag));
|
||||
if (value || arrayValues?.length) {
|
||||
if (colId === buildTimeFilter.id) {
|
||||
addBuildTimeFilter(value, colId);
|
||||
} else if (colId === sizeFilter.id) {
|
||||
addSizeFilter(value, colId);
|
||||
} else {
|
||||
addFilter($columns, colId, operator, value, arrayValues);
|
||||
}
|
||||
}
|
||||
queries.apply();
|
||||
trackEvent(Submit.ApplyQuickFilter, {
|
||||
source: 'function_deployments',
|
||||
column: colId,
|
||||
value: value || arrayValues.join(', ')
|
||||
});
|
||||
}
|
||||
|
||||
function addSizeFilter(value: string, colId: string) {
|
||||
addFilter($columns, colId, ValidOperators.GreaterThanOrEqual, value);
|
||||
}
|
||||
function addBuildTimeFilter(value: string, colId: string) {
|
||||
const now = new Date();
|
||||
const isoValue = new Date(now.getTime() - parseInt(value));
|
||||
addFilter($columns, colId, ValidOperators.GreaterThanOrEqual, isoValue.toISOString());
|
||||
addFilter($columns, colId, ValidOperators.LessThanOrEqual, now.toISOString());
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each [typeFilter] as filter}
|
||||
<Popover padding="none" let:toggle let:showing>
|
||||
<!--TODO: add tracking back event="apply_quick_filter" -->
|
||||
<Tag on:click={toggle}>
|
||||
{#key filter.tag}
|
||||
<span use:tagFormat>
|
||||
{filter?.tag ?? filter.title}
|
||||
</span>
|
||||
{/key}
|
||||
<Icon icon={showing ? IconChevronUp : IconChevronDown} slot="end" size="s" />
|
||||
</Tag>
|
||||
<svelte:fragment slot="tooltip" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
{#each filter.options as option (option.value + option.checked)}
|
||||
<SelectSearchCheckbox
|
||||
padding={8}
|
||||
bind:value={option.checked}
|
||||
on:click={() => {
|
||||
option.checked = !option.checked;
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
filter?.array ? null : option.checked ? option.value : null,
|
||||
filter?.array
|
||||
? (filter.options
|
||||
.filter((opt) => opt.checked)
|
||||
.map((opt) => opt.value) ?? [])
|
||||
: []
|
||||
);
|
||||
}}>
|
||||
{option.label}
|
||||
</SelectSearchCheckbox>
|
||||
{/each}
|
||||
{#if filter.options.some((option) => option.checked)}
|
||||
<ActionMenu.Item.Button
|
||||
on:click={(e) => {
|
||||
filter.tag = null;
|
||||
addFilterAndApply(filter.id, filter.title, filter.operator, null, []);
|
||||
toggle(e);
|
||||
}}>
|
||||
Clear selection
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/each}
|
||||
{#each [sizeFilter] as filter}
|
||||
<Popover padding="none" let:toggle let:showing>
|
||||
<!--TODO: add tracking back event="apply_quick_filter" -->
|
||||
|
||||
<Tag on:click={toggle}>
|
||||
{#key filter.tag}
|
||||
<span use:tagFormat>
|
||||
{filter?.tag ?? filter.title}
|
||||
</span>
|
||||
{/key}
|
||||
|
||||
<Icon icon={showing ? IconChevronUp : IconChevronDown} slot="end" size="s" />
|
||||
</Tag>
|
||||
<svelte:fragment slot="tooltip" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
{#each filter.options as option (option.value + option.checked)}
|
||||
<ActionMenu.Item.Button
|
||||
on:click={(e) => {
|
||||
addFilterAndApply(
|
||||
filter.id,
|
||||
filter.title,
|
||||
filter.operator,
|
||||
filter?.array ? null : option.value,
|
||||
[]
|
||||
);
|
||||
toggle(e);
|
||||
}}>
|
||||
{option.label}
|
||||
</ActionMenu.Item.Button>
|
||||
{/each}
|
||||
{#if filter?.tag}
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => {
|
||||
filter.show = false;
|
||||
filter.tag = null;
|
||||
addFilterAndApply(filter.id, filter.title, filter.operator, null, []);
|
||||
}}>
|
||||
Clear selection
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/each}
|
||||
@@ -13,8 +13,7 @@ export const columns = writable<Column[]>([
|
||||
width: 110,
|
||||
array: true,
|
||||
format: 'enum',
|
||||
elements: ['ready', 'processing', 'building', 'waiting', 'cancelled', 'failed'],
|
||||
filter: false
|
||||
elements: ['ready', 'processing', 'building', 'waiting', 'cancelled', 'failed']
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@
|
||||
import { CardGrid } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, InputSelect } from '$lib/elements/forms';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { iconPath } from '$lib/stores/app';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { getIconFromRuntime } from '$lib/stores/runtimes';
|
||||
@@ -16,7 +17,7 @@
|
||||
let buildRuntime = site?.buildRuntime;
|
||||
|
||||
let buildRuntimeOptions = framework.runtimes.map((runtime) => ({
|
||||
label: runtime,
|
||||
label: capitalize(runtime),
|
||||
value: runtime,
|
||||
leadingHtml: `<img src='${$iconPath(getIconFromRuntime(runtime), 'color')}' style='inline-size: var(--icon-size-m)' />`
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user