Skip to content

Commit

Permalink
Next: Organization switcher and filter improvements. (#1821)
Browse files Browse the repository at this point in the history
* Updated dependencies

* Filter updates

* Added ability to switch organizations.

* Fixed filter builder

* Reset page filters on org change

* Fixed ci
  • Loading branch information
niemyjski authored Feb 8, 2025
1 parent bca1f40 commit 53f00d2
Show file tree
Hide file tree
Showing 26 changed files with 747 additions and 745 deletions.
556 changes: 279 additions & 277 deletions src/Exceptionless.Web/ClientApp/package-lock.json

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions src/Exceptionless.Web/ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,32 @@
},
"devDependencies": {
"@iconify-json/lucide": "^1.2.25",
"@playwright/test": "^1.50.0",
"@playwright/test": "^1.50.1",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.1",
"@sveltejs/kit": "^2.17.1",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@types/eslint": "^9.6.1",
"@types/node": "^22.12.0",
"@types/node": "^22.13.1",
"@types/throttle-debounce": "^5.0.2",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"eslint": "^9.19.0",
"eslint": "^9.20.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-perfectionist": "^4.7.0",
"eslint-plugin-perfectionist": "^4.8.0",
"eslint-plugin-svelte": "^2.46.1",
"npm-run-all": "^4.1.5",
"postcss": "^8.5.1",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.19.6",
"svelte": "^5.19.9",
"svelte-check": "^4.1.4",
"swagger-typescript-api": "^13.0.23",
"tslib": "^2.8.1",
"typescript": "^5.7.3",
"typescript-eslint": "^8.22.0",
"vite": "^6.0.11",
"vitest": "3.0.4"
"typescript-eslint": "^8.23.0",
"vite": "^6.1.0",
"vitest": "3.0.5"
},
"dependencies": {
"@exceptionless/browser": "^3.1.0",
Expand All @@ -59,18 +59,18 @@
"@tanstack/svelte-query-devtools": "https://pkg.pr.new/@tanstack/svelte-query-devtools@28f98f9",
"@tanstack/svelte-table": "^9.0.0-alpha.10",
"@typeschema/class-validator": "^0.3.0",
"bits-ui": "^1.0.0-next.78",
"bits-ui": "^1.0.0-next.89",
"class-validator": "^0.14.1",
"clsx": "^2.1.1",
"formsnap": "^2.0.0",
"lucide-svelte": "^0.474.0",
"kit-query-params": "^0.0.26",
"lucide-svelte": "^0.475.0",
"mode-watcher": "^0.5.1",
"oidc-client-ts": "^3.1.0",
"pretty-ms": "^9.2.0",
"runed": "^0.23.2",
"svelte-sonner": "^0.3.28",
"svelte-time": "^2.0.0",
"sveltekit-search-params": "^4.0.0-next.0",
"sveltekit-superforms": "^2.23.1",
"tailwind-merge": "^2.6.0",
"tailwind-variants": "^0.3.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import { builderContext, type FacetFilterBuilder, type IFilter } from '$comp/faceted-filter';
import { onMount } from 'svelte';
import BooleanFacetedFilter from './boolean-faceted-filter.svelte';
import { BooleanFilter } from './models.svelte';
Expand All @@ -13,15 +12,12 @@
const { priority = 0, term, title = 'Boolean' }: Props = $props();
onMount(() => {
const builder: FacetFilterBuilder<BooleanFilter> = {
component: BooleanFacetedFilter,
create: (filter?: BooleanFilter) => filter ?? new BooleanFilter(term),
priority,
title
};
const builder: FacetFilterBuilder<BooleanFilter> = {
component: BooleanFacetedFilter,
create: (filter?: BooleanFilter) => filter ?? new BooleanFilter(term),
priority,
title
};
builderContext.set(`boolean-${term}`, builder as unknown as FacetFilterBuilder<IFilter>);
return () => builderContext.delete('boolean');
});
builderContext.set(`boolean-${term}`, builder as unknown as FacetFilterBuilder<IFilter>);
</script>
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import { builderContext, type FacetFilterBuilder, type IFilter } from '$comp/faceted-filter';
import { onMount } from 'svelte';
import DateFacetedFilter from './date-faceted-filter.svelte';
import { DateFilter } from './models.svelte';
Expand All @@ -13,15 +12,12 @@
const { priority = 0, term, title = 'Date Range' }: Props = $props();
onMount(() => {
const builder: FacetFilterBuilder<DateFilter> = {
component: DateFacetedFilter,
create: (filter?: DateFilter) => filter ?? new DateFilter(term),
priority,
title
};
const builder: FacetFilterBuilder<DateFilter> = {
component: DateFacetedFilter,
create: (filter?: DateFilter) => filter ?? new DateFilter(term),
priority,
title
};
builderContext.set(`date-${term}`, builder as unknown as FacetFilterBuilder<IFilter>);
return () => builderContext.delete('date');
});
builderContext.set(`date-${term}`, builder as unknown as FacetFilterBuilder<IFilter>);
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,88 @@ import type { IFilter } from '$comp/faceted-filter';

import { organization } from '$features/organizations/context.svelte';

import { getKeywordFilter, getProjectFilter, getStackFilter } from './models.svelte';
import { DateFilter, KeywordFilter, type ProjectFilter, type StringFilter } from './models.svelte';

const filterCache = new Map<null | string, IFilter[]>();

export function applyDefaultDateFilter(filters: IFilter[], time: null | string): IFilter[] {
if (!time) {
return filters;
}

const dateFilter = filters.find((f) => f.key === 'date-date');
if (dateFilter) {
return filters;
}

return [...filters, new DateFilter('date', time)];
}

export function clearFilterCache() {
filterCache.clear();
}

export function filterChanged(filters: IFilter[], addedOrUpdated: IFilter): IFilter[] {
const index = filters.findIndex((f) => f.id === addedOrUpdated.id);
if (index === -1) {
return processFilterRules([...filters, addedOrUpdated]);
}

return processFilterRules([...filters.slice(0, index), addedOrUpdated, ...filters.slice(index + 1)]);
}

export function filterRemoved(filters: IFilter[], removed?: IFilter): IFilter[] {
// If detail is undefined, remove all filters.
if (!removed) {
return [];
}

return filters.filter((f) => f.id !== removed.id);
}

export function getFiltersFromCache(filter: null | string): IFilter[] {
if (!filter) {
return [];
}

if (filterCache.has(filter)) {
return filterCache.get(filter) ?? [];
}

// If filter is not in cache, return it in a new KeywordFilter instance.
return [new KeywordFilter(filter)];
}

export function getKeywordFilter(filters: IFilter[]): KeywordFilter | undefined {
return filters.find((f) => f.type === 'keyword') as KeywordFilter;
}

export function getProjectFilter(filters: IFilter[]): ProjectFilter {
return filters.find((f) => f.type === 'project') as ProjectFilter;
}

export function getStackFilter(filters: IFilter[]): StringFilter | undefined {
return filters.find((f) => f.type === 'string') as StringFilter;
}

export function quote(value?: null | string): string | undefined {
return value ? `"${value}"` : undefined;
}

export function quoteIfSpecialCharacters(value?: null | string): null | string | undefined {
// Check for lucene special characters or whitespace
const regex = new RegExp('\\+|\\-|\\&|\\||\\!|\\(|\\)|\\{|\\}|\\[|\\]|\\^|\\"|\\~|\\*|\\?|\\:|\\\\|\\/|\\s', 'g');

if (value && value.match(regex)) {
return quote(value);
}

return value;
}

export function shouldRefreshPersistentEventChanged(
filters: IFilter[],
filter: string,
filter: null | string,
organization_id?: string,
project_id?: string,
stack_id?: string,
Expand All @@ -19,7 +96,7 @@ export function shouldRefreshPersistentEventChanged(
if (id) {
// This could match any kind of lucene query (even must not filtering)
const keywordFilter = getKeywordFilter(filters);
if (keywordFilter && !keywordFilter.isEmpty()) {
if (keywordFilter && keywordFilter.value) {
if (keywordFilter.value!.includes(id)) {
return true;
}
Expand All @@ -28,14 +105,14 @@ export function shouldRefreshPersistentEventChanged(

if (stack_id) {
const stackFilter = getStackFilter(filters);
if (stackFilter && !stackFilter.isEmpty()) {
if (stackFilter && stackFilter.value) {
return stackFilter.value === stack_id;
}
}

if (project_id) {
const projectFilter = getProjectFilter(filters);
if (projectFilter && !projectFilter.isEmpty()) {
if (projectFilter && projectFilter.value.length) {
return projectFilter.value.includes(project_id);
}
}
Expand All @@ -46,3 +123,46 @@ export function shouldRefreshPersistentEventChanged(

return true;
}

export function toFilter(filters: IFilter[]): string {
return filters
.map((f) => f.toFilter())
.filter(Boolean)
.join(' ')
.trim();
}

export function updateFilterCache(filter: null | string, filters: IFilter[]) {
if (filter) {
filterCache.set(filter, filters);
}
}

function processFilterRules(filters: IFilter[]): IFilter[] {
// 1. There can only be one date filter by term at a time.
// 2. There can only be one project filter.
const uniqueFilters = new Map<string, IFilter>();
for (const filter of filters) {
if (filter.type === 'project' || filter.type === 'date') {
const existingFilter = uniqueFilters.get(filter.key);
if (existingFilter) {
existingFilter.id = filter.id;
if ('value' in existingFilter && 'value' in filter) {
if (Array.isArray(existingFilter.value) && Array.isArray(filter.value)) {
existingFilter.value = [...new Set([...existingFilter.value, ...filter.value])];
} else if (filter.value !== undefined) {
existingFilter.value = filter.value;
}
} else {
throw new Error('Unable to merge filters');
}
}

uniqueFilters.set(filter.key, existingFilter ?? filter);
} else {
uniqueFilters.set(filter.id, filter);
}
}

return Array.from(uniqueFilters.values());
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,6 @@ import DateFacetedFilterTrigger from './date-faceted-filter-trigger.svelte';
import DateFacetedFilter from './date-faceted-filter.svelte';
import KeywordFacetedFilterBuilder from './keyword-faceted-filter-builder.svelte';
import KeywordFacetedFilter from './keyword-faceted-filter.svelte';
export {
type BooleanFilter,
type DateFilter,
KeywordFilter,
type NumberFilter,
ProjectFilter,
ReferenceFilter,
SessionFilter,
StatusFilter,
type StringFilter,
TypeFilter,
type VersionFilter
} from './models.svelte';
import NumberFacetedFilterBuilder from './number-faceted-filter-builder.svelte';
import NumberFacetedFilterTrigger from './number-faceted-filter-trigger.svelte';
import NumberFacetedFilter from './number-faceted-filter.svelte';
Expand Down Expand Up @@ -110,3 +97,17 @@ export {
VersionFacetedFilterTrigger,
VersionFacetedFilterTrigger as VersionTrigger
};

export {
type BooleanFilter,
type DateFilter,
KeywordFilter,
type NumberFilter,
ProjectFilter,
ReferenceFilter,
SessionFilter,
StatusFilter,
type StringFilter,
TypeFilter,
type VersionFilter
} from './models.svelte';
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import { builderContext, type FacetFilterBuilder, type IFilter } from '$comp/faceted-filter';
import { onMount } from 'svelte';
import KeywordFacetedFilter from './keyword-faceted-filter.svelte';
import { KeywordFilter } from './models.svelte';
Expand All @@ -12,15 +11,12 @@
const { priority = 0, title = 'Keyword' }: Props = $props();
onMount(() => {
const builder: FacetFilterBuilder<KeywordFilter> = {
component: KeywordFacetedFilter,
create: (filter?: KeywordFilter) => filter ?? new KeywordFilter(),
priority,
title
};
const builder: FacetFilterBuilder<KeywordFilter> = {
component: KeywordFacetedFilter,
create: (filter?: KeywordFilter) => filter ?? new KeywordFilter(),
priority,
title
};
builderContext.set('keyword', builder as unknown as FacetFilterBuilder<IFilter>);
return () => builderContext.delete('keyword');
});
builderContext.set('keyword', builder as unknown as FacetFilterBuilder<IFilter>);
</script>
Loading

0 comments on commit 53f00d2

Please sign in to comment.