mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 08:42:13 +02:00
Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
407fc1240a | ||
|
|
2bc307c564 | ||
|
|
c42790e6f2 | ||
|
|
ecdbea2aab | ||
|
|
7cb98456b1 | ||
|
|
4f80317b14 | ||
|
|
7a589d65a3 | ||
|
|
acd5d04ee9 | ||
|
|
998c85e393 | ||
|
|
70478bd1fa | ||
|
|
4c06feeb50 | ||
|
|
2acd6d5ce0 | ||
|
|
56e7c0c7d0 | ||
|
|
7948070be6 | ||
|
|
a9139fcca0 | ||
|
|
99b3dc65a7 | ||
|
|
599c7a175e | ||
|
|
0a03dc652c | ||
|
|
53f72c2192 | ||
|
|
53334ad3dc | ||
|
|
4d7ddf7be1 | ||
|
|
9dfdd0841f | ||
|
|
7f662291e4 | ||
|
|
9bbd32424e | ||
|
|
41b5fe3a19 | ||
|
|
d35195a591 | ||
|
|
ee229d3711 | ||
|
|
9eb12a5348 | ||
|
|
0bb8343f0b | ||
|
|
d5c13f5c26 | ||
|
|
9e57fe1531 | ||
|
|
8b10e0d9f0 | ||
|
|
45d7dc90b3 | ||
|
|
da2683391d | ||
|
|
ea50830dae | ||
|
|
985a7ca973 | ||
|
|
2c649bed07 | ||
|
|
78fad54d6a | ||
|
|
05c583fd81 | ||
|
|
b99912abd6 | ||
|
|
c3a5a8a4e8 | ||
|
|
ad451fd9c9 | ||
|
|
ce31e0cfff | ||
|
|
545dcea3e8 | ||
|
|
29accb2e6f | ||
|
|
8cb8a1ed72 | ||
|
|
a8beed5c5c | ||
|
|
8d59fdc732 | ||
|
|
a7fe18fd65 | ||
|
|
e80835c380 | ||
|
|
bbd60075fa | ||
|
|
622bb87b89 | ||
|
|
3d3c9876eb | ||
|
|
ea588be0f7 | ||
|
|
0fe1779ce4 | ||
|
|
71f2d3b87a | ||
|
|
8d0670d05c | ||
|
|
1b6eccfb36 | ||
|
|
9a7c2cedf5 | ||
|
|
55b9a6bf9f | ||
|
|
9ddca843c8 | ||
|
|
2a1786147b | ||
|
|
1bc572c4be | ||
|
|
f961796c7c | ||
|
|
ff1f564527 | ||
|
|
6ef61221bf | ||
|
|
1b7d17fb9e | ||
|
|
04f7d1f8bf | ||
|
|
2edbe0df08 | ||
|
|
a96dc90104 | ||
|
|
3e8b966dc8 | ||
|
|
1ee94f10c4 | ||
|
|
74866edadb | ||
|
|
e402ebc14c | ||
|
|
4297cead16 | ||
|
|
da021bbd10 | ||
|
|
ab48de6e9b | ||
|
|
314b905e8f | ||
|
|
db60c0fefb | ||
|
|
713d9464a2 | ||
|
|
84cc4a35d4 | ||
|
|
6a64b8658a | ||
|
|
b8ee827068 | ||
|
|
8fea1b6e3d | ||
|
|
96934f5f22 | ||
|
|
a2ad4d30e7 | ||
|
|
0623588019 | ||
|
|
f1d087da44 | ||
|
|
5dca4a0fd3 | ||
|
|
7d085274de | ||
|
|
1da9184a14 | ||
|
|
12c429597c | ||
|
|
314a9ef61c | ||
|
|
5961504577 | ||
|
|
a7757e547c |
@@ -28,13 +28,17 @@ import Permission, {
|
||||
UserPermission,
|
||||
UserTenantAccessPermission,
|
||||
} from '../Types/Permission';
|
||||
import { ColumnAccessControl } from '../Types/Database/AccessControl/AccessControl';
|
||||
import {
|
||||
ColumnAccessControl,
|
||||
ColumnBillingAccessControl,
|
||||
} from '../Types/Database/AccessControl/AccessControl';
|
||||
import { getColumnAccessControlForAllColumns } from '../Types/Database/AccessControl/ColumnAccessControl';
|
||||
import BadDataException from '../Types/Exception/BadDataException';
|
||||
import { PlanSelect } from '../Types/Billing/SubscriptionPlan';
|
||||
import { EnableWorkflowOn } from '../Types/Model/EnableWorkflow';
|
||||
import IconProp from '../Types/Icon/IconProp';
|
||||
import Text from '../Types/Text';
|
||||
import { getColumnBillingAccessControlForAllColumns } from '../Types/Database/AccessControl/ColumnBillingAccessControl';
|
||||
|
||||
export type DbTypes =
|
||||
| string
|
||||
@@ -202,6 +206,14 @@ export default class BaseModel extends BaseEntity {
|
||||
return dictionary[columnName] as TableColumnMetadata;
|
||||
}
|
||||
|
||||
public getColumnBillingAccessControl(
|
||||
columnName: string
|
||||
): ColumnBillingAccessControl {
|
||||
const dictionary: Dictionary<ColumnBillingAccessControl> =
|
||||
getColumnBillingAccessControlForAllColumns(this);
|
||||
return dictionary[columnName] as ColumnBillingAccessControl;
|
||||
}
|
||||
|
||||
public getColumnAccessControlFor(
|
||||
columnName: string
|
||||
): ColumnAccessControl | null {
|
||||
|
||||
11
Common/Types/Call/CallRequest.ts
Normal file
11
Common/Types/Call/CallRequest.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export interface Say {
|
||||
sayMessage: string;
|
||||
}
|
||||
|
||||
export enum CallAction {
|
||||
Hangup = 'Hangup',
|
||||
}
|
||||
|
||||
export default interface CallRequest {
|
||||
data: Array<Say | CallAction>;
|
||||
}
|
||||
9
Common/Types/Call/CallStatus.ts
Normal file
9
Common/Types/Call/CallStatus.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
enum CallStatus {
|
||||
Success = 'Success',
|
||||
Error = 'Error',
|
||||
LowBalance = 'Low Balance',
|
||||
MissedCall = 'Missed Call',
|
||||
Busy = 'Busy',
|
||||
}
|
||||
|
||||
export default CallStatus;
|
||||
@@ -17,3 +17,9 @@ export interface BillingAccessControl {
|
||||
update: PlanSelect;
|
||||
delete: PlanSelect;
|
||||
}
|
||||
|
||||
export interface ColumnBillingAccessControl {
|
||||
create: PlanSelect;
|
||||
read: PlanSelect;
|
||||
update: PlanSelect;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'reflect-metadata';
|
||||
import BaseModel from '../../../Models/BaseModel';
|
||||
import Dictionary from '../../Dictionary';
|
||||
import { ReflectionMetadataType } from '../../Reflection';
|
||||
import { ColumnBillingAccessControl } from './AccessControl';
|
||||
|
||||
const accessControlSymbol: Symbol = Symbol('ColumnBillingAccessControl');
|
||||
|
||||
export default (
|
||||
accessControl: ColumnBillingAccessControl
|
||||
): ReflectionMetadataType => {
|
||||
return Reflect.metadata(accessControlSymbol, accessControl);
|
||||
};
|
||||
|
||||
export const getColumnBillingAccessControl: Function = (
|
||||
target: BaseModel,
|
||||
propertyKey: string
|
||||
): ColumnBillingAccessControl => {
|
||||
return Reflect.getMetadata(
|
||||
accessControlSymbol,
|
||||
target,
|
||||
propertyKey
|
||||
) as ColumnBillingAccessControl;
|
||||
};
|
||||
|
||||
export const getColumnBillingAccessControlForAllColumns: Function = <
|
||||
T extends BaseModel
|
||||
>(
|
||||
target: T
|
||||
): Dictionary<ColumnBillingAccessControl> => {
|
||||
const dictonary: Dictionary<ColumnBillingAccessControl> = {};
|
||||
const keys: Array<string> = Object.keys(target);
|
||||
|
||||
for (const key of keys) {
|
||||
if (Reflect.getMetadata(accessControlSymbol, target, key)) {
|
||||
dictonary[key] = Reflect.getMetadata(
|
||||
accessControlSymbol,
|
||||
target,
|
||||
key
|
||||
) as ColumnBillingAccessControl;
|
||||
}
|
||||
}
|
||||
|
||||
return dictonary;
|
||||
};
|
||||
@@ -19,6 +19,7 @@ export interface TableColumnMetadata {
|
||||
type: TableColumnType;
|
||||
canReadOnRelationQuery?: boolean;
|
||||
modelType?: { new (): BaseModel };
|
||||
forceGetDefaultValueOnCreate?: () => string | number | boolean; // overwrites any value that is being passed and generates a new one. Useful for generating OTPs, etc.
|
||||
}
|
||||
|
||||
export default (props: TableColumnMetadata): ReflectionMetadataType => {
|
||||
|
||||
@@ -32,6 +32,7 @@ enum EmailTemplateType {
|
||||
StatusPageOwnerAdded = 'StatusPageOwnerAdded.hbs',
|
||||
StatusPageOwnerAnnouncementPosted = 'StatusPageOwnerAnnouncementPosted.hbs',
|
||||
SimpleMessage = 'SimpleMessage.hbs',
|
||||
VerificationCode = 'VerificationCode.hbs',
|
||||
}
|
||||
|
||||
export default EmailTemplateType;
|
||||
|
||||
@@ -94,6 +94,12 @@ enum IconProp {
|
||||
TransparentCube = 'TransparentCube',
|
||||
Logs = 'Logs',
|
||||
Bolt = 'Bolt',
|
||||
BarsArrowUp = 'BarsArrowUp',
|
||||
BarsArrowDown = 'BarsArrowDown',
|
||||
Bell = 'Bell',
|
||||
BellRinging = 'BellRinging',
|
||||
AdjustmentVertical = 'AdjustmentVertical',
|
||||
AdjustmentHorizontal = 'AdjustmentHorizontal',
|
||||
}
|
||||
|
||||
export default IconProp;
|
||||
|
||||
@@ -22,6 +22,7 @@ import { BaseEntity } from 'typeorm';
|
||||
import EqualToOrNull from './Database/EqualToOrNull';
|
||||
import NotEqual from './Database/NotEqual';
|
||||
import { CheckOn, FilterType } from './Monitor/CriteriaFilter';
|
||||
import CallRequest from './Call/CallRequest';
|
||||
|
||||
export enum ObjectType {
|
||||
ObjectID = 'ObjectID',
|
||||
@@ -122,6 +123,7 @@ export type JSONValue =
|
||||
| Array<JSONValue>
|
||||
| Array<Permission>
|
||||
| Array<JSONValue>
|
||||
| CallRequest
|
||||
| undefined
|
||||
| null;
|
||||
|
||||
|
||||
7
Common/Types/NotificationRule/NotificationRuleType.ts
Normal file
7
Common/Types/NotificationRule/NotificationRuleType.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
enum NoticationRuleType {
|
||||
ON_CALL_INCIDENT_CREATED = 'When incident is created during on call',
|
||||
WHEN_USER_GOES_ON_CALL = 'When user goes on call',
|
||||
WHEN_USER_GOES_OFF_CALL = 'When user goes off call',
|
||||
}
|
||||
|
||||
export default NoticationRuleType;
|
||||
@@ -0,0 +1,7 @@
|
||||
enum OnCallDutyPolicyStatus {
|
||||
SuccessfullyAcknowledged = 'Successfully Acknowledged',
|
||||
FailedToAcknowledge = 'Failed to Acknowledge',
|
||||
Error = 'Error',
|
||||
}
|
||||
|
||||
export default OnCallDutyPolicyStatus;
|
||||
8
Common/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts
Normal file
8
Common/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
enum OnCallDutyPolicyStatus {
|
||||
SuccessfullyAcknowledged = 'Successfully Acknowledged',
|
||||
ExecutionInProgress = 'Execution in Progress',
|
||||
FailedToAcknowledge = 'Failed to Acknowledge',
|
||||
Error = 'Error',
|
||||
}
|
||||
|
||||
export default OnCallDutyPolicyStatus;
|
||||
@@ -63,6 +63,11 @@ enum Permission {
|
||||
CanEditMonitorCustomField = 'CanEditMonitorCustomField',
|
||||
CanReadMonitorCustomField = 'CanReadMonitorCustomField',
|
||||
|
||||
CanCreateOnCallDutyPolicyCustomField = 'CanCreateOnCallDutyPolicyCustomField',
|
||||
CanDeleteOnCallDutyPolicyCustomField = 'CanDeleteOnCallDutyPolicyCustomField',
|
||||
CanEditOnCallDutyPolicyCustomField = 'CanEditOnCallDutyPolicyCustomField',
|
||||
CanReadOnCallDutyPolicyCustomField = 'CanReadOnCallDutyPolicyCustomField',
|
||||
|
||||
CanCreateScheduledMaintenanceCustomField = 'CanCreateScheduledMaintenanceCustomField',
|
||||
CanDeleteScheduledMaintenanceCustomField = 'CanDeleteScheduledMaintenanceCustomField',
|
||||
CanEditScheduledMaintenanceCustomField = 'CanEditScheduledMaintenanceCustomField',
|
||||
@@ -73,11 +78,10 @@ enum Permission {
|
||||
CanEditMonitorProbe = 'CanEditMonitorProbe',
|
||||
CanReadMonitorProbe = 'CanReadMonitorProbe',
|
||||
|
||||
CanCreateSmsLog = 'CanCreateSmsLog',
|
||||
CanDeleteSmsLog = 'CanDeleteSmsLog',
|
||||
CanEditSmsLog = 'CanEditSmsLog',
|
||||
CanReadSmsLog = 'CanReadSmsLog',
|
||||
|
||||
CanReadCallLog = 'CanReadCallLog',
|
||||
|
||||
CanCreateIncidentOwnerTeam = 'CanCreateIncidentOwnerTeam',
|
||||
CanDeleteIncidentOwnerTeam = 'CanDeleteIncidentOwnerTeam',
|
||||
CanEditIncidentOwnerTeam = 'CanEditIncidentOwnerTeam',
|
||||
@@ -296,10 +300,31 @@ enum Permission {
|
||||
CanReadProjectStatusPage = 'CanReadProjectStatusPage',
|
||||
|
||||
// Resource Permissions (Team Permission)
|
||||
CanCreateProjectOnCallDuty = 'CanCreateProjectOnCallDuty',
|
||||
CanEditProjectOnCallDuty = 'CanEditProjectOnCallDuty',
|
||||
CanDeleteProjectOnCallDuty = 'CanDeleteProjectOnCallDuty',
|
||||
CanReadProjectOnCallDuty = 'CanReadProjectOnCallDuty',
|
||||
CanCreateProjectOnCallDutyPolicy = 'CanCreateProjectOnCallDutyPolicy',
|
||||
CanEditProjectOnCallDutyPolicy = 'CanEditProjectOnCallDutyPolicy',
|
||||
CanDeleteProjectOnCallDutyPolicy = 'CanDeleteProjectOnCallDutyPolicy',
|
||||
CanReadProjectOnCallDutyPolicy = 'CanReadProjectOnCallDutyPolicy',
|
||||
|
||||
CanReadProjectOnCallDutyPolicyExecutionLogTimeline = 'CanReadProjectOnCallDutyPolicyExecutionLogTimeline',
|
||||
CanReadProjectOnCallDutyPolicyExecutionLog = 'CanReadProjectOnCallDutyPolicyExecutionLog',
|
||||
|
||||
// Resource Permissions (Team Permission)
|
||||
CanCreateProjectOnCallDutyPolicyEscalationRule = 'CanCreateProjectOnCallDutyPolicyEscalationRule',
|
||||
CanEditProjectOnCallDutyPolicyEscalationRule = 'CanEditProjectOnCallDutyPolicyEscalationRule',
|
||||
CanDeleteProjectOnCallDutyPolicyEscalationRule = 'CanDeleteProjectOnCallDutyPolicyEscalationRule',
|
||||
CanReadProjectOnCallDutyPolicyEscalationRule = 'CanReadProjectOnCallDutyPolicyEscalationRule',
|
||||
|
||||
// Resource Permissions (Team Permission)
|
||||
CanCreateProjectOnCallDutyPolicyEscalationRuleUser = 'CanCreateProjectOnCallDutyPolicyEscalationRuleUser',
|
||||
CanEditProjectOnCallDutyPolicyEscalationRuleUser = 'CanEditProjectOnCallDutyPolicyEscalationRuleUser',
|
||||
CanDeleteProjectOnCallDutyPolicyEscalationRuleUser = 'CanDeleteProjectOnCallDutyPolicyEscalationRuleUser',
|
||||
CanReadProjectOnCallDutyPolicyEscalationRuleUser = 'CanReadProjectOnCallDutyPolicyEscalationRuleUser',
|
||||
|
||||
// Resource Permissions (Team Permission)
|
||||
CanCreateProjectOnCallDutyPolicyEscalationRuleTeam = 'CanCreateProjectOnCallDutyPolicyEscalationRuleTeam',
|
||||
CanEditProjectOnCallDutyPolicyEscalationRuleTeam = 'CanEditProjectOnCallDutyPolicyEscalationRuleTeam',
|
||||
CanDeleteProjectOnCallDutyPolicyEscalationRuleTeam = 'CanDeleteProjectOnCallDutyPolicyEscalationRuleTeam',
|
||||
CanReadProjectOnCallDutyPolicyEscalationRuleTeam = 'CanReadProjectOnCallDutyPolicyEscalationRuleTeam',
|
||||
|
||||
// Project SMTP Config (Team Permission)
|
||||
CanCreateProjectSMTPConfig = 'CanCreateProjectSMTPConfig',
|
||||
@@ -1446,32 +1471,163 @@ export class PermissionHelper {
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateProjectOnCallDuty,
|
||||
title: 'Can Create On-Call Duty',
|
||||
permission:
|
||||
Permission.CanReadProjectOnCallDutyPolicyExecutionLogTimeline,
|
||||
title: 'Can Read On-Call Duty Policy Execution Log Timeline',
|
||||
description:
|
||||
'This permission can read teams in on-call duty execution log timeline.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
|
||||
{
|
||||
permission:
|
||||
Permission.CanReadProjectOnCallDutyPolicyExecutionLog,
|
||||
title: 'Can Read On-Call Duty Policy Execution Log',
|
||||
description:
|
||||
'This permission can read teams in on-call duty execution log.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
|
||||
{
|
||||
permission:
|
||||
Permission.CanCreateProjectOnCallDutyPolicyEscalationRuleTeam,
|
||||
title: 'Can Create On-Call Duty Policy Escalation Rule',
|
||||
description:
|
||||
'This permission can create teams in on-call duty escalation rule this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanDeleteProjectOnCallDutyPolicyEscalationRuleTeam,
|
||||
title: 'Can Delete On-Call Duty Policy Escalation Rule Team',
|
||||
description:
|
||||
'This permission can delete teams in on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanEditProjectOnCallDutyPolicyEscalationRuleTeam,
|
||||
title: 'Can Edit On-Call Duty Policy Escalation Rule Team',
|
||||
description:
|
||||
'This permission can edit teams in on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanReadProjectOnCallDutyPolicyEscalationRuleTeam,
|
||||
title: 'Can Read On-Call Duty Policy Escalation Rule Team',
|
||||
description:
|
||||
'This permission can read teams in on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
|
||||
{
|
||||
permission:
|
||||
Permission.CanCreateProjectOnCallDutyPolicyEscalationRuleUser,
|
||||
title: 'Can Create On-Call Duty Policy Escalation Rule User',
|
||||
description:
|
||||
'This permission can create on-call duty escalation rule this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanDeleteProjectOnCallDutyPolicyEscalationRuleUser,
|
||||
title: 'Can Delete On-Call Duty Policy Escalation Rule User',
|
||||
description:
|
||||
'This permission can delete on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanEditProjectOnCallDutyPolicyEscalationRuleUser,
|
||||
title: 'Can Edit On-Call Duty Policy Escalation Rule User',
|
||||
description:
|
||||
'This permission can edit on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanReadProjectOnCallDutyPolicyEscalationRuleUser,
|
||||
title: 'Can Read On-Call Duty Policy Escalation Rule User',
|
||||
description:
|
||||
'This permission can read on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
|
||||
{
|
||||
permission:
|
||||
Permission.CanCreateProjectOnCallDutyPolicyEscalationRule,
|
||||
title: 'Can Create On-Call Duty Policy Escalation Rule',
|
||||
description:
|
||||
'This permission can create on-call duty escalation rule this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanDeleteProjectOnCallDutyPolicyEscalationRule,
|
||||
title: 'Can Delete On-Call Duty Policy Escalation Rule',
|
||||
description:
|
||||
'This permission can delete on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanEditProjectOnCallDutyPolicyEscalationRule,
|
||||
title: 'Can Edit On-Call Duty Policy Escalation Rule',
|
||||
description:
|
||||
'This permission can edit on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission:
|
||||
Permission.CanReadProjectOnCallDutyPolicyEscalationRule,
|
||||
title: 'Can Read On-Call Duty Policy Escalation Rule',
|
||||
description:
|
||||
'This permission can read on-call duty escalation rule of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateProjectOnCallDutyPolicy,
|
||||
title: 'Can Create On-Call Duty Policy',
|
||||
description:
|
||||
'This permission can create on-call duty this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanDeleteProjectOnCallDuty,
|
||||
title: 'Can Delete On-Call Duty',
|
||||
permission: Permission.CanDeleteProjectOnCallDutyPolicy,
|
||||
title: 'Can Delete On-Call Duty Policy',
|
||||
description:
|
||||
'This permission can delete on-call duty of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanEditProjectOnCallDuty,
|
||||
title: 'Can Edit On-Call Duty',
|
||||
permission: Permission.CanEditProjectOnCallDutyPolicy,
|
||||
title: 'Can Edit On-Call Duty Policy',
|
||||
description:
|
||||
'This permission can edit on-call duty of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanReadProjectOnCallDuty,
|
||||
title: 'Can Read On-Call Duty',
|
||||
permission: Permission.CanReadProjectOnCallDutyPolicy,
|
||||
title: 'Can Read On-Call Duty Policy',
|
||||
description:
|
||||
'This permission can read on-call duty of this project.',
|
||||
isAssignableToTenant: true,
|
||||
@@ -1541,6 +1697,39 @@ export class PermissionHelper {
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateOnCallDutyPolicyCustomField,
|
||||
title: 'Can Create On Call Policy Custom Field',
|
||||
description:
|
||||
'This permission can create On Call Policy Custom Field this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanDeleteOnCallDutyPolicyCustomField,
|
||||
title: 'Can Delete On Call Policy Custom Field',
|
||||
description:
|
||||
'This permission can delete On Call Policy Custom Field of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanEditOnCallDutyPolicyCustomField,
|
||||
title: 'Can Edit On Call Policy Custom Field',
|
||||
description:
|
||||
'This permission can edit On Call Policy Custom Field of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanReadOnCallDutyPolicyCustomField,
|
||||
title: 'Can Read On Call Policy Custom Field',
|
||||
description:
|
||||
'This permission can read On Call Policy Custom Field of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateMonitorCustomField,
|
||||
title: 'Can Create Monitor Custom Field',
|
||||
@@ -1673,29 +1862,6 @@ export class PermissionHelper {
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateSmsLog,
|
||||
title: 'Can Create SMS Log',
|
||||
description: 'This permission can create SMS Log this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanDeleteSmsLog,
|
||||
title: 'Can Delete SMS Log',
|
||||
description:
|
||||
'This permission can delete SMS Log of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanEditSmsLog,
|
||||
title: 'Can Edit SMS Log',
|
||||
description:
|
||||
'This permission can edit SMS Log of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanReadSmsLog,
|
||||
title: 'Can Read SMS Log',
|
||||
@@ -1705,6 +1871,15 @@ export class PermissionHelper {
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanReadCallLog,
|
||||
title: 'Can Read Call Log',
|
||||
description:
|
||||
'This permission can read Call Logs of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateMonitorProbe,
|
||||
title: 'Can Create Monitor Probe',
|
||||
|
||||
@@ -16,6 +16,22 @@ export default class Text {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static generateRandomNumber(length?: number): string {
|
||||
if (!length) {
|
||||
length = 10;
|
||||
}
|
||||
|
||||
let result: string = '';
|
||||
const characters: string = '12134567890';
|
||||
const charactersLength: number = characters.length;
|
||||
for (let i: number = 0; i < length; i++) {
|
||||
result += characters.charAt(
|
||||
Math.floor(Math.random() * charactersLength)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static convertNumberToWords(num: number): string {
|
||||
const words: Array<string> = [
|
||||
'first',
|
||||
|
||||
@@ -18,6 +18,7 @@ export enum ComponentInputType {
|
||||
Email = 'Email',
|
||||
CronTab = 'CronTab',
|
||||
Query = 'Database Query',
|
||||
Select = 'Database Select',
|
||||
BaseModel = 'Database Record',
|
||||
BaseModelArray = 'Database Records',
|
||||
JSONArray = 'List of JSON',
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class BaseModelComponent {
|
||||
placeholder: 'Example: {"columnName": "value", ...}',
|
||||
},
|
||||
{
|
||||
type: ComponentInputType.Query,
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
@@ -92,7 +92,7 @@ export default class BaseModelComponent {
|
||||
placeholder: 'Example: {"columnName": "value", ...}',
|
||||
},
|
||||
{
|
||||
type: ComponentInputType.Query,
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
@@ -288,7 +288,16 @@ export default class BaseModelComponent {
|
||||
iconProp: IconProp.Bolt,
|
||||
tableName: model.tableName!,
|
||||
componentType: ComponentType.Trigger,
|
||||
arguments: [],
|
||||
arguments: [
|
||||
{
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
id: 'select',
|
||||
placeholder: 'Example: {"columnName": true, ...}',
|
||||
},
|
||||
],
|
||||
returnValues: [
|
||||
{
|
||||
id: 'data',
|
||||
@@ -422,7 +431,16 @@ export default class BaseModelComponent {
|
||||
iconProp: IconProp.Bolt,
|
||||
tableName: model.tableName!,
|
||||
componentType: ComponentType.Trigger,
|
||||
arguments: [],
|
||||
arguments: [
|
||||
{
|
||||
type: ComponentInputType.Select,
|
||||
name: 'Select Fields',
|
||||
description: `Select on ${model.singularName}`,
|
||||
required: true,
|
||||
id: 'select',
|
||||
placeholder: 'Example: {"columnName": true, ...}',
|
||||
},
|
||||
],
|
||||
returnValues: [
|
||||
{
|
||||
id: 'data',
|
||||
|
||||
@@ -3,3 +3,4 @@ export const EVERY_DAY: string = '0 8 * * *';
|
||||
export const EVERY_HOUR: string = '1 * * * *';
|
||||
export const EVERY_FIVE_MINUTE: string = '*/5 * * * *';
|
||||
export const EVERY_FIVE_SECONDS: string = '*/5 * * * * *';
|
||||
export const EVERY_WEEK: string = '0 0 * * 0';
|
||||
|
||||
@@ -609,6 +609,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
projectId: statusPage.projectId!,
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
note: true,
|
||||
incidentId: true,
|
||||
},
|
||||
@@ -792,6 +793,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
projectId: statusPage.projectId!,
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
note: true,
|
||||
scheduledMaintenanceId: true,
|
||||
},
|
||||
@@ -1340,6 +1342,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
projectId: statusPage.projectId!,
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
note: true,
|
||||
scheduledMaintenanceId: true,
|
||||
},
|
||||
@@ -1696,6 +1699,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
projectId: statusPage.projectId!,
|
||||
},
|
||||
select: {
|
||||
createdAt: true,
|
||||
note: true,
|
||||
incidentId: true,
|
||||
},
|
||||
|
||||
122
CommonServer/API/UserCallAPI.ts
Normal file
122
CommonServer/API/UserCallAPI.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import UserCall from 'Model/Models/UserCall';
|
||||
import UserCallService, {
|
||||
Service as UserCallServiceType,
|
||||
} from '../Services/UserCallService';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import UserMiddleware from '../Middleware/UserAuthorization';
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
OneUptimeRequest,
|
||||
} from '../Utils/Express';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import Response from '../Utils/Response';
|
||||
import UserSMS from 'Model/Models/UserSMS';
|
||||
|
||||
export default class UserCallAPI extends BaseAPI<
|
||||
UserCall,
|
||||
UserCallServiceType
|
||||
> {
|
||||
public constructor() {
|
||||
super(UserCall, UserCallService);
|
||||
|
||||
this.router.post(
|
||||
`/user-call/verify`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
req = req as OneUptimeRequest;
|
||||
|
||||
if (!req.body.itemId) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid item ID')
|
||||
);
|
||||
}
|
||||
|
||||
if (!req.body.code) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid code')
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the code matches and verify the phone number.
|
||||
const item: UserSMS | null = await this.service.findOneById({
|
||||
id: req.body['itemId'],
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
userId: true,
|
||||
verificationCode: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Item not found')
|
||||
);
|
||||
}
|
||||
|
||||
//cehck user id
|
||||
|
||||
if (
|
||||
item.userId?.toString() !==
|
||||
(
|
||||
req as OneUptimeRequest
|
||||
)?.userAuthorization?.userId?.toString()
|
||||
) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid user ID')
|
||||
);
|
||||
}
|
||||
|
||||
if (item.verificationCode !== req.body['code']) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid code')
|
||||
);
|
||||
}
|
||||
|
||||
await this.service.updateOneById({
|
||||
id: item.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
data: {
|
||||
isVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
this.router.post(
|
||||
`/user-call/resend-verification-code`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
req = req as OneUptimeRequest;
|
||||
|
||||
if (!req.body.itemId) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid item ID')
|
||||
);
|
||||
}
|
||||
|
||||
await this.service.resendVerificationCode(req.body.itemId);
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
123
CommonServer/API/UserEmailAPI.ts
Normal file
123
CommonServer/API/UserEmailAPI.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import UserEmail from 'Model/Models/UserEmail';
|
||||
import UserEmailService, {
|
||||
Service as UserEmailServiceType,
|
||||
} from '../Services/UserEmailService';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
OneUptimeRequest,
|
||||
} from '../Utils/Express';
|
||||
import UserMiddleware from '../Middleware/UserAuthorization';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import Response from '../Utils/Response';
|
||||
|
||||
export default class UserEmailAPI extends BaseAPI<
|
||||
UserEmail,
|
||||
UserEmailServiceType
|
||||
> {
|
||||
public constructor() {
|
||||
super(UserEmail, UserEmailService);
|
||||
|
||||
this.router.post(
|
||||
`${new this.entityType().getCrudApiPath()?.toString()}/verify`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
req = req as OneUptimeRequest;
|
||||
|
||||
if (!req.body.itemId) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid item ID')
|
||||
);
|
||||
}
|
||||
|
||||
if (!req.body.code) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid code')
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the code matches and verify the email.
|
||||
const item: UserEmail | null = await this.service.findOneById({
|
||||
id: req.body['itemId'],
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
userId: true,
|
||||
verificationCode: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Item not found')
|
||||
);
|
||||
}
|
||||
|
||||
//cehck user id
|
||||
|
||||
if (
|
||||
item.userId?.toString() !==
|
||||
(
|
||||
req as OneUptimeRequest
|
||||
)?.userAuthorization?.userId?.toString()
|
||||
) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid user ID')
|
||||
);
|
||||
}
|
||||
|
||||
if (item.verificationCode !== req.body['code']) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid code')
|
||||
);
|
||||
}
|
||||
|
||||
await this.service.updateOneById({
|
||||
id: item.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
data: {
|
||||
isVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
this.router.post(
|
||||
`${new this.entityType()
|
||||
.getCrudApiPath()
|
||||
?.toString()}/resend-verification-code`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
req = req as OneUptimeRequest;
|
||||
|
||||
if (!req.body.itemId) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid item ID')
|
||||
);
|
||||
}
|
||||
|
||||
await this.service.resendVerificationCode(req.body.itemId);
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
118
CommonServer/API/UserSmsAPI.ts
Normal file
118
CommonServer/API/UserSmsAPI.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import UserSMS from 'Model/Models/UserSMS';
|
||||
import UserSMSService, {
|
||||
Service as UserSMSServiceType,
|
||||
} from '../Services/UserSmsService';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import UserMiddleware from '../Middleware/UserAuthorization';
|
||||
import {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
OneUptimeRequest,
|
||||
} from '../Utils/Express';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import Response from '../Utils/Response';
|
||||
|
||||
export default class UserSMSAPI extends BaseAPI<UserSMS, UserSMSServiceType> {
|
||||
public constructor() {
|
||||
super(UserSMS, UserSMSService);
|
||||
|
||||
this.router.post(
|
||||
`/user-sms/verify`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
req = req as OneUptimeRequest;
|
||||
|
||||
if (!req.body.itemId) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid item ID')
|
||||
);
|
||||
}
|
||||
|
||||
if (!req.body.code) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid code')
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the code matches and verify the phone number.
|
||||
const item: UserSMS | null = await this.service.findOneById({
|
||||
id: req.body['itemId'],
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
userId: true,
|
||||
verificationCode: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Item not found')
|
||||
);
|
||||
}
|
||||
|
||||
//cehck user id
|
||||
|
||||
if (
|
||||
item.userId?.toString() !==
|
||||
(
|
||||
req as OneUptimeRequest
|
||||
)?.userAuthorization?.userId?.toString()
|
||||
) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid user ID')
|
||||
);
|
||||
}
|
||||
|
||||
if (item.verificationCode !== req.body['code']) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid code')
|
||||
);
|
||||
}
|
||||
|
||||
await this.service.updateOneById({
|
||||
id: item.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
data: {
|
||||
isVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
this.router.post(
|
||||
`/user-sms/resend-verification-code`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
req = req as OneUptimeRequest;
|
||||
|
||||
if (!req.body.itemId) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid item ID')
|
||||
);
|
||||
}
|
||||
|
||||
await this.service.resendVerificationCode(req.body.itemId);
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,8 @@ export default class UserMiddleware {
|
||||
if (tenantId) {
|
||||
oneuptimeRequest.tenantId = tenantId;
|
||||
|
||||
// check if the force sso for login is present and if it is, check if the sso token is present and if it is then allow, otherwise decline.
|
||||
// update last active of project
|
||||
await ProjectService.updateLastActive(tenantId);
|
||||
}
|
||||
|
||||
if (ProjectMiddleware.hasApiKey(req)) {
|
||||
|
||||
12
CommonServer/Services/CallLogService.ts
Normal file
12
CommonServer/Services/CallLogService.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/CallLog';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
this.hardDeleteItemsOlderThanInDays('createdAt', 30);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
44
CommonServer/Services/CallService.ts
Normal file
44
CommonServer/Services/CallService.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import EmptyResponseData from 'Common/Types/API/EmptyResponse';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import API from 'Common/Utils/API';
|
||||
import { NotificationHostname } from '../Config';
|
||||
import Protocol from 'Common/Types/API/Protocol';
|
||||
import ClusterKeyAuthorization from '../Middleware/ClusterKeyAuthorization';
|
||||
import Phone from 'Common/Types/Phone';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import CallRequest from 'Common/Types/Call/CallRequest';
|
||||
|
||||
export default class CallService {
|
||||
public static async makeCall(
|
||||
to: Phone,
|
||||
callRequest: CallRequest,
|
||||
options: {
|
||||
projectId?: ObjectID | undefined; // project id for sms log
|
||||
from?: Phone; // from phone number
|
||||
isSensitive?: boolean; // if true, message will not be logged
|
||||
}
|
||||
): Promise<HTTPResponse<EmptyResponseData>> {
|
||||
const body: JSONObject = {
|
||||
to: to.toString(),
|
||||
callRequest: callRequest,
|
||||
from: options.from?.toString(),
|
||||
projectId: options.projectId?.toString(),
|
||||
isSensitive: options.isSensitive,
|
||||
};
|
||||
|
||||
return await API.post<EmptyResponseData>(
|
||||
new URL(
|
||||
Protocol.HTTP,
|
||||
NotificationHostname,
|
||||
new Route('/call/make-call')
|
||||
),
|
||||
body,
|
||||
{
|
||||
...ClusterKeyAuthorization.getClusterKeyHeaders(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDuty';
|
||||
import Model from 'Model/Models/DataMigration';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
@@ -47,7 +47,6 @@ import API from 'Common/Utils/API';
|
||||
import Protocol from 'Common/Types/API/Protocol';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import ClusterKeyAuthorization from '../Middleware/ClusterKeyAuthorization';
|
||||
import Text from 'Common/Types/Text';
|
||||
|
||||
@@ -152,7 +151,21 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected checkRequiredFields(data: TBaseModel): void {
|
||||
protected generateDefaultValues(data: TBaseModel): TBaseModel {
|
||||
const tableColumns: Array<string> = data.getTableColumns().columns;
|
||||
|
||||
for (const column of tableColumns) {
|
||||
const metadata: TableColumnMetadata =
|
||||
data.getTableColumnMetadata(column);
|
||||
if (metadata.forceGetDefaultValueOnCreate) {
|
||||
(data as any)[column] = metadata.forceGetDefaultValueOnCreate();
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
protected checkRequiredFields(data: TBaseModel): TBaseModel {
|
||||
// Check required fields.
|
||||
|
||||
const relatationalColumns: Dictionary<string> = {};
|
||||
@@ -204,6 +217,8 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
throw new BadDataException(`${requiredField} is required`);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
protected async onBeforeCreate(
|
||||
@@ -502,7 +517,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
}
|
||||
|
||||
public async onTrigger(
|
||||
model: TBaseModel,
|
||||
id: ObjectID,
|
||||
projectId: ObjectID,
|
||||
triggerType: DatabaseTriggerType
|
||||
): Promise<void> {
|
||||
@@ -517,7 +532,9 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
)
|
||||
),
|
||||
{
|
||||
data: JSONFunctions.toJSON(model, this.entityType),
|
||||
data: {
|
||||
_id: id.toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
...ClusterKeyAuthorization.getClusterKeyHeaders(),
|
||||
@@ -545,7 +562,8 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
data.setColumnValue(tenantColumnName, _createdBy.props.tenantId);
|
||||
}
|
||||
|
||||
this.checkRequiredFields(data);
|
||||
data = this.generateDefaultValues(data);
|
||||
data = this.checkRequiredFields(data);
|
||||
|
||||
if (!this.isValid(data)) {
|
||||
throw new BadDataException('Data is not valid');
|
||||
@@ -601,7 +619,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
)))
|
||||
) {
|
||||
await this.onTrigger(
|
||||
createBy.data,
|
||||
createBy.data.id!,
|
||||
createBy.props.tenantId ||
|
||||
createBy.data.getValue<ObjectID>(
|
||||
this.getModel().getTenantColumn()!
|
||||
@@ -937,7 +955,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
deleteBy.props.tenantId
|
||||
) {
|
||||
await this.onTrigger(
|
||||
item,
|
||||
item.id!,
|
||||
deleteBy.props.tenantId ||
|
||||
item.getValue<ObjectID>(
|
||||
this.getModel().getTenantColumn()!
|
||||
@@ -975,15 +993,27 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
withDeleted?: boolean | undefined
|
||||
): Promise<Array<TBaseModel>> {
|
||||
try {
|
||||
let automaticallyAddedCreatedAtInSelect: boolean = false;
|
||||
|
||||
if (!findBy.sort || Object.keys(findBy.sort).length === 0) {
|
||||
findBy.sort = {
|
||||
createdAt: SortOrder.Descending,
|
||||
};
|
||||
|
||||
if (!findBy.select) {
|
||||
findBy.select = {} as any;
|
||||
}
|
||||
|
||||
if (!(findBy.select as any)['createdAt']) {
|
||||
(findBy.select as any)['createdAt'] = true;
|
||||
automaticallyAddedCreatedAtInSelect = true;
|
||||
}
|
||||
}
|
||||
|
||||
const onFind: OnFind<TBaseModel> = findBy.props.ignoreHooks
|
||||
? { findBy, carryForward: [] }
|
||||
: await this.onBeforeFind(findBy);
|
||||
const onBeforeFind: FindBy<TBaseModel> = onFind.findBy;
|
||||
const onBeforeFind: FindBy<TBaseModel> = { ...onFind.findBy };
|
||||
const carryForward: any = onFind.carryForward;
|
||||
|
||||
if (
|
||||
@@ -997,10 +1027,6 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
(onBeforeFind.select as any)['_id'] = true;
|
||||
}
|
||||
|
||||
if (!(onBeforeFind.select as any)['createdAt']) {
|
||||
(onBeforeFind.select as any)['createdAt'] = true;
|
||||
}
|
||||
|
||||
const result: {
|
||||
query: Query<TBaseModel>;
|
||||
select: Select<TBaseModel> | null;
|
||||
@@ -1043,6 +1069,13 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
decryptedItems,
|
||||
onBeforeFind
|
||||
);
|
||||
|
||||
for (const item of decryptedItems) {
|
||||
if (automaticallyAddedCreatedAtInSelect) {
|
||||
delete (item as any).createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
if (!findBy.props.ignoreHooks) {
|
||||
decryptedItems = await (
|
||||
await this.onFindSuccess(
|
||||
@@ -1213,7 +1246,7 @@ class DatabaseService<TBaseModel extends BaseModel> {
|
||||
))
|
||||
) {
|
||||
await this.onTrigger(
|
||||
item,
|
||||
item.id!,
|
||||
updateBy.props.tenantId ||
|
||||
item.getValue<ObjectID>(
|
||||
this.getModel().getTenantColumn()!
|
||||
|
||||
@@ -27,7 +27,7 @@ import StatusPageHeaderLinkService from './StatusPageHeaderLinkService';
|
||||
import StatusPagePrivateUserService from './StatusPagePrivateUserService';
|
||||
|
||||
// On Call Duty
|
||||
import OnCallDutyService from './OnCallDutyService';
|
||||
import OnCallDutyService from './OnCallDutyPolicyService';
|
||||
|
||||
// Monitors
|
||||
import MonitorService from './MonitorService';
|
||||
|
||||
@@ -47,6 +47,22 @@ export class Service extends DatabaseService<Model> {
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<Model>
|
||||
): Promise<OnCreate<Model>> {
|
||||
if (!createBy.data.monitorType) {
|
||||
throw new BadDataException(
|
||||
'Monitor type required to create monitor.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!Object.values(MonitorType).includes(createBy.data.monitorType)) {
|
||||
throw new BadDataException(
|
||||
`Invalid monitor type "${
|
||||
createBy.data.monitorType
|
||||
}". Valid monitor types are ${Object.values(MonitorType).join(
|
||||
', '
|
||||
)}.`
|
||||
);
|
||||
}
|
||||
|
||||
if (!createBy.props.tenantId) {
|
||||
throw new BadDataException('ProjectId required to create monitor.');
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export default class NotificationService {
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners:
|
||||
false, // reset this flag
|
||||
lowCallAndSMSBalanceNotificationSentToOwners: false, // reset this flag
|
||||
notEnabledSmsNotificationSentToOwners: false,
|
||||
notEnabledSmsOrCallNotificationSentToOwners: false,
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
|
||||
10
CommonServer/Services/OnCallDutyPolicyCustomFieldService.ts
Normal file
10
CommonServer/Services/OnCallDutyPolicyCustomFieldService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDutyPolicyCustomField';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
@@ -0,0 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDutyPolicyEscalationRule';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
@@ -0,0 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDutyPolicyEscalationRuleTeam';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
@@ -0,0 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDutyPolicyEscalationRuleUser';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
10
CommonServer/Services/OnCallDutyPolicyExecutionLogService.ts
Normal file
10
CommonServer/Services/OnCallDutyPolicyExecutionLogService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDutyPolicyExecutionLog';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
@@ -0,0 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDutyPolicyExecutionLogTimeline';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
10
CommonServer/Services/OnCallDutyPolicyService.ts
Normal file
10
CommonServer/Services/OnCallDutyPolicyService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/OnCallDutyPolicy';
|
||||
import DatabaseService from './DatabaseService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
@@ -47,6 +47,8 @@ import MailService from './MailService';
|
||||
import logger from '../Utils/Logger';
|
||||
import Email from 'Common/Types/Email';
|
||||
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
|
||||
import UserService from './UserService';
|
||||
import UserNotificationRuleService from './UserNotificationRuleService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
@@ -75,6 +77,10 @@ export class Service extends DatabaseService<Model> {
|
||||
) {
|
||||
throw new BadDataException('Plan is invalid.');
|
||||
}
|
||||
|
||||
data.data.planName = SubscriptionPlan.getPlanSelect(
|
||||
data.data.paymentProviderPlanId
|
||||
);
|
||||
}
|
||||
|
||||
// check if the user has the project with the same name. If yes, reject.
|
||||
@@ -117,6 +123,28 @@ export class Service extends DatabaseService<Model> {
|
||||
);
|
||||
}
|
||||
|
||||
const user: User | null = await UserService.findOneById({
|
||||
id: data.props.userId,
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
companyPhoneNumber: true,
|
||||
companyName: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new BadDataException('User not found.');
|
||||
}
|
||||
|
||||
data.data.createdOwnerName = user.name!;
|
||||
data.data.createdOwnerEmail = user.email!;
|
||||
data.data.createdOwnerPhone = user.companyPhoneNumber!;
|
||||
data.data.createdOwnerCompanyName = user.companyName!;
|
||||
|
||||
return Promise.resolve({ createBy: data, carryForward: null });
|
||||
}
|
||||
|
||||
@@ -198,6 +226,9 @@ export class Service extends DatabaseService<Model> {
|
||||
data: {
|
||||
paymentProviderSubscriptionId: subscription.id,
|
||||
trialEndsAt: subscription.trialEndsAt || new Date(),
|
||||
planName: SubscriptionPlan.getPlanSelect(
|
||||
updateBy.data.paymentProviderPlanId! as string
|
||||
),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
@@ -318,13 +349,13 @@ export class Service extends DatabaseService<Model> {
|
||||
});
|
||||
}
|
||||
|
||||
createdItem = await this.addDefaultIncidentSeverity(createdItem);
|
||||
createdItem = await this.addDefaultProjectTeams(createdItem);
|
||||
createdItem = await this.addDefaultMonitorStatus(createdItem);
|
||||
createdItem = await this.addDefaultIncidentState(createdItem);
|
||||
createdItem = await this.addDefaultScheduledMaintenanceState(
|
||||
createdItem
|
||||
);
|
||||
createdItem = await this.addDefaultIncidentSeverity(createdItem);
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
@@ -597,9 +628,40 @@ export class Service extends DatabaseService<Model> {
|
||||
createdItem.createdByUserId!
|
||||
);
|
||||
|
||||
const user: User | null = await UserService.findOneById({
|
||||
id: createdItem.createdByUserId!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
isEmailVerified: true,
|
||||
email: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (user && user.isEmailVerified) {
|
||||
await UserNotificationRuleService.addDefaultNotifictionRuleForUser(
|
||||
createdItem.id!,
|
||||
user.id!,
|
||||
user.email!
|
||||
);
|
||||
}
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
|
||||
public async updateLastActive(projectId: ObjectID): Promise<void> {
|
||||
await this.updateOneById({
|
||||
id: projectId,
|
||||
data: {
|
||||
lastActive: OneUptimeDate.getCurrentDate(),
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public async getOwners(projectId: ObjectID): Promise<Array<User>> {
|
||||
if (!projectId) {
|
||||
throw new BadDataException('Project ID is required');
|
||||
|
||||
@@ -17,6 +17,7 @@ export default class SmsService {
|
||||
options: {
|
||||
projectId?: ObjectID | undefined; // project id for sms log
|
||||
from?: Phone; // from phone number
|
||||
isSensitive?: boolean; // if true, message will not be logged
|
||||
}
|
||||
): Promise<HTTPResponse<EmptyResponseData>> {
|
||||
const body: JSONObject = {
|
||||
@@ -24,6 +25,7 @@ export default class SmsService {
|
||||
message,
|
||||
from: options.from?.toString(),
|
||||
projectId: options.projectId?.toString(),
|
||||
isSensitive: options.isSensitive,
|
||||
};
|
||||
|
||||
return await API.post<EmptyResponseData>(
|
||||
|
||||
@@ -31,6 +31,7 @@ import logger from '../Utils/Logger';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import TeamMember from 'Model/Models/TeamMember';
|
||||
import UserNotificationRuleService from './UserNotificationRuleService';
|
||||
|
||||
export class TeamMemberService extends DatabaseService<TeamMember> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
@@ -40,6 +41,32 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<TeamMember>
|
||||
): Promise<OnCreate<TeamMember>> {
|
||||
// check if this project can have more members.
|
||||
|
||||
if (IsBillingEnabled && createBy.data.projectId) {
|
||||
const project: Project | null = await ProjectService.findOneById({
|
||||
id: createBy.data.projectId!,
|
||||
select: {
|
||||
seatLimit: true,
|
||||
paymentProviderSubscriptionSeats: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
project &&
|
||||
project.seatLimit &&
|
||||
project.paymentProviderSubscriptionSeats &&
|
||||
project.paymentProviderSubscriptionSeats >= project.seatLimit
|
||||
) {
|
||||
throw new BadDataException(
|
||||
'You have reached the user limit. You cannot invite any more users to this project. Please contact billing@oneuptime.com to increase your user limit.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createBy.data.hasAcceptedInvitation = false;
|
||||
|
||||
if (createBy.miscDataProps && createBy.miscDataProps['email']) {
|
||||
@@ -153,6 +180,10 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
|
||||
},
|
||||
select: {
|
||||
userId: true,
|
||||
user: {
|
||||
email: true,
|
||||
isEmailVerified: true,
|
||||
},
|
||||
projectId: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
@@ -165,6 +196,17 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
|
||||
|
||||
for (const item of items) {
|
||||
await this.refreshTokens(item.userId!, item.projectId!);
|
||||
|
||||
if (
|
||||
updateBy.data.hasAcceptedInvitation &&
|
||||
item.user?.isEmailVerified
|
||||
) {
|
||||
await UserNotificationRuleService.addDefaultNotifictionRuleForUser(
|
||||
item.projectId!,
|
||||
item.userId!,
|
||||
item.user?.email!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { updateBy, carryForward: onUpdate.carryForward };
|
||||
|
||||
175
CommonServer/Services/UserCallService.ts
Normal file
175
CommonServer/Services/UserCallService.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/UserCall';
|
||||
import DatabaseService, { OnCreate, OnDelete } from './DatabaseService';
|
||||
import CreateBy from '../Types/Database/CreateBy';
|
||||
import ProjectService from './ProjectService';
|
||||
import Project from 'Model/Models/Project';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import CallService from './CallService';
|
||||
import logger from '../Utils/Logger';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Text from 'Common/Types/Text';
|
||||
import CallRequest, { CallAction } from 'Common/Types/Call/CallRequest';
|
||||
import DeleteBy from '../Types/Database/DeleteBy';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import UserNotificationRuleService from './UserNotificationRuleService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
|
||||
protected override async onBeforeDelete(
|
||||
deleteBy: DeleteBy<Model>
|
||||
): Promise<OnDelete<Model>> {
|
||||
const itemsToDelete: Array<Model> = await this.findBy({
|
||||
query: deleteBy.query,
|
||||
select: {
|
||||
_id: true,
|
||||
projectId: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_MAX,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const item of itemsToDelete) {
|
||||
await UserNotificationRuleService.deleteBy({
|
||||
query: {
|
||||
userCallId: item.id!,
|
||||
projectId: item.projectId!,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
deleteBy,
|
||||
carryForward: null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<Model>
|
||||
): Promise<OnCreate<Model>> {
|
||||
if (!createBy.props.isRoot && createBy.data.isVerified) {
|
||||
throw new BadDataException('isVerified cannot be set to true');
|
||||
}
|
||||
|
||||
// check if this project has SMS and Call mEnabled.
|
||||
|
||||
const project: Project | null = await ProjectService.findOneById({
|
||||
id: createBy.data.projectId!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
enableCallNotifications: true,
|
||||
smsOrCallCurrentBalanceInUSDCents: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new BadDataException('Project not found');
|
||||
}
|
||||
|
||||
if (!project.enableCallNotifications) {
|
||||
throw new BadDataException(
|
||||
'Call notifications are disabled for this project. Please enable them in Project Settings > Notification Settings.'
|
||||
);
|
||||
}
|
||||
|
||||
if (project?.smsOrCallCurrentBalanceInUSDCents! <= 100) {
|
||||
throw new BadDataException(
|
||||
'Your SMS balance is low. Please recharge your SMS balance in Project Settings > Notification Settings.'
|
||||
);
|
||||
}
|
||||
|
||||
return { carryForward: null, createBy };
|
||||
}
|
||||
|
||||
protected override async onCreateSuccess(
|
||||
_onCreate: OnCreate<Model>,
|
||||
createdItem: Model
|
||||
): Promise<Model> {
|
||||
if (!createdItem.isVerified) {
|
||||
this.sendVerificationCode(createdItem);
|
||||
}
|
||||
return createdItem;
|
||||
}
|
||||
|
||||
public async resendVerificationCode(itemId: ObjectID): Promise<void> {
|
||||
const item: Model | null = await this.findOneById({
|
||||
id: itemId,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
phone: true,
|
||||
verificationCode: true,
|
||||
isVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
throw new BadDataException(
|
||||
'Item with ID ' + itemId.toString() + ' not found'
|
||||
);
|
||||
}
|
||||
|
||||
if (item.isVerified) {
|
||||
throw new BadDataException('Phone Number already verified');
|
||||
}
|
||||
|
||||
// generate new verification code
|
||||
item.verificationCode = Text.generateRandomNumber(6);
|
||||
|
||||
await this.updateOneById({
|
||||
id: item.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
data: {
|
||||
verificationCode: item.verificationCode,
|
||||
},
|
||||
});
|
||||
|
||||
this.sendVerificationCode(item);
|
||||
}
|
||||
|
||||
public sendVerificationCode(item: Model): void {
|
||||
const callRequest: CallRequest = {
|
||||
data: [
|
||||
{
|
||||
sayMessage:
|
||||
'Your verification code is ' +
|
||||
item.verificationCode?.split('').join(' '), // add space to make it more clear
|
||||
},
|
||||
{
|
||||
sayMessage:
|
||||
'Your verification code is ' +
|
||||
item.verificationCode?.split('').join(' '), // add space to make it more clear
|
||||
},
|
||||
{
|
||||
sayMessage: 'Thank you for using OneUptime. Goodbye.',
|
||||
},
|
||||
CallAction.Hangup,
|
||||
],
|
||||
};
|
||||
|
||||
// send verifiction sms.
|
||||
CallService.makeCall(item.phone!, callRequest, {
|
||||
projectId: item.projectId,
|
||||
isSensitive: true,
|
||||
}).catch((err: Error) => {
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
134
CommonServer/Services/UserEmailService.ts
Normal file
134
CommonServer/Services/UserEmailService.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/UserEmail';
|
||||
import DatabaseService, { OnCreate, OnDelete } from './DatabaseService';
|
||||
import MailService from './MailService';
|
||||
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
|
||||
import logger from '../Utils/Logger';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import Text from 'Common/Types/Text';
|
||||
import DeleteBy from '../Types/Database/DeleteBy';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import UserNotificationRuleService from './UserNotificationRuleService';
|
||||
import CreateBy from '../Types/Database/CreateBy';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
|
||||
protected override async onBeforeDelete(
|
||||
deleteBy: DeleteBy<Model>
|
||||
): Promise<OnDelete<Model>> {
|
||||
const itemsToDelete: Array<Model> = await this.findBy({
|
||||
query: deleteBy.query,
|
||||
select: {
|
||||
_id: true,
|
||||
projectId: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_MAX,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const item of itemsToDelete) {
|
||||
await UserNotificationRuleService.deleteBy({
|
||||
query: {
|
||||
userEmailId: item.id!,
|
||||
projectId: item.projectId!,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
deleteBy,
|
||||
carryForward: null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<Model>
|
||||
): Promise<OnCreate<Model>> {
|
||||
if (!createBy.props.isRoot && createBy.data.isVerified) {
|
||||
throw new BadDataException('isVerified cannot be set to true');
|
||||
}
|
||||
|
||||
return {
|
||||
createBy,
|
||||
carryForward: null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override async onCreateSuccess(
|
||||
_onCreate: OnCreate<Model>,
|
||||
createdItem: Model
|
||||
): Promise<Model> {
|
||||
if (!createdItem.isVerified) {
|
||||
// send verification code
|
||||
this.sendVerificationCode(createdItem);
|
||||
}
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
|
||||
public async resendVerificationCode(itemId: ObjectID): Promise<void> {
|
||||
const item: Model | null = await this.findOneById({
|
||||
id: itemId,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
email: true,
|
||||
verificationCode: true,
|
||||
isVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
throw new BadDataException(
|
||||
'Item with ID ' + itemId.toString() + ' not found'
|
||||
);
|
||||
}
|
||||
|
||||
if (item.isVerified) {
|
||||
throw new BadDataException('Email already verified');
|
||||
}
|
||||
|
||||
// generate new verification code
|
||||
item.verificationCode = Text.generateRandomNumber(6);
|
||||
|
||||
await this.updateOneById({
|
||||
id: item.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
data: {
|
||||
verificationCode: item.verificationCode,
|
||||
},
|
||||
});
|
||||
|
||||
this.sendVerificationCode(item);
|
||||
}
|
||||
|
||||
public sendVerificationCode(item: Model): void {
|
||||
MailService.sendMail({
|
||||
toEmail: item.email!,
|
||||
templateType: EmailTemplateType.VerificationCode,
|
||||
vars: {
|
||||
code: item.verificationCode!,
|
||||
subject: 'Verify this email address',
|
||||
},
|
||||
subject: 'Verify this email address',
|
||||
}).catch((err: Error) => {
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
189
CommonServer/Services/UserNotificationRuleService.ts
Normal file
189
CommonServer/Services/UserNotificationRuleService.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/UserNotificationRule';
|
||||
import DatabaseService, { OnCreate } from './DatabaseService';
|
||||
import CreateBy from '../Types/Database/CreateBy';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Email from 'Common/Types/Email';
|
||||
import IncidentSeverityService from './IncidentSeverityService';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import IncidentSeverity from 'Model/Models/IncidentSeverity';
|
||||
import UserEmailService from './UserEmailService';
|
||||
import UserEmail from 'Model/Models/UserEmail';
|
||||
import NotificationRuleType from 'Common/Types/NotificationRule/NotificationRuleType';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<Model>
|
||||
): Promise<OnCreate<Model>> {
|
||||
if (
|
||||
!createBy.data.userCallId &&
|
||||
!createBy.data.userCall &&
|
||||
!createBy.data.userEmail &&
|
||||
!createBy.data.userSms &&
|
||||
!createBy.data.userSmsId &&
|
||||
!createBy.data.userEmailId
|
||||
) {
|
||||
throw new BadDataException('Call, SMS, or Email is required');
|
||||
}
|
||||
|
||||
return {
|
||||
createBy,
|
||||
carryForward: null,
|
||||
};
|
||||
}
|
||||
|
||||
public async addDefaultNotifictionRuleForUser(
|
||||
projectId: ObjectID,
|
||||
userId: ObjectID,
|
||||
email: Email
|
||||
): Promise<void> {
|
||||
const incidentSeverities: Array<IncidentSeverity> =
|
||||
await IncidentSeverityService.findBy({
|
||||
query: {
|
||||
projectId,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
});
|
||||
|
||||
//check userEmail
|
||||
|
||||
let userEmail: UserEmail | null = await UserEmailService.findOneBy({
|
||||
query: {
|
||||
projectId,
|
||||
userId,
|
||||
email,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!userEmail) {
|
||||
userEmail = new UserEmail();
|
||||
userEmail.projectId = projectId;
|
||||
userEmail.userId = userId;
|
||||
userEmail.email = email;
|
||||
userEmail.isVerified = true;
|
||||
|
||||
userEmail = await UserEmailService.create({
|
||||
data: userEmail,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// create for incident severities.
|
||||
for (const incidentSeverity of incidentSeverities) {
|
||||
//check if this rule already exists.
|
||||
const existingRule: Model | null = await this.findOneBy({
|
||||
query: {
|
||||
projectId,
|
||||
userId,
|
||||
userEmailId: userEmail.id!,
|
||||
incidentSeverityId: incidentSeverity.id!,
|
||||
ruleType: NotificationRuleType.ON_CALL_INCIDENT_CREATED,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingRule) {
|
||||
continue; // skip this rule.
|
||||
}
|
||||
|
||||
const notificationRule: Model = new Model();
|
||||
|
||||
notificationRule.projectId = projectId;
|
||||
notificationRule.userId = userId;
|
||||
notificationRule.userEmailId = userEmail.id!;
|
||||
notificationRule.incidentSeverityId = incidentSeverity.id!;
|
||||
notificationRule.notifyAfterMinutes = 0;
|
||||
notificationRule.ruleType =
|
||||
NotificationRuleType.ON_CALL_INCIDENT_CREATED;
|
||||
|
||||
await this.create({
|
||||
data: notificationRule,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//check if this rule already exists.
|
||||
const existingRuleOnCall: Model | null = await this.findOneBy({
|
||||
query: {
|
||||
projectId,
|
||||
userId,
|
||||
userEmailId: userEmail.id!,
|
||||
ruleType: NotificationRuleType.WHEN_USER_GOES_ON_CALL,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingRuleOnCall) {
|
||||
// on and off call.
|
||||
const onCallRule: Model = new Model();
|
||||
|
||||
onCallRule.projectId = projectId;
|
||||
onCallRule.userId = userId;
|
||||
onCallRule.userEmailId = userEmail.id!;
|
||||
onCallRule.notifyAfterMinutes = 0;
|
||||
onCallRule.ruleType = NotificationRuleType.WHEN_USER_GOES_ON_CALL;
|
||||
|
||||
await this.create({
|
||||
data: onCallRule,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//check if this rule already exists.
|
||||
const existingRuleOffCall: Model | null = await this.findOneBy({
|
||||
query: {
|
||||
projectId,
|
||||
userId,
|
||||
userEmailId: userEmail.id!,
|
||||
ruleType: NotificationRuleType.WHEN_USER_GOES_OFF_CALL,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingRuleOffCall) {
|
||||
// on and off call.
|
||||
const offCallRule: Model = new Model();
|
||||
|
||||
offCallRule.projectId = projectId;
|
||||
offCallRule.userId = userId;
|
||||
offCallRule.userEmailId = userEmail.id!;
|
||||
offCallRule.notifyAfterMinutes = 0;
|
||||
offCallRule.ruleType = NotificationRuleType.WHEN_USER_GOES_OFF_CALL;
|
||||
|
||||
await this.create({
|
||||
data: offCallRule,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
@@ -15,6 +15,9 @@ import EmailVerificationToken from 'Model/Models/EmailVerificationToken';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import EmailVerificationTokenService from './EmailVerificationTokenService';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import TeamMember from 'Model/Models/TeamMember';
|
||||
import TeamMemberService from './TeamMemberService';
|
||||
import UserNotificationRuleService from './UserNotificationRuleService';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
@@ -78,6 +81,51 @@ export class Service extends DatabaseService<Model> {
|
||||
}
|
||||
}
|
||||
|
||||
if (onUpdate && onUpdate.updateBy.data.isEmailVerified) {
|
||||
// if the email is verified then create default policies for this user.
|
||||
|
||||
const newUsers: Array<Model> = await this.findBy({
|
||||
query: onUpdate.updateBy.query,
|
||||
select: {
|
||||
_id: true,
|
||||
email: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
});
|
||||
|
||||
for (const user of newUsers) {
|
||||
// emai is verified. create default policies for this user.
|
||||
const teamMembers: Array<TeamMember> =
|
||||
await TeamMemberService.findBy({
|
||||
query: {
|
||||
userId: user.id!,
|
||||
hasAcceptedInvitation: true,
|
||||
},
|
||||
select: {
|
||||
projectId: true,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const member of teamMembers) {
|
||||
// create default policies for this user.
|
||||
await UserNotificationRuleService.addDefaultNotifictionRuleForUser(
|
||||
member.projectId!,
|
||||
user.id!,
|
||||
user.email!
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onUpdate && onUpdate.updateBy.data.email) {
|
||||
const newUsers: Array<Model> = await this.findBy({
|
||||
query: onUpdate.updateBy.query,
|
||||
|
||||
160
CommonServer/Services/UserSmsService.ts
Normal file
160
CommonServer/Services/UserSmsService.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/UserSMS';
|
||||
import DatabaseService, { OnCreate, OnDelete } from './DatabaseService';
|
||||
import CreateBy from '../Types/Database/CreateBy';
|
||||
import ProjectService from './ProjectService';
|
||||
import Project from 'Model/Models/Project';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import SmsService from './SmsService';
|
||||
import logger from '../Utils/Logger';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Text from 'Common/Types/Text';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import UserNotificationRuleService from './UserNotificationRuleService';
|
||||
import DeleteBy from '../Types/Database/DeleteBy';
|
||||
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
}
|
||||
|
||||
protected override async onBeforeDelete(
|
||||
deleteBy: DeleteBy<Model>
|
||||
): Promise<OnDelete<Model>> {
|
||||
const itemsToDelete: Array<Model> = await this.findBy({
|
||||
query: deleteBy.query,
|
||||
select: {
|
||||
_id: true,
|
||||
projectId: true,
|
||||
},
|
||||
skip: 0,
|
||||
limit: LIMIT_MAX,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const item of itemsToDelete) {
|
||||
await UserNotificationRuleService.deleteBy({
|
||||
query: {
|
||||
userSmsId: item.id!,
|
||||
projectId: item.projectId!,
|
||||
},
|
||||
limit: LIMIT_MAX,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
deleteBy,
|
||||
carryForward: null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<Model>
|
||||
): Promise<OnCreate<Model>> {
|
||||
// check if this project has SMS and Call mEnabled.
|
||||
|
||||
if (!createBy.props.isRoot && createBy.data.isVerified) {
|
||||
throw new BadDataException('isVerified cannot be set to true');
|
||||
}
|
||||
|
||||
const project: Project | null = await ProjectService.findOneById({
|
||||
id: createBy.data.projectId!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
enableSmsNotifications: true,
|
||||
smsOrCallCurrentBalanceInUSDCents: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new BadDataException('Project not found');
|
||||
}
|
||||
|
||||
if (!project.enableSmsNotifications) {
|
||||
throw new BadDataException(
|
||||
'SMS notifications are disabled for this project. Please enable them in Project Settings > Notification Settings.'
|
||||
);
|
||||
}
|
||||
|
||||
if (project?.smsOrCallCurrentBalanceInUSDCents! <= 100) {
|
||||
throw new BadDataException(
|
||||
'Your SMS balance is low. Please recharge your SMS balance in Project Settings > Notification Settings.'
|
||||
);
|
||||
}
|
||||
|
||||
return { carryForward: null, createBy };
|
||||
}
|
||||
|
||||
protected override async onCreateSuccess(
|
||||
_onCreate: OnCreate<Model>,
|
||||
createdItem: Model
|
||||
): Promise<Model> {
|
||||
if (!createdItem.isVerified) {
|
||||
this.sendVerificationCode(createdItem);
|
||||
}
|
||||
|
||||
return createdItem;
|
||||
}
|
||||
|
||||
public async resendVerificationCode(itemId: ObjectID): Promise<void> {
|
||||
const item: Model | null = await this.findOneById({
|
||||
id: itemId,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
phone: true,
|
||||
verificationCode: true,
|
||||
isVerified: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
throw new BadDataException(
|
||||
'Item with ID ' + itemId.toString() + ' not found'
|
||||
);
|
||||
}
|
||||
|
||||
if (item.isVerified) {
|
||||
throw new BadDataException('Phone Number already verified');
|
||||
}
|
||||
|
||||
// generate new verification code
|
||||
item.verificationCode = Text.generateRandomNumber(6);
|
||||
|
||||
await this.updateOneById({
|
||||
id: item.id!,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
data: {
|
||||
verificationCode: item.verificationCode,
|
||||
},
|
||||
});
|
||||
|
||||
this.sendVerificationCode(item);
|
||||
}
|
||||
|
||||
public sendVerificationCode(item: Model): void {
|
||||
// send verifiction sms.
|
||||
SmsService.sendSms(
|
||||
item.phone!,
|
||||
'Your verification code is ' + item.verificationCode,
|
||||
{
|
||||
projectId: item.projectId,
|
||||
isSensitive: true,
|
||||
}
|
||||
).catch((err: Error) => {
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
export default new Service();
|
||||
@@ -223,6 +223,7 @@ describe('probeService', () => {
|
||||
select: {
|
||||
_id: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
},
|
||||
props: { isRoot: true },
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import ComponentMetadata from 'Common/Types/Workflow/Component';
|
||||
import ComponentMetadata, { Port } from 'Common/Types/Workflow/Component';
|
||||
import DatabaseService from '../../../../Services/DatabaseService';
|
||||
import { ExpressRequest, ExpressResponse } from '../../../../Utils/Express';
|
||||
import Response from '../../../../Utils/Response';
|
||||
@@ -12,6 +12,10 @@ import WorkflowService from '../../../../Services/WorkflowService';
|
||||
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
|
||||
import Workflow from 'Model/Models/Workflow';
|
||||
import ClusterKeyAuthorization from '../../../../Middleware/ClusterKeyAuthorization';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import { RunOptions, RunReturnType } from '../../ComponentCode';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Select from '../../../Database/Select';
|
||||
|
||||
export default class OnTriggerBaseModel<
|
||||
TBaseModel extends BaseModel
|
||||
@@ -19,12 +23,14 @@ export default class OnTriggerBaseModel<
|
||||
public modelId: string = '';
|
||||
public type: string = '';
|
||||
|
||||
public service: DatabaseService<TBaseModel> | null = null;
|
||||
|
||||
public constructor(
|
||||
modelService: DatabaseService<TBaseModel>,
|
||||
type: string
|
||||
) {
|
||||
super();
|
||||
|
||||
this.service = modelService;
|
||||
this.modelId = `${Text.pascalCaseToDashes(
|
||||
modelService.getModel().tableName!
|
||||
)}`;
|
||||
@@ -66,6 +72,80 @@ export default class OnTriggerBaseModel<
|
||||
);
|
||||
}
|
||||
|
||||
public override async run(
|
||||
args: JSONObject,
|
||||
options: RunOptions
|
||||
): Promise<RunReturnType> {
|
||||
const data: JSONObject = args['data'] as JSONObject;
|
||||
|
||||
const successPort: Port | undefined = this.getMetadata().outPorts.find(
|
||||
(p: Port) => {
|
||||
return p.id === 'success';
|
||||
}
|
||||
);
|
||||
|
||||
if (!successPort) {
|
||||
throw options.onError(
|
||||
new BadDataException('Success port not found')
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!data['_id'] ||
|
||||
!args['select'] ||
|
||||
Object.keys(args['select']).length === 0
|
||||
) {
|
||||
return {
|
||||
returnValues: {
|
||||
model: data
|
||||
? JSONFunctions.toJSON(
|
||||
data as any,
|
||||
this.service!.entityType
|
||||
)
|
||||
: null,
|
||||
},
|
||||
executePort: successPort,
|
||||
};
|
||||
}
|
||||
|
||||
let select: Select<TBaseModel> = args['select'] as Select<TBaseModel>;
|
||||
|
||||
if (select) {
|
||||
select = JSONFunctions.deserialize(
|
||||
args['select'] as JSONObject
|
||||
) as Select<TBaseModel>;
|
||||
}
|
||||
|
||||
const model: TBaseModel | null = await this.service!.findOneById({
|
||||
id: new ObjectID(args['_id'] as string),
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
select: {
|
||||
_id: true,
|
||||
...select,
|
||||
},
|
||||
});
|
||||
|
||||
if (!model) {
|
||||
throw new BadDataException(
|
||||
('Model not found with id ' + args['_id']) as string
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
returnValues: {
|
||||
model: data
|
||||
? JSONFunctions.toJSON(
|
||||
model as any,
|
||||
this.service!.entityType
|
||||
)
|
||||
: null,
|
||||
},
|
||||
executePort: successPort,
|
||||
};
|
||||
}
|
||||
|
||||
public async initTrigger(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
@@ -95,6 +175,8 @@ export default class OnTriggerBaseModel<
|
||||
for (const workflow of workflows) {
|
||||
/// Run Graph.
|
||||
|
||||
/// Find the object and send data.
|
||||
|
||||
const executeWorkflow: ExecuteWorkflowType = {
|
||||
workflowId: workflow.id!,
|
||||
returnValues: {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import { ExpressRouter } from '../../Utils/Express';
|
||||
import ComponentCode from './ComponentCode';
|
||||
import ComponentCode, { RunOptions, RunReturnType } from './ComponentCode';
|
||||
import { Port } from 'Common/Types/Workflow/Component';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
|
||||
export interface ExecuteWorkflowType {
|
||||
workflowId: ObjectID;
|
||||
@@ -44,6 +46,30 @@ export default class TrigegrCode extends ComponentCode {
|
||||
super();
|
||||
}
|
||||
|
||||
public override async run(
|
||||
args: JSONObject,
|
||||
options: RunOptions
|
||||
): Promise<RunReturnType> {
|
||||
const successPort: Port | undefined = this.getMetadata().outPorts.find(
|
||||
(p: Port) => {
|
||||
return p.id === 'success';
|
||||
}
|
||||
);
|
||||
|
||||
if (!successPort) {
|
||||
throw options.onError(
|
||||
new BadDataException('Success port not found')
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
returnValues: {
|
||||
...args,
|
||||
},
|
||||
executePort: successPort,
|
||||
};
|
||||
}
|
||||
|
||||
public async setupComponent(props: InitProps): Promise<void> {
|
||||
this.executeWorkflow = props.executeWorkflow;
|
||||
this.scheduleWorkflow = props.scheduleWorkflow;
|
||||
|
||||
@@ -13,7 +13,10 @@ import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import QueryHelper from '../Types/Database/QueryHelper';
|
||||
import Columns from 'Common/Types/Database/Columns';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import { ColumnAccessControl } from 'Common/Types/Database/AccessControl/AccessControl';
|
||||
import {
|
||||
ColumnAccessControl,
|
||||
ColumnBillingAccessControl,
|
||||
} from 'Common/Types/Database/AccessControl/AccessControl';
|
||||
import RelationSelect from '../Types/Database/RelationSelect';
|
||||
import Typeof from 'Common/Types/Typeof';
|
||||
import { TableColumnMetadata } from 'Common/Types/Database/TableColumn';
|
||||
@@ -174,10 +177,83 @@ export default class ModelPermission {
|
||||
!permissionColumns.columns.includes(key) &&
|
||||
tableColumns.includes(key)
|
||||
) {
|
||||
if (
|
||||
requestType === DatabaseRequestType.Create &&
|
||||
tableColumnMetadata.forceGetDefaultValueOnCreate
|
||||
) {
|
||||
continue; // this is a special case where we want to force the default value on create.
|
||||
}
|
||||
|
||||
throw new BadDataException(
|
||||
`User is not allowed to ${requestType} on ${key} column of ${model.singularName}`
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
IsBillingEnabled &&
|
||||
props.currentPlan &&
|
||||
model.getColumnBillingAccessControl(key)
|
||||
) {
|
||||
const billingAccessControl: ColumnBillingAccessControl =
|
||||
model.getColumnBillingAccessControl(key);
|
||||
|
||||
if (
|
||||
requestType === DatabaseRequestType.Create &&
|
||||
billingAccessControl.create
|
||||
) {
|
||||
if (
|
||||
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
||||
billingAccessControl.create,
|
||||
props.currentPlan,
|
||||
getAllEnvVars()
|
||||
)
|
||||
) {
|
||||
throw new PaymentRequiredException(
|
||||
'Please upgrade your plan to ' +
|
||||
billingAccessControl.create +
|
||||
' to access this feature'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
requestType === DatabaseRequestType.Read &&
|
||||
billingAccessControl.read
|
||||
) {
|
||||
if (
|
||||
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
||||
billingAccessControl.read,
|
||||
props.currentPlan,
|
||||
getAllEnvVars()
|
||||
)
|
||||
) {
|
||||
throw new PaymentRequiredException(
|
||||
'Please upgrade your plan to ' +
|
||||
billingAccessControl.read +
|
||||
' to access this feature'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
requestType === DatabaseRequestType.Update &&
|
||||
billingAccessControl.update
|
||||
) {
|
||||
if (
|
||||
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
||||
billingAccessControl.update,
|
||||
props.currentPlan,
|
||||
getAllEnvVars()
|
||||
)
|
||||
) {
|
||||
throw new PaymentRequiredException(
|
||||
'Please upgrade your plan to ' +
|
||||
billingAccessControl.update +
|
||||
' to access this feature'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,10 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
modelIdToEdit?: ObjectID | undefined;
|
||||
onError?: ((error: string) => void) | undefined;
|
||||
onBeforeCreate?:
|
||||
| ((item: TBaseModel | BaseModel) => Promise<TBaseModel | BaseModel>)
|
||||
| ((
|
||||
item: TBaseModel | BaseModel,
|
||||
miscDataProps: JSONObject
|
||||
) => Promise<TBaseModel | BaseModel>)
|
||||
| undefined;
|
||||
saveRequestOptions?: RequestOptions | undefined;
|
||||
doNotFetchExistingModel?: boolean | undefined;
|
||||
@@ -453,7 +456,10 @@ const ModelForm: Function = <TBaseModel extends BaseModel>(
|
||||
) as BaseModel;
|
||||
|
||||
if (props.onBeforeCreate && props.formType === FormType.Create) {
|
||||
tBaseModel = await props.onBeforeCreate(tBaseModel);
|
||||
tBaseModel = await props.onBeforeCreate(
|
||||
tBaseModel,
|
||||
miscDataProps
|
||||
);
|
||||
}
|
||||
|
||||
result = await ModelAPI.createOrUpdate<TBaseModel>(
|
||||
|
||||
@@ -421,6 +421,54 @@ const Icon: FunctionComponent<ComponentProps> = ({
|
||||
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.BarsArrowDown) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3 4.5h14.25M3 9h9.75M3 13.5h9.75m4.5-4.5v12m0 0l-3.75-3.75M17.25 21L21 17.25"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.Bell) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.AdjustmentHorizontal) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.AdjustmentVertical) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M6 13.5V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m12-3V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m-6-9V3.75m0 3.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 9.75V10.5"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.BellRinging) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0M3.124 7.5A8.969 8.969 0 015.292 3m13.416 0a8.969 8.969 0 012.168 4.5"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.BarsArrowUp) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3 4.5h14.25M3 9h9.75M3 13.5h9.75m4.5-4.5v12m0 0l-3.75-3.75M17.25 21L21 17.25"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.CheckCircle) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
cardProps: CardProps;
|
||||
modelDetailProps: ModeDetailProps<TBaseModel>;
|
||||
isEditable?: undefined | boolean;
|
||||
onSaveSuccess?: undefined | ((item: TBaseModel) => void);
|
||||
editButtonText?: undefined | string;
|
||||
formSteps?: undefined | Array<FormStep<TBaseModel>>;
|
||||
formFields?: undefined | Fields<TBaseModel>;
|
||||
@@ -100,9 +101,12 @@ const CardModelDetail: Function = <TBaseModel extends BaseModel>(
|
||||
setShowModal(false);
|
||||
}}
|
||||
submitButtonText={`Save Changes`}
|
||||
onSuccess={(_item: TBaseModel) => {
|
||||
onSuccess={(item: TBaseModel) => {
|
||||
setShowModal(false);
|
||||
setRefresher(!refresher);
|
||||
if (props.onSaveSuccess) {
|
||||
props.onSaveSuccess(item);
|
||||
}
|
||||
}}
|
||||
name={props.name}
|
||||
modelType={props.modelDetailProps.modelType}
|
||||
|
||||
@@ -6,7 +6,7 @@ import ModelForm, {
|
||||
} from '../Forms/ModelForm';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import ButtonType from '../Button/ButtonTypes';
|
||||
import { JSONObjectOrArray } from 'Common/Types/JSON';
|
||||
import { JSONObject, JSONObjectOrArray } from 'Common/Types/JSON';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Alert, { AlertType } from '../Alerts/Alert';
|
||||
import FormValues from '../Forms/Types/FormValues';
|
||||
@@ -26,7 +26,9 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
submitButtonStyleType?: undefined | ButtonStyleType;
|
||||
formProps: ModelFormComponentProps<TBaseModel>;
|
||||
modelIdToEdit?: ObjectID | undefined;
|
||||
onBeforeCreate?: ((item: TBaseModel) => Promise<TBaseModel>) | undefined;
|
||||
onBeforeCreate?:
|
||||
| ((item: TBaseModel, miscDataProps: JSONObject) => Promise<TBaseModel>)
|
||||
| undefined;
|
||||
footer?: ReactElement | undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,9 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
query?: Query<TBaseModel>;
|
||||
onBeforeFetch?: (() => Promise<JSONObject>) | undefined;
|
||||
createInitialValues?: FormValues<TBaseModel> | undefined;
|
||||
onBeforeCreate?: ((item: TBaseModel) => Promise<TBaseModel>) | undefined;
|
||||
onBeforeCreate?:
|
||||
| ((item: TBaseModel, miscDataProps: JSONObject) => Promise<TBaseModel>)
|
||||
| undefined;
|
||||
onCreateSuccess?: ((item: TBaseModel) => Promise<TBaseModel>) | undefined;
|
||||
createVerb?: string;
|
||||
showTableAs?: ShowTableAs | undefined;
|
||||
@@ -568,7 +570,14 @@ const ModelTable: Function = <TBaseModel extends BaseModel>(
|
||||
}
|
||||
|
||||
for (const moreField of selectMoreFields) {
|
||||
if (model.getTableColumnMetadata(moreField)) {
|
||||
if (
|
||||
model.getTableColumnMetadata(moreField) &&
|
||||
model.isEntityColumn(moreField)
|
||||
) {
|
||||
(selectFields as Dictionary<boolean>)[moreField] = (
|
||||
props.selectMoreFields as any
|
||||
)[moreField];
|
||||
} else if (model.getTableColumnMetadata(moreField)) {
|
||||
(selectFields as Dictionary<boolean>)[moreField] = true;
|
||||
} else {
|
||||
throw new BadDataException(
|
||||
@@ -1261,7 +1270,7 @@ const ModelTable: Function = <TBaseModel extends BaseModel>(
|
||||
|
||||
return (
|
||||
<>
|
||||
{getCardComponent()}
|
||||
<div className="mt-5 mb-5">{getCardComponent()}</div>
|
||||
|
||||
{showModel ? (
|
||||
<ModelFormModal<TBaseModel>
|
||||
@@ -1305,7 +1314,10 @@ const ModelTable: Function = <TBaseModel extends BaseModel>(
|
||||
await props.onCreateSuccess(item);
|
||||
}
|
||||
}}
|
||||
onBeforeCreate={async (item: TBaseModel) => {
|
||||
onBeforeCreate={async (
|
||||
item: TBaseModel,
|
||||
miscDataProps: JSONObject
|
||||
) => {
|
||||
if (
|
||||
showTableAs === ShowTableAs.OrderedStatesList &&
|
||||
props.orderedStatesListProps?.orderField &&
|
||||
@@ -1318,7 +1330,10 @@ const ModelTable: Function = <TBaseModel extends BaseModel>(
|
||||
}
|
||||
|
||||
if (props.onBeforeCreate) {
|
||||
item = await props.onBeforeCreate(item);
|
||||
item = await props.onBeforeCreate(
|
||||
item,
|
||||
miscDataProps
|
||||
);
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -73,7 +73,11 @@ const Page: FunctionComponent<ComponentProps> = (
|
||||
{props.children}
|
||||
</div>
|
||||
)}
|
||||
{props.isLoading && <PageLoader isVisible={true} />}
|
||||
{props.isLoading && (
|
||||
<div className="col-span-10">
|
||||
<PageLoader isVisible={true} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
)}
|
||||
|
||||
@@ -74,6 +74,8 @@ const RunModal: FunctionComponent<ComponentProps> = (
|
||||
ComponentInputType.BaseModelArray ||
|
||||
args.type ===
|
||||
ComponentInputType.Query ||
|
||||
args.type ===
|
||||
ComponentInputType.Select ||
|
||||
args.type ===
|
||||
ComponentInputType.StringDictionary) &&
|
||||
component.returnValues[args.id] &&
|
||||
|
||||
@@ -80,6 +80,12 @@ export const componentInputTypeToFormFieldType: Function = (
|
||||
};
|
||||
}
|
||||
|
||||
if (componentInputType === ComponentInputType.Select) {
|
||||
return {
|
||||
fieldType: FormFieldSchemaType.JSON,
|
||||
};
|
||||
}
|
||||
|
||||
if (componentInputType === ComponentInputType.StringDictionary) {
|
||||
return {
|
||||
fieldType: FormFieldSchemaType.JSON,
|
||||
|
||||
@@ -95,11 +95,13 @@ import SettingsIncidentSeverity from './Pages/Settings/IncidentSeverity';
|
||||
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 SettingsCallSms from './Pages/Settings/CallSms';
|
||||
import SettingsInvoices from './Pages/Settings/Invoices';
|
||||
import MonitorCustomFields from './Pages/Settings/MonitorCustomFields';
|
||||
import StatusPageCustomFields from './Pages/Settings/StatusPageCustomFields';
|
||||
import IncidentCustomFields from './Pages/Settings/IncidentCustomFields';
|
||||
import OnCallDutyPolicyCustomFields from './Pages/Settings/OnCallDutyPolicyCustomFields';
|
||||
import ScheduledMaintenanceCustomFields from './Pages/Settings/ScheduledMaintenanceCusomFields';
|
||||
|
||||
import ActiveIncidents from './Pages/Global/ActiveIncidents';
|
||||
@@ -109,8 +111,15 @@ import ProjectInvitations from './Pages/Global/ProjectInvitations';
|
||||
import UserProfileOverview from './Pages/Global/UserProfile/Index';
|
||||
import UserProfilePicture from './Pages/Global/UserProfile/Picture';
|
||||
import UserProfilePassword from './Pages/Global/UserProfile/Password';
|
||||
|
||||
// On Call Duty
|
||||
import OnCallDutyPage from './Pages/OnCallDuty/OnCallDuties';
|
||||
import OnCallDutyPoliciesPage from './Pages/OnCallDuty/OnCallDutyPolicies';
|
||||
import OnCallDutyPolicyView from './Pages/OnCallDuty/OnCallDutyPolicy/Index';
|
||||
import OnCallDutyPolicyViewDelete from './Pages/OnCallDuty/OnCallDutyPolicy/Delete';
|
||||
import OnCallDutyPolicyViewLogs from './Pages/OnCallDuty/OnCallDutyPolicy/ExecutionLogs';
|
||||
import OnCallDutyPolicyViewLogsView from './Pages/OnCallDuty/OnCallDutyPolicy/ExecutionLogView';
|
||||
import OnCallDutyPolicyViewEscalation from './Pages/OnCallDuty/OnCallDutyPolicy/Escalation';
|
||||
import OnCallDutyPolicyViewCustomFields from './Pages/OnCallDuty/OnCallDutyPolicy/CustomFields';
|
||||
|
||||
// Monitors
|
||||
import MonitorPage from './Pages/Monitor/Monitors';
|
||||
@@ -120,10 +129,12 @@ import MonitorViewCriteria from './Pages/Monitor/View/Criteria';
|
||||
import MonitorViewStatusTimeline from './Pages/Monitor/View/StatusTimeline';
|
||||
import MonitorIncidents from './Pages/Monitor/View/Incidents';
|
||||
import MonitorInoperational from './Pages/Monitor/NotOperationalMonitors';
|
||||
import MonitorDisabled from './Pages/Monitor/DisabledMonitors';
|
||||
import MonitorViewCustomFields from './Pages/Monitor/View/CustomFields';
|
||||
import MonitorViewInterval from './Pages/Monitor/View/Interval';
|
||||
import MonitorViewProbes from './Pages/Monitor/View/Probes';
|
||||
import MonitorViewOwner from './Pages/Monitor/View/Owners';
|
||||
import MonitorViewSettings from './Pages/Monitor/View/Settings';
|
||||
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
import Logout from './Pages/Logout/Logout';
|
||||
@@ -139,6 +150,9 @@ import API from 'CommonUI/src/Utils/API/API';
|
||||
import BillingPaymentMethod from 'Model/Models/BillingPaymentMethod';
|
||||
import PageComponentProps from './Pages/PageComponentProps';
|
||||
|
||||
import UserSettingsNotificationMethods from './Pages/UserSettings/NotificationMethods';
|
||||
import UserSettingsNotificationRules from './Pages/UserSettings/OnCallRules';
|
||||
|
||||
const App: FunctionComponent = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
Navigation.setLocation(useLocation());
|
||||
@@ -411,6 +425,33 @@ const App: FunctionComponent = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[PageMap.MONITOR_VIEW_SETTINGS]?.toString() ||
|
||||
''
|
||||
}
|
||||
element={
|
||||
<MonitorViewSettings
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.MONITOR_VIEW_SETTINGS] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.MONITORS_DISABLED]?.toString() || ''}
|
||||
element={
|
||||
<MonitorDisabled
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.MONITORS_DISABLED] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.MONITOR_VIEW]?.toString() || ''}
|
||||
element={
|
||||
@@ -1173,6 +1214,25 @@ const App: FunctionComponent = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPolicyViewCustomFields
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap
|
||||
.ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[PageMap.INCIDENT_PUBLIC_NOTE]?.toString() || ''
|
||||
@@ -1403,6 +1463,20 @@ const App: FunctionComponent = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[PageMap.SETTINGS_CALL_LOGS]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<SettingsCallLog
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.SETTINGS_CALL_LOGS] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_CALL_SMS]?.toString() || ''}
|
||||
element={
|
||||
@@ -1642,6 +1716,25 @@ const App: FunctionComponent = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.SETTINGS_ON_CALL_DUTY_POLICY_CUSTOM_FIELDS
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPolicyCustomFields
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap
|
||||
.SETTINGS_ON_CALL_DUTY_POLICY_CUSTOM_FIELDS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.SETTINGS_BILLING]?.toString() || ''}
|
||||
element={
|
||||
@@ -1727,7 +1820,7 @@ const App: FunctionComponent = () => {
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.ON_CALL_DUTY]?.toString() || ''}
|
||||
element={
|
||||
<OnCallDutyPage
|
||||
<OnCallDutyPoliciesPage
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.SETTINGS_TEAM_VIEW] as Route
|
||||
@@ -1736,6 +1829,113 @@ const App: FunctionComponent = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICIES]?.toString() ||
|
||||
''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPoliciesPage
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICIES] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPolicyView
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPolicyViewDelete
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPolicyViewEscalation
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPolicyViewLogs
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap
|
||||
.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOG_VIEW
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<OnCallDutyPolicyViewLogsView
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap
|
||||
.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOG_VIEW
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Misc Routes */}
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.LOGOUT]?.toString() || ''}
|
||||
@@ -1818,6 +2018,54 @@ const App: FunctionComponent = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
{/* User Settings */}
|
||||
|
||||
<PageRoute
|
||||
path={RouteMap[PageMap.USER_SETTINGS]?.toString() || ''}
|
||||
element={
|
||||
<UserSettingsNotificationMethods
|
||||
{...commonPageProps}
|
||||
pageRoute={RouteMap[PageMap.USER_SETTINGS] as Route}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.USER_SETTINGS_NOTIFICATION_METHODS
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<UserSettingsNotificationMethods
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.USER_SETTINGS_NOTIFICATION_METHODS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.USER_SETTINGS_ON_CALL_RULES
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<UserSettingsNotificationRules
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.USER_SETTINGS_ON_CALL_RULES
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* 👇️ only match this when no other routes match */}
|
||||
<PageRoute
|
||||
path="*"
|
||||
|
||||
@@ -6,7 +6,7 @@ const DashboardFooter: FunctionComponent = () => {
|
||||
return (
|
||||
<Footer
|
||||
className="bg-white h-16 inset-x-0 bottom-0 px-8"
|
||||
copyright="OneUptime Limited."
|
||||
copyright="HackerBay, Inc."
|
||||
links={[
|
||||
{
|
||||
title: 'Help and Support',
|
||||
|
||||
52
Dashboard/src/Components/Monitor/DisabledWarning.tsx
Normal file
52
Dashboard/src/Components/Monitor/DisabledWarning.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import React, { FunctionComponent, ReactElement, useState } from 'react';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Alert, { AlertType } from 'CommonUI/src/Components/Alerts/Alert';
|
||||
import { useAsyncEffect } from 'use-async-effect';
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
|
||||
export interface ComponentProps {
|
||||
monitorId: ObjectID | undefined;
|
||||
refreshToggle?: boolean | undefined;
|
||||
}
|
||||
|
||||
const DisabledWarning: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
setIsLoading(true);
|
||||
const monitorCount: number = await ModelAPI.count(Monitor, {
|
||||
_id: props.monitorId,
|
||||
disableActiveMonitoring: true,
|
||||
});
|
||||
|
||||
if (monitorCount > 0) {
|
||||
setIsDisabled(true);
|
||||
} else {
|
||||
setIsDisabled(false);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
}, [props.refreshToggle]);
|
||||
|
||||
if (isLoading) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (isDisabled) {
|
||||
return (
|
||||
<Alert
|
||||
type={AlertType.DANGER}
|
||||
strongTitle="This monitor is disabled"
|
||||
title="We are not monitoring this monitor since it is disabled. To enable active monitoring, please go to Settings."
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default DisabledWarning;
|
||||
@@ -129,6 +129,18 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
),
|
||||
}}
|
||||
>
|
||||
{/* <NavBarMenuItem
|
||||
title="On-Call Duty"
|
||||
description="Manage your on-call schedules, escalations and more."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
)}
|
||||
icon={IconProp.Call}
|
||||
onClick={() => {
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/> */}
|
||||
|
||||
<NavBarMenuItem
|
||||
title="Workflows"
|
||||
description="Integrate OneUptime with the rest of your ecosystem."
|
||||
@@ -142,7 +154,7 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
<NavBarMenuItem
|
||||
title="Project Settings"
|
||||
description="Review or manage project settings here."
|
||||
description="Review or manage settings related to this project here."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SETTINGS] as Route
|
||||
)}
|
||||
@@ -151,6 +163,17 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/>
|
||||
{/* <NavBarMenuItem
|
||||
title="User Settings"
|
||||
description="Review or manage user settings related to this project here."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.USER_SETTINGS] as Route
|
||||
)}
|
||||
icon={IconProp.User}
|
||||
onClick={() => {
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/> */}
|
||||
|
||||
{/* <NavBarMenuItem
|
||||
title="Logs Management"
|
||||
@@ -168,15 +191,6 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
)}
|
||||
icon={IconProp.Error}
|
||||
/>
|
||||
<NavBarMenuItem
|
||||
title="On-Call Duty"
|
||||
description='Manage you on-call schedules, escalations and more.'
|
||||
route={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
)}
|
||||
icon={IconProp.Call}
|
||||
></NavBarMenuItem>
|
||||
|
||||
|
||||
<NavBarMenuItem
|
||||
title="Reports"
|
||||
|
||||
295
Dashboard/src/Components/NotificationMethods/Call.tsx
Normal file
295
Dashboard/src/Components/NotificationMethods/Call.tsx
Normal file
@@ -0,0 +1,295 @@
|
||||
import UserCall from 'Model/Models/UserCall';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import BasicFormModal from 'CommonUI/src/Components/FormModal/BasicFormModal';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
|
||||
import { DASHBOARD_API_URL } from 'CommonUI/src/Config';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
|
||||
const Call: FunctionComponent = (): ReactElement => {
|
||||
const [showVerificationCodeModal, setShowVerificationCodeModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [showResendCodeModal, setShowResendCodeModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [error, setError] = useState<string>('');
|
||||
const [currentItem, setCurrentItem] = useState<JSONObject | null>(null);
|
||||
const [refreshToggle, setRefreshToggle] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
const [
|
||||
showVerificationCodeResentModal,
|
||||
setShowVerificationCodeResentModal,
|
||||
] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setError('');
|
||||
}, [showVerificationCodeModal]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModelTable<UserCall>
|
||||
modelType={UserCall}
|
||||
query={{
|
||||
projectId: DashboardNavigation.getProjectId()?.toString(),
|
||||
userId: User.getUserId().toString(),
|
||||
}}
|
||||
refreshToggle={refreshToggle}
|
||||
onBeforeCreate={(model: UserCall): UserCall => {
|
||||
model.projectId = DashboardNavigation.getProjectId()!;
|
||||
model.userId = User.getUserId();
|
||||
return model;
|
||||
}}
|
||||
createVerb={'Add'}
|
||||
actionButtons={[
|
||||
{
|
||||
title: 'Verify',
|
||||
buttonStyleType: ButtonStyleType.SUCCESS_OUTLINE,
|
||||
icon: IconProp.Check,
|
||||
isVisible: (item: JSONObject): boolean => {
|
||||
if (item['isVerified']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onClick: async (
|
||||
item: JSONObject,
|
||||
onCompleteAction: Function,
|
||||
onError: (err: Error) => void
|
||||
) => {
|
||||
try {
|
||||
setCurrentItem(item);
|
||||
setShowVerificationCodeModal(true);
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Resend Code',
|
||||
buttonStyleType: ButtonStyleType.NORMAL,
|
||||
icon: IconProp.Call,
|
||||
isVisible: (item: JSONObject): boolean => {
|
||||
if (item['isVerified']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onClick: async (
|
||||
item: JSONObject,
|
||||
onCompleteAction: Function,
|
||||
onError: (err: Error) => void
|
||||
) => {
|
||||
try {
|
||||
setCurrentItem(item);
|
||||
setShowResendCodeModal(true);
|
||||
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
id="user-call"
|
||||
name="User Settings > Notification Methods > Call"
|
||||
isDeleteable={true}
|
||||
isEditable={false}
|
||||
isCreateable={true}
|
||||
cardProps={{
|
||||
icon: IconProp.Call,
|
||||
title: 'Phone Numbers for Call Notifications',
|
||||
description:
|
||||
'Manage Phone Numbers that will receive call notifications for this project.',
|
||||
}}
|
||||
noItemsMessage={
|
||||
'No phone numbers found. Please add one to receive notifications.'
|
||||
}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
phone: true,
|
||||
},
|
||||
title: 'Phone Number',
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
placeholder: '+11234567890',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
showFilterButton={false}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
phone: true,
|
||||
},
|
||||
title: 'Phone Number',
|
||||
type: FieldType.Phone,
|
||||
isFilterable: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isVerified: true,
|
||||
},
|
||||
title: 'Verified',
|
||||
type: FieldType.Boolean,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{showVerificationCodeModal && currentItem ? (
|
||||
<BasicFormModal
|
||||
title={'Verify Phone Number'}
|
||||
onClose={() => {
|
||||
setShowVerificationCodeModal(false);
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
name="Verify Phone Number"
|
||||
submitButtonText={'Verify'}
|
||||
onSubmit={async (item: JSONObject) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response:
|
||||
| HTTPResponse<JSONObject>
|
||||
| HTTPErrorResponse = await API.post(
|
||||
URL.fromString(
|
||||
DASHBOARD_API_URL.toString()
|
||||
).addRoute('/user-call/verify'),
|
||||
{
|
||||
code: item['code'],
|
||||
projectId:
|
||||
DashboardNavigation.getProjectId()?.toString(),
|
||||
itemId: currentItem['_id'],
|
||||
}
|
||||
);
|
||||
|
||||
if (response.isFailure()) {
|
||||
setError(API.getFriendlyMessage(response));
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setShowVerificationCodeModal(false);
|
||||
setRefreshToggle(!refreshToggle);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(API.getFriendlyMessage(e));
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
formProps={{
|
||||
error: error || '',
|
||||
fields: [
|
||||
{
|
||||
title: 'Verification Code',
|
||||
description: `We're calling you with your verifiction code. Please make sure this device can receive calls.`,
|
||||
field: {
|
||||
code: true,
|
||||
},
|
||||
placeholder: '123456',
|
||||
required: true,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
maxLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Number,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{showResendCodeModal && currentItem ? (
|
||||
<ConfirmModal
|
||||
title={`Resend Code`}
|
||||
error={error}
|
||||
description={
|
||||
'Are you sure you want to resend verification code? We will make a call to this number.'
|
||||
}
|
||||
submitButtonText={'Resend Code'}
|
||||
onClose={() => {
|
||||
setShowResendCodeModal(false);
|
||||
setError('');
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
onSubmit={async () => {
|
||||
try {
|
||||
const response:
|
||||
| HTTPResponse<JSONObject>
|
||||
| HTTPErrorResponse = await API.post(
|
||||
URL.fromString(
|
||||
DASHBOARD_API_URL.toString()
|
||||
).addRoute(
|
||||
'/user-call/resend-verification-code'
|
||||
),
|
||||
{
|
||||
projectId:
|
||||
DashboardNavigation.getProjectId()?.toString(),
|
||||
itemId: currentItem['_id'],
|
||||
}
|
||||
);
|
||||
|
||||
if (response.isFailure()) {
|
||||
setError(API.getFriendlyMessage(response));
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setShowResendCodeModal(false);
|
||||
setShowVerificationCodeResentModal(true);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{showVerificationCodeResentModal ? (
|
||||
<ConfirmModal
|
||||
title={`Calling you with your verification code`}
|
||||
error={error}
|
||||
description={
|
||||
'We are calling you with your verification code. Please make sure this device can receive calls.'
|
||||
}
|
||||
submitButtonText={'Close'}
|
||||
onSubmit={async () => {
|
||||
setShowVerificationCodeResentModal(false);
|
||||
setError('');
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Call;
|
||||
295
Dashboard/src/Components/NotificationMethods/Email.tsx
Normal file
295
Dashboard/src/Components/NotificationMethods/Email.tsx
Normal file
@@ -0,0 +1,295 @@
|
||||
import UserEmail from 'Model/Models/UserEmail';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import BasicFormModal from 'CommonUI/src/Components/FormModal/BasicFormModal';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
|
||||
import { DASHBOARD_API_URL } from 'CommonUI/src/Config';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
|
||||
const Email: FunctionComponent = (): ReactElement => {
|
||||
const [showVerificationCodeModal, setShowVerificationCodeModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [showResendCodeModal, setShowResendCodeModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [error, setError] = useState<string>('');
|
||||
const [currentItem, setCurrentItem] = useState<JSONObject | null>(null);
|
||||
const [refreshToggle, setRefreshToggle] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
const [
|
||||
showVerificationCodeResentModal,
|
||||
setShowVerificationCodeResentModal,
|
||||
] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setError('');
|
||||
}, [showVerificationCodeModal]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModelTable<UserEmail>
|
||||
modelType={UserEmail}
|
||||
query={{
|
||||
projectId: DashboardNavigation.getProjectId()?.toString(),
|
||||
userId: User.getUserId().toString(),
|
||||
}}
|
||||
refreshToggle={refreshToggle}
|
||||
onBeforeCreate={(model: UserEmail): UserEmail => {
|
||||
model.projectId = DashboardNavigation.getProjectId()!;
|
||||
model.userId = User.getUserId();
|
||||
return model;
|
||||
}}
|
||||
createVerb={'Add'}
|
||||
actionButtons={[
|
||||
{
|
||||
title: 'Verify',
|
||||
buttonStyleType: ButtonStyleType.SUCCESS_OUTLINE,
|
||||
icon: IconProp.Check,
|
||||
isVisible: (item: JSONObject): boolean => {
|
||||
if (item['isVerified']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onClick: async (
|
||||
item: JSONObject,
|
||||
onCompleteAction: Function,
|
||||
onError: (err: Error) => void
|
||||
) => {
|
||||
try {
|
||||
setCurrentItem(item);
|
||||
setShowVerificationCodeModal(true);
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Resend Code',
|
||||
buttonStyleType: ButtonStyleType.NORMAL,
|
||||
icon: IconProp.Email,
|
||||
isVisible: (item: JSONObject): boolean => {
|
||||
if (item['isVerified']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onClick: async (
|
||||
item: JSONObject,
|
||||
onCompleteAction: Function,
|
||||
onError: (err: Error) => void
|
||||
) => {
|
||||
try {
|
||||
setCurrentItem(item);
|
||||
setShowResendCodeModal(true);
|
||||
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
id="user-emails"
|
||||
name="User Settings > Notification Methods > Emails"
|
||||
isDeleteable={true}
|
||||
isEditable={false}
|
||||
isCreateable={true}
|
||||
cardProps={{
|
||||
icon: IconProp.Email,
|
||||
title: 'Emails for Notifications',
|
||||
description:
|
||||
'Manage emails that will receive notifications for this project.',
|
||||
}}
|
||||
noItemsMessage={
|
||||
'No emails found. Please add one to receive notifications.'
|
||||
}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: 'Email',
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
required: true,
|
||||
placeholder: 'you@company.com',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
showFilterButton={false}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
title: 'Email',
|
||||
type: FieldType.Email,
|
||||
isFilterable: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isVerified: true,
|
||||
},
|
||||
title: 'Verified',
|
||||
type: FieldType.Boolean,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{showVerificationCodeModal && currentItem ? (
|
||||
<BasicFormModal
|
||||
title={'Verify Email'}
|
||||
onClose={() => {
|
||||
setShowVerificationCodeModal(false);
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
name="Verify Email"
|
||||
submitButtonText={'Verify'}
|
||||
onSubmit={async (item: JSONObject) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response:
|
||||
| HTTPResponse<JSONObject>
|
||||
| HTTPErrorResponse = await API.post(
|
||||
URL.fromString(
|
||||
DASHBOARD_API_URL.toString()
|
||||
).addRoute('/user-email/verify'),
|
||||
{
|
||||
code: item['code'],
|
||||
projectId:
|
||||
DashboardNavigation.getProjectId()?.toString(),
|
||||
itemId: currentItem['_id'],
|
||||
}
|
||||
);
|
||||
|
||||
if (response.isFailure()) {
|
||||
setError(API.getFriendlyMessage(response));
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setShowVerificationCodeModal(false);
|
||||
setRefreshToggle(!refreshToggle);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(API.getFriendlyMessage(e));
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
formProps={{
|
||||
error: error || '',
|
||||
fields: [
|
||||
{
|
||||
title: 'Verification Code',
|
||||
description: `We have sent verifiction code to your email. Please dont forget to check your spam.`,
|
||||
field: {
|
||||
code: true,
|
||||
},
|
||||
placeholder: '123456',
|
||||
required: true,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
maxLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Number,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{showResendCodeModal && currentItem ? (
|
||||
<ConfirmModal
|
||||
title={`Resend Code`}
|
||||
error={error}
|
||||
description={
|
||||
'Are you sure you want to resend verification code?'
|
||||
}
|
||||
submitButtonText={'Resend Code'}
|
||||
onClose={() => {
|
||||
setShowResendCodeModal(false);
|
||||
setError('');
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
onSubmit={async () => {
|
||||
try {
|
||||
const response:
|
||||
| HTTPResponse<JSONObject>
|
||||
| HTTPErrorResponse = await API.post(
|
||||
URL.fromString(
|
||||
DASHBOARD_API_URL.toString()
|
||||
).addRoute(
|
||||
'/user-email/resend-verification-code'
|
||||
),
|
||||
{
|
||||
projectId:
|
||||
DashboardNavigation.getProjectId()?.toString(),
|
||||
itemId: currentItem['_id'],
|
||||
}
|
||||
);
|
||||
|
||||
if (response.isFailure()) {
|
||||
setError(API.getFriendlyMessage(response));
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setShowResendCodeModal(false);
|
||||
setShowVerificationCodeResentModal(true);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{showVerificationCodeResentModal ? (
|
||||
<ConfirmModal
|
||||
title={`Code sent successfully`}
|
||||
error={error}
|
||||
description={
|
||||
'We have sent verification code to your email. Please dont forget to check your spam.'
|
||||
}
|
||||
submitButtonText={'Close'}
|
||||
onSubmit={async () => {
|
||||
setShowVerificationCodeResentModal(false);
|
||||
setError('');
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Email;
|
||||
295
Dashboard/src/Components/NotificationMethods/SMS.tsx
Normal file
295
Dashboard/src/Components/NotificationMethods/SMS.tsx
Normal file
@@ -0,0 +1,295 @@
|
||||
import UserSMS from 'Model/Models/UserSMS';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import BasicFormModal from 'CommonUI/src/Components/FormModal/BasicFormModal';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
|
||||
import { DASHBOARD_API_URL } from 'CommonUI/src/Config';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
|
||||
const SMS: FunctionComponent = (): ReactElement => {
|
||||
const [showVerificationCodeModal, setShowVerificationCodeModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [showResendCodeModal, setShowResendCodeModal] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [error, setError] = useState<string>('');
|
||||
const [currentItem, setCurrentItem] = useState<JSONObject | null>(null);
|
||||
const [refreshToggle, setRefreshToggle] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
const [
|
||||
showVerificationCodeResentModal,
|
||||
setShowVerificationCodeResentModal,
|
||||
] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setError('');
|
||||
}, [showVerificationCodeModal]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModelTable<UserSMS>
|
||||
modelType={UserSMS}
|
||||
query={{
|
||||
projectId: DashboardNavigation.getProjectId()?.toString(),
|
||||
userId: User.getUserId().toString(),
|
||||
}}
|
||||
refreshToggle={refreshToggle}
|
||||
onBeforeCreate={(model: UserSMS): UserSMS => {
|
||||
model.projectId = DashboardNavigation.getProjectId()!;
|
||||
model.userId = User.getUserId();
|
||||
return model;
|
||||
}}
|
||||
createVerb={'Add'}
|
||||
actionButtons={[
|
||||
{
|
||||
title: 'Verify',
|
||||
buttonStyleType: ButtonStyleType.SUCCESS_OUTLINE,
|
||||
icon: IconProp.Check,
|
||||
isVisible: (item: JSONObject): boolean => {
|
||||
if (item['isVerified']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onClick: async (
|
||||
item: JSONObject,
|
||||
onCompleteAction: Function,
|
||||
onError: (err: Error) => void
|
||||
) => {
|
||||
try {
|
||||
setCurrentItem(item);
|
||||
setShowVerificationCodeModal(true);
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Resend Code',
|
||||
buttonStyleType: ButtonStyleType.NORMAL,
|
||||
icon: IconProp.SMS,
|
||||
isVisible: (item: JSONObject): boolean => {
|
||||
if (item['isVerified']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
onClick: async (
|
||||
item: JSONObject,
|
||||
onCompleteAction: Function,
|
||||
onError: (err: Error) => void
|
||||
) => {
|
||||
try {
|
||||
setCurrentItem(item);
|
||||
setShowResendCodeModal(true);
|
||||
|
||||
onCompleteAction();
|
||||
} catch (err) {
|
||||
onCompleteAction();
|
||||
onError(err as Error);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
id="user-sms"
|
||||
name="User Settings > Notification Methods > SMS"
|
||||
isDeleteable={true}
|
||||
isEditable={false}
|
||||
isCreateable={true}
|
||||
cardProps={{
|
||||
icon: IconProp.SMS,
|
||||
title: 'Phone Numbers for SMS Notifications',
|
||||
description:
|
||||
'Manage Phone Numbers that will receive SMS notifications for this project.',
|
||||
}}
|
||||
noItemsMessage={
|
||||
'No phone numbers found. Please add one to receive notifications.'
|
||||
}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
phone: true,
|
||||
},
|
||||
title: 'Phone Number',
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
placeholder: '+11234567890',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
showFilterButton={false}
|
||||
columns={[
|
||||
{
|
||||
field: {
|
||||
phone: true,
|
||||
},
|
||||
title: 'Phone Number',
|
||||
type: FieldType.Phone,
|
||||
isFilterable: false,
|
||||
},
|
||||
{
|
||||
field: {
|
||||
isVerified: true,
|
||||
},
|
||||
title: 'Verified',
|
||||
type: FieldType.Boolean,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{showVerificationCodeModal && currentItem ? (
|
||||
<BasicFormModal
|
||||
title={'Verify Phone Number'}
|
||||
onClose={() => {
|
||||
setShowVerificationCodeModal(false);
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
name="Verify Phone Number"
|
||||
submitButtonText={'Verify'}
|
||||
onSubmit={async (item: JSONObject) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response:
|
||||
| HTTPResponse<JSONObject>
|
||||
| HTTPErrorResponse = await API.post(
|
||||
URL.fromString(
|
||||
DASHBOARD_API_URL.toString()
|
||||
).addRoute('/user-sms/verify'),
|
||||
{
|
||||
code: item['code'],
|
||||
projectId:
|
||||
DashboardNavigation.getProjectId()?.toString(),
|
||||
itemId: currentItem['_id'],
|
||||
}
|
||||
);
|
||||
|
||||
if (response.isFailure()) {
|
||||
setError(API.getFriendlyMessage(response));
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setShowVerificationCodeModal(false);
|
||||
setRefreshToggle(!refreshToggle);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(API.getFriendlyMessage(e));
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
formProps={{
|
||||
error: error || '',
|
||||
fields: [
|
||||
{
|
||||
title: 'Verification Code',
|
||||
description: `We have sent an SMS with your verifiction code. Please dont forget to check your spam.`,
|
||||
field: {
|
||||
code: true,
|
||||
},
|
||||
placeholder: '123456',
|
||||
required: true,
|
||||
validation: {
|
||||
minLength: 6,
|
||||
maxLength: 6,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Number,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{showResendCodeModal && currentItem ? (
|
||||
<ConfirmModal
|
||||
title={`Resend Code`}
|
||||
error={error}
|
||||
description={
|
||||
'Are you sure you want to resend verification code?'
|
||||
}
|
||||
submitButtonText={'Resend Code'}
|
||||
onClose={() => {
|
||||
setShowResendCodeModal(false);
|
||||
setError('');
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
onSubmit={async () => {
|
||||
try {
|
||||
const response:
|
||||
| HTTPResponse<JSONObject>
|
||||
| HTTPErrorResponse = await API.post(
|
||||
URL.fromString(
|
||||
DASHBOARD_API_URL.toString()
|
||||
).addRoute(
|
||||
'/user-sms/resend-verification-code'
|
||||
),
|
||||
{
|
||||
projectId:
|
||||
DashboardNavigation.getProjectId()?.toString(),
|
||||
itemId: currentItem['_id'],
|
||||
}
|
||||
);
|
||||
|
||||
if (response.isFailure()) {
|
||||
setError(API.getFriendlyMessage(response));
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setShowResendCodeModal(false);
|
||||
setShowVerificationCodeResentModal(true);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
setIsLoading(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{showVerificationCodeResentModal ? (
|
||||
<ConfirmModal
|
||||
title={`Code sent successfully`}
|
||||
error={error}
|
||||
description={
|
||||
'We have sent verification code to your sms. Please dont forget to check your spam.'
|
||||
}
|
||||
submitButtonText={'Close'}
|
||||
onSubmit={async () => {
|
||||
setShowVerificationCodeResentModal(false);
|
||||
setError('');
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SMS;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { DropdownOption } from 'CommonUI/src/Components/Dropdown/Dropdown';
|
||||
|
||||
const NotifyAfterMinutesDropdownOptions: Array<DropdownOption> = [
|
||||
{
|
||||
value: 0,
|
||||
label: 'Immediately',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: '5 minutes',
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
label: '10 minutes',
|
||||
},
|
||||
{
|
||||
value: 15,
|
||||
label: '15 minutes',
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
label: '30 minutes',
|
||||
},
|
||||
{
|
||||
value: 60,
|
||||
label: '1 hour',
|
||||
},
|
||||
];
|
||||
|
||||
export default NotifyAfterMinutesDropdownOptions;
|
||||
@@ -62,24 +62,6 @@ const Home: FunctionComponent<PageComponentProps> = (
|
||||
required: true,
|
||||
title: 'Full Name',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
companyName: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: 'Acme, Inc.',
|
||||
required: true,
|
||||
title: 'Company Name',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
companyPhoneNumber: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Phone,
|
||||
required: true,
|
||||
placeholder: '+1-123-456-7890',
|
||||
title: 'Phone Number',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
showDetailsInNumberOfColumns: 2,
|
||||
@@ -98,18 +80,6 @@ const Home: FunctionComponent<PageComponentProps> = (
|
||||
},
|
||||
title: 'Email',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
companyName: true,
|
||||
},
|
||||
title: 'Company Name',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
companyPhoneNumber: true,
|
||||
},
|
||||
title: 'Company Phone Number',
|
||||
},
|
||||
],
|
||||
modelId: UserUtil.getUserId(),
|
||||
}}
|
||||
|
||||
@@ -28,28 +28,28 @@ const IncidentCustomFields: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Custom Fields',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW_CUSTOM_FIELDS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -26,28 +26,28 @@ const IncidentDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Delete Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW_DELETE] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -47,21 +47,21 @@ const IncidentView: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -40,28 +40,28 @@ const IncidentDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Private Notes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_INTERNAL_NOTE] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -39,28 +39,28 @@ const IncidentOwners: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Owners',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW_OWNERS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -39,28 +39,28 @@ const PublicNote: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Public Notes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_PUBLIC_NOTE] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -23,7 +23,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Overview',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Info}
|
||||
@@ -35,7 +35,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.INCIDENT_VIEW_STATE_TIMELINE
|
||||
] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.List}
|
||||
@@ -46,7 +46,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Owners',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW_OWNERS] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Team}
|
||||
@@ -59,7 +59,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Private Notes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_INTERNAL_NOTE] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Lock}
|
||||
@@ -69,7 +69,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Public Notes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_PUBLIC_NOTE] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Public}
|
||||
@@ -84,7 +84,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.INCIDENT_VIEW_CUSTOM_FIELDS
|
||||
] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.TableCells}
|
||||
@@ -95,7 +95,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Delete Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW_DELETE] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Trash}
|
||||
|
||||
@@ -35,28 +35,28 @@ const IncidentDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Incident',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Status Timeline',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.INCIDENT_VIEW_STATE_TIMELINE] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
59
Dashboard/src/Pages/Monitor/DisabledMonitors.tsx
Normal file
59
Dashboard/src/Pages/Monitor/DisabledMonitors.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import Page from 'CommonUI/src/Components/Page/Page';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import PageComponentProps from '../PageComponentProps';
|
||||
import MonitorTable from '../../Components/Monitor/MonitorTable';
|
||||
import DashboardSideMenu from './SideMenu';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
|
||||
const DisabledMonitors: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps
|
||||
): ReactElement => {
|
||||
return (
|
||||
<Page
|
||||
title={'Home'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Disabled Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS_DISABLED] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={
|
||||
<DashboardSideMenu
|
||||
project={props.currentProject || undefined}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MonitorTable
|
||||
viewPageRoute={RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route
|
||||
)}
|
||||
query={{
|
||||
projectId: DashboardNavigation.getProjectId()?.toString(),
|
||||
disableActiveMonitoring: true,
|
||||
}}
|
||||
noItemsMessage="No disabled monitors. All monitors in active state."
|
||||
title="Disabled Monitors"
|
||||
description="Here is a list of all the monitors which are in disbaled state."
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisabledMonitors;
|
||||
@@ -48,6 +48,21 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<CountModelSideMenuItem<Monitor>
|
||||
link={{
|
||||
title: 'Disabled Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS_DISABLED] as Route
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Error}
|
||||
badgeType={BadgeType.DANGER}
|
||||
modelType={Monitor}
|
||||
countQuery={{
|
||||
projectId: props.project?._id,
|
||||
disableActiveMonitoring: true,
|
||||
}}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@ import API from 'CommonUI/src/Utils/API/API';
|
||||
import ComponentLoader from 'CommonUI/src/Components/ComponentLoader/ComponentLoader';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import EmptyState from 'CommonUI/src/Components/EmptyState/EmptyState';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorCriteria: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@@ -189,33 +190,34 @@ const MonitorCriteria: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Criteria',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_CRITERIA] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
{getPageContent()}
|
||||
</ModelPage>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import CustomFieldsDetail from 'CommonUI/src/Components/CustomFields/CustomField
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
import MonitorCustomField from 'Model/Models/MonitorCustomField';
|
||||
import ProjectUtil from 'CommonUI/src/Utils/Project';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorCustomFields: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@@ -28,33 +29,34 @@ const MonitorCustomFields: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Custom Fields',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_DELETE] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
<CustomFieldsDetail
|
||||
title="Monitor Custom Fields"
|
||||
description="Custom fields help you add new fields to your resources in OneUptime."
|
||||
|
||||
@@ -9,6 +9,7 @@ import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorDelete: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@@ -26,33 +27,34 @@ const MonitorDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Delete Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_DELETE] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
<ModelDelete
|
||||
modelType={Monitor}
|
||||
modelId={modelId}
|
||||
|
||||
@@ -21,6 +21,7 @@ import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import IncidentSeverity from 'Model/Models/IncidentSeverity';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
const MonitorIncidents: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
@@ -37,33 +38,34 @@ const MonitorIncidents: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_INCIDENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
<ModelTable<Incident>
|
||||
modelType={Incident}
|
||||
id="incidents-table"
|
||||
|
||||
@@ -29,6 +29,7 @@ import ModelAPI, { ListResult } from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import MonitorStatusTimeline from 'Model/Models/MonitorStatusTimeline';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorView: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@@ -92,26 +93,27 @@ const MonitorView: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
{/* Monitor View */}
|
||||
<CardModelDetail
|
||||
name="Monitor Details"
|
||||
|
||||
@@ -25,6 +25,7 @@ import API from 'CommonUI/src/Utils/API/API';
|
||||
import ComponentLoader from 'CommonUI/src/Components/ComponentLoader/ComponentLoader';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import EmptyState from 'CommonUI/src/Components/EmptyState/EmptyState';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorCriteria: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@@ -160,33 +161,34 @@ const MonitorCriteria: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Criteria',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_CRITERIA] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
{getPageContent()}
|
||||
</ModelPage>
|
||||
);
|
||||
|
||||
@@ -22,6 +22,7 @@ import MonitorOwnerUser from 'Model/Models/MonitorOwnerUser';
|
||||
import User from 'Model/Models/User';
|
||||
import UserElement from '../../../Components/User/User';
|
||||
import ProjectUser from '../../../Utils/ProjectUser';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorOwners: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@@ -39,33 +40,34 @@ const MonitorOwners: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Owners',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_OWNERS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
<ModelTable<MonitorOwnerTeam>
|
||||
modelType={MonitorOwnerTeam}
|
||||
id="table-monitor-owner-team"
|
||||
|
||||
@@ -31,6 +31,7 @@ import ProbeElement from '../../../Components/Probe/Probe';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { DASHBOARD_API_URL } from 'CommonUI/src/Config';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorProbes: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@@ -249,33 +250,34 @@ const MonitorProbes: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Probes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_PROBES] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
{getPageContent()}
|
||||
</ModelPage>
|
||||
);
|
||||
|
||||
193
Dashboard/src/Pages/Monitor/View/Settings.tsx
Normal file
193
Dashboard/src/Pages/Monitor/View/Settings.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ModelPage from 'CommonUI/src/Components/Page/ModelPage';
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import MonitorType from 'Common/Types/Monitor/MonitorType';
|
||||
import ModelAPI from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import ComponentLoader from 'CommonUI/src/Components/ComponentLoader/ComponentLoader';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import EmptyState from 'CommonUI/src/Components/EmptyState/EmptyState';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
|
||||
const MonitorCriteria: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
const [alertRefreshToggle, setAlertRefreshToggle] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
const fetchItem: Function = async (): Promise<void> => {
|
||||
// get item.
|
||||
setIsLoading(true);
|
||||
|
||||
setError('');
|
||||
try {
|
||||
const item: Monitor | null = await ModelAPI.getItem(
|
||||
Monitor,
|
||||
modelId,
|
||||
{
|
||||
monitorType: true,
|
||||
} as any,
|
||||
{}
|
||||
);
|
||||
|
||||
if (!item) {
|
||||
setError(`Monitor not found`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setMonitorType(item.monitorType);
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const [monitorType, setMonitorType] = useState<MonitorType | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// fetch the model
|
||||
fetchItem();
|
||||
}, []);
|
||||
|
||||
const getPageContent: Function = (): ReactElement => {
|
||||
if (!monitorType || isLoading) {
|
||||
return <ComponentLoader />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
if (monitorType === MonitorType.Manual) {
|
||||
return (
|
||||
<EmptyState
|
||||
icon={IconProp.Settings}
|
||||
title={'No Settings for Manual Monitors'}
|
||||
description={
|
||||
<>
|
||||
This is a manual monitor and it cannot have any
|
||||
settings. You can have monitor settings on other
|
||||
monitor types.{' '}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CardModelDetail
|
||||
name="Monitor Settings"
|
||||
editButtonText="Edit Settings"
|
||||
cardProps={{
|
||||
title: 'Monitor Settings',
|
||||
description:
|
||||
'Here are some advanced settings for this monitor.',
|
||||
icon: IconProp.Settings,
|
||||
}}
|
||||
onSaveSuccess={() => {
|
||||
setAlertRefreshToggle(!alertRefreshToggle);
|
||||
}}
|
||||
isEditable={true}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
disableActiveMonitoring: true,
|
||||
},
|
||||
|
||||
title: 'Disable Active Monitoring',
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
showDetailsInNumberOfColumns: 1,
|
||||
modelType: Monitor,
|
||||
id: 'model-detail-monitors',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
disableActiveMonitoring: true,
|
||||
},
|
||||
title: 'Disable Active Monitoring',
|
||||
fieldType: FieldType.Boolean,
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
title="Monitor"
|
||||
modelType={Monitor}
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Criteria',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_CRITERIA] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning
|
||||
monitorId={modelId}
|
||||
refreshToggle={alertRefreshToggle}
|
||||
/>
|
||||
{getPageContent()}
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default MonitorCriteria;
|
||||
@@ -23,7 +23,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Overview',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Info}
|
||||
@@ -33,7 +33,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Owners',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_OWNERS] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Team}
|
||||
@@ -43,7 +43,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Criteria',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_CRITERIA] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Criteria}
|
||||
@@ -53,7 +53,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Interval',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_INTERVAL] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Clock}
|
||||
@@ -68,7 +68,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.MONITOR_VIEW_STATUS_TIMELINE
|
||||
] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.List}
|
||||
@@ -78,7 +78,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Incidents',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_INCIDENTS] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Alert}
|
||||
@@ -91,7 +91,7 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
title: 'Probes',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_PROBES] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Signal}
|
||||
@@ -103,17 +103,27 @@ const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.MONITOR_VIEW_CUSTOM_FIELDS
|
||||
] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.TableCells}
|
||||
/>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Settings',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_SETTINGS] as Route,
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Settings}
|
||||
/>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Delete Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_DELETE] as Route,
|
||||
props.modelId
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Trash}
|
||||
|
||||
@@ -19,6 +19,7 @@ import Statusbubble from 'CommonUI/src/Components/StatusBubble/StatusBubble';
|
||||
import Color from 'Common/Types/Color';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
const MonitorDelete: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps
|
||||
): ReactElement => {
|
||||
@@ -35,33 +36,34 @@ const MonitorDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Monitors',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITORS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Monitor',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Status Timeline',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.MONITOR_VIEW_STATUS_TIMELINE] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<DisabledWarning monitorId={modelId} />
|
||||
<ModelTable<MonitorStatusTimeline>
|
||||
modelType={MonitorStatusTimeline}
|
||||
id="table-monitor-status-timeline"
|
||||
|
||||
@@ -5,7 +5,7 @@ import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import PageComponentProps from '../PageComponentProps';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import OnCallDuty from 'Model/Models/OnCallDuty';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
@@ -15,12 +15,13 @@ import LabelsElement from '../../Components/Label/Labels';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
|
||||
const OnCallDutyPage: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
return (
|
||||
<Page
|
||||
title={'On-Call-Duty'}
|
||||
title={'On-Call Duty'}
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
@@ -34,23 +35,30 @@ const OnCallDutyPage: FunctionComponent<PageComponentProps> = (
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Policies',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICIES] as Route
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ModelTable<OnCallDuty>
|
||||
modelType={OnCallDuty}
|
||||
<ModelTable<OnCallDutyPolicy>
|
||||
modelType={OnCallDutyPolicy}
|
||||
id="on-call-duty-table"
|
||||
isDeleteable={false}
|
||||
name="Scheduled Maintenance Events > On Call Duties"
|
||||
isEditable={true}
|
||||
name="On Call > Policies"
|
||||
showViewIdButton={true}
|
||||
isEditable={false}
|
||||
isCreateable={true}
|
||||
isViewable={true}
|
||||
cardProps={{
|
||||
icon: IconProp.Call,
|
||||
title: 'On Call Duties',
|
||||
title: 'On Call Duty Policies',
|
||||
description:
|
||||
'Here is a list of on-call-duty schedules for this project.',
|
||||
'Here is a list of on-call-duty policies for this project.',
|
||||
}}
|
||||
noItemsMessage={'No on-call-duty found.'}
|
||||
noItemsMessage={'No on-call policy found.'}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
@@ -0,0 +1,73 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ModelPage from 'CommonUI/src/Components/Page/ModelPage';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import CustomFieldsDetail from 'CommonUI/src/Components/CustomFields/CustomFieldsDetail';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
import OnCallDutyPolicyCustomField from 'Model/Models/OnCallDutyPolicyCustomField';
|
||||
import ProjectUtil from 'CommonUI/src/Utils/Project';
|
||||
|
||||
const OnCallDutyPolicyCustomFields: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
title="On Call Policy"
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'On Call Duty',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Custom Fields',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS
|
||||
] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<CustomFieldsDetail
|
||||
title="Custom Fields"
|
||||
description="Custom fields help you add new fields to your resources in OneUptime."
|
||||
modelType={OnCallDutyPolicy}
|
||||
customFieldType={OnCallDutyPolicyCustomField}
|
||||
name="Custom Fields"
|
||||
projectId={ProjectUtil.getCurrentProject()!.id!}
|
||||
modelId={modelId}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnCallDutyPolicyCustomFields;
|
||||
71
Dashboard/src/Pages/OnCallDuty/OnCallDutyPolicy/Delete.tsx
Normal file
71
Dashboard/src/Pages/OnCallDuty/OnCallDutyPolicy/Delete.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ModelPage from 'CommonUI/src/Components/Page/ModelPage';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
|
||||
const OnCallPolicyDelete: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
title="On Call Policy"
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'On Call Duty',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Delete On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE
|
||||
] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<ModelDelete
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnCallPolicyDelete;
|
||||
@@ -0,0 +1,71 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ModelPage from 'CommonUI/src/Components/Page/ModelPage';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
|
||||
const OnCallPolicyDelete: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
title="On Call Policy"
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'On Call Duty',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Delete On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE
|
||||
] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<ModelDelete
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnCallPolicyDelete;
|
||||
@@ -0,0 +1,71 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ModelPage from 'CommonUI/src/Components/Page/ModelPage';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
|
||||
const OnCallPolicyDelete: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
title="On Call Policy"
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'On Call Duty',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Delete On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE
|
||||
] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<ModelDelete
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnCallPolicyDelete;
|
||||
@@ -0,0 +1,71 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ModelPage from 'CommonUI/src/Components/Page/ModelPage';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
|
||||
const OnCallPolicyDelete: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
title="On Call Policy"
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'On Call Duty',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Delete On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE
|
||||
] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
<ModelDelete
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
onDeleteSuccess={() => {
|
||||
Navigation.navigate(
|
||||
RouteMap[PageMap.ON_CALL_DUTY] as Route
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnCallPolicyDelete;
|
||||
171
Dashboard/src/Pages/OnCallDuty/OnCallDutyPolicy/Index.tsx
Normal file
171
Dashboard/src/Pages/OnCallDuty/OnCallDutyPolicy/Index.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import ModelPage from 'CommonUI/src/Components/Page/ModelPage';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import Label from 'Model/Models/Label';
|
||||
import { JSONArray, JSONObject } from 'Common/Types/JSON';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import LabelsElement from '../../../Components/Label/Labels';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
|
||||
const OnCallDutyPolicyView: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
const modelId: ObjectID = Navigation.getLastParamAsObjectID();
|
||||
|
||||
return (
|
||||
<ModelPage
|
||||
title="On Call Policy"
|
||||
modelType={OnCallDutyPolicy}
|
||||
modelId={modelId}
|
||||
modelNameField="name"
|
||||
breadcrumbLinks={[
|
||||
{
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.STATUS_PAGES] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View On Call Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.STATUS_PAGE_VIEW] as Route,
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
sideMenu={<SideMenu modelId={modelId} />}
|
||||
>
|
||||
{/* OnCallDutyPolicy View */}
|
||||
<CardModelDetail
|
||||
name="On Call Policy > On Call Policy Details"
|
||||
cardProps={{
|
||||
title: 'On Call Policy Details',
|
||||
description: "Here's more details for this on call policy.",
|
||||
icon: IconProp.Call,
|
||||
}}
|
||||
formSteps={[
|
||||
{
|
||||
title: 'On Call Policy Info',
|
||||
id: 'on-call-policy-info',
|
||||
},
|
||||
{
|
||||
title: 'Labels',
|
||||
id: 'labels',
|
||||
},
|
||||
]}
|
||||
isEditable={true}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
stepId: 'on-call-policy-info',
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'On Call Policy Name',
|
||||
validation: {
|
||||
minLength: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
stepId: 'on-call-policy-info',
|
||||
title: 'Description',
|
||||
fieldType: FormFieldSchemaType.LongText,
|
||||
required: true,
|
||||
placeholder: 'Description',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
labels: true,
|
||||
},
|
||||
title: 'Labels ',
|
||||
stepId: 'labels',
|
||||
description:
|
||||
'Team members with access to these labels will only be able to access this resource. This is optional and an advanced feature.',
|
||||
fieldType: FormFieldSchemaType.MultiSelectDropdown,
|
||||
dropdownModal: {
|
||||
type: Label,
|
||||
labelField: 'name',
|
||||
valueField: '_id',
|
||||
},
|
||||
required: false,
|
||||
placeholder: 'Labels',
|
||||
},
|
||||
]}
|
||||
modelDetailProps={{
|
||||
showDetailsInNumberOfColumns: 2,
|
||||
modelType: OnCallDutyPolicy,
|
||||
id: 'model-detail-monitors',
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
_id: true,
|
||||
},
|
||||
title: 'On Call Policy ID',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
title: 'Name',
|
||||
},
|
||||
{
|
||||
field: {
|
||||
labels: {
|
||||
name: true,
|
||||
color: true,
|
||||
},
|
||||
},
|
||||
title: 'Labels',
|
||||
type: FieldType.Text,
|
||||
getElement: (item: JSONObject): ReactElement => {
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
(item['labels'] as JSONArray) ||
|
||||
[],
|
||||
Label
|
||||
) as Array<Label>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: {
|
||||
description: true,
|
||||
},
|
||||
title: 'Description',
|
||||
},
|
||||
],
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
</ModelPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnCallDutyPolicyView;
|
||||
89
Dashboard/src/Pages/OnCallDuty/OnCallDutyPolicy/SideMenu.tsx
Normal file
89
Dashboard/src/Pages/OnCallDuty/OnCallDutyPolicy/SideMenu.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import SideMenu from 'CommonUI/src/Components/SideMenu/SideMenu';
|
||||
import SideMenuItem from 'CommonUI/src/Components/SideMenu/SideMenuItem';
|
||||
import SideMenuSection from 'CommonUI/src/Components/SideMenu/SideMenuSection';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageMap from '../../../Utils/PageMap';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
|
||||
export interface ComponentProps {
|
||||
modelId: ObjectID;
|
||||
}
|
||||
|
||||
const DashboardSideMenu: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
return (
|
||||
<SideMenu>
|
||||
<SideMenuSection title="Basic">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Overview',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.ON_CALL_DUTY_POLICY_VIEW] as Route,
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Info}
|
||||
/>
|
||||
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Escalation Rules',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_ESCALATION
|
||||
] as Route,
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.BarsArrowDown}
|
||||
/>
|
||||
</SideMenuSection>
|
||||
|
||||
<SideMenuSection title="Advanced">
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Execution Logs',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_EXECUTION_LOGS
|
||||
] as Route,
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Logs}
|
||||
/>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Custom Fields',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_CUSTOM_FIELDS
|
||||
] as Route,
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.TableCells}
|
||||
/>
|
||||
<SideMenuItem
|
||||
link={{
|
||||
title: 'Delete Policy',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[
|
||||
PageMap.ON_CALL_DUTY_POLICY_VIEW_DELETE
|
||||
] as Route,
|
||||
{ modelId: props.modelId }
|
||||
),
|
||||
}}
|
||||
icon={IconProp.Trash}
|
||||
className="danger-on-hover"
|
||||
/>
|
||||
</SideMenuSection>
|
||||
</SideMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardSideMenu;
|
||||
@@ -28,21 +28,21 @@ const ScheduledMaintenanceCustomFields: FunctionComponent<
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Scheduled Maintenances',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Scheduled Maintenance',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -51,7 +51,7 @@ const ScheduledMaintenanceCustomFields: FunctionComponent<
|
||||
RouteMap[
|
||||
PageMap.SCHEDULED_MAINTENANCE_VIEW_CUSTOM_FIELDS
|
||||
] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -26,21 +26,21 @@ const IncidentDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Scheduled Maintenance Events',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Scheduled Maintenance Event',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -49,7 +49,7 @@ const IncidentDelete: FunctionComponent<PageComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.SCHEDULED_MAINTENANCE_VIEW_DELETE
|
||||
] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -47,21 +47,21 @@ const ScheduledMaintenanceView: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Scheduled Maintenance Events',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Scheduled Maintenance Event',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -39,21 +39,21 @@ const ScheduledMaintenanceDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Scheduled Maintenance Events',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Scheduled Maintenance Event',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -62,7 +62,7 @@ const ScheduledMaintenanceDelete: FunctionComponent<PageComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.SCHEDULED_MAINTENANCE_INTERNAL_NOTE
|
||||
] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -39,21 +39,21 @@ const ScheduledMaintenanceOwners: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Scheduled MaintenanceOwnerss',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Scheduled Maintenance',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -62,7 +62,7 @@ const ScheduledMaintenanceOwners: FunctionComponent<PageComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.SCHEDULED_MAINTENANCE_VIEW_OWNERS
|
||||
] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -39,21 +39,21 @@ const PublicNote: FunctionComponent<PageComponentProps> = (
|
||||
title: 'Project',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.HOME] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Scheduled Maintenance Events',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_EVENTS] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'View Scheduled Maintenance Event',
|
||||
to: RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.SCHEDULED_MAINTENANCE_VIEW] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -62,7 +62,7 @@ const PublicNote: FunctionComponent<PageComponentProps> = (
|
||||
RouteMap[
|
||||
PageMap.SCHEDULED_MAINTENANCE_PUBLIC_NOTE
|
||||
] as Route,
|
||||
modelId
|
||||
{ modelId }
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user