Skip to content

Commit

Permalink
Vacation Mode feature to turn off notifications.
Browse files Browse the repository at this point in the history
  • Loading branch information
areyeslo committed Aug 23, 2024
1 parent 1e72894 commit cad54ff
Show file tree
Hide file tree
Showing 27 changed files with 335 additions and 35 deletions.
6 changes: 5 additions & 1 deletion api/net/Areas/Admin/Controllers/ReportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,11 @@ public IActionResult GetDashboardReport(int id)
foreach (var distribution in distributions)
{
// Flatten the list of subscribers by extracting users in a distribution list.
subscribers.AddRange(distribution.User!.Distribution.Select(d => new UserReportModel(distribution, d.LinkedUser!)));
subscribers.AddRange(
distribution.User!.Distribution
.Where(d => d.LinkedUser != null)
.Select(d => new UserReportModel(distribution, d.LinkedUser!))
);
}
subscribers = subscribers.Distinct().ToList(); // Remove duplicates.
var instance = report.Instances.OrderByDescending(r => r.Id).FirstOrDefault();
Expand Down
1 change: 1 addition & 0 deletions api/net/Areas/Subscriber/Controllers/UserController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public IActionResult Update(UserModel model)
return new JsonResult(new UserModel(result));
}


/// <summary>
/// Find colleagues related to logged user.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion api/net/Areas/Subscriber/Controllers/WorkOrderController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ public async Task<IActionResult> RequestTranscriptionAsync(long contentId)
NotificationId = notificationId,
ContentId = contentId,
RequestorId = workOrder.RequestorId,
To = !String.IsNullOrWhiteSpace(user.PreferredEmail) ? user.PreferredEmail : user.Email,
To = user != null && !user.IsVacationMode()
? (!string.IsNullOrWhiteSpace(user.PreferredEmail) ? user.PreferredEmail : user.Email) ?? string.Empty
: "",
};
await _kafkaProducer.SendMessageAsync(_kafkaOptions.NotificationTopic, request);
}
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion app/subscriber/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"sheetjs": "file:packages/xlsx-0.20.1.tgz",
"styled-components": "6.1.11",
"stylis": "4.3.2",
"tno-core": "0.1.134"
"tno-core": "./tno-core-0.1.134.tgz"
},
"devDependencies": {
"@testing-library/jest-dom": "6.4.5",
Expand Down
28 changes: 27 additions & 1 deletion app/subscriber/src/components/user-profile/UserProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ScreenSizes } from 'components/layout/constants';
import React from 'react';
import { BiLogOut } from 'react-icons/bi';
import { FaChevronCircleDown, FaUserCircle } from 'react-icons/fa';
import { FaChevronCircleDown, FaUmbrellaBeach, FaUserCircle } from 'react-icons/fa';
import { Link } from 'react-router-dom';
import { Tooltip } from 'react-tooltip';
import { useUsers } from 'store/hooks';
Expand All @@ -21,6 +21,11 @@ export const UserProfile: React.FC = () => {

const [profileMenu, setProfileMenu] = React.useState(false);

const isVacationMode: boolean = !!impersonate
? impersonate?.preferences?.isVacationMode ?? false
: profile?.preferences?.isVacationMode ?? false;
const [showVacationMode, setShowVacationMode] = React.useState(false);

const isAdmin = keycloak.hasClaim(Claim.administrator);

React.useEffect(() => {
Expand All @@ -41,8 +46,29 @@ export const UserProfile: React.FC = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [profile?.preferences?.impersonate, impersonate, storeImpersonate]);

React.useEffect(() => {
if (isVacationMode !== undefined) {
setShowVacationMode(isVacationMode);
}
}, [isVacationMode]);

const handleVacationModeToggle = () => {
setShowVacationMode(false);
};

return (
<styled.UserProfile>
<Row>
{showVacationMode && (
<span className="vacation-mode-label">
<FaUmbrellaBeach className="icon" />
Vacation mode enabled
<span className="close-button" onClick={handleVacationModeToggle}>
[x]
</span>
</span>
)}
</Row>
<Row
data-tooltip-id="my-info"
direction="row"
Expand Down
21 changes: 21 additions & 0 deletions app/subscriber/src/components/user-profile/styled/UserProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ export const UserProfile = styled.div`
align-items: center;
margin-left: auto;
.vacation-mode-label {
color: #008000;
display: flex;
align-items: center;
.icon {
margin-right: 5px;
}
.close-button {
color: red;
margin-left: 5px;
cursor: pointer;
font-weight: bold;
&:hover {
text-decoration: underline;
}
}
}
.username-info {
display: flex;
align-items: center;
Expand Down
76 changes: 76 additions & 0 deletions app/subscriber/src/features/settings/MyAccountSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import { FaEnvelope, FaToggleOff, FaToggleOn, FaUmbrellaBeach } from 'react-icons/fa';
import { toast } from 'react-toastify';
import { useUsers } from 'store/hooks';
import { useProfileStore } from 'store/slices';
import { ISubscriberUserModel, ToggleButton } from 'tno-core';

import * as styled from './styled';

const MyAccountSettings = () => {
const { updateUser } = useUsers();
const [{ profile, impersonate }, { storeMyProfile, storeImpersonate }] = useProfileStore();
const isVacationMode: boolean = !!impersonate
? impersonate?.preferences?.isVacationMode ?? false
: profile?.preferences?.isVacationMode ?? false;

const toggleVacationMode = React.useCallback(async () => {
if (!profile) {
toast.error('User information is missing. Please try again later');
return;
}
const baseProfile = impersonate ?? profile;
const createUser = (): ISubscriberUserModel => {
// use impersonate if it exists, otherwise use profile
return {
...baseProfile,
preferences: {
...baseProfile.preferences,
isVacationMode: !isVacationMode,
},
};
};
const user = createUser();

try {
!!impersonate ? storeImpersonate(user) : storeMyProfile(user);
await updateUser(user, !!impersonate);
toast.success('Vacation mode has successfully been updated.');
} catch (error) {
// Handle the error, if needed
console.error('Failed to update user:', error);
}
}, [profile, impersonate, isVacationMode, storeImpersonate, storeMyProfile, updateUser]);

return (
<styled.MyAccountSettings>
<div className="header-row">
<FaEnvelope className="icon" />
<span className="header-text">E-mail notifications</span>
</div>
<p className="description">
Enabling vacation mode will <strong>turn off</strong> all MMI emails to you, until you
disable vacation mode. This will include subscriptions to MMI Products, Alerts and Reports.
</p>
<div className="toggleContainer">
<ToggleButton
on={<FaToggleOn />}
off={<FaToggleOff />}
onClick={toggleVacationMode}
width="25px"
height="25px"
color="#008000"
label={
<span className="vacation-mode-label">
<FaUmbrellaBeach className="icon" />
Vacation Mode
</span>
}
value={isVacationMode}
/>
</div>
</styled.MyAccountSettings>
);
};

export default MyAccountSettings;
29 changes: 29 additions & 0 deletions app/subscriber/src/features/settings/SettingsLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FaCog } from 'react-icons/fa';
import { Col, Row, Show } from 'tno-core';

import { SettingsSessionEnum } from './constants/SettingsSessionEnum';
import MyAccountSettings from './MyAccountSettings';
import { MyMinisterSettings } from './MyMinisterSettings';
import * as styled from './styled';

Expand All @@ -21,6 +22,16 @@ export const SettingsLanding: React.FC<{}> = () => {
<Col className="left-side">
<PageSection header="Settings" includeHeaderIcon className="menuPanel">
<div className="link-box-container">
<div className="link-box" onClick={() => show(SettingsSessionEnum.MyAccount)}>
<h2>
<FaCog className="list-icon" />
<span>My Account</span>
</h2>
<div className="description">
Alerts are configurable search queries that will send stories to you when they meet
your query criteria.
</div>
</div>
<div className="link-box" onClick={() => show(SettingsSessionEnum.MyMinister)}>
<h2>
<FaCog className="list-icon" />
Expand All @@ -47,6 +58,24 @@ export const SettingsLanding: React.FC<{}> = () => {
</Col>
<Show visible={true}>
<Col className="right-side">
<Show visible={session === SettingsSessionEnum.MyAccount}>
<PageSection
header={
<Row className="header-row">
<div className="title">My Account</div>
<Action
variant="close"
className="close-button"
title="Revert"
onClick={() => setSession('')}
/>
</Row>
}
includeHeaderIcon
>
<MyAccountSettings />
</PageSection>
</Show>
<Show visible={session === SettingsSessionEnum.MyMinister}>
<PageSection
header={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum SettingsSessionEnum {
MyAccount = 'MyAccount',
MyMinister = 'MyMinister',
MyColleagues = 'MyColleagues',
}
47 changes: 47 additions & 0 deletions app/subscriber/src/features/settings/styled/MyAccountSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import styled from 'styled-components';
import { Col } from 'tno-core';

export const MyAccountSettings = styled(Col)`
padding: 0.6em;
.header-row {
display: flex;
align-items: center;
font-size: 1rem;
color: #333;
margin-top: 0.5rem;
margin-bottom: 0.25rem;
margin-left: 0.75rem;
border-bottom: 1px solid #ccc;
.icon {
font-size: 1.2rem;
margin-right: 0.75rem;
}
.header-text {
font-weight: bold;
font-size: 1.1rem;
}
}
.description {
margin-left: 2.8rem;
}
.toggleContainer {
display: flex;
align-items: center;
margin-left: 3rem;
.vacation-mode-label {
color: #008000;
display: flex;
align-items: center;
}
.icon {
margin-right: 5px;
}
}
`;
1 change: 1 addition & 0 deletions app/subscriber/src/features/settings/styled/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './MyAccountSettings';
export * from './MyMinisterSettings';
export * from './SettingsLanding';
Binary file added app/subscriber/tno-core-0.1.134.tgz
Binary file not shown.
20 changes: 10 additions & 10 deletions app/subscriber/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10670,7 +10670,7 @@ __metadata:
sheetjs: "file:packages/xlsx-0.20.1.tgz"
styled-components: 6.1.11
stylis: 4.3.2
tno-core: 0.1.134
tno-core: ./tno-core-0.1.134.tgz
typescript: 4.9.5
languageName: unknown
linkType: soft
Expand Down Expand Up @@ -14702,9 +14702,9 @@ __metadata:
languageName: node
linkType: hard

"tno-core@npm:0.1.121":
version: 0.1.121
resolution: "tno-core@npm:0.1.121"
"tno-core@file:./tno-core-0.1.134.tgz::locator=mmi-subscriber-app%40workspace%3A.":
version: 0.1.134
resolution: "tno-core@file:./tno-core-0.1.134.tgz::locator=mmi-subscriber-app%40workspace%3A."
dependencies:
"@elastic/elasticsearch": ^8.13.1
"@fortawesome/free-solid-svg-icons": ^6.4.2
Expand Down Expand Up @@ -14736,14 +14736,15 @@ __metadata:
react-tooltip: ^5.10.0
styled-components: ^6.1.11
stylis: ^4.3.2
tno-core: 0.1.121
yup: ^1.1.1
checksum: 6340d9078452e1567f87aef9f5533d9404456706f9a9e78b254575bf7b9d527ff6cb51ae7426431f4bed0a444818fabe95311942133dde7c81e3ab6495ae2059
checksum: 8288ebf2bf8c3d416777f7053e2bcbae93a9f368982dd406a3645a3ed9995a009a37cb328c4e3778cb5bf9322ebdaa5e58f31509852f1e244a80e70f3a417da9
languageName: node
linkType: hard

"tno-core@npm:0.1.134":
version: 0.1.134
resolution: "tno-core@npm:0.1.134"
"tno-core@npm:0.1.121":
version: 0.1.121
resolution: "tno-core@npm:0.1.121"
dependencies:
"@elastic/elasticsearch": ^8.13.1
"@fortawesome/free-solid-svg-icons": ^6.4.2
Expand Down Expand Up @@ -14775,9 +14776,8 @@ __metadata:
react-tooltip: ^5.10.0
styled-components: ^6.1.11
stylis: ^4.3.2
tno-core: 0.1.121
yup: ^1.1.1
checksum: 7c70811a336ec041fb389c065a790beaf29419eea5b46f365d363d7c2299debb9a43eee29d2674d6261791421f0b7c29f013145692f0119cd2b0e67fc3aa07de
checksum: 6340d9078452e1567f87aef9f5533d9404456706f9a9e78b254575bf7b9d527ff6cb51ae7426431f4bed0a444818fabe95311942133dde7c81e3ab6495ae2059
languageName: node
linkType: hard

Expand Down
4 changes: 4 additions & 0 deletions libs/net/dal/Migrations/TNOContextModelSnapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5108,6 +5108,10 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasColumnType("boolean")
.HasColumnName("is_system_account");
b.Property<bool>("IsVacationMode")
.HasColumnType("boolean")
.HasColumnName("is_vacation_mode");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(250)
Expand Down
2 changes: 1 addition & 1 deletion libs/net/dal/Services/Interfaces/IUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ public interface IUserService : IBaseService<User, int>
User? TransferAccount(API.Areas.Admin.Models.User.TransferAccountModel account);

IEnumerable<User> GetDistributionList(int userId);
}
}
2 changes: 1 addition & 1 deletion libs/net/dal/Services/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -562,4 +562,4 @@ join u in this.Context.Users on ud.LinkedUserId equals u.Id
select u).ToArray();
}
#endregion
}
}
Loading

0 comments on commit cad54ff

Please sign in to comment.