Skip to content

Commit

Permalink
Adds functionality for users to create notifications for various plat…
Browse files Browse the repository at this point in the history
…forms
  • Loading branch information
KSJaay committed Aug 24, 2024
1 parent 4fc7062 commit 1020ae5
Show file tree
Hide file tree
Showing 95 changed files with 3,322 additions and 74 deletions.
5 changes: 4 additions & 1 deletion app/components/icons/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { FiLayout } from 'react-icons/fi';
import { HiStatusOffline, HiStatusOnline } from 'react-icons/hi';
import { IoMdEyeOff, IoMdEye, IoMdClose, IoMdHelpCircle } from 'react-icons/io';
import { LiaSyncSolid } from 'react-icons/lia';
import { MdEdit, MdErrorOutline } from 'react-icons/md';
import { MdEdit, MdErrorOutline, MdNotifications } from 'react-icons/md';
import {
FaCircleCheck,
FaEllipsisVertical,
Expand All @@ -24,6 +24,7 @@ import {
FaTrashCan,
} from 'react-icons/fa6';
import { IoArrowBack, IoColorPalette, IoGrid, IoReload } from 'react-icons/io5';
import { RiStackFill } from 'react-icons/ri';
import StatusLogo from './statusLogo';

export {
Expand Down Expand Up @@ -57,5 +58,7 @@ export {
LiaSyncSolid,
MdEdit,
MdErrorOutline,
MdNotifications,
RiStackFill,
StatusLogo,
};
6 changes: 2 additions & 4 deletions app/components/icons/statusLogo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ const StatusLogo = ({ size = 250 }) => {
globalStore: { getAllMonitors },
} = useContextStore();

const monitors = getAllMonitors();

const totalMonitors = monitors.length;
const offlineMonitors = monitors.filter(
const totalMonitors = getAllMonitors.length;
const offlineMonitors = getAllMonitors.filter(
(monitor = {}) => monitor.heartbeats[0]?.isDown
).length;

Expand Down
6 changes: 4 additions & 2 deletions app/components/modal/monitor/pages/http/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ const MonitorAddHttp = ({ inputs, errors, handleInput }) => {
handleSelect={handleMethodSelect}
/>
{errors.method && (
<label className="text-input-error" id="text-input-http-method-error">{errors.method}</label>
<label className="input-error" id="text-input-http-method-error">
{errors.method}
</label>
)}

<MonitorHttpStatusCodes
Expand All @@ -42,7 +44,7 @@ const MonitorAddHttp = ({ inputs, errors, handleInput }) => {
/>

{errors.valid_status_codes && (
<label className="text-input-error">{errors.valid_status_codes}</label>
<label className="input-error">{errors.valid_status_codes}</label>
)}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion app/components/modal/monitor/pages/http/methods.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const MonitorHttpMethods = ({ handleSelect, selectValue = defaultValue }) => {

return (
<>
<label className="text-input-label">Method</label>
<label className="input-label">Method</label>
<Dropdown.Container
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
Expand Down
2 changes: 1 addition & 1 deletion app/components/modal/monitor/pages/http/statusCodes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const MonitorHttpStatusCodes = ({

return (
<>
<label className="text-input-label">Accepted Status Codes</label>
<label className="input-label">Accepted Status Codes</label>
<Select.Container isOpen={selectIsOpen} toggleSelect={toggleSelect}>
<Select.Trigger
asInput
Expand Down
4 changes: 2 additions & 2 deletions app/components/modal/monitor/pages/initial/type.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const MonitorInitialDropdown = ({ inputs, errors, handleInput }) => {
const { dropdownIsOpen, toggleDropdown } = useDropdown();
return (
<>
<label className="text-input-label">Monitor Type</label>
<label className="input-label">Monitor Type</label>
<Dropdown.Container
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
Expand Down Expand Up @@ -44,7 +44,7 @@ const MonitorInitialDropdown = ({ inputs, errors, handleInput }) => {
</Dropdown.List>
</Dropdown.Container>
{errors.type && (
<label id="text-input-error-input-type" className="text-input-error">
<label id="text-input-error-input-type" className="input-error">
{errors.type}
</label>
)}
Expand Down
37 changes: 37 additions & 0 deletions app/components/modal/notification/delete.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// import dependencies
import PropTypes from 'prop-types';

// import local files
import Modal from '../../ui/modal';

const NotificationDeleteModal = ({ name, handleClose, handleConfirm }) => {
return (
<>
<Modal.Title>Are you absolutely sure?</Modal.Title>
<Modal.Message style={{ width: '400px' }}>
By continuing you will be deleting{' '}
<span style={{ fontWeight: '600', color: 'var(--primary-700)' }}>
{name} notification
</span>{' '}
from all linked monitors.{' '}
<span style={{ fontWeight: '600' }}>This action cannot be undone.</span>
</Modal.Message>
<Modal.Actions>
<Modal.Button onClick={handleClose}>Cancel</Modal.Button>
<Modal.Button color="red" onClick={handleConfirm}>
Confirm
</Modal.Button>
</Modal.Actions>
</>
);
};

NotificationDeleteModal.displayName = 'NotificationDeleteModal';

NotificationDeleteModal.propTypes = {
name: PropTypes.string.isRequired,
handleClose: PropTypes.func.isRequired,
handleConfirm: PropTypes.func.isRequired,
};

export default NotificationDeleteModal;
83 changes: 83 additions & 0 deletions app/components/modal/notification/dropdown/platform.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// import dependencies
import PropTypes from 'prop-types';

// import local files
import Dropdown from '../../../ui/dropdown';
import useDropdown from '../../../../hooks/useDropdown';

const notifications = {
Discord: { name: 'Discord', icon: 'discord.svg' },
Slack: { name: 'Slack', icon: 'slack.svg' },
Telegram: { name: 'Telegram', icon: 'telegram.svg' },
Webhook: { name: 'Webhook', icon: 'webhook.svg' },
};

const NotificationModalPlatform = ({ isEdit, setPlatform, platform }) => {
const { dropdownIsOpen, toggleDropdown } = useDropdown();

return (
<>
<label className="input-label">Notification Type</label>
{isEdit && (
<Dropdown.Trigger asInput>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<img
src={`/notifications/${notifications[platform].icon}`}
style={{ width: '22px' }}
/>
<div>{notifications[platform].name}</div>
</div>
</Dropdown.Trigger>
)}
{!isEdit && (
<Dropdown.Container
position="right"
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
id="home-menu-layout"
>
<Dropdown.Trigger
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
asInput
>
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<img
src={`/notifications/${notifications[platform].icon}`}
style={{ width: '22px' }}
/>
<div>{notifications[platform].name}</div>
</div>
</Dropdown.Trigger>
<Dropdown.List isOpen={dropdownIsOpen} fullWidth>
{Object.values(notifications).map((notification) => (
<Dropdown.Item
onClick={() => {
setPlatform({ key: 'platform', value: notification.name });
toggleDropdown();
}}
key={notification.name}
>
<img
src={`/notifications/${notification.icon}`}
style={{ width: '20px' }}
/>
<div>{notification.name}</div>
</Dropdown.Item>
))}
</Dropdown.List>
</Dropdown.Container>
)}
</>
);
};

NotificationModalPlatform.displayName = 'NotificationModalPlatform';

NotificationModalPlatform.propTypes = {
isEdit: PropTypes.bool,
setPlatform: PropTypes.func.isRequired,
platform: PropTypes.string.isRequired,
};

export default NotificationModalPlatform;
55 changes: 55 additions & 0 deletions app/components/modal/notification/dropdown/type.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// import dependencies
import PropTypes from 'prop-types';

// import local files
import Dropdown from '../../../ui/dropdown';
import useDropdown from '../../../../hooks/useDropdown';

const NotificationModalType = ({ messageType = 'basic', setMessageType }) => {
const { dropdownIsOpen, toggleDropdown } = useDropdown();

return (
<>
<label className="input-label">Message Type</label>
<Dropdown.Container
position="right"
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
id="home-menu-layout"
>
<Dropdown.Trigger
isOpen={dropdownIsOpen}
toggleDropdown={toggleDropdown}
asInput
>
{messageType.charAt(0).toUpperCase() + messageType.slice(1)}
</Dropdown.Trigger>
<Dropdown.List isOpen={dropdownIsOpen} fullWidth>
{['Basic', 'Pretty', 'Nerdy'].map((type) => (
<Dropdown.Item
key={type}
onClick={() => {
setMessageType({
key: 'messageType',
value: type.toLowerCase(),
});
toggleDropdown();
}}
>
{type}
</Dropdown.Item>
))}
</Dropdown.List>
</Dropdown.Container>
</>
);
};

NotificationModalType.displayName = 'NotificationModalType';

NotificationModalType.propTypes = {
messageType: PropTypes.oneOf(['basic', 'pretty', 'nerdy']),
setMessageType: PropTypes.func,
};

export default NotificationModalType;
103 changes: 103 additions & 0 deletions app/components/modal/notification/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import './styles.scss';

// import dependencies
import { useEffect } from 'react';
import PropTypes from 'prop-types';

// import local files
import Modal from '../../ui/modal';
import NotificationModalType from './dropdown/type';
import NotificationModalPlatform from './dropdown/platform';
import NotificationsTemplates from '../../../../shared/notifications';
import useNotificationForm from '../../../hooks/useNotificationForm';
import NotificationModalPayload from './payload';
import * as inputForPlatform from './platform';

// Notification types
// - Up and running
// - Application went down
// - Warning (If the monitor goes down once it shows a warning, else throw error)

const NotificationModal = ({ values, isEdit, closeModal, addNotification }) => {
const { inputs, errors, handleInput, handleSubmit } = useNotificationForm(
values,
isEdit,
closeModal
);

const handleKeydown = (event) => {
if (event?.key === 'Escape' || event?.key === 'Esc') {
closeModal();
}
};

useEffect(() => {
document.addEventListener('keydown', handleKeydown);

return () => {
document.removeEventListener('keydown', handleKeydown);
};
}, []);

const message = NotificationsTemplates[inputs.platform][inputs.messageType];

const PlatformInputs = inputForPlatform[inputs.platform];

return (
<Modal.Container
closeButton={closeModal}
contentProps={{ style: { maxWidth: '650px', width: '100%' } }}
>
<Modal.Title style={{ textAlign: 'center' }}>
{isEdit ? 'Edit Notification' : 'Add Notification'}
</Modal.Title>

{errors['general'] && (
<div className="input-error-general">{errors['general']}</div>
)}

<Modal.Message>
<NotificationModalPlatform
isEdit={isEdit}
values={inputs}
setPlatform={handleInput}
platform={inputs.platform}
/>
<PlatformInputs
values={inputs}
errors={errors}
handleInput={handleInput}
/>
<NotificationModalType
messageType={inputs.messageType}
setMessageType={handleInput}
/>
<label className="input-label">Payload</label>
<NotificationModalPayload message={message} />
</Modal.Message>

<Modal.Actions>
<Modal.Button color={'red'} onClick={closeModal}>
Cancel
</Modal.Button>
<Modal.Button
color={'green'}
onClick={() => handleSubmit(addNotification)}
>
{isEdit ? 'Update' : 'Create'}
</Modal.Button>
</Modal.Actions>
</Modal.Container>
);
};

NotificationModal.displayName = 'NotificationModal';

NotificationModal.propTypes = {
isEdit: PropTypes.bool,
values: PropTypes.object,
closeModal: PropTypes.func,
addNotification: PropTypes.func,
};

export default NotificationModal;
Loading

0 comments on commit 1020ae5

Please sign in to comment.