Skip to content

Commit 73053a5

Browse files
committed
feat(ui): enhance notification summary block with priority counts and layout options
1 parent e648c5c commit 73053a5

File tree

8 files changed

+329
-19
lines changed

8 files changed

+329
-19
lines changed
Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,65 @@
1-
import { CMS } from '@o2s/configs.integrations';
1+
import { CMS, Notifications } from '@o2s/configs.integrations';
22

33
import { NotificationSummaryBlock } from './notification-summary.model';
44

55
export const mapNotificationSummary = (
66
cms: CMS.Model.NotificationSummaryBlock.NotificationSummaryBlock,
7+
notifications: Notifications.Model.Notifications,
78
_locale: string,
89
): NotificationSummaryBlock => {
9-
return {
10+
const priorityCounts = notifications.data.reduce(
11+
(acc, notification) => {
12+
acc[notification.priority] = (acc[notification.priority] || 0) + 1;
13+
return acc;
14+
},
15+
{} as Record<'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL', number>,
16+
);
17+
18+
const result: NotificationSummaryBlock = {
1019
__typename: 'NotificationSummaryBlock',
1120
id: cms.id,
21+
layout: cms.layout,
1222
};
23+
24+
if (cms.high) {
25+
result.high = {
26+
title: cms.high.title,
27+
icon: cms.high.icon,
28+
value: priorityCounts.HIGH || 0,
29+
description: cms.high.message,
30+
color: 'text-destructive',
31+
};
32+
}
33+
34+
if (cms.medium) {
35+
result.medium = {
36+
title: cms.medium.title,
37+
icon: cms.medium.icon,
38+
value: priorityCounts.MEDIUM || 0,
39+
description: cms.medium.message,
40+
color: 'text-badge-secondary-background',
41+
};
42+
}
43+
44+
if (cms.low) {
45+
result.low = {
46+
title: cms.low.title,
47+
icon: cms.low.icon,
48+
value: priorityCounts.LOW || 0,
49+
description: cms.low.message,
50+
color: 'text-muted-foreground',
51+
};
52+
}
53+
54+
if (cms.critical) {
55+
result.critical = {
56+
title: cms.critical.title,
57+
icon: cms.critical.icon,
58+
value: priorityCounts.CRITICAL || 0,
59+
description: cms.critical.message,
60+
color: 'text-destructive',
61+
};
62+
}
63+
64+
return result;
1365
};
Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
1-
import { CMS } from '@o2s/configs.integrations';
2-
31
import { Models } from '@o2s/utils.api-harmonization';
42

53
export class NotificationSummaryBlock extends Models.Block.Block {
64
__typename!: 'NotificationSummaryBlock';
5+
layout?: 'vertical' | 'horizontal';
6+
high?: {
7+
title: string;
8+
icon?: string;
9+
value: number;
10+
description?: string;
11+
color?: string;
12+
};
13+
medium?: {
14+
title: string;
15+
icon?: string;
16+
value: number;
17+
description?: string;
18+
color?: string;
19+
};
20+
low?: {
21+
title: string;
22+
icon?: string;
23+
value: number;
24+
description?: string;
25+
color?: string;
26+
};
27+
critical?: {
28+
title: string;
29+
icon?: string;
30+
value: number;
31+
description?: string;
32+
color?: string;
33+
};
734
}

packages/blocks/notification-summary/src/api-harmonization/notification-summary.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DynamicModule, Module } from '@nestjs/common';
2-
import { CMS } from '@o2s/configs.integrations';
2+
import { CMS, Notifications } from '@o2s/configs.integrations';
33

44
import * as Framework from '@o2s/framework/modules';
55

@@ -17,6 +17,10 @@ export class NotificationSummaryBlockModule {
1717
provide: CMS.Service,
1818
useExisting: Framework.CMS.Service,
1919
},
20+
{
21+
provide: Notifications.Service,
22+
useExisting: Framework.Notifications.Service,
23+
},
2024
],
2125
controllers: [NotificationSummaryController],
2226
exports: [NotificationSummaryService],
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@nestjs/common';
2-
import { CMS } from '@o2s/configs.integrations';
2+
import { CMS, Notifications } from '@o2s/configs.integrations';
33
import { Observable, forkJoin, map } from 'rxjs';
44

55
import { Models } from '@o2s/utils.api-harmonization';
@@ -10,14 +10,24 @@ import { GetNotificationSummaryBlockQuery } from './notification-summary.request
1010

1111
@Injectable()
1212
export class NotificationSummaryService {
13-
constructor(private readonly cmsService: CMS.Service) {}
13+
constructor(
14+
private readonly cmsService: CMS.Service,
15+
private readonly notificationService: Notifications.Service,
16+
) {}
1417

1518
getNotificationSummaryBlock(
1619
query: GetNotificationSummaryBlockQuery,
1720
headers: Models.Headers.AppHeaders,
1821
): Observable<NotificationSummaryBlock> {
1922
const cms = this.cmsService.getNotificationSummaryBlock({ ...query, locale: headers['x-locale'] });
23+
const notifications = this.notificationService.getNotificationList({
24+
limit: 1000,
25+
offset: 0,
26+
locale: headers['x-locale'],
27+
});
2028

21-
return forkJoin([cms]).pipe(map(([cms]) => mapNotificationSummary(cms, headers['x-locale'])));
29+
return forkJoin([notifications, cms]).pipe(
30+
map(([notifications, cms]) => mapNotificationSummary(cms, notifications, headers['x-locale'])),
31+
);
2232
}
2333
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import type { Meta, StoryObj } from '@storybook/nextjs';
2+
3+
import { NotificationSummaryPure } from './NotificationSummary.client';
4+
5+
const meta = {
6+
title: 'Blocks/NotificationSummary',
7+
component: NotificationSummaryPure,
8+
} satisfies Meta<typeof NotificationSummaryPure>;
9+
10+
export default meta;
11+
12+
type Story = StoryObj<typeof meta>;
13+
14+
export const Default: Story = {
15+
args: {
16+
routing: {
17+
locales: ['en', 'de', 'pl'],
18+
defaultLocale: 'en',
19+
pathnames: {
20+
'/login': {
21+
en: '/sign-in',
22+
de: '/einloggen',
23+
pl: '/logowanie',
24+
},
25+
},
26+
},
27+
__typename: 'NotificationSummaryBlock',
28+
id: 'notification-summary-1',
29+
layout: 'horizontal',
30+
high: {
31+
title: 'High Priority',
32+
icon: 'AlertCircle',
33+
value: 12,
34+
description: 'High priority notifications',
35+
color: 'text-destructive',
36+
},
37+
medium: {
38+
title: 'Medium Priority',
39+
icon: 'Info',
40+
value: 5,
41+
description: 'Medium priority notifications',
42+
color: 'text-badge-secondary-background',
43+
},
44+
accessToken:
45+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSmFuZSBEb2UiLCJlbWFpbCI6ImphbmVAZXhhbXBsZS5jb20iLCJyb2xlIjoic2VsZnNlcnZpY2Vfb3JnX2FkbWluIiwiY3VzdG9tZXIiOnsiaWQiOiJjdXN0LTAwMSIsInJvbGVzIjpbInNlbGZzZXJ2aWNlX29yZ191c2VyIiwic2VsZnNlcnZpY2Vfb3JnX3VzZXIiLCJzZWxmc2VydmljZV9vcmdfYWRtaW4iXSwibmFtZSI6IkFjbWUgQ29ycG9yYXRpb24ifSWiaWF0IjoxNzU2MzI0NTg0fQ.wFAXi1DbgN67z8xQcqZdGz9YeAolbim3lecVIzV2rv0',
46+
locale: 'en',
47+
},
48+
};
49+
50+
export const Vertical: Story = {
51+
args: {
52+
...Default.args,
53+
layout: 'vertical',
54+
},
55+
};
56+
57+
export const AllPriorities: Story = {
58+
args: {
59+
...Default.args,
60+
critical: {
61+
title: 'Critical Priority',
62+
icon: 'AlertTriangle',
63+
value: 3,
64+
description: 'Critical priority notifications',
65+
color: 'text-destructive',
66+
},
67+
high: {
68+
title: 'High Priority',
69+
icon: 'AlertCircle',
70+
value: 12,
71+
description: 'High priority notifications',
72+
color: 'text-destructive',
73+
},
74+
medium: {
75+
title: 'Medium Priority',
76+
icon: 'Info',
77+
value: 5,
78+
description: 'Medium priority notifications',
79+
color: 'text-badge-secondary-background',
80+
},
81+
low: {
82+
title: 'Low Priority',
83+
icon: 'Bell',
84+
value: 8,
85+
description: 'Low priority notifications',
86+
color: 'text-muted-foreground',
87+
},
88+
},
89+
};
90+
91+
export const SinglePriority: Story = {
92+
args: {
93+
...Default.args,
94+
high: {
95+
title: 'High Priority',
96+
icon: 'AlertCircle',
97+
value: 12,
98+
description: 'High priority notifications',
99+
color: 'text-destructive',
100+
},
101+
medium: undefined,
102+
},
103+
};
Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,55 @@
11
'use client';
22

3-
import { createNavigation } from 'next-intl/navigation';
43
import React from 'react';
54

5+
import { cn } from '@o2s/ui/lib/utils';
6+
7+
import { InfoCard } from '@o2s/ui/components/Cards/InfoCard';
8+
import { DynamicIcon, DynamicIconProps } from '@o2s/ui/components/DynamicIcon';
9+
import { RichText } from '@o2s/ui/components/RichText';
10+
11+
import { Typography } from '@o2s/ui/elements/typography';
12+
613
import { NotificationSummaryPureProps } from './NotificationSummary.types';
714

8-
export const NotificationSummaryPure: React.FC<NotificationSummaryPureProps> = ({
9-
locale,
10-
accessToken,
11-
routing,
12-
...component
13-
}) => {
14-
const { Link: LinkComponent } = createNavigation(routing);
15+
export const NotificationSummaryPure: React.FC<NotificationSummaryPureProps> = ({ ...component }) => {
16+
const { high, medium, low, critical, layout } = component;
17+
18+
const isVertical = layout === 'vertical';
19+
const containerClass = cn('w-full gap-6', isVertical ? 'flex flex-col' : 'grid grid-cols-1 md:grid-cols-2');
20+
21+
const priorityCards = [
22+
{ key: 'critical' as const, data: critical },
23+
{ key: 'high' as const, data: high },
24+
{ key: 'medium' as const, data: medium },
25+
{ key: 'low' as const, data: low },
26+
].filter(({ data }) => data !== undefined);
1527

1628
return (
17-
<div className="w-full flex flex-col gap-4">
18-
{component.__typename}: {component.id}
29+
<div className={containerClass}>
30+
{priorityCards.map(({ key, data }) => (
31+
<InfoCard
32+
key={key}
33+
title={data!.title}
34+
value={
35+
<Typography variant="highlightedBig" className={data!.color}>
36+
{data!.value}
37+
</Typography>
38+
}
39+
description={
40+
data!.description ? (
41+
<div className="line-clamp-3">
42+
<RichText content={data!.description} className={data!.color} />
43+
</div>
44+
) : undefined
45+
}
46+
icon={
47+
data!.icon ? (
48+
<DynamicIcon name={data!.icon as DynamicIconProps['name']} className={data!.color} />
49+
) : undefined
50+
}
51+
/>
52+
))}
1953
</div>
2054
);
2155
};
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import { Block } from '@/utils/models';
1+
import { Block, InfoCard } from '@/utils/models';
22

33
export class NotificationSummaryBlock extends Block.Block {
44
title?: string;
5+
high?: InfoCard.InfoCard;
6+
medium?: InfoCard.InfoCard;
7+
low?: InfoCard.InfoCard;
8+
critical?: InfoCard.InfoCard;
9+
layout?: 'vertical' | 'horizontal';
510
}

0 commit comments

Comments
 (0)