Skip to content

Commit 765757a

Browse files
authored
Merge pull request #29 from SyncfusionExamples/999936-employee-management
999936: fixed the bugs and UI changes
2 parents 4ff3f27 + 5073279 commit 765757a

File tree

13 files changed

+350
-293
lines changed

13 files changed

+350
-293
lines changed

Employee_Managment_App/src/App.tsx

Lines changed: 59 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,14 +134,20 @@ 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 */}
89146
<SidebarComponent
90147
width={`${SIDEBAR_WIDTH}px`}
91148
dockSize={`${SIDEBAR_WIDTH_COLLAPSED}px`}
92149
enableDock={sbEnableDock}
150+
enableGestures={false}
93151
isOpen={sbIsOpen}
94152
type={sbType as any}
95153
position="Left"

Employee_Managment_App/src/components/Achievements.css

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ body,
3434
/* removed decorative radial gradients */
3535
min-height: 100%;
3636
color: var(--text);
37-
font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
38-
}
37+
font-family: 'Roboto', sans-serif;
38+
}
3939

4040
/* Toolbar - flat */
4141
.achievements-toolbar {
@@ -70,14 +70,17 @@ body,
7070
}
7171

7272
.toolbar-field {
73-
display: grid;
73+
display: flex;
74+
justify-content: center;
75+
align-items: center;
7476
gap: 6px;
7577
font-size: 12px;
7678
color: var(--muted);
7779
}
7880

7981
.field-label {
8082
opacity: .9;
83+
font-size: 14px;
8184
}
8285

8386
/* Syncfusion dropdowns */
@@ -144,7 +147,7 @@ body,
144147
.lb-section {
145148
background: var(--surface);
146149
border: 1px solid var(--border);
147-
border-radius: 10px;
150+
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
148151
overflow: hidden;
149152
}
150153

@@ -153,6 +156,7 @@ body,
153156
display: flex;
154157
align-items: center;
155158
gap: 10px;
159+
height: 70px;
156160
padding: 10px 12px;
157161
border-bottom: 1px solid var(--border);
158162
background: #fff;
@@ -171,18 +175,21 @@ body,
171175
.bg-r-overall {
172176
background: #fb5757;
173177
color: #fff;
178+
font-size: 22px;
174179
}
175180

176181
.icon-task,
177182
.bg-r-task {
178183
background: #d65e22;
179184
color: #fff;
185+
font-size: 22px;
180186
}
181187

182188
.icon-att,
183189
.bg-r-attendance {
184190
background: #317701;
185191
color: #fff;
192+
font-size: 22px;
186193
}
187194

188195
.lb-header-text {
@@ -196,7 +203,7 @@ body,
196203
}
197204

198205
.lb-sub {
199-
font-size: 10px;
206+
font-size: 14px;
200207
color: #fff;
201208
letter-spacing: 1px;
202209
padding-top: 5px;
@@ -292,13 +299,6 @@ body,
292299
font-size: 13px;
293300
letter-spacing: .2px;
294301
font-feature-settings: "tnum" on, "lnum" on;
295-
background: #f8fafc;
296302
padding: 4px 8px;
297-
border-radius: 6px;
298-
border: 1px solid #e5e7eb;
299303
font-family: 'Roboto', sans-serif;
300304
}
301-
302-
.score.flat {
303-
background: #f8fafc;
304-
}

Employee_Managment_App/src/components/Achievements.tsx

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -209,14 +209,14 @@ const Achievements: React.FC<AchievementsProps> = ({ userInfo, onlyTeamIfUser =
209209

210210
<div className="toolbar-right">
211211
<label className="toolbar-field">
212-
<span className="field-label">Role</span>
212+
<span className="field-label" >Role</span>
213213
<DropDownListComponent
214214
id="role-ddl"
215215
cssClass="sf-field-input"
216216
dataSource={roles}
217217
fields={{ text: 'text', value: 'value' }}
218218
value={role}
219-
width="180px"
219+
width="135px"
220220
placeholder="Select role"
221221
change={(e: any) => setRole(e.value)}
222222
aria-label="Filter by role"
@@ -231,6 +231,7 @@ const Achievements: React.FC<AchievementsProps> = ({ userInfo, onlyTeamIfUser =
231231
id="month-ddl"
232232
cssClass="sf-field-input"
233233
dataSource={months}
234+
width="100px"
234235
fields={{ text: 'text', value: 'value' }}
235236
value={month}
236237
placeholder="Select month"
@@ -247,6 +248,7 @@ const Achievements: React.FC<AchievementsProps> = ({ userInfo, onlyTeamIfUser =
247248
id="year-ddl"
248249
cssClass="sf-field-input"
249250
dataSource={years}
251+
width="100px"
250252
fields={{ text: 'text', value: 'value' }}
251253
value={year}
252254
placeholder="Select year"
@@ -270,10 +272,7 @@ const Achievements: React.FC<AchievementsProps> = ({ userInfo, onlyTeamIfUser =
270272
<div className="lb-sections">
271273
<section className="lb-section">
272274
<header className="lb-header bg-r-overall">
273-
<div className="lb-header-icon icon-overall" aria-hidden>
274-
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
275-
<path d="M7 10a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm10 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM12 9a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm-7 8v-1a4 4 0 0 1 4-4h2a4 4 0 0 1 4 4v1H5zm10 0v-1a5 5 0 0 1 5-5h.5A1.5 1.5 0 0 1 22 12.5V17H15z" />
276-
</svg>
275+
<div className="lb-header-icon icon-overall e-icons e-user" aria-hidden>
277276
</div>
278277
<div className="lb-header-text">
279278
<div className="lb-title">Overall</div>
@@ -297,10 +296,7 @@ const Achievements: React.FC<AchievementsProps> = ({ userInfo, onlyTeamIfUser =
297296

298297
<section className="lb-section">
299298
<header className="lb-header bg-r-task">
300-
<div className="lb-header-icon icon-task" aria-hidden>
301-
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
302-
<path d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
303-
</svg>
299+
<div className="lb-header-icon icon-task e-icons e-check-tick" aria-hidden>
304300
</div>
305301
<div className="lb-header-text">
306302
<div className="lb-title">Task</div>
@@ -324,10 +320,7 @@ const Achievements: React.FC<AchievementsProps> = ({ userInfo, onlyTeamIfUser =
324320

325321
<section className="lb-section">
326322
<header className="lb-header bg-r-attendance">
327-
<div className="lb-header-icon icon-att" aria-hidden>
328-
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
329-
<path d="M19 3h-1V1h-2v2H8V1H6v2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2zm0 16H5V8h14v11z" />
330-
</svg>
323+
<div className="lb-header-icon icon-att e-icons e-day" aria-hidden>
331324
</div>
332325
<div className="lb-header-text">
333326
<div className="lb-title">Attendance</div>

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;

0 commit comments

Comments
 (0)