Skip to content

Next: Project Management #1856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 65 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
0189e5b
next: Added Project Routes
niemyjski Mar 11, 2025
49adff4
next: project list (WIP)
niemyjski Mar 11, 2025
cafe736
next: Add new project
niemyjski Mar 11, 2025
fd35295
Merge branch 'main' into feature/next-project-management
niemyjski Mar 11, 2025
237accc
Merge branch 'main' into feature/next-project-management
niemyjski Mar 12, 2025
cdf69dc
next: event's page handle unable to load project.
niemyjski Mar 14, 2025
c5cde3b
next: don't allow deletion if in progress.
niemyjski Mar 14, 2025
8012f96
next: updated deps
niemyjski Mar 14, 2025
eb5170f
Fixed project errors
niemyjski Mar 14, 2025
18a751a
next: reworked accounts page to use new layout components
niemyjski Mar 14, 2025
0a33604
WIP: Manage Project Page
niemyjski Mar 15, 2025
cec849d
Reworked routes so they are dynamic and resolvable by href.
niemyjski Mar 15, 2025
426866a
next: updated deps
niemyjski Mar 18, 2025
f2d29bc
Added aspire launch task
niemyjski Mar 18, 2025
7c69e0e
next: Cleanup
niemyjski Mar 18, 2025
b790835
next: Added api keys page
niemyjski Mar 18, 2025
1dbe160
next: Updated Deps
niemyjski Mar 19, 2025
dc00d02
Added Helper to normalize log level
niemyjski Mar 19, 2025
2a0c356
Added off to log level default levels
niemyjski Mar 19, 2025
bbf373c
next: wip added model code gen missed and added todo
niemyjski Mar 19, 2025
6eac2bd
next: WIP - Project Settings Log Level Dropdown
niemyjski Mar 19, 2025
c61a15a
Fixed some rendering issues with manage project page
niemyjski Mar 22, 2025
0971585
next: added project settings page
niemyjski Mar 23, 2025
c16357e
Updated copilot instructions
niemyjski Mar 23, 2025
7e7cd89
Updated deps
niemyjski Mar 23, 2025
f1093dc
Added client-settings page
niemyjski Mar 23, 2025
90ed0d4
next: Update deps
niemyjski Mar 24, 2025
0bc04e2
next: renamed config values page
niemyjski Mar 24, 2025
3981788
WIP: Integrations page
niemyjski Mar 24, 2025
17f9a1e
next: Added ability to link slack via oauth
niemyjski Mar 27, 2025
a747efc
next: Update slack notification settings.
niemyjski Mar 27, 2025
4ef8c26
Merge remote-tracking branch 'origin/main' into feature/next-project-…
niemyjski Mar 28, 2025
642ec87
next: move notification settings into a form.
niemyjski Mar 28, 2025
5e94f6d
next: updated deps
niemyjski Mar 28, 2025
3e9eb04
Updated packages
niemyjski Apr 2, 2025
88314eb
Converted configure page
niemyjski Apr 2, 2025
18a4ce7
Added alert component
niemyjski Apr 2, 2025
616baca
fixed lint issue
niemyjski Apr 2, 2025
178b9c2
use an alert
niemyjski Apr 2, 2025
8e667e6
Added support for code blocks
niemyjski Apr 3, 2025
69d56df
next: Updated the project configure page
niemyjski Apr 6, 2025
a65930f
Updated deps
niemyjski Apr 6, 2025
b392205
next: redirect on new event on the project configure page.
niemyjski Apr 6, 2025
57a6980
next: updated add project to specify the redirect qs.
niemyjski Apr 6, 2025
d4d1c8e
next: get the default token for the configure page
niemyjski Apr 6, 2025
898837e
Tweaked table column picker
niemyjski Apr 9, 2025
e24cbcf
Tweaked configure text
niemyjski Apr 9, 2025
92279d8
Added shadcn Card Action component
niemyjski Apr 9, 2025
a875705
Renamed table.
niemyjski Apr 11, 2025
6a52b4f
redirect on add.
niemyjski Apr 14, 2025
870a86a
Updated deps
niemyjski Apr 14, 2025
da2056f
Fixed split layout menus
niemyjski Apr 14, 2025
4d5db9f
Fixed linting
niemyjski Apr 14, 2025
f879a15
WIP: Shared table options
niemyjski Apr 14, 2025
8255228
Merge remote-tracking branch 'origin/main' into feature/next-project-…
niemyjski Apr 14, 2025
3577813
removed missing attribute.
niemyjski Apr 14, 2025
b26b222
Updated Dependencies for runes support
niemyjski Apr 18, 2025
c0045bd
Fixed queries not executing when parameters changed.
niemyjski Apr 21, 2025
15a439b
Renamed some queries.
niemyjski Apr 21, 2025
e9a5cc8
Reworked Tables
niemyjski Apr 21, 2025
620d796
moved action into card header.
niemyjski Apr 21, 2025
ad5544e
Cleaned up formatting of the project settings
niemyjski Apr 21, 2025
938e2cf
Fixed overflow of really long project names.
niemyjski Apr 21, 2025
ecc6e9c
wip: truncate table columns
niemyjski Apr 21, 2025
d13568b
PR Feedback
niemyjski Apr 21, 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/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Located in the `src/Exceptionless.Web/ClientApp` directory.
- Code can be formatted and linted with `npm run format` and checked for errors with `npm run check` tasks.
- Don't use namespace imports unless importing svelte-shadcn component or from a barrel export index file.
- Limit use of $effect as there is usually a better way to solve the problem like using $derived.
- All single-line control statements in JavaScript must be enclosed in curly braces to ensure unambiguous control flow, enhance readability, and prevent potential errors arising from unintended statement grouping.

- **Architecture & Components:**
- Follow the Composite Component Pattern.
Expand Down
19 changes: 17 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Aspire",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/Exceptionless.AppHost/bin/Debug/net9.0/Exceptionless.AppHost.dll",
"args": [],
"cwd": "${workspaceFolder}/src/Exceptionless.AppHost",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"AppMode": "Development"
}
},
{
"name": "Web",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/Exceptionless.Web/bin/Debug/net9.0/Exceptionless.Web.dll",
"args": [],
"cwd": "${workspaceFolder}/src/Exceptionless.Web",
Expand All @@ -24,7 +40,6 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/Exceptionless.Job/bin/Debug/net9.0/Exceptionless.Job.dll",
"args": [],
"cwd": "${workspaceFolder}",
Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"clsx",
"cmdk",
"colour",
"Contoso",
"dockerignore",
"editorconfig",
"elasticsearch",
Expand All @@ -43,6 +44,8 @@
"Serilog",
"sessionend",
"shadcn",
"shiki",
"shikijs",
"sonner",
"standardjs",
"superforms",
Expand All @@ -51,6 +54,7 @@
"typeschema",
"WCAG",
"websockets",
"Writeline",
"Xunit"
],
"css.customData": [
Expand Down
1 change: 0 additions & 1 deletion src/Exceptionless.Web/ClientApp/.npmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
engine-strict=true
resolution-mode=highest
2,406 changes: 1,530 additions & 876 deletions src/Exceptionless.Web/ClientApp/package-lock.json

Large diffs are not rendered by default.

82 changes: 42 additions & 40 deletions src/Exceptionless.Web/ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,70 +25,72 @@
"upgrade": "ncu -i"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.5",
"@eslint/compat": "^1.2.7",
"@eslint/js": "^9.22.0",
"@iconify-json/lucide": "^1.2.29",
"@playwright/test": "^1.51.0",
"@storybook/addon-a11y": "^8.6.4",
"@storybook/addon-essentials": "^8.6.4",
"@storybook/addon-interactions": "^8.6.4",
"@storybook/addon-svelte-csf": "^5.0.0-next.26",
"@storybook/blocks": "^8.6.4",
"@storybook/svelte": "^8.6.4",
"@storybook/sveltekit": "^8.6.4",
"@storybook/test": "^8.6.4",
"@chromatic-com/storybook": "^3.2.6",
"@eslint/compat": "^1.2.8",
"@eslint/js": "^9.25.0",
"@iconify-json/lucide": "^1.2.37",
"@playwright/test": "^1.52.0",
"@storybook/addon-a11y": "^8.6.12",
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-svelte-csf": "^5.0.0-next.28",
"@storybook/blocks": "^8.6.12",
"@storybook/svelte": "^8.6.12",
"@storybook/sveltekit": "^8.6.12",
"@storybook/test": "^8.6.12",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.19.0",
"@sveltejs/kit": "^2.20.7",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/vite": "^4.0.12",
"@tailwindcss/vite": "^4.1.4",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/svelte": "^5.2.7",
"@types/eslint": "^9.6.1",
"@types/node": "^22.13.10",
"@types/node": "^22.14.1",
"@types/throttle-debounce": "^5.0.2",
"cross-env": "^7.0.3",
"eslint": "^9.22.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-perfectionist": "^4.10.1",
"eslint-plugin-svelte": "^3.1.0",
"jsdom": "^26.0.0",
"eslint": "^9.25.0",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-perfectionist": "^4.11.0",
"eslint-plugin-svelte": "^3.5.1",
"jsdom": "^26.1.0",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"storybook": "^8.6.4",
"svelte": "^5.22.6",
"svelte-check": "^4.1.5",
"swagger-typescript-api": "^13.0.23",
"storybook": "^8.6.12",
"svelte": "^5.28.1",
"svelte-check": "^4.1.6",
"swagger-typescript-api": "^13.1.1",
"tslib": "^2.8.1",
"typescript": "^5.8.2",
"typescript-eslint": "^8.26.1",
"vite": "^6.2.1",
"vitest": "3.0.8"
"typescript": "^5.8.3",
"typescript-eslint": "^8.30.1",
"vite": "^6.3.2",
"vitest": "3.1.1"
},
"dependencies": {
"@exceptionless/browser": "^3.1.0",
"@exceptionless/fetchclient": "^0.40.0",
"@tanstack/svelte-query": "https://pkg.pr.new/@tanstack/svelte-query@28f98f9",
"@tanstack/svelte-query-devtools": "https://pkg.pr.new/@tanstack/svelte-query-devtools@28f98f9",
"@tanstack/svelte-query": "https://pkg.pr.new/@tanstack/svelte-query@8c9ce9",
"@tanstack/svelte-query-devtools": "https://pkg.pr.new/@tanstack/svelte-query-devtools@8c9ce9",
"@tanstack/svelte-table": "^9.0.0-alpha.10",
"@typeschema/class-validator": "^0.3.0",
"bits-ui": "^1.3.9",
"bits-ui": "^1.3.19",
"class-validator": "^0.14.1",
"clsx": "^2.1.1",
"formsnap": "^2.0.0",
"dompurify": "^3.2.5",
"formsnap": "^2.0.1",
"kit-query-params": "^0.0.26",
"lucide-svelte": "^0.479.0",
"mode-watcher": "^0.5.1",
"oidc-client-ts": "^3.1.0",
"@lucide/svelte": "^0.501.0",
"mode-watcher": "^1.0.2",
"oidc-client-ts": "^3.2.0",
"pretty-ms": "^9.2.0",
"runed": "^0.24.0",
"runed": "^0.25.0",
"shiki": "^3.2.2",
"svelte-sonner": "^0.3.28",
"svelte-time": "^2.0.1",
"sveltekit-superforms": "^2.24.0",
"tailwind-merge": "^3.0.2",
"sveltekit-superforms": "^2.24.1",
"tailwind-merge": "^3.2.0",
"tailwind-variants": "^1.0.0",
"tailwindcss": "^4.0.12",
"tailwindcss": "^4.1.4",
"throttle-debounce": "^5.0.2"
},
"type": "module"
Expand Down
2 changes: 2 additions & 0 deletions src/Exceptionless.Web/ClientApp/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### TODO

- Investigate loading data in - export function load({ url, fetch }) {}
- <https://svelte.dev/docs/svelte/class#The-class:-directive> migrate from cn / cslx
- IsBoolean on model gen.

#### shadcn svelte upgrade

Expand Down
1 change: 1 addition & 0 deletions src/Exceptionless.Web/ClientApp/src/lib/assets/slack.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@ import { PersistedState } from 'runed';

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

export interface OAuthLoginOptions extends OAuthPopupOptions {
redirectUrl?: string;
}

export interface OAuthPopupOptions {
authUrl: string;
clientId: string;
extraParams?: Record<string, string>;
popupOptions?: { height: number; width: number };
provider: SupportedOAuthProviders;
scope: string;
}

export interface OAuthResponseData {
code: string;
state: string;
}

export type SupportedOAuthProviders = 'facebook' | 'github' | 'google' | 'live' | 'slack';

const authSerializer = {
deserialize: (value: null | string): null | string => {
if (value === '') {
Expand All @@ -30,6 +50,7 @@ export const facebookClientId = env.PUBLIC_FACEBOOK_APPID;
export const gitHubClientId = env.PUBLIC_GITHUB_APPID;
export const googleClientId = env.PUBLIC_GOOGLE_APPID;
export const microsoftClientId = env.PUBLIC_MICROSOFT_APPID;
export const slackClientId = env.PUBLIC_SLACK_APPID;
export const enableOAuthLogin = facebookClientId || gitHubClientId || googleClientId || microsoftClientId;

export async function facebookLogin(redirectUrl?: string) {
Expand Down Expand Up @@ -128,15 +149,43 @@ export async function logout() {
accessToken.current = null;
}

async function oauthLogin(options: {
authUrl: string;
clientId: string;
extraParams?: Record<string, string>;
popupOptions?: { height: number; width: number };
provider: string;
redirectUrl?: string;
scope: string;
}) {
export async function slackOAuthLogin(): Promise<string> {
if (!slackClientId) {
throw new Error('Slack client id not set');
}

const data = await openOAuthPopup({
authUrl: 'https://slack.com/oauth/authorize',
clientId: slackClientId,
extraParams: {
state: encodeURIComponent(Math.random().toString(36).substring(2))
},
popupOptions: { height: 630, width: 580 },
provider: 'slack',
scope: 'incoming-webhook'
});

return data.code;
}

async function oauthLogin(options: OAuthLoginOptions) {
const data = await openOAuthPopup(options);

const client = useFetchClient();
const response = await client.postJSON<TokenResult>(`auth/${options.provider}`, {
clientId: options.clientId,
code: data.code,
redirectUri: window.location.origin,
state: data.state
});

if (response.ok && response.data?.token) {
accessToken.current = response.data.token;
await goto(options.redirectUrl || '/');
}
}

async function openOAuthPopup(options: OAuthPopupOptions): Promise<OAuthResponseData> {
const width = options.popupOptions?.width || 500;
const height = options.popupOptions?.height || 500;
const features = {
Expand All @@ -158,25 +207,19 @@ async function oauthLogin(options: {
);

const url = `${options.authUrl}?${new URLSearchParams(params).toString()}`;

const popup = window.open(url, options.provider, stringifyOptions(features));
popup?.focus();

const data = await waitForUrl(popup!, redirectUrl);
if (options.extraParams?.state && data.state !== options.extraParams.state) throw new Error('Invalid state');
if (!popup) {
throw new Error('Failed to open popup window');
}

const client = useFetchClient();
const response = await client.postJSON<TokenResult>(`auth/${options.provider}`, {
clientId: options.clientId,
code: data.code,
redirectUri: redirectUrl,
state: data.state
});
popup.focus();

if (response.ok && response.data?.token) {
accessToken.current = response.data.token;
await goto(options.redirectUrl || '/');
const data = await waitForUrl(popup!, redirectUrl);
if (options.extraParams?.state && data.state !== options.extraParams.state) {
throw new Error('Invalid state');
}

return data;
}

function stringifyOptions(options: object): string {
Expand All @@ -189,7 +232,7 @@ function stringifyOptions(options: object): string {
return parts.join(',');
}

function waitForUrl(popup: Window, redirectUri: string): Promise<{ code: string; state: string }> {
function waitForUrl(popup: Window, redirectUri: string): Promise<OAuthResponseData> {
return new Promise((resolve, reject) => {
const polling = setInterval(() => {
if (!popup || popup.closed || popup.closed === undefined) {
Expand All @@ -210,7 +253,7 @@ function waitForUrl(popup: Window, redirectUri: string): Promise<{ code: string;
if ('error' in params && (params as { error: string }).error) {
reject(new Error((params as { error: string }).error));
} else {
resolve(params);
resolve(params as OAuthResponseData);
}
} else {
reject(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export function getStackCountQuery(request: GetStackCountRequest) {

return response.data!;
},
queryKey: queryKeys.stacksCount(request.route.stackId)
queryKey: [...queryKeys.stacksCount(request.route.stackId), { params: request.params }]
}));
}

Expand Down
Loading