Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
139ee30
Adds Chrome MCP verification instruction
niemyjski Oct 30, 2025
d392f2a
Adds separator above project data table
niemyjski Oct 30, 2025
b642533
Improves authentication handling
niemyjski Oct 30, 2025
6ae0b3e
Adds stack log level component
niemyjski Oct 30, 2025
7cb34a4
Improves stack filter tooltip appearance
niemyjski Oct 30, 2025
44150de
Adds skeleton for event counts
niemyjski Oct 30, 2025
aae068d
Adds Storybook stories for filter triggers
niemyjski Oct 30, 2025
5b87fd6
Improves query performance for faceted filters.
niemyjski Oct 30, 2025
f7471e3
Adds WebSocket client with reconnection logic
niemyjski Nov 4, 2025
75c449e
Updated deps
niemyjski Nov 4, 2025
2187970
Merge branch 'main' into feature/october-updates
niemyjski Nov 7, 2025
79c2640
updated deps
niemyjski Nov 13, 2025
5d45631
Refines error handling and auth logic
niemyjski Nov 13, 2025
eb9b5c8
Improves WebSocket client resilience
niemyjski Nov 13, 2025
08a8709
Improves token caching for auth
niemyjski Nov 13, 2025
7919780
Added eslint-plugin-query
niemyjski Nov 13, 2025
e512ece
Prevents notification banner from showing during page load
niemyjski Nov 13, 2025
5c006c4
Clarifies documentation context usage.
niemyjski Nov 22, 2025
278b80d
Merge branch 'main' into feature/october-updates
niemyjski Dec 6, 2025
7796ed5
Updated deps
niemyjski Dec 6, 2025
050a456
Adds updated date to the organization view.
niemyjski Dec 6, 2025
e52e283
Extends organization search criteria
niemyjski Dec 6, 2025
1684e63
Added users to sidebar
niemyjski Dec 6, 2025
9d96cb6
Enhances notification component with new variants
niemyjski Dec 6, 2025
8af2f2e
Adds impersonation notification component
niemyjski Dec 6, 2025
7f7b5a0
Hides organization list during impersonation
niemyjski Dec 6, 2025
20c206f
Adds admin organization search query
niemyjski Dec 6, 2025
8b5e56e
Adds impersonation notification
niemyjski Dec 6, 2025
35e91f3
Extends badge variants with new color options
niemyjski Dec 6, 2025
e78a83d
Updates organization notification links
niemyjski Dec 6, 2025
1998d1c
Updates svelte and lucide dependencies.
niemyjski Dec 6, 2025
461001e
Refactors organization API interfaces.
niemyjski Dec 6, 2025
5e0d7c3
Adds impersonate organization functionality
niemyjski Dec 6, 2025
0385edf
Improves organization selection and display
niemyjski Dec 7, 2025
f35e9b3
Enhances organization selection UI
niemyjski Dec 7, 2025
c72369e
Uses current organization ID directly
niemyjski Dec 7, 2025
cccb187
Enhances organization impersonation flow
niemyjski Dec 7, 2025
1fd176b
Updates routes to use resolve function
niemyjski Dec 7, 2025
cd309dc
Improves text wrapping and reactivity.
niemyjski Dec 7, 2025
ae4d93d
Navigates to app route before updating state
niemyjski Dec 7, 2025
921074d
Regenerated api.ts
niemyjski Dec 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/instructions/frontend.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Located in the `src/Exceptionless.Web/ClientApp` directory.
- When there is a linting error, always try to run `npm run format` first.
- Limit use of $effect as there is usually a better way to solve the problem like using $derived.
- **Do NOT** use any server-side Svelte features.
- Always use the chrome mcp to verify visual and functional correctness, default to using the new site /next path.

## Architecture & Components

Expand Down
2 changes: 1 addition & 1 deletion .github/instructions/general.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ applyTo: "**"
You are a distinguished engineer and are expected to deliver high-quality code that adheres to the guidelines below.
All contributions must respect existing formatting and conventions specified in the `.editorconfig` file.

use context7 for documentation
Use context7 when I need code generation, setup or configuration steps, or library/api documentation.

## Code Style & Minimal Diffs

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public Task<FindResults<Organization>> GetByCriteriaAsync(string? criteria, Comm
{
var filter = Query<Organization>.MatchAll();
if (!String.IsNullOrWhiteSpace(criteria))
filter &= Query<Organization>.Term(o => o.Name, criteria);
filter &= (Query<Organization>.Term(o => o.Id, criteria) || Query<Organization>.Term(o => o.Name, criteria));

if (paid.HasValue)
{
Expand Down
7 changes: 7 additions & 0 deletions src/Exceptionless.Web/ClientApp/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { includeIgnoreFile } from '@eslint/compat';
import js from '@eslint/js';
import pluginQuery from '@tanstack/eslint-plugin-query';
import prettier from 'eslint-config-prettier';
import perfectionist from 'eslint-plugin-perfectionist';
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
Expand All @@ -17,6 +18,7 @@ export default ts.config(
...svelte.configs['flat/recommended'],
perfectionist.configs['recommended-natural'],
prettier,
...pluginQuery.configs['flat/recommended'],
...svelte.configs['flat/prettier'],
{
languageOptions: {
Expand All @@ -37,6 +39,11 @@ export default ts.config(
{
ignores: ['build/', '.svelte-kit/', 'dist/', 'src/lib/generated/api.ts', 'src/lib/features/shared/components/ui/']
},
{
rules: {
'@tanstack/query/exhaustive-deps': 'off'
}
},
{
rules: {
'svelte/no-navigation-without-resolve': 'off'
Expand Down
361 changes: 237 additions & 124 deletions src/Exceptionless.Web/ClientApp/package-lock.json

Large diffs are not rendered by default.

79 changes: 41 additions & 38 deletions src/Exceptionless.Web/ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,74 +25,77 @@
"upgrade": "ncu -i"
},
"devDependencies": {
"@chromatic-com/storybook": "^4.1.1",
"@eslint/compat": "^1.4.0",
"@eslint/js": "^9.38.0",
"@iconify-json/lucide": "^1.2.71",
"@playwright/test": "^1.56.1",
"@storybook/addon-a11y": "^10.0.0",
"@storybook/addon-docs": "^10.0.0",
"@chromatic-com/storybook": "^4.1.3",
"@eslint/compat": "^2.0.0",
"@eslint/js": "^9.39.1",
"@iconify-json/lucide": "^1.2.79",
"@playwright/test": "^1.57.0",
"@storybook/addon-a11y": "^10.1.4",
"@storybook/addon-docs": "^10.1.4",
"@storybook/addon-svelte-csf": "^5.0.10",
"@storybook/sveltekit": "^10.0.0",
"@storybook/sveltekit": "^10.1.4",
"@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.48.2",
"@sveltejs/kit": "^2.49.1",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@tailwindcss/vite": "^4.1.16",
"@tailwindcss/vite": "^4.1.17",
"@tanstack/eslint-plugin-query": "^5.91.2",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/svelte": "^5.2.8",
"@testing-library/svelte": "^5.2.9",
"@types/eslint": "^9.6.1",
"@types/node": "^24.9.2",
"@types/node": "^24.10.1",
"@types/throttle-debounce": "^5.0.2",
"cross-env": "^10.1.0",
"eslint": "^9.38.0",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-perfectionist": "^4.15.1",
"eslint-plugin-storybook": "^10.0.0",
"eslint-plugin-svelte": "^3.12.5",
"jsdom": "^27.0.1",
"prettier": "^3.6.2",
"eslint-plugin-storybook": "^10.1.4",
"eslint-plugin-svelte": "^3.13.1",
"jsdom": "^27.2.0",
"prettier": "^3.7.4",
"prettier-plugin-svelte": "^3.4.0",
"prettier-plugin-tailwindcss": "^0.7.1",
"storybook": "^10.0.0",
"svelte": "^5.43.0",
"svelte-check": "^4.3.3",
"prettier-plugin-tailwindcss": "^0.7.2",
"storybook": "^10.1.4",
"svelte": "^5.45.6",
"svelte-check": "^4.3.4",
"swagger-typescript-api": "^13.2.16",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.2",
"vite": "^7.1.12",
"vitest": "4.0.4"
"typescript-eslint": "^8.48.1",
"vite": "^7.2.6",
"vitest": "4.0.15",
"vitest-websocket-mock": "^0.5.0",
"zod": "^4.1.13"
},
"dependencies": {
"@exceptionless/browser": "^3.1.0",
"@exceptionless/fetchclient": "^0.44.0",
"@internationalized/date": "^3.10.0",
"@lucide/svelte": "^0.548.0",
"@tanstack/svelte-query": "^6.0.3",
"@tanstack/svelte-query-devtools": "^6.0.0",
"@lucide/svelte": "^0.556.0",
"@tanstack/svelte-query": "^6.0.10",
"@tanstack/svelte-query-devtools": "^6.0.2",
"@tanstack/svelte-table": "^9.0.0-alpha.10",
"@types/d3-scale": "^4.0.9",
"@types/d3-shape": "^3.1.7",
"@typeschema/class-validator": "^0.3.0",
"bits-ui": "^2.14.1",
"class-validator": "^0.14.2",
"bits-ui": "^2.14.4",
"class-validator": "^0.14.3",
"clsx": "^2.1.1",
"d3-scale": "^4.0.2",
"dompurify": "^3.3.0",
"formsnap": "^2.0.1",
"kit-query-params": "^0.0.26",
"layerchart": "^2.0.0-next.40",
"layerchart": "^2.0.0-next.43",
"mode-watcher": "^1.1.0",
"oidc-client-ts": "^3.3.0",
"oidc-client-ts": "^3.4.1",
"pretty-ms": "^9.3.0",
"runed": "^0.35.1",
"shiki": "^3.14.0",
"svelte-sonner": "^1.0.5",
"runed": "^0.37.0",
"shiki": "^3.19.0",
"svelte-sonner": "^1.0.7",
"svelte-time": "^2.0.2",
"sveltekit-superforms": "^2.28.0",
"tailwind-merge": "^3.3.1",
"tailwind-variants": "^3.1.1",
"tailwindcss": "^4.1.16",
"sveltekit-superforms": "^2.28.1",
"tailwind-merge": "^3.4.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.17",
"throttle-debounce": "^5.0.2",
"tw-animate-css": "^1.4.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/state';
import { env } from '$env/dynamic/public';
import { CachedPersistedState } from '$features/shared/utils/cached-persisted-state.svelte';
import { useFetchClient } from '@exceptionless/fetchclient';
import { PersistedState } from 'runed';

import type { Login, TokenResult } from './models';

Expand All @@ -29,22 +29,14 @@ export type SupportedOAuthProviders = 'facebook' | 'github' | 'google' | 'live'

const authSerializer = {
deserialize: (value: null | string): null | string => {
if (value === '') {
return null;
}

return value;
return value === '' ? null : value;
},
serialize: (value: null | string): string => {
if (value === null) {
return '';
}

return value;
return value === null ? '' : value;
}
};

export const accessToken = new PersistedState<null | string>('satellizer_token', null, { serializer: authSerializer });
export const accessToken = new CachedPersistedState<null | string>('satellizer_token', null, { serializer: authSerializer });

export const enableAccountCreation = env.PUBLIC_ENABLE_ACCOUNT_CREATION === 'true';
export const facebookClientId = env.PUBLIC_FACEBOOK_APPID;
Expand Down Expand Up @@ -147,7 +139,8 @@ export async function login(email: string, password: string) {
export async function logout() {
const client = useFetchClient();
await client.get('auth/logout', { expectedStatusCodes: [200, 401] });
accessToken.current = null;

accessToken.current = '';
}

export async function slackOAuthLogin(): Promise<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface GetEventsParams {
}

export interface GetOrganizationCountRequest {
enabled?: () => boolean;
params?: {
aggregations?: string;
filter?: string;
Expand Down Expand Up @@ -171,7 +172,7 @@ export function getOrganizationCountQuery(request: GetOrganizationCountRequest)
const queryClient = useQueryClient();

return createQuery<CountResult, ProblemDetails>(() => ({
enabled: () => !!accessToken.current && !!request.route.organizationId,
enabled: () => !!accessToken.current && !!request.route.organizationId && (request.enabled?.() ?? true),
queryClient,
queryFn: async ({ signal }: { signal: AbortSignal }) => {
const client = useFetchClient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
{:else if showXmlCodeEditor}
<CodeBlock {code} language="xml" />
{:else}
<pre class="bg-muted rounded p-2 break-words whitespace-pre-wrap"><Code class="px-0"><div class="bg-inherit">{clipboardData}</div></Code
<pre class="bg-muted rounded p-2 wrap-break-word whitespace-pre-wrap"><Code class="px-0"><div class="bg-inherit">{clipboardData}</div></Code
></pre>
{/if}
{:else}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script lang="ts" module>
import Filter from '@lucide/svelte/icons/filter';
import { defineMeta } from '@storybook/addon-svelte-csf';

import BooleanTrigger from './boolean-faceted-filter-trigger.svelte';

const { Story } = defineMeta({
tags: ['autodocs'],
title: 'Components/Events/Filters/Boolean Filter Trigger'
});
</script>

<Story name="With Text">
<div class="flex flex-wrap gap-2 p-4">
<BooleanTrigger changed={() => {}} term="fixed" value={true}>
<Filter class="size-3" />
Fixed: true
</BooleanTrigger>
<BooleanTrigger changed={() => {}} term="hidden" value={false}>
<Filter class="size-3" />
Hidden: false
</BooleanTrigger>
<BooleanTrigger changed={() => {}} term="critical" value={true}>
<Filter class="size-3" />
Critical: true
</BooleanTrigger>
</div>
</Story>

<Story name="Icon Only">
<div class="flex gap-2 p-4">
<BooleanTrigger changed={() => {}} term="fixed" value={true} />
<BooleanTrigger changed={() => {}} term="hidden" value={false} />
<BooleanTrigger changed={() => {}} term="critical" value={true} />
</div>
</Story>
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,20 @@

import { BooleanFilter } from './models.svelte';

type Props = ButtonProps & {
type Props = Omit<ButtonProps, 'value'> & {
changed: (filter: BooleanFilter) => void;
term: string;
value?: boolean;
};
let { changed, children, class: className, term, value, ...props }: Props = $props();

const title = `Search ${term}:${value}`;
</script>

<Button
variant="ghost"
size={children ? 'xs' : 'icon-xs'}
onclick={() => changed(new BooleanFilter(term, value))}
{title}
class={[children ? '' : 'opacity-50 hover:opacity-100 focus-visible:opacity-100', className]}
title={`Search ${term}:${value}`}
class={['cursor-pointer', children ? '' : 'opacity-50 hover:opacity-100 focus-visible:opacity-100', className]}
{...props}
>
{#if children}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script lang="ts" module>
import Filter from '@lucide/svelte/icons/filter';
import { defineMeta } from '@storybook/addon-svelte-csf';

import DateTrigger from './date-faceted-filter-trigger.svelte';

const { Story } = defineMeta({
tags: ['autodocs'],
title: 'Components/Events/Filters/Date Filter Trigger'
});
</script>

<Story name="With Text">
<div class="flex flex-wrap gap-2 p-4">
<DateTrigger changed={() => {}} term="date" value="2024-01-15">
<Filter class="size-3" />
date:2024-01-15
</DateTrigger>
<DateTrigger changed={() => {}} term="created" value="2024-10-29">
<Filter class="size-3" />
created:2024-10-29
</DateTrigger>
<DateTrigger changed={() => {}} term="updated" value="2024-11-01">
<Filter class="size-3" />
updated:2024-11-01
</DateTrigger>
</div>
</Story>

<Story name="Icon Only">
<div class="flex gap-2 p-4">
<DateTrigger changed={() => {}} term="date" value="2024-01-15" />
<DateTrigger changed={() => {}} term="created" value="2024-10-29" />
<DateTrigger changed={() => {}} term="updated" value="2024-11-01" />
</div>
</Story>
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,20 @@

import { DateFilter } from './models.svelte';

type Props = ButtonProps & {
type Props = Omit<ButtonProps, 'value'> & {
changed: (filter: DateFilter) => void;
term: string;
value?: string;
};
let { changed, children, class: className, term, value, ...props }: Props = $props();

const title = `Search ${term}:${value}`;
</script>

<Button
variant="ghost"
size={children ? 'xs' : 'icon-xs'}
onclick={() => changed(new DateFilter(term, value))}
{title}
class={[children ? '' : 'opacity-50 hover:opacity-100 focus-visible:opacity-100', className]}
onclick={() => changed(new DateFilter(term, value ?? undefined))}
title={`Search ${term}:${value}`}
class={['cursor-pointer', children ? '' : 'opacity-50 hover:opacity-100 focus-visible:opacity-100', className]}
{...props}
>
{#if children}
Expand Down
Loading
Loading