diff --git a/Common/Types/Mail/MailStatus.ts b/Common/Types/Mail/MailStatus.ts new file mode 100644 index 0000000000..d325ed0d27 --- /dev/null +++ b/Common/Types/Mail/MailStatus.ts @@ -0,0 +1,6 @@ +enum MailStatus { + Success = 'Success', + Error = 'Error', +} + +export default MailStatus; diff --git a/Common/Types/Permission.ts b/Common/Types/Permission.ts index 385df58f64..29516aea35 100644 --- a/Common/Types/Permission.ts +++ b/Common/Types/Permission.ts @@ -79,7 +79,7 @@ enum Permission { CanReadMonitorProbe = 'CanReadMonitorProbe', CanReadSmsLog = 'CanReadSmsLog', - + CanReadEmailLog = 'CanReadEmailLog', CanReadCallLog = 'CanReadCallLog', CanCreateIncidentOwnerTeam = 'CanCreateIncidentOwnerTeam', diff --git a/CommonServer/Services/EmailLogService.ts b/CommonServer/Services/EmailLogService.ts index 294af13df8..6fdd6621e0 100644 --- a/CommonServer/Services/EmailLogService.ts +++ b/CommonServer/Services/EmailLogService.ts @@ -5,6 +5,8 @@ import DatabaseService from './DatabaseService'; export class Service extends DatabaseService { public constructor(postgresDatabase?: PostgresDatabase) { super(Model, postgresDatabase); + this.hardDeleteItemsOlderThanInDays('createdAt', 30); } } + export default new Service(); diff --git a/Dashboard/src/App.tsx b/Dashboard/src/App.tsx index 6718e290ae..8b4434ef23 100644 --- a/Dashboard/src/App.tsx +++ b/Dashboard/src/App.tsx @@ -96,6 +96,7 @@ import SettingsBilling from './Pages/Settings/Billing'; import SettingsSSO from './Pages/Settings/SSO'; import SettingsSmsLog from './Pages/Settings/SmsLog'; import SettingsCallLog from './Pages/Settings/CallLog'; +import SettingsEmailLog from './Pages/Settings/EmailLog'; import SettingsCallSms from './Pages/Settings/CallSms'; import SettingsInvoices from './Pages/Settings/Invoices'; import MonitorCustomFields from './Pages/Settings/MonitorCustomFields'; @@ -374,7 +375,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.HOME_NOT_OPERATIONAL_MONITORS + PageMap.HOME_NOT_OPERATIONAL_MONITORS ] as Route } /> @@ -394,8 +395,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .HOME_ONGOING_SCHEDULED_MAINTENANCE_EVENTS + PageMap + .HOME_ONGOING_SCHEDULED_MAINTENANCE_EVENTS ] as Route } /> @@ -423,7 +424,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.MONITORS_INOPERATIONAL + PageMap.MONITORS_INOPERATIONAL ] as Route } /> @@ -506,7 +507,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.MONITOR_VIEW_STATUS_TIMELINE + PageMap.MONITOR_VIEW_STATUS_TIMELINE ] as Route } /> @@ -538,7 +539,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.MONITOR_VIEW_INCIDENTS + PageMap.MONITOR_VIEW_INCIDENTS ] as Route } /> @@ -556,7 +557,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.MONITOR_VIEW_CUSTOM_FIELDS + PageMap.MONITOR_VIEW_CUSTOM_FIELDS ] as Route } /> @@ -736,7 +737,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_DELETE + PageMap.STATUS_PAGE_VIEW_DELETE ] as Route } /> @@ -754,7 +755,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_BRANDING + PageMap.STATUS_PAGE_VIEW_BRANDING ] as Route } /> @@ -772,7 +773,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_CUSTOM_HTML_CSS + PageMap.STATUS_PAGE_VIEW_CUSTOM_HTML_CSS ] as Route } /> @@ -790,7 +791,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_ADVANCED_OPTIONS + PageMap.STATUS_PAGE_VIEW_ADVANCED_OPTIONS ] as Route } /> @@ -808,7 +809,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_CUSTOM_FIELDS + PageMap.STATUS_PAGE_VIEW_CUSTOM_FIELDS ] as Route } /> @@ -825,7 +826,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_OWNERS + PageMap.STATUS_PAGE_VIEW_OWNERS ] as Route } /> @@ -857,7 +858,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_EMAIL_SUBSCRIBERS + PageMap.STATUS_PAGE_VIEW_EMAIL_SUBSCRIBERS ] as Route } /> @@ -875,8 +876,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .STATUS_PAGE_VIEW_AUTHENTICATION_SETTINGS + PageMap + .STATUS_PAGE_VIEW_AUTHENTICATION_SETTINGS ] as Route } /> @@ -894,7 +895,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_CUSTOM_SMTP + PageMap.STATUS_PAGE_VIEW_CUSTOM_SMTP ] as Route } /> @@ -912,7 +913,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_SETTINGS + PageMap.STATUS_PAGE_VIEW_SETTINGS ] as Route } /> @@ -930,7 +931,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_PRIVATE_USERS + PageMap.STATUS_PAGE_VIEW_PRIVATE_USERS ] as Route } /> @@ -948,7 +949,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_SMS_SUBSCRIBERS + PageMap.STATUS_PAGE_VIEW_SMS_SUBSCRIBERS ] as Route } /> @@ -966,7 +967,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_HEADER_STYLE + PageMap.STATUS_PAGE_VIEW_HEADER_STYLE ] as Route } /> @@ -984,7 +985,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_FOOTER_STYLE + PageMap.STATUS_PAGE_VIEW_FOOTER_STYLE ] as Route } /> @@ -1002,7 +1003,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_NAVBAR_STYLE + PageMap.STATUS_PAGE_VIEW_NAVBAR_STYLE ] as Route } /> @@ -1020,7 +1021,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_WEBHOOK_SUBSCRIBERS + PageMap.STATUS_PAGE_VIEW_WEBHOOK_SUBSCRIBERS ] as Route } /> @@ -1038,7 +1039,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_EMBEDDED + PageMap.STATUS_PAGE_VIEW_EMBEDDED ] as Route } /> @@ -1056,7 +1057,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_RESOURCES + PageMap.STATUS_PAGE_VIEW_RESOURCES ] as Route } /> @@ -1074,7 +1075,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_DOMAINS + PageMap.STATUS_PAGE_VIEW_DOMAINS ] as Route } /> @@ -1091,7 +1092,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_GROUPS + PageMap.STATUS_PAGE_VIEW_GROUPS ] as Route } /> @@ -1109,7 +1110,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.STATUS_PAGE_VIEW_ANNOUNCEMENTS + PageMap.STATUS_PAGE_VIEW_ANNOUNCEMENTS ] as Route } /> @@ -1177,7 +1178,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.INCIDENT_VIEW_STATE_TIMELINE + PageMap.INCIDENT_VIEW_STATE_TIMELINE ] as Route } /> @@ -1194,7 +1195,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.INCIDENT_INTERNAL_NOTE + PageMap.INCIDENT_INTERNAL_NOTE ] as Route } /> @@ -1212,7 +1213,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.INCIDENT_VIEW_CUSTOM_FIELDS + PageMap.INCIDENT_VIEW_CUSTOM_FIELDS ] as Route } /> @@ -1230,8 +1231,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS + PageMap + .ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS ] as Route } /> @@ -1279,7 +1280,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SCHEDULED_MAINTENANCE_EVENTS + PageMap.SCHEDULED_MAINTENANCE_EVENTS ] as Route } /> @@ -1297,7 +1298,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.ONGOING_SCHEDULED_MAINTENANCE_EVENTS + PageMap.ONGOING_SCHEDULED_MAINTENANCE_EVENTS ] as Route } /> @@ -1315,7 +1316,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SCHEDULED_MAINTENANCE_VIEW + PageMap.SCHEDULED_MAINTENANCE_VIEW ] as Route } /> @@ -1333,8 +1334,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS + PageMap + .SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS ] as Route } /> @@ -1352,7 +1353,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE + PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE ] as Route } /> @@ -1370,7 +1371,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SCHEDULED_MAINTENANCE_VIEW_OWNERS + PageMap.SCHEDULED_MAINTENANCE_VIEW_OWNERS ] as Route } /> @@ -1388,8 +1389,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .SCHEDULED_MAINTENANCE_VIEW_STATE_TIMELINE + PageMap + .SCHEDULED_MAINTENANCE_VIEW_STATE_TIMELINE ] as Route } /> @@ -1407,7 +1408,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SCHEDULED_MAINTENANCE_INTERNAL_NOTE + PageMap.SCHEDULED_MAINTENANCE_INTERNAL_NOTE ] as Route } /> @@ -1425,7 +1426,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE + PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE ] as Route } /> @@ -1482,6 +1483,21 @@ const App: FunctionComponent = () => { } /> + + + } + /> + { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_MONITORS_STATUS + PageMap.SETTINGS_MONITORS_STATUS ] as Route } /> @@ -1543,7 +1559,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_INCIDENTS_STATE + PageMap.SETTINGS_INCIDENTS_STATE ] as Route } /> @@ -1561,7 +1577,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_SCHEDULED_MAINTENANCE_STATE + PageMap.SETTINGS_SCHEDULED_MAINTENANCE_STATE ] as Route } /> @@ -1589,7 +1605,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_INCIDENTS_SEVERITY + PageMap.SETTINGS_INCIDENTS_SEVERITY ] as Route } /> @@ -1659,7 +1675,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_MONITOR_CUSTOM_FIELDS + PageMap.SETTINGS_MONITOR_CUSTOM_FIELDS ] as Route } /> @@ -1677,7 +1693,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_STATUS_PAGE_CUSTOM_FIELDS + PageMap.SETTINGS_STATUS_PAGE_CUSTOM_FIELDS ] as Route } /> @@ -1695,8 +1711,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .SETTINGS_SCHEDULED_MAINTENANCE_CUSTOM_FIELDS + PageMap + .SETTINGS_SCHEDULED_MAINTENANCE_CUSTOM_FIELDS ] as Route } /> @@ -1714,7 +1730,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_INCIDENT_CUSTOM_FIELDS + PageMap.SETTINGS_INCIDENT_CUSTOM_FIELDS ] as Route } /> @@ -1732,8 +1748,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .SETTINGS_ON_CALL_DUTY_POLICY_CUSTOM_FIELDS + PageMap + .SETTINGS_ON_CALL_DUTY_POLICY_CUSTOM_FIELDS ] as Route } /> @@ -1763,7 +1779,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.SETTINGS_BILLING_INVOICES + PageMap.SETTINGS_BILLING_INVOICES ] as Route } /> @@ -1843,7 +1859,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.ON_CALL_DUTY_EXECUTION_LOGS + PageMap.ON_CALL_DUTY_EXECUTION_LOGS ] as Route } /> @@ -1861,7 +1877,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.ON_CALL_DUTY_EXECUTION_LOGS_TIMELINE + PageMap.ON_CALL_DUTY_EXECUTION_LOGS_TIMELINE ] as Route } /> @@ -1894,7 +1910,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.ON_CALL_DUTY_POLICY_VIEW + PageMap.ON_CALL_DUTY_POLICY_VIEW ] as Route } /> @@ -1912,7 +1928,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE + PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE ] as Route } /> @@ -1930,7 +1946,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION + PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION ] as Route } /> @@ -1948,8 +1964,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS + PageMap + .ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS ] as Route } /> @@ -1967,8 +1983,8 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap - .ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOG_VIEW + PageMap + .ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOG_VIEW ] as Route } /> @@ -2080,7 +2096,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.USER_SETTINGS_ON_CALL_LOGS + PageMap.USER_SETTINGS_ON_CALL_LOGS ] as Route } /> @@ -2098,7 +2114,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.USER_SETTINGS_ON_CALL_LOGS_TIMELINE + PageMap.USER_SETTINGS_ON_CALL_LOGS_TIMELINE ] as Route } /> @@ -2116,7 +2132,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.USER_SETTINGS_NOTIFICATION_SETTINGS + PageMap.USER_SETTINGS_NOTIFICATION_SETTINGS ] as Route } /> @@ -2134,7 +2150,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.USER_SETTINGS_NOTIFICATION_METHODS + PageMap.USER_SETTINGS_NOTIFICATION_METHODS ] as Route } /> @@ -2152,7 +2168,7 @@ const App: FunctionComponent = () => { {...commonPageProps} pageRoute={ RouteMap[ - PageMap.USER_SETTINGS_ON_CALL_RULES + PageMap.USER_SETTINGS_ON_CALL_RULES ] as Route } /> diff --git a/Dashboard/src/Pages/Settings/EmailLog.tsx b/Dashboard/src/Pages/Settings/EmailLog.tsx new file mode 100644 index 0000000000..48e0467b29 --- /dev/null +++ b/Dashboard/src/Pages/Settings/EmailLog.tsx @@ -0,0 +1,204 @@ +import Route from 'Common/Types/API/Route'; +import React, { FunctionComponent, ReactElement, useState } from 'react'; +import PageMap from '../../Utils/PageMap'; +import RouteMap, { RouteUtil } from '../../Utils/RouteMap'; +import PageComponentProps from '../PageComponentProps'; +import FieldType from 'CommonUI/src/Components/Types/FieldType'; +import IconProp from 'Common/Types/Icon/IconProp'; +import EmailLog from 'Model/Models/EmailLog'; +import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable'; +import DashboardNavigation from '../../Utils/Navigation'; +import { JSONObject } from 'Common/Types/JSON'; +import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button'; +import DashboardSideMenu from './SideMenu'; +import Page from 'CommonUI/src/Components/Page/Page'; +import Pill from 'CommonUI/src/Components/Pill/Pill'; +import EmailStatus from 'Common/Types/Mail/MailStatus'; +import { Green, Red } from 'Common/Types/BrandColors'; +import Columns from 'CommonUI/src/Components/ModelTable/Columns'; +import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal'; + +const EmailLogs: FunctionComponent = ( + _props: PageComponentProps +): ReactElement => { + const [showViewEmailTextModal, setShowViewEmailTextModal] = + useState(false); + const [EmailText, setEmailText] = useState(''); + const [EmailModelTitle, setEmailModalTitle] = useState(''); + + const modelTableColumns: Columns = [ + { + field: { + _id: true, + }, + title: 'Log ID', + type: FieldType.Text, + isFilterable: true, + }, + { + field: { + toEmail: true, + }, + isFilterable: true, + + title: 'To Email', + type: FieldType.Email, + }, + { + field: { + fromEmail: true, + }, + isFilterable: true, + + title: 'To Email', + type: FieldType.Email, + }, + { + field: { + createdAt: true, + }, + title: 'Sent at', + type: FieldType.DateTime, + isFilterable: true, + }, + { + field: { + status: true, + }, + title: 'Status', + type: FieldType.Text, + getElement: (item: JSONObject): ReactElement => { + if (item['status']) { + return ( + + ); + } + + return <>; + }, + isFilterable: true, + }, + ]; + + + return ( + } + > + <> + + modelType={EmailLog} + id="Email-logs-table" + isDeleteable={false} + isEditable={false} + isCreateable={false} + name="Email Logs" + query={{ + projectId: + DashboardNavigation.getProjectId()?.toString(), + }} + selectMoreFields={{ + subject: true, + statusMessage: true, + }} + actionButtons={[ + { + title: 'View Email Text', + buttonStyleType: ButtonStyleType.NORMAL, + icon: IconProp.List, + onClick: async ( + item: JSONObject, + onCompleteAction: Function + ) => { + setEmailText( + JSON.stringify(item['subject']) as string + ); + + setEmailModalTitle('Subject of Email Message'); + setShowViewEmailTextModal(true); + + onCompleteAction(); + }, + }, + { + title: 'View Status Message', + buttonStyleType: ButtonStyleType.NORMAL, + icon: IconProp.Error, + onClick: async ( + item: JSONObject, + onCompleteAction: Function + ) => { + setEmailText(item['statusMessage'] as string); + + setEmailModalTitle('Status Message'); + setShowViewEmailTextModal(true); + + onCompleteAction(); + }, + }, + ]} + isViewable={false} + cardProps={{ + icon: IconProp.Logs, + title: 'Email Logs', + description: + 'Logs of all the emails sent by this project in the last 30 days.', + }} + noItemsMessage={ + 'Looks like no email is sent by this project in the last 30 days.' + } + showRefreshButton={true} + showFilterButton={true} + columns={modelTableColumns} + /> + + {showViewEmailTextModal && ( + + {EmailText} + + } + onSubmit={() => { + setShowViewEmailTextModal(false); + }} + submitButtonText="Close" + submitButtonType={ButtonStyleType.NORMAL} + /> + )} + + + ); +}; + +export default EmailLogs; diff --git a/Dashboard/src/Pages/Settings/SideMenu.tsx b/Dashboard/src/Pages/Settings/SideMenu.tsx index fa546098b0..53a02c9055 100644 --- a/Dashboard/src/Pages/Settings/SideMenu.tsx +++ b/Dashboard/src/Pages/Settings/SideMenu.tsx @@ -47,7 +47,7 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { title: 'Custom Fields', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap.SETTINGS_MONITOR_CUSTOM_FIELDS + PageMap.SETTINGS_MONITOR_CUSTOM_FIELDS ] as Route ), }} @@ -60,7 +60,7 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { title: 'Custom Fields', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap.SETTINGS_STATUS_PAGE_CUSTOM_FIELDS + PageMap.SETTINGS_STATUS_PAGE_CUSTOM_FIELDS ] as Route ), }} @@ -73,8 +73,8 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { title: 'Custom Fields', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap - .SETTINGS_ON_CALL_DUTY_POLICY_CUSTOM_FIELDS + PageMap + .SETTINGS_ON_CALL_DUTY_POLICY_CUSTOM_FIELDS ] as Route ), }} @@ -96,7 +96,7 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { title: 'Incident Severity', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap.SETTINGS_INCIDENTS_SEVERITY + PageMap.SETTINGS_INCIDENTS_SEVERITY ] as Route ), }} @@ -107,7 +107,7 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { title: 'Custom Fields', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap.SETTINGS_INCIDENT_CUSTOM_FIELDS + PageMap.SETTINGS_INCIDENT_CUSTOM_FIELDS ] as Route ), }} @@ -127,7 +127,7 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { title: 'Event State', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap.SETTINGS_SCHEDULED_MAINTENANCE_STATE + PageMap.SETTINGS_SCHEDULED_MAINTENANCE_STATE ] as Route ), }} @@ -138,8 +138,8 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { title: 'Custom Fields', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap - .SETTINGS_SCHEDULED_MAINTENANCE_CUSTOM_FIELDS + PageMap + .SETTINGS_SCHEDULED_MAINTENANCE_CUSTOM_FIELDS ] as Route ), }} @@ -194,6 +194,15 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { }} icon={IconProp.Call} /> + { title: 'Invoices', to: RouteUtil.populateRouteParams( RouteMap[ - PageMap.SETTINGS_BILLING_INVOICES + PageMap.SETTINGS_BILLING_INVOICES ] as Route ), }} diff --git a/Dashboard/src/Utils/PageMap.ts b/Dashboard/src/Utils/PageMap.ts index 642406d8cb..57a2dd41d7 100644 --- a/Dashboard/src/Utils/PageMap.ts +++ b/Dashboard/src/Utils/PageMap.ts @@ -168,6 +168,7 @@ enum PageMap { // SMS and Call SETTINGS_CALL_SMS = 'SETTINGS_CALL_SMS', SETTINGS_SMS_LOGS = 'SETTINGS_SMS_LOGS', + SETTINGS_EMAIL_LOGS = 'SETTINGS_EMAIL_LOGS', SETTINGS_CALL_LOGS = 'SETTINGS_CALL_LOGS', } diff --git a/Dashboard/src/Utils/RouteMap.ts b/Dashboard/src/Utils/RouteMap.ts index 6f7162a1aa..84560e9976 100644 --- a/Dashboard/src/Utils/RouteMap.ts +++ b/Dashboard/src/Utils/RouteMap.ts @@ -356,6 +356,10 @@ const RouteMap: Dictionary = { `/dashboard/${RouteParams.ProjectID}/settings/sms-logs` ), + [PageMap.SETTINGS_EMAIL_LOGS]: new Route( + `/dashboard/${RouteParams.ProjectID}/settings/email-logs` + ), + [PageMap.SETTINGS_CALL_LOGS]: new Route( `/dashboard/${RouteParams.ProjectID}/settings/call-logs` ), diff --git a/DashboardAPI/Index.ts b/DashboardAPI/Index.ts index ddb5e1d5d6..1908e67a38 100755 --- a/DashboardAPI/Index.ts +++ b/DashboardAPI/Index.ts @@ -85,6 +85,11 @@ import SmsLogService, { Service as SmsLogServiceType, } from 'CommonServer/Services/SmsLogService'; +import EmailLog from 'Model/Models/EmailLog'; +import EmailLogService, { + Service as EmailLogServiceType, +} from 'CommonServer/Services/EmailLogService'; + import CallLog from 'Model/Models/CallLog'; import CallLogService, { Service as CallLogServiceType, @@ -295,6 +300,8 @@ import IncidentCustomFieldService, { Service as IncidentCustomFieldServiceType, } from 'CommonServer/Services/IncidentCustomFieldService'; + + import OnCallDutyPolicyExecutionLogTimeline from 'Model/Models/OnCallDutyPolicyExecutionLogTimeline'; import OnCallDutyPolicyExecutionLogTimelineService, { Service as OnCallDutyPolicyExecutionLogTimelineServiceType, @@ -719,6 +726,11 @@ app.use( new BaseAPI(SmsLog, SmsLogService).getRouter() ); +app.use( + `/${APP_NAME.toLocaleLowerCase()}`, + new BaseAPI(EmailLog, EmailLogService).getRouter() +); + app.use( `/${APP_NAME.toLocaleLowerCase()}`, new BaseAPI( diff --git a/Model/Models/EmailLog.ts b/Model/Models/EmailLog.ts index abc361a59b..064099c205 100644 --- a/Model/Models/EmailLog.ts +++ b/Model/Models/EmailLog.ts @@ -1,46 +1,235 @@ -import { Column, Entity } from 'typeorm'; +import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm'; import BaseModel from 'Common/Models/BaseModel'; -import User from './User'; import Project from './Project'; +import CrudApiEndpoint from 'Common/Types/Database/CrudApiEndpoint'; +import MailStatus from 'Common/Types/Mail/MailStatus'; +import Route from 'Common/Types/API/Route'; +import TableColumnType from 'Common/Types/Database/TableColumnType'; +import TableColumn from 'Common/Types/Database/TableColumn'; +import ColumnType from 'Common/Types/Database/ColumnType'; +import ObjectID from 'Common/Types/ObjectID'; +import ColumnLength from 'Common/Types/Database/ColumnLength'; +import TableAccessControl from 'Common/Types/Database/AccessControl/TableAccessControl'; +import Permission from 'Common/Types/Permission'; +import ColumnAccessControl from 'Common/Types/Database/AccessControl/ColumnAccessControl'; +import TenantColumn from 'Common/Types/Database/TenantColumn'; +import TableMetadata from 'Common/Types/Database/TableMetadata'; +import EnableWorkflow from 'Common/Types/Model/EnableWorkflow'; +import IconProp from 'Common/Types/Icon/IconProp'; +import EnableDocumentation from 'Common/Types/Model/EnableDocumentation'; import Email from 'Common/Types/Email'; -import EmailTemplateType from 'Common/Types/Email/EmailTemplateType'; -import OperationResult from 'Common/Types/Operation/OperationResult'; -import Hostname from 'Common/Types/API/Hostname'; +@EnableDocumentation() +@TenantColumn('projectId') +@TableAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + delete: [], + update: [], +}) +@CrudApiEndpoint(new Route('/email-log')) @Entity({ name: 'EmailLog', }) +@EnableWorkflow({ + create: true, + delete: false, + update: false, + read: true, +}) +@TableMetadata({ + tableName: 'EmailLog', + singularName: 'Email Log', + pluralName: 'Email Logs', + icon: IconProp.Email, + tableDescription: + 'Logs of all the Email sent out to all users and subscribers for this project.', +}) export default class EmailLog extends BaseModel { - @Column() - public fromEmail?: Email = undefined; + @ColumnAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + update: [], + }) + @TableColumn({ + manyToOneRelationColumn: 'projectId', + type: TableColumnType.Entity, + modelType: Project, + title: 'Project', + description: + 'Relation to Project Resource in which this object belongs', + }) + @ManyToOne( + (_type: string) => { + return Project; + }, + { + eager: false, + nullable: true, + onDelete: 'CASCADE', + orphanedRowAction: 'nullify', + } + ) + @JoinColumn({ name: 'projectId' }) + public project?: Project = undefined; - @Column() - public fromName?: string = undefined; + @ColumnAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + update: [], + }) + @Index() + @TableColumn({ + type: TableColumnType.ObjectID, + required: true, + canReadOnRelationQuery: true, + title: 'Project ID', + description: + 'ID of your OneUptime Project in which this object belongs', + }) + @Column({ + type: ColumnType.ObjectID, + nullable: false, + transformer: ObjectID.getDatabaseTransformer(), + }) + public projectId?: ObjectID = undefined; - @Column() - public project?: Project; - - @Column() + @ColumnAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + update: [], + }) + @Index() + @TableColumn({ + required: true, + type: TableColumnType.Email, + title: 'To Email', + description: 'Email address where the mail was sent', + canReadOnRelationQuery: false, + }) + @Column({ + nullable: false, + type: ColumnType.Email, + length: ColumnLength.Email, + transformer: Email.getDatabaseTransformer(), + }) public toEmail?: Email = undefined; - @Column() + @ColumnAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + update: [], + }) + @Index() + @TableColumn({ + required: true, + type: TableColumnType.Email, + title: 'From Email', + description: 'Email address where the mail was sent from', + canReadOnRelationQuery: false, + }) + @Column({ + nullable: false, + type: ColumnType.Email, + length: ColumnLength.Email, + transformer: Email.getDatabaseTransformer(), + }) + public fromEmail?: Email = undefined; + + @ColumnAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + update: [], + }) + @TableColumn({ + required: true, + type: TableColumnType.LongText, + title: 'Email Subject', + description: 'Subject of the email sent', + canReadOnRelationQuery: false, + }) + @Column({ + nullable: false, + type: ColumnType.LongText, + length: ColumnLength.LongText, + }) public subject?: string = undefined; - @Column() - public body?: string = undefined; + @ColumnAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + update: [], + }) + @TableColumn({ + required: false, + type: TableColumnType.LongText, + title: 'Status Message', + description: 'Status Message (if any)', + canReadOnRelationQuery: false, + }) + @Column({ + nullable: true, + type: ColumnType.LongText, + length: ColumnLength.LongText, + }) + public statusMessage?: string = undefined; - @Column() - public templateType?: EmailTemplateType; - - @Column() - public status?: OperationResult; - - @Column() - public errorDescription?: string = undefined; - - @Column() - public smtpHost?: Hostname; - - @Column() - public deletedByUser?: User; + @ColumnAccessControl({ + create: [], + read: [ + Permission.ProjectOwner, + Permission.ProjectAdmin, + Permission.ProjectMember, + Permission.CanReadEmailLog, + ], + update: [], + }) + @TableColumn({ + required: true, + type: TableColumnType.ShortText, + title: 'Status of the SMS', + description: 'Status of the SMS sent', + canReadOnRelationQuery: false, + }) + @Column({ + nullable: false, + type: ColumnType.ShortText, + length: ColumnLength.ShortText, + }) + public status?: MailStatus = undefined; } diff --git a/Model/Models/Index.ts b/Model/Models/Index.ts index a574e44947..563282d75f 100644 --- a/Model/Models/Index.ts +++ b/Model/Models/Index.ts @@ -111,6 +111,7 @@ import DataMigration from './DataMigration'; // Short link. import ShortLink from './ShortLink'; +import EmailLog from './EmailLog'; export default [ User, @@ -194,6 +195,7 @@ export default [ SmsLog, CallLog, + EmailLog, UserEmail, UserSms, @@ -207,4 +209,6 @@ export default [ DataMigration, ShortLink, + + ];