Skip to content

Commit 5073279

Browse files
committed
999936: fixed the bugs and UI changes
1 parent 4988e7f commit 5073279

File tree

5 files changed

+109
-24
lines changed

5 files changed

+109
-24
lines changed

Employee_Managment_App/src/App.tsx

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// src/App.tsx
2-
import React, { useState, useEffect } from 'react';
2+
import React, { useState, useEffect, useMemo } from 'react';
33
import { BrowserRouter as Router, Routes, Route, NavLink } from 'react-router-dom';
44
import './App.css';
55
import TopNav from './components/TopNav';
@@ -8,6 +8,7 @@ import Policies from './components/Policies';
88
import Achievements from './components/Achievements';
99
import Organization from './components/Organization';
1010
import EmployeeInfo from './components/EmployeeInfo';
11+
import { PanelItem } from './components/Announcement';
1112

1213
// Syncfusion Sidebar
1314
import { SidebarComponent } from '@syncfusion/ej2-react-navigations';
@@ -45,11 +46,62 @@ function App() {
4546
const [sidebarCollapsed, setSidebarCollapsed] = useState(false); // desktop dock
4647
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); // mobile overlay
4748
const isDesktop = useMediaQuery('(min-width: 992px)');
49+
50+
// Notification items with read status
51+
const [notificationItems, setNotificationItems] = useState<PanelItem[]>([
52+
{ id: 1, title: 'Interview for Customer Support Specialist', subtitle: 'Announcement', date: 'Sep 18', type: 'notification', content: 'You are invited for the Customer Support Specialist interview on Sep 22 at 11:00 AM. Please bring your updated resume and ID.', read: false },
53+
{ id: 2, title: 'Interview for Facilities Executive', subtitle: 'Announcement', date: 'Sep 9', type: 'notification', content: 'Facilities Executive interview is scheduled for Sep 23 at 2:30 PM in Meeting Room A.', read: false },
54+
{ id: 3, title: 'Interview for Office Coordinator / Admin', subtitle: 'Announcement', date: 'Sep 5', type: 'notification', content: 'Your interview for the Office Coordinator / Admin position is scheduled on Sep 25 at 10:00 AM in Conference Room B. Please bring your updated resume and a government-issued ID.', read: false },
55+
]);
56+
57+
const [announcementItems, setAnnouncementItems] = useState<PanelItem[]>([
58+
{ id: 'a1', title: 'Policy Update: Remote Work Guidelines', subtitle: 'Corporate Communication', date: 'Sep 16', type: 'announcement', content: 'We have updated our Remote Work Guidelines effective Oct 1. Key changes include flexible core hours and equipment reimbursement policy. Please read the full policy on the intranet.', read: false },
59+
{ id: 'a2', title: 'Holiday: Office Closed on 2nd Oct', subtitle: 'HR', date: 'Sep 14', type: 'announcement', content: 'In observance of a public holiday, all offices will remain closed on 2nd October. Normal operations resume on 3rd October.', read: false },
60+
{ id: 'a3', title: 'Quarterly Town Hall this Friday', subtitle: 'Admin', date: 'Sep 12', type: 'announcement', content: 'Join us for the Quarterly Town Hall on Sep 20 at 4:00 PM in the Main Auditorium. Leadership will share company updates, upcoming initiatives, and answer your questions. Attendance is encouraged.', read: false },
61+
]);
4862

4963
useEffect(() => {
5064
if (isDesktop) setMobileSidebarOpen(false);
5165
}, [isDesktop]);
5266

67+
// Calculate unread counts
68+
const notifications = useMemo(() => ({
69+
bell: 3,
70+
chat: notificationItems.filter(item => !item.read).length,
71+
tasks: 1,
72+
announcements: announcementItems.filter(item => !item.read).length,
73+
}), [notificationItems, announcementItems]);
74+
75+
// Handler to mark a single item as read
76+
const handleMarkRead = (itemId: string | number, isNotification: boolean) => {
77+
if (isNotification) {
78+
setNotificationItems((prev) =>
79+
prev.map((item) =>
80+
item.id === itemId ? { ...item, read: true } : item
81+
)
82+
);
83+
} else {
84+
setAnnouncementItems((prev) =>
85+
prev.map((item) =>
86+
item.id === itemId ? { ...item, read: true } : item
87+
)
88+
);
89+
}
90+
};
91+
92+
// Handler to mark all as read
93+
const handleMarkAllRead = (tab: 'notifications' | 'announcements') => {
94+
if (tab === 'notifications') {
95+
setNotificationItems((prev) =>
96+
prev.map((item) => ({ ...item, read: true }))
97+
);
98+
} else {
99+
setAnnouncementItems((prev) =>
100+
prev.map((item) => ({ ...item, read: true }))
101+
);
102+
}
103+
};
104+
53105
const layoutVars: LayoutCSSVars = {
54106
'--sidebar-expanded': `${SIDEBAR_WIDTH}px`,
55107
'--sidebar-collapsed': `${SIDEBAR_WIDTH_COLLAPSED}px`,
@@ -82,7 +134,12 @@ function App() {
82134
companyName="NexGen7 Software"
83135
userFullName="Test Person"
84136
headerOffsetLeft={isDesktop ? (sidebarCollapsed ? SIDEBAR_WIDTH_COLLAPSED : SIDEBAR_WIDTH) : 0}
137+
notifications={notifications}
138+
notificationItems={notificationItems}
139+
announcementItems={announcementItems}
85140
onSearch={(q) => console.log('Search:', q)}
141+
onMarkRead={handleMarkRead}
142+
onMarkAllRead={handleMarkAllRead}
86143
/>
87144

88145
{/* Syncfusion Sidebar */}

Employee_Managment_App/src/components/Announcement.css

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,37 @@
137137
}
138138
.annc-panel-item {
139139
display: grid;
140-
grid-template-columns: 36px 1fr;
140+
grid-template-columns: 36px 1fr 20px;
141141
gap: 10px;
142142
padding: 10px 8px;
143143
border-radius: 12px;
144144
border: 1px solid transparent;
145-
transition: background 160ms ease, border-color 160ms ease;
145+
transition: background 160ms ease, border-color 160ms ease, opacity 160ms ease;
146+
position: relative;
147+
}
148+
.annc-panel-item.is-unread {
149+
background: #f0fdf9;
150+
border-color: #d1fae5;
151+
}
152+
.annc-panel-item.is-read {
153+
opacity: 0.75;
146154
}
147155
.annc-panel-item:hover {
148156
background: #f8fffb;
149157
border-color: #e8f7f1;
150158
}
159+
.annc-panel-item.is-unread:hover {
160+
background: #ecfdf5;
161+
}
162+
.unread-indicator {
163+
width: 8px;
164+
height: 8px;
165+
background: var(--annc-theme);
166+
border-radius: 50%;
167+
align-self: center;
168+
justify-self: end;
169+
margin-right: 4px;
170+
}
151171
.annc-item-icon {
152172
width: 36px;
153173
height: 36px;
@@ -169,6 +189,14 @@
169189
text-overflow: ellipsis;
170190
white-space: nowrap;
171191
}
192+
.annc-item-title.unread {
193+
font-weight: 700;
194+
color: var(--annc-text);
195+
}
196+
.annc-item-title.read {
197+
font-weight: 500;
198+
color: #94a3b8;
199+
}
172200
.annc-item-meta {
173201
margin-top: 2px;
174202
font-size: 12px;

Employee_Managment_App/src/components/Announcement.tsx

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export type AnnouncementPanelProps = {
2626
onClose: () => void;
2727
onChangeTab?: (tab: 'notifications' | 'announcements') => void;
2828
onMarkAllRead?: (tab: 'notifications' | 'announcements') => void;
29-
onMarkRead?: (item: PanelItem) => void;
29+
onMarkRead?: (itemId: string | number, isNotification: boolean) => void;
3030
};
3131

3232
export const AnnouncementPanel: React.FC<AnnouncementPanelProps> = ({
@@ -84,19 +84,8 @@ export const AnnouncementPanel: React.FC<AnnouncementPanelProps> = ({
8484
};
8585

8686
const handleMarkAllRead = () => {
87-
// Notify parent (if provided)
87+
// Notify parent to update notification counts
8888
onMarkAllRead?.(tab);
89-
90-
// Keep your existing badge-hiding logic (targets TopNav icon buttons)
91-
const selector =
92-
tab === 'announcements'
93-
? '.icon-btn.e-icons.e-audio span'
94-
: '.icon-btn.e-icons.e-multiple-comment span';
95-
document.querySelectorAll(selector).forEach((el) => {
96-
const span = el as HTMLElement;
97-
span.style.display = 'none';
98-
span.setAttribute('aria-hidden', 'true');
99-
});
10089
};
10190

10291
const items = tab === 'notifications' ? notificationItems : announcementItems;
@@ -162,25 +151,26 @@ export const AnnouncementPanel: React.FC<AnnouncementPanelProps> = ({
162151
{items.map((it) => (
163152
<li
164153
key={it.id}
165-
className="annc-panel-item"
154+
className={`annc-panel-item ${it.read ? 'is-read' : 'is-unread'}`}
166155
onClick={() => openDetail(it)}
167156
role="button"
168157
tabIndex={0}
169158
onKeyDown={(e) => e.key === 'Enter' && openDetail(it)}
170-
title="View details"
159+
title={`${it.read ? 'Read' : 'Unread'} - ${it.title}`}
171160
>
172161
<span className="panel-item-icon" aria-hidden="true">
173162
<i className={it.iconClass ?? (it.type === 'announcement' ? 'e-icons e-audio' : 'e-icons e-multiple-comment')} />
174163
</span>
175164
<div className="annc-item-body">
176-
<div className="annc-item-title">{it.title}</div>
165+
<div className={`annc-item-title ${it.read ? 'read' : 'unread'}`}>{it.title}</div>
177166
{(it.subtitle || it.date) && (
178167
<div className="annc-item-meta">
179168
{it.subtitle && <span className="meta">{it.subtitle}</span>}
180169
{it.date && <span className="meta dot">{it.date}</span>}
181170
</div>
182171
)}
183172
</div>
173+
{!it.read && <span className="unread-indicator" aria-label="Unread"></span>}
184174
</li>
185175
))}
186176
</ul>
@@ -200,7 +190,7 @@ export const AnnouncementPanel: React.FC<AnnouncementPanelProps> = ({
200190
open={detailOpen}
201191
item={selectedItem}
202192
onClose={closeDetail}
203-
onMarkRead={(it) => onMarkRead?.(it)}
193+
onMarkRead={onMarkRead}
204194
/>
205195
)}
206196
</>

Employee_Managment_App/src/components/AnnouncementDetailDialog.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export type AnnouncementDetailDialogProps = {
2121
open: boolean;
2222
item: DetailItem;
2323
onClose: () => void;
24-
onMarkRead?: (item: DetailItem) => void;
24+
onMarkRead?: (itemId: string | number, isNotification: boolean) => void;
2525
};
2626

2727
const dialogAnimation: AnimationSettingsModel = { effect: 'Zoom', duration: 140 };
@@ -92,8 +92,12 @@ export const AnnouncementDetailDialog: React.FC<AnnouncementDetailDialogProps> =
9292

9393
const footerTemplate = () => (
9494
<div className="annc-dlg-footer">
95-
{item && (
96-
<ButtonComponent cssClass="e-primary" onClick={onClose}>
95+
{item && !item.read && (
96+
<ButtonComponent cssClass="e-primary" onClick={() => {
97+
const isNotification = item.type === 'notification';
98+
onMarkRead?.(item.id, isNotification);
99+
onClose();
100+
}}>
97101
Mark as read
98102
</ButtonComponent>
99103
)}

Employee_Managment_App/src/components/TopNav.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type TopNavProps = {
2727
onProfile?: () => void;
2828
onSignOut?: () => void;
2929
userFullName?: string;
30+
onMarkRead?: (itemId: string | number, isNotification: boolean) => void;
31+
onMarkAllRead?: (tab: 'notifications' | 'announcements') => void;
3032
};
3133

3234
const TopNav: React.FC<TopNavProps> = ({
@@ -55,6 +57,8 @@ const TopNav: React.FC<TopNavProps> = ({
5557
onProfile,
5658
onSignOut,
5759
userFullName = 'Test Person',
60+
onMarkRead,
61+
onMarkAllRead,
5862
}) => {
5963
const [query, setQuery] = useState('');
6064
const [avatarMenuOpen, setAvatarMenuOpen] = useState(false);
@@ -236,7 +240,7 @@ const TopNav: React.FC<TopNavProps> = ({
236240
setPanelOpen(true);
237241
onOpenNotifications?.();
238242
}} title="Messages">
239-
{!!notifications.chat && <span className="badge">{notifications.announcements}</span>}
243+
{!!notifications.chat && <span className="badge">{notifications.chat}</span>}
240244
</button>
241245

242246
<button
@@ -324,6 +328,8 @@ const TopNav: React.FC<TopNavProps> = ({
324328
topOffset={TOPNAV_HEIGHT}
325329
onClose={() => setPanelOpen(false)}
326330
onChangeTab={(t) => setPanelTab(t)}
331+
onMarkAllRead={onMarkAllRead}
332+
onMarkRead={onMarkRead}
327333
/>
328334
</>
329335
);

0 commit comments

Comments
 (0)