Skip to content

Commit dcc90fc

Browse files
authored
Next: Organization Notifications (#1956)
* Organization Notifications * Fixed component * Fixed storybook not working with $env/dynamic/public. * Organization Notifications text improvements and chat support. * Updated client deps * Updated frontend instructions with some more best practices. * Updated color scheme and bottom padding. * Hide organization notifications on non organization pages (e.g., Add Organization, List Organizations, My Account) * Fixed notification rendering. * Fixed package lock
1 parent 0d9587c commit dcc90fc

29 files changed

+853
-203
lines changed

.github/instructions/frontend.instructions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ Located in the `src/Exceptionless.Web/ClientApp` directory.
3636
- Ensure semantic HTML, mobile-first design, and WCAG 2.2 Level AA compliance.
3737
- Use shadcn-svelte components (based on [bits-ui](https://bits-ui.com/docs/llms.txt)).
3838
- Look for new components in the shadcn-svelte documentation
39+
- **Navigation**: Prefer `href` over `onclick` with `goto()` for better linkability, SEO, and accessibility.
40+
- Users can right-click to open in new tabs, copy links, and screen readers work better
41+
- Use `onclick` with `goto()` only for complex navigation logic or when additional processing is needed
3942

4043
## API Calls
4144

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import type { StorybookConfig } from '@storybook/sveltekit';
22

3+
import { mergeConfig } from 'vite';
4+
35
const config: StorybookConfig = {
46
addons: ['@storybook/addon-svelte-csf', '@chromatic-com/storybook', '@storybook/addon-a11y', '@storybook/addon-docs'],
5-
framework: {
6-
name: '@storybook/sveltekit',
7-
options: {}
8-
},
9-
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|ts|svelte)']
7+
framework: '@storybook/sveltekit',
8+
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|ts|svelte)'],
9+
10+
async viteFinal(config) {
11+
return mergeConfig(config, {
12+
envPrefix: ['PUBLIC_'],
13+
resolve: {
14+
alias: {
15+
// Mock SvelteKit's $env/dynamic/public for Storybook
16+
'$env/dynamic/public': new URL('./mocks/env.js', import.meta.url).pathname
17+
}
18+
}
19+
});
20+
}
1021
};
1122
export default config;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Mock for $env/dynamic/public in Storybook
2+
export const env = {
3+
// Filter to only include PUBLIC_ prefixed environment variables
4+
...Object.fromEntries(Object.entries(import.meta.env).filter(([key]) => key.startsWith('PUBLIC_')))
5+
};

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

Lines changed: 189 additions & 185 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: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@
2727
"devDependencies": {
2828
"@chromatic-com/storybook": "^4.1.1",
2929
"@eslint/compat": "^1.3.2",
30-
"@eslint/js": "^9.33.0",
30+
"@eslint/js": "^9.34.0",
3131
"@iconify-json/lucide": "^1.2.63",
3232
"@playwright/test": "^1.55.0",
33-
"@storybook/addon-a11y": "^9.1.2",
34-
"@storybook/addon-docs": "^9.1.2",
33+
"@storybook/addon-a11y": "^9.1.3",
34+
"@storybook/addon-docs": "^9.1.3",
3535
"@storybook/addon-svelte-csf": "^5.0.8",
36-
"@storybook/sveltekit": "^9.1.2",
36+
"@storybook/sveltekit": "^9.1.3",
3737
"@sveltejs/adapter-static": "^3.0.9",
38-
"@sveltejs/kit": "^2.36.1",
38+
"@sveltejs/kit": "^2.36.2",
3939
"@sveltejs/vite-plugin-svelte": "^6.1.3",
4040
"@tailwindcss/vite": "^4.1.12",
4141
"@testing-library/jest-dom": "^6.8.0",
@@ -44,17 +44,17 @@
4444
"@types/node": "^24.3.0",
4545
"@types/throttle-debounce": "^5.0.2",
4646
"cross-env": "^10.0.0",
47-
"eslint": "^9.33.0",
47+
"eslint": "^9.34.0",
4848
"eslint-config-prettier": "^10.1.8",
4949
"eslint-plugin-perfectionist": "^4.15.0",
50-
"eslint-plugin-storybook": "^9.1.2",
50+
"eslint-plugin-storybook": "^9.1.3",
5151
"eslint-plugin-svelte": "^3.11.0",
5252
"jsdom": "^26.1.0",
5353
"prettier": "^3.6.2",
5454
"prettier-plugin-svelte": "^3.4.0",
5555
"prettier-plugin-tailwindcss": "^0.6.14",
56-
"storybook": "^9.1.2",
57-
"svelte": "^5.38.2",
56+
"storybook": "^9.1.3",
57+
"svelte": "^5.38.3",
5858
"svelte-check": "^4.3.1",
5959
"swagger-typescript-api": "^13.2.8",
6060
"tslib": "^2.8.1",
@@ -67,7 +67,7 @@
6767
"@exceptionless/browser": "^3.1.0",
6868
"@exceptionless/fetchclient": "^0.44.0",
6969
"@internationalized/date": "^3.8.2",
70-
"@lucide/svelte": "^0.540.0",
70+
"@lucide/svelte": "^0.541.0",
7171
"@tanstack/svelte-query": "https://pkg.pr.new/@tanstack/svelte-query@8c9ce9",
7272
"@tanstack/svelte-query-devtools": "https://pkg.pr.new/@tanstack/svelte-query-devtools@8c9ce9",
7373
"@tanstack/svelte-table": "^9.0.0-alpha.10",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script lang="ts">
2+
import type { NotificationProps } from '$comp/notification';
3+
4+
import { Notification, NotificationDescription, NotificationTitle } from '$comp/notification';
5+
import { A } from '$comp/typography';
6+
7+
interface Props extends NotificationProps {
8+
name: string;
9+
organizationId: string;
10+
}
11+
12+
let { name, organizationId, ...restProps }: Props = $props();
13+
</script>
14+
15+
<Notification variant="success" {...restProps}>
16+
<NotificationTitle>{name} is currently on a free plan.</NotificationTitle>
17+
<NotificationDescription>
18+
<A href={`/next/organization/${organizationId}/billing?changePlan=true`}>Upgrade now</A>
19+
to enable premium features and extra storage!
20+
</NotificationDescription>
21+
</Notification>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script lang="ts">
2+
import type { NotificationProps } from '$comp/notification';
3+
4+
import { Notification, NotificationDescription, NotificationTitle } from '$comp/notification';
5+
import { A } from '$comp/typography';
6+
7+
interface Props extends NotificationProps {
8+
name: string;
9+
organizationId: string;
10+
}
11+
12+
let { name, organizationId, ...restProps }: Props = $props();
13+
</script>
14+
15+
<Notification variant="destructive" {...restProps}>
16+
<NotificationTitle>Events are currently being throttled for {name}</NotificationTitle>
17+
<NotificationDescription>
18+
Events are currently being throttled to prevent using up your plan limit in a small window of time.
19+
<A href={`/next/organization/${organizationId}/billing?changePlan=true`}>Upgrade now</A> to increase your limits.
20+
</NotificationDescription>
21+
</Notification>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import FreePlanNotification from './free-plan-notification.svelte';
2+
import HourlyOverageNotification from './hourly-overage-notification.svelte';
3+
import MonthlyOverageNotification from './monthly-overage-notification.svelte';
4+
import PremiumUpgradeNotification from './premium-upgrade-notification.svelte';
5+
import ProjectConfigurationNotification from './project-configuration-notification.svelte';
6+
import RequestLimitNotification from './request-limit-notification.svelte';
7+
import SetupFirstProjectNotification from './setup-first-project-notification.svelte';
8+
import SuspendedOrganizationNotification from './suspended-organization-notification.svelte';
9+
10+
export type { NotificationProps } from '$comp/notification';
11+
12+
export {
13+
FreePlanNotification,
14+
HourlyOverageNotification,
15+
MonthlyOverageNotification,
16+
PremiumUpgradeNotification,
17+
ProjectConfigurationNotification,
18+
RequestLimitNotification,
19+
SetupFirstProjectNotification,
20+
SuspendedOrganizationNotification
21+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script lang="ts">
2+
import type { NotificationProps } from '$comp/notification';
3+
4+
import { Notification, NotificationDescription, NotificationTitle } from '$comp/notification';
5+
import { A } from '$comp/typography';
6+
7+
interface Props extends NotificationProps {
8+
name: string;
9+
organizationId: string;
10+
}
11+
12+
let { name, organizationId, ...restProps }: Props = $props();
13+
</script>
14+
15+
<Notification variant="destructive" {...restProps}>
16+
<NotificationTitle>{name} has reached its monthly plan limit.</NotificationTitle>
17+
<NotificationDescription>
18+
<A href={`/next/organization/${organizationId}/billing?changePlan=true`}>Upgrade now</A>
19+
to continue receiving events.
20+
</NotificationDescription>
21+
</Notification>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<script module lang="ts">
2+
import type { ViewProject } from '$features/projects/models';
3+
4+
import { SuspensionCode } from '$features/organizations/models';
5+
import { defineMeta } from '@storybook/addon-svelte-csf';
6+
7+
import FreePlanNotification from './free-plan-notification.svelte';
8+
import HourlyOverageNotification from './hourly-overage-notification.svelte';
9+
import MonthlyOverageNotification from './monthly-overage-notification.svelte';
10+
import PremiumUpgradeNotification from './premium-upgrade-notification.svelte';
11+
import ProjectConfigurationNotification from './project-configuration-notification.svelte';
12+
import RequestLimitNotification from './request-limit-notification.svelte';
13+
import SetupFirstProjectNotification from './setup-first-project-notification.svelte';
14+
import SuspendedOrganizationNotification from './suspended-organization-notification.svelte';
15+
16+
const mockProjects: ViewProject[] = [
17+
{
18+
created_utc: '2024-01-01T00:00:00Z',
19+
data: undefined,
20+
delete_bot_data_enabled: false,
21+
event_count: 150,
22+
has_premium_features: false,
23+
has_slack_integration: false,
24+
id: '1',
25+
is_configured: false,
26+
name: 'Frontend App',
27+
organization_id: 'org1',
28+
organization_name: 'Test Org',
29+
promoted_tabs: [],
30+
stack_count: 25,
31+
usage: [],
32+
usage_hours: []
33+
}
34+
];
35+
36+
const { Story } = defineMeta({
37+
tags: ['autodocs'],
38+
title: 'Components/Organizations/Notifications'
39+
});
40+
</script>
41+
42+
<Story name="Free Plan">
43+
<FreePlanNotification name="Acme Corporation" organizationId="org1" />
44+
</Story>
45+
<Story name="Monthly Overage">
46+
<MonthlyOverageNotification name="Tech Startup Inc" organizationId="org1" />
47+
</Story>
48+
<Story name="Hourly Overage">
49+
<HourlyOverageNotification name="High Traffic Corp" organizationId="org1" />
50+
</Story>
51+
52+
<Story name="Request Limit Exceeded">
53+
<RequestLimitNotification name="API Heavy Organization" isChatEnabled={false} openChat={() => {}} />
54+
</Story>
55+
56+
<Story name="Suspended - Billing">
57+
<SuspendedOrganizationNotification
58+
name="Overdue Payment Corp"
59+
suspensionCode={SuspensionCode.Billing}
60+
isChatEnabled={false}
61+
openChat={() => {}}
62+
organizationId="org1"
63+
/>
64+
</Story>
65+
66+
<Story name="Suspended - Billing (Chat Enabled)">
67+
<SuspendedOrganizationNotification
68+
name="Overdue Payment Corp"
69+
suspensionCode={SuspensionCode.Billing}
70+
isChatEnabled={true}
71+
openChat={() => console.log('Chat opened for billing suspension')}
72+
organizationId="org1"
73+
/>
74+
</Story>
75+
76+
<Story name="Suspended - Abuse">
77+
<SuspendedOrganizationNotification
78+
name="Violation Corp"
79+
suspensionCode={SuspensionCode.Abuse}
80+
isChatEnabled={false}
81+
openChat={() => {}}
82+
organizationId="org1"
83+
/>
84+
</Story>
85+
86+
<Story name="Suspended - Overage">
87+
<SuspendedOrganizationNotification
88+
name="Exceeded Limits Corp"
89+
suspensionCode={SuspensionCode.Overage}
90+
isChatEnabled={false}
91+
openChat={() => {}}
92+
organizationId="org1"
93+
/>
94+
</Story>
95+
96+
<Story name="Suspended - Other">
97+
<SuspendedOrganizationNotification
98+
name="Generic Suspension Corp"
99+
suspensionCode={SuspensionCode.Other}
100+
isChatEnabled={false}
101+
openChat={() => {}}
102+
organizationId="org1"
103+
/>
104+
</Story>
105+
106+
<Story name="Premium Upgrade">
107+
<PremiumUpgradeNotification name="Feature Seeker Corp" organizationId="org1" />
108+
</Story>
109+
110+
<Story name="Setup First Project">
111+
<SetupFirstProjectNotification />
112+
</Story>
113+
114+
<Story name="Project Configuration">
115+
<ProjectConfigurationNotification projects={mockProjects} />
116+
</Story>

0 commit comments

Comments
 (0)