Skip to content

Commit be585b8

Browse files
authored
Next Gen UI: November Updates (#2042)
* Adds Chrome MCP verification instruction Adds instruction to always use the chrome mcp to verify visual and functional correctness. This ensures consistent testing and validation, particularly with the new site /next path. * Adds separator above project data table Adds a separator line above the project data table for better visual separation of the header and content. This improves the overall layout and readability of the projects page. * Improves authentication handling Enhances authentication logic by directly checking the access token to avoid reactivity issues with PersistedState. Also, resets the access token to an empty string instead of null when a 401 error is encountered. * Adds stack log level component Adds a component to display the log level associated with a stack. This enhances the stack card by providing users with quick insights into the severity of events within a stack. * Improves stack filter tooltip appearance Enhances the stack filter tooltip to provide a better user experience by improving the visual appearance and interaction. The tooltip trigger now indicates it is clickable. It also now includes accessibility attributes. * Adds skeleton for event counts Adds a skeleton loading indicator to the stack card while event counts are loading. This improves the user experience by providing visual feedback that data is being fetched. * Adds Storybook stories for filter triggers Creates Storybook stories for Boolean, Date, Level, Number, Project, Reference, Session, Status, String, Tag, Type, and Version filter triggers. These stories provide visual examples of the filter trigger components, showcasing both "With Text" and "Icon Only" variations, facilitating easier development and testing. Also, refactors faceted filter trigger components to omit ButtonProps 'value' and sets the button class to 'cursor-pointer'. Removes the title const and adds a title property to the Button component. * Improves query performance for faceted filters. Optimizes faceted filter queries by enabling them only when the dropdown is open, reducing unnecessary API calls and improving performance. Adds an `enabled` option to count queries to prevent fetching data until the filter dropdown is opened. This enhancement reduces initial load times and optimizes resource usage, specifically for the project and tag faceted filters. * Adds WebSocket client with reconnection logic Implements a WebSocket client with automatic reconnection capabilities. This change introduces a robust WebSocket client, managing connection state, and handling reconnections with exponential backoff. It addresses potential connection drops and ensures a stable communication channel. The client now handles authentication failures gracefully, preventing unnecessary reconnection attempts. It includes comprehensive tests to ensure reliability and proper behavior in various scenarios. * Updated deps * updated deps * Refines error handling and auth logic Improves the error handling in the layout to prevent infinite loops and unexpected behavior by more accurately determining which errors should be retried. This change also streamlines the authentication logic by clearing the access token only if it exists, preventing potential issues. * Improves WebSocket client resilience Increases the WebSocket connection timeout to provide a more reliable connection. Adds try/catch to WebSocket creation to handle errors. Adds enhanced logging for debugging purposes, providing more context on connection issues and authentication failures. Prevents reconnection attempts after authentication failures. * Improves token caching for auth Introduces CachedPersistedState to improve token caching and prevent excessive reactive signal triggers. This reduces unnecessary reads from localStorage and optimizes the authentication flow. Adds vitest setup to mock local storage in client tests to avoid errors and warnings. Fixes a logout issue where the access token was not being cleared properly. svecosystem/runed#382 * Added eslint-plugin-query TanStack/query#9643 * Prevents notification banner from showing during page load * Clarifies documentation context usage. Specifies when to use context7 for documentation purposes, focusing on code generation, setup, configuration steps, and library/API details. * Updated deps * Adds updated date to the organization view. Extends the organization view model to include the last updated date. This allows clients to accurately reflect the most recent state of the organization. * Extends organization search criteria Expands organization search to include organization ID, allowing users to find organizations by either name or ID. * Added users to sidebar * Enhances notification component with new variants Extends the notification component to support additional variants, including impersonation and information. These new variants allow for more versatile and informative notifications within the application. The component now accepts action and icon slots to further customize the notification's appearance and behavior, providing more context and interaction options. * Adds impersonation notification component Creates a notification component to inform users when they are impersonating another user. This component includes a button to stop impersonating and redirect the user to their default organization settings. * Hides organization list during impersonation Ensures the organization list is not displayed when a user is impersonating another user. This prevents unintended actions or confusion when operating under a different user's context. * Adds admin organization search query Introduces a new query for searching organizations in the admin section. This enables administrators to efficiently find organizations based on various criteria, improving overall management capabilities. Includes necessary interface definitions for request parameters and supports filtering by criteria, limit, mode, page, paid, and suspended status. * Adds impersonation notification Adds a notification to inform users when they are impersonating an organization. This helps to improve transparency and awareness for global administrators. The notification is displayed only when a global admin is viewing an organization they do not belong to. * Extends badge variants with new color options Adds red, amber, and orange color variants to the badge component. This allows for more diverse visual cues and improved user interface expressiveness. * Updates organization notification links Updates organization notification links to use the new app router. Adds organization over limit and suspension indicators to the organization table. * Updates svelte and lucide dependencies. Updates svelte and lucide dependencies to their latest versions. These updates likely include bug fixes, performance improvements, and new features. * Refactors organization API interfaces. Moves the `GetAdminSearchOrganizationsParams` and `GetAdminSearchOrganizationsRequest` interfaces to the bottom of the file for better organization and readability. * Adds impersonate organization functionality Implements the ability for admin users to impersonate organizations. This includes a new dialog for selecting an organization to impersonate, the ability to stop impersonating, and updates to the sidebar to reflect the impersonation status. It introduces pagination for the organization selection list and provides filtering options based on plan type and suspension status. Also, the pagination component was refactored to use Radix UI primitives for improved accessibility and consistency. * Improves organization selection and display Refactors the organization selection and display logic in the application layout. - Introduces a new `PageNumber` component for consistent pagination display. - Updates the organization switcher to handle impersonated organizations. - Redirects non-admin users to the add organization page if no organizations exist. - Ensures the first organization is selected if none is currently selected. * Enhances organization selection UI Improves the organization selection dialog by adding total count display and adjusting pagination layout for better responsiveness on smaller screens. * Uses current organization ID directly Simplifies organization notifications by using the current organization ID directly. This avoids storing the organization ID separately, streamlining the component's logic and ensuring it always reflects the currently selected organization. * Enhances organization impersonation flow Improves the organization impersonation feature by streamlining navigation and updating the organization context directly. This change simplifies the process of switching between organizations when impersonating, ensuring a smoother user experience. It also fixes an issue where query string parameters might be applied during redirection. * Updates routes to use resolve function Updates all internal links to use the resolve function for improved route handling and consistency. This change ensures that all routes are correctly resolved, regardless of the base URL or environment configuration. It replaces hardcoded paths with dynamically generated paths using the resolve function. * Improves text wrapping and reactivity. Refactors several components to improve text wrapping and whitespace handling for better readability, especially in code and stack trace displays. Additionally, updates multiple components to use `$derived` for reactive property updates, ensuring UI elements are updated efficiently in response to data changes. This change enhances the user experience by ensuring text is displayed correctly and UI elements react appropriately to changes in data. * Navigates to app route before updating state Ensures the application navigates to the main app route before updating the organization state during impersonation or when stopping impersonation. This resolves potential issues where the state update might occur before the route is fully loaded, leading to inconsistent behavior. * Regenerated api.ts Comparing the uncommitted changes to the git branch version, here are the actual code differences (ignoring comments, whitespace, and quote styles): Key Changes: Added // @ts-nocheck at line 3 Moved StackStatus enum to the top of the file (before BillingStatus) Removed multi-line comment documentation for BillingStatus enum (the one explaining 0=Trialing, 1=Active, etc.) Removed multi-line comment documentation for StackStatus enum (the one explaining the enum values) Reordered enum exports - StackStatus now comes before BillingStatus Changed card_last4 property in ViewOrganization - was quoted as a string key ('card_last4') in the git version, now it's a regular identifier (card_last4) Removed multi-line comment for BillingStatus enum in the ViewOrganization class (the one explaining billing status values) Reformatted code with different indentation and line breaks (but this is purely whitespace) Summary: The substantive code changes are: Addition of @ts-nocheck directive Reordering of StackStatus and BillingStatus enums Removal of verbose comment documentation for enum values Fix of card_last4 property formatting (removed unnecessary quotes)
1 parent 1c87a14 commit be585b8

File tree

116 files changed

+3700
-643
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+3700
-643
lines changed

.github/instructions/frontend.instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Located in the `src/Exceptionless.Web/ClientApp` directory.
1414
- When there is a linting error, always try to run `npm run format` first.
1515
- Limit use of $effect as there is usually a better way to solve the problem like using $derived.
1616
- **Do NOT** use any server-side Svelte features.
17+
- Always use the chrome mcp to verify visual and functional correctness, default to using the new site /next path.
1718

1819
## Architecture & Components
1920

.github/instructions/general.instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ applyTo: "**"
88
You are a distinguished engineer and are expected to deliver high-quality code that adheres to the guidelines below.
99
All contributions must respect existing formatting and conventions specified in the `.editorconfig` file.
1010

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

1313
## Code Style & Minimal Diffs
1414

src/Exceptionless.Core/Repositories/OrganizationRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public Task<FindResults<Organization>> GetByCriteriaAsync(string? criteria, Comm
5151
{
5252
var filter = Query<Organization>.MatchAll();
5353
if (!String.IsNullOrWhiteSpace(criteria))
54-
filter &= Query<Organization>.Term(o => o.Name, criteria);
54+
filter &= (Query<Organization>.Term(o => o.Id, criteria) || Query<Organization>.Term(o => o.Name, criteria));
5555

5656
if (paid.HasValue)
5757
{

src/Exceptionless.Web/ClientApp/eslint.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { includeIgnoreFile } from '@eslint/compat';
22
import js from '@eslint/js';
3+
import pluginQuery from '@tanstack/eslint-plugin-query';
34
import prettier from 'eslint-config-prettier';
45
import perfectionist from 'eslint-plugin-perfectionist';
56
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
@@ -17,6 +18,7 @@ export default ts.config(
1718
...svelte.configs['flat/recommended'],
1819
perfectionist.configs['recommended-natural'],
1920
prettier,
21+
...pluginQuery.configs['flat/recommended'],
2022
...svelte.configs['flat/prettier'],
2123
{
2224
languageOptions: {
@@ -37,6 +39,11 @@ export default ts.config(
3739
{
3840
ignores: ['build/', '.svelte-kit/', 'dist/', 'src/lib/generated/api.ts', 'src/lib/features/shared/components/ui/']
3941
},
42+
{
43+
rules: {
44+
'@tanstack/query/exhaustive-deps': 'off'
45+
}
46+
},
4047
{
4148
rules: {
4249
'svelte/no-navigation-without-resolve': 'off'

src/Exceptionless.Web/ClientApp/package-lock.json

Lines changed: 237 additions & 124 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Exceptionless.Web/ClientApp/package.json

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,74 +25,77 @@
2525
"upgrade": "ncu -i"
2626
},
2727
"devDependencies": {
28-
"@chromatic-com/storybook": "^4.1.1",
29-
"@eslint/compat": "^1.4.0",
30-
"@eslint/js": "^9.38.0",
31-
"@iconify-json/lucide": "^1.2.71",
32-
"@playwright/test": "^1.56.1",
33-
"@storybook/addon-a11y": "^10.0.0",
34-
"@storybook/addon-docs": "^10.0.0",
28+
"@chromatic-com/storybook": "^4.1.3",
29+
"@eslint/compat": "^2.0.0",
30+
"@eslint/js": "^9.39.1",
31+
"@iconify-json/lucide": "^1.2.79",
32+
"@playwright/test": "^1.57.0",
33+
"@storybook/addon-a11y": "^10.1.4",
34+
"@storybook/addon-docs": "^10.1.4",
3535
"@storybook/addon-svelte-csf": "^5.0.10",
36-
"@storybook/sveltekit": "^10.0.0",
36+
"@storybook/sveltekit": "^10.1.4",
3737
"@sveltejs/adapter-static": "^3.0.10",
38-
"@sveltejs/kit": "^2.48.2",
38+
"@sveltejs/kit": "^2.49.1",
3939
"@sveltejs/vite-plugin-svelte": "^6.2.1",
40-
"@tailwindcss/vite": "^4.1.16",
40+
"@tailwindcss/vite": "^4.1.17",
41+
"@tanstack/eslint-plugin-query": "^5.91.2",
4142
"@testing-library/jest-dom": "^6.9.1",
42-
"@testing-library/svelte": "^5.2.8",
43+
"@testing-library/svelte": "^5.2.9",
4344
"@types/eslint": "^9.6.1",
44-
"@types/node": "^24.9.2",
45+
"@types/node": "^24.10.1",
4546
"@types/throttle-debounce": "^5.0.2",
4647
"cross-env": "^10.1.0",
47-
"eslint": "^9.38.0",
48+
"eslint": "^9.39.1",
4849
"eslint-config-prettier": "^10.1.8",
4950
"eslint-plugin-perfectionist": "^4.15.1",
50-
"eslint-plugin-storybook": "^10.0.0",
51-
"eslint-plugin-svelte": "^3.12.5",
52-
"jsdom": "^27.0.1",
53-
"prettier": "^3.6.2",
51+
"eslint-plugin-storybook": "^10.1.4",
52+
"eslint-plugin-svelte": "^3.13.1",
53+
"jsdom": "^27.2.0",
54+
"prettier": "^3.7.4",
5455
"prettier-plugin-svelte": "^3.4.0",
55-
"prettier-plugin-tailwindcss": "^0.7.1",
56-
"storybook": "^10.0.0",
57-
"svelte": "^5.43.0",
58-
"svelte-check": "^4.3.3",
56+
"prettier-plugin-tailwindcss": "^0.7.2",
57+
"storybook": "^10.1.4",
58+
"svelte": "^5.45.6",
59+
"svelte-check": "^4.3.4",
5960
"swagger-typescript-api": "^13.2.16",
6061
"tslib": "^2.8.1",
6162
"typescript": "^5.9.3",
62-
"typescript-eslint": "^8.46.2",
63-
"vite": "^7.1.12",
64-
"vitest": "4.0.4"
63+
"typescript-eslint": "^8.48.1",
64+
"vite": "^7.2.6",
65+
"vitest": "4.0.15",
66+
"vitest-websocket-mock": "^0.5.0",
67+
"zod": "^4.1.13"
6568
},
6669
"dependencies": {
6770
"@exceptionless/browser": "^3.1.0",
6871
"@exceptionless/fetchclient": "^0.44.0",
6972
"@internationalized/date": "^3.10.0",
70-
"@lucide/svelte": "^0.548.0",
71-
"@tanstack/svelte-query": "^6.0.3",
72-
"@tanstack/svelte-query-devtools": "^6.0.0",
73+
"@lucide/svelte": "^0.556.0",
74+
"@tanstack/svelte-query": "^6.0.10",
75+
"@tanstack/svelte-query-devtools": "^6.0.2",
7376
"@tanstack/svelte-table": "^9.0.0-alpha.10",
7477
"@types/d3-scale": "^4.0.9",
7578
"@types/d3-shape": "^3.1.7",
7679
"@typeschema/class-validator": "^0.3.0",
77-
"bits-ui": "^2.14.1",
78-
"class-validator": "^0.14.2",
80+
"bits-ui": "^2.14.4",
81+
"class-validator": "^0.14.3",
7982
"clsx": "^2.1.1",
8083
"d3-scale": "^4.0.2",
8184
"dompurify": "^3.3.0",
8285
"formsnap": "^2.0.1",
8386
"kit-query-params": "^0.0.26",
84-
"layerchart": "^2.0.0-next.40",
87+
"layerchart": "^2.0.0-next.43",
8588
"mode-watcher": "^1.1.0",
86-
"oidc-client-ts": "^3.3.0",
89+
"oidc-client-ts": "^3.4.1",
8790
"pretty-ms": "^9.3.0",
88-
"runed": "^0.35.1",
89-
"shiki": "^3.14.0",
90-
"svelte-sonner": "^1.0.5",
91+
"runed": "^0.37.0",
92+
"shiki": "^3.19.0",
93+
"svelte-sonner": "^1.0.7",
9194
"svelte-time": "^2.0.2",
92-
"sveltekit-superforms": "^2.28.0",
93-
"tailwind-merge": "^3.3.1",
94-
"tailwind-variants": "^3.1.1",
95-
"tailwindcss": "^4.1.16",
95+
"sveltekit-superforms": "^2.28.1",
96+
"tailwind-merge": "^3.4.0",
97+
"tailwind-variants": "^3.2.2",
98+
"tailwindcss": "^4.1.17",
9699
"throttle-debounce": "^5.0.2",
97100
"tw-animate-css": "^1.4.0"
98101
},

src/Exceptionless.Web/ClientApp/src/lib/features/auth/index.svelte.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { goto } from '$app/navigation';
22
import { resolve } from '$app/paths';
33
import { page } from '$app/state';
44
import { env } from '$env/dynamic/public';
5+
import { CachedPersistedState } from '$features/shared/utils/cached-persisted-state.svelte';
56
import { useFetchClient } from '@exceptionless/fetchclient';
6-
import { PersistedState } from 'runed';
77

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

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

3030
const authSerializer = {
3131
deserialize: (value: null | string): null | string => {
32-
if (value === '') {
33-
return null;
34-
}
35-
36-
return value;
32+
return value === '' ? null : value;
3733
},
3834
serialize: (value: null | string): string => {
39-
if (value === null) {
40-
return '';
41-
}
42-
43-
return value;
35+
return value === null ? '' : value;
4436
}
4537
};
4638

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

4941
export const enableAccountCreation = env.PUBLIC_ENABLE_ACCOUNT_CREATION === 'true';
5042
export const facebookClientId = env.PUBLIC_FACEBOOK_APPID;
@@ -147,7 +139,8 @@ export async function login(email: string, password: string) {
147139
export async function logout() {
148140
const client = useFetchClient();
149141
await client.get('auth/logout', { expectedStatusCodes: [200, 401] });
150-
accessToken.current = null;
142+
143+
accessToken.current = '';
151144
}
152145

153146
export async function slackOAuthLogin(): Promise<string> {

src/Exceptionless.Web/ClientApp/src/lib/features/events/api.svelte.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export interface GetEventsParams {
7474
}
7575

7676
export interface GetOrganizationCountRequest {
77+
enabled?: () => boolean;
7778
params?: {
7879
aggregations?: string;
7980
filter?: string;
@@ -171,7 +172,7 @@ export function getOrganizationCountQuery(request: GetOrganizationCountRequest)
171172
const queryClient = useQueryClient();
172173

173174
return createQuery<CountResult, ProblemDetails>(() => ({
174-
enabled: () => !!accessToken.current && !!request.route.organizationId,
175+
enabled: () => !!accessToken.current && !!request.route.organizationId && (request.enabled?.() ?? true),
175176
queryClient,
176177
queryFn: async ({ signal }: { signal: AbortSignal }) => {
177178
const client = useFetchClient();

src/Exceptionless.Web/ClientApp/src/lib/features/events/components/extended-data-item.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
{:else if showXmlCodeEditor}
162162
<CodeBlock {code} language="xml" />
163163
{:else}
164-
<pre class="bg-muted rounded p-2 break-words whitespace-pre-wrap"><Code class="px-0"><div class="bg-inherit">{clipboardData}</div></Code
164+
<pre class="bg-muted rounded p-2 wrap-break-word whitespace-pre-wrap"><Code class="px-0"><div class="bg-inherit">{clipboardData}</div></Code
165165
></pre>
166166
{/if}
167167
{:else}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<script lang="ts" module>
2+
import Filter from '@lucide/svelte/icons/filter';
3+
import { defineMeta } from '@storybook/addon-svelte-csf';
4+
5+
import BooleanTrigger from './boolean-faceted-filter-trigger.svelte';
6+
7+
const { Story } = defineMeta({
8+
tags: ['autodocs'],
9+
title: 'Components/Events/Filters/Boolean Filter Trigger'
10+
});
11+
</script>
12+
13+
<Story name="With Text">
14+
<div class="flex flex-wrap gap-2 p-4">
15+
<BooleanTrigger changed={() => {}} term="fixed" value={true}>
16+
<Filter class="size-3" />
17+
Fixed: true
18+
</BooleanTrigger>
19+
<BooleanTrigger changed={() => {}} term="hidden" value={false}>
20+
<Filter class="size-3" />
21+
Hidden: false
22+
</BooleanTrigger>
23+
<BooleanTrigger changed={() => {}} term="critical" value={true}>
24+
<Filter class="size-3" />
25+
Critical: true
26+
</BooleanTrigger>
27+
</div>
28+
</Story>
29+
30+
<Story name="Icon Only">
31+
<div class="flex gap-2 p-4">
32+
<BooleanTrigger changed={() => {}} term="fixed" value={true} />
33+
<BooleanTrigger changed={() => {}} term="hidden" value={false} />
34+
<BooleanTrigger changed={() => {}} term="critical" value={true} />
35+
</div>
36+
</Story>

0 commit comments

Comments
 (0)