Skip to content

Commit 2086291

Browse files
authored
Merge pull request #115 from design-sparx/claude/add-system-notifications-01WmAjjkvm38e7XKm6ircfDH
Add system-wide notification banner feature
2 parents 79ac7d1 + baeb0e4 commit 2086291

File tree

15 files changed

+432
-54
lines changed

15 files changed

+432
-54
lines changed

app/auth/password-reset/layout.tsx

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,41 @@
22

33
import { ReactNode } from 'react';
44

5-
import { Center, Stack } from '@mantine/core';
5+
import { Box, Center, Stack } from '@mantine/core';
66
import Image from 'next/image';
77

8+
import { SystemNotificationBanner } from '@/components';
9+
810
type AuthProps = {
911
children: ReactNode;
1012
};
1113

1214
function PasswordLayout({ children }: AuthProps) {
1315
return (
14-
<Center
16+
<Box
1517
style={{
16-
height: '100vh',
18+
display: 'flex',
19+
flexDirection: 'column',
20+
minHeight: '100vh',
1721
width: '100vw',
1822
}}
1923
>
20-
<Stack>
21-
<Center>
22-
<Image
23-
src="/logo-no-background.png"
24-
alt="DesignSparx logo"
25-
width={96}
26-
height={96}
27-
style={{ objectFit: 'contain' }}
28-
/>
29-
</Center>
30-
{children}
31-
</Stack>
32-
</Center>
24+
<SystemNotificationBanner layout="auth" />
25+
<Center style={{ flex: 1 }}>
26+
<Stack>
27+
<Center>
28+
<Image
29+
src="/logo-no-background.png"
30+
alt="DesignSparx logo"
31+
width={96}
32+
height={96}
33+
style={{ objectFit: 'contain' }}
34+
/>
35+
</Center>
36+
{children}
37+
</Stack>
38+
</Center>
39+
</Box>
3340
);
3441
}
3542

app/auth/signin/layout.tsx

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,41 @@
22

33
import { ReactNode } from 'react';
44

5-
import { Center, Stack } from '@mantine/core';
5+
import { Box, Center, Stack } from '@mantine/core';
66
import Image from 'next/image';
77

8+
import { SystemNotificationBanner } from '@/components';
9+
810
type AuthProps = {
911
children: ReactNode;
1012
};
1113

1214
function SignInLayout({ children }: AuthProps) {
1315
return (
14-
<Center
16+
<Box
1517
style={{
16-
height: '100vh',
18+
display: 'flex',
19+
flexDirection: 'column',
20+
minHeight: '100vh',
1721
width: '100vw',
1822
}}
1923
>
20-
<Stack>
21-
<Center>
22-
<Image
23-
src="/logo-no-background.png"
24-
alt="DesignSparx logo"
25-
width={96}
26-
height={96}
27-
style={{ objectFit: 'contain' }}
28-
/>
29-
</Center>
30-
{children}
31-
</Stack>
32-
</Center>
24+
<SystemNotificationBanner layout="auth" />
25+
<Center style={{ flex: 1 }}>
26+
<Stack>
27+
<Center>
28+
<Image
29+
src="/logo-no-background.png"
30+
alt="DesignSparx logo"
31+
width={96}
32+
height={96}
33+
style={{ objectFit: 'contain' }}
34+
/>
35+
</Center>
36+
{children}
37+
</Stack>
38+
</Center>
39+
</Box>
3340
);
3441
}
3542

app/auth/signup/layout.tsx

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,41 @@
22

33
import { ReactNode } from 'react';
44

5-
import { Center, Stack } from '@mantine/core';
5+
import { Box, Center, Stack } from '@mantine/core';
66
import Image from 'next/image';
77

8+
import { SystemNotificationBanner } from '@/components';
9+
810
type AuthProps = {
911
children: ReactNode;
1012
};
1113

1214
function SignUpLayout({ children }: AuthProps) {
1315
return (
14-
<Center
16+
<Box
1517
style={{
16-
height: '100vh',
18+
display: 'flex',
19+
flexDirection: 'column',
20+
minHeight: '100vh',
1721
width: '100vw',
1822
}}
1923
>
20-
<Stack>
21-
<Center>
22-
<Image
23-
src="/logo-no-background.png"
24-
alt="DesignSparx logo"
25-
width={96}
26-
height={96}
27-
style={{ objectFit: 'contain' }}
28-
/>
29-
</Center>
30-
{children}
31-
</Stack>
32-
</Center>
24+
<SystemNotificationBanner layout="auth" />
25+
<Center style={{ flex: 1 }}>
26+
<Stack>
27+
<Center>
28+
<Image
29+
src="/logo-no-background.png"
30+
alt="DesignSparx logo"
31+
width={96}
32+
height={96}
33+
style={{ objectFit: 'contain' }}
34+
/>
35+
</Center>
36+
{children}
37+
</Stack>
38+
</Center>
39+
</Box>
3340
);
3441
}
3542

app/layout.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
ThemeCustomizerProvider,
2020
useThemeCustomizer,
2121
} from '@/contexts/theme-customizer';
22+
import { SystemNotificationsProvider } from '@/contexts/system-notifications';
2223
import { createDynamicTheme } from '@/theme';
2324
import '@mantine/core/styles.css';
2425
import '@mantine/dates/styles.css';
@@ -53,7 +54,7 @@ function ThemeProvider({ children }: { children: React.ReactNode }) {
5354
config.appearance.compact,
5455
]);
5556

56-
// Update CSS custom properties when theme changes
57+
// Update CSS custom properties when the theme changes
5758
useEffect(() => {
5859
const root = document.documentElement;
5960

@@ -120,7 +121,8 @@ export default function RootLayout({
120121
children: React.ReactNode;
121122
}) {
122123
return (
123-
<html lang="en">{/* className={openSans.className} */}
124+
<html lang="en">
125+
{/* className={openSans.className} */}
124126
<head>
125127
<title>DesignSparx - Nextjs Mantine Admin Dashboard Template</title>
126128
<link
@@ -158,9 +160,11 @@ export default function RootLayout({
158160
</head>
159161
<body>
160162
<AuthProvider>
161-
<ThemeCustomizerProvider>
162-
<ThemeProvider>{children}</ThemeProvider>
163-
</ThemeCustomizerProvider>
163+
<SystemNotificationsProvider>
164+
<ThemeCustomizerProvider>
165+
<ThemeProvider>{children}</ThemeProvider>
166+
</ThemeCustomizerProvider>
167+
</SystemNotificationsProvider>
164168
</AuthProvider>
165169
</body>
166170
</html>
File renamed without changes.

components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,4 @@ export { default as CalendarView } from './calendar-view';
6767
export { default as ErrorAlert } from './error-alert';
6868
export { default as Faqs } from './faqs';
6969
export { default as LanguagePicker } from './language-picker';
70+
export { default as SystemNotificationBanner } from './system-notification-banner';
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
'use client';
2+
3+
import { Alert, Anchor, Group, Stack, StackProps } from '@mantine/core';
4+
import {
5+
IconAlertTriangle,
6+
IconCircleCheck,
7+
IconInfoCircle,
8+
IconX,
9+
} from '@tabler/icons-react';
10+
11+
import { useSystemNotifications } from '@/contexts/system-notifications';
12+
import {
13+
NotificationType,
14+
SystemNotification,
15+
} from '@/contexts/system-notifications/types';
16+
17+
interface SystemNotificationBannerProps
18+
extends Pick<StackProps, 'm' | 'mb' | 'mt' | 'my' | 'mx' | 'p'> {
19+
layout?: 'main' | 'guest' | 'auth';
20+
}
21+
22+
const notificationConfig: Record<
23+
NotificationType,
24+
{ color: string; icon: React.ReactNode }
25+
> = {
26+
info: {
27+
color: 'blue',
28+
icon: <IconInfoCircle size={16} />,
29+
},
30+
warning: {
31+
color: 'yellow',
32+
icon: <IconAlertTriangle size={16} />,
33+
},
34+
error: {
35+
color: 'red',
36+
icon: <IconX size={16} />,
37+
},
38+
success: {
39+
color: 'green',
40+
icon: <IconCircleCheck size={16} />,
41+
},
42+
};
43+
44+
const SystemNotificationBanner = ({
45+
layout,
46+
...others
47+
}: SystemNotificationBannerProps) => {
48+
const { getActiveNotifications, dismissNotification } =
49+
useSystemNotifications();
50+
const activeNotifications = getActiveNotifications(layout);
51+
52+
if (activeNotifications.length === 0) {
53+
return null;
54+
}
55+
56+
return (
57+
<Stack gap={0} {...others}>
58+
{activeNotifications.map((notification: SystemNotification) => {
59+
const config = notificationConfig[notification.type];
60+
61+
return (
62+
<Alert
63+
key={notification.id}
64+
bg={`var(--mantine-color-${config.color}-filled)`}
65+
c="white"
66+
py="xs"
67+
px="md"
68+
icon={config.icon}
69+
withCloseButton={notification.dismissible !== false}
70+
onClose={() => dismissNotification(notification.id)}
71+
>
72+
<Group gap="xs" wrap="nowrap">
73+
{notification.message}
74+
{notification.action?.href && (
75+
<Anchor
76+
href={notification.action.href}
77+
target="_blank"
78+
rel="noopener noreferrer"
79+
c="white"
80+
underline="always"
81+
style={{ flexShrink: 0 }}
82+
>
83+
{notification.action.label}
84+
</Anchor>
85+
)}
86+
</Group>
87+
</Alert>
88+
);
89+
})}
90+
</Stack>
91+
);
92+
};
93+
94+
export default SystemNotificationBanner;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './SystemNotificationBanner';

0 commit comments

Comments
 (0)