mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
Add new fields for current status and uptime precision in StatusPageGroup
This commit is contained in:
parent
6c73747564
commit
d79d17e230
@ -541,6 +541,9 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
order: true,
|
||||
description: true,
|
||||
isExpandedByDefault: true,
|
||||
showCurrentStatus: true,
|
||||
showUptimePercent: true,
|
||||
uptimePercentPrecision: true,
|
||||
},
|
||||
sort: {
|
||||
order: SortOrder.Ascending,
|
||||
|
@ -1,18 +1,29 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1726831037585 implements MigrationInterface {
|
||||
public name = 'MigrationName1726831037585'
|
||||
public name = "MigrationName1726831037585";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "StatusPageGroup" ADD "showCurrentStatus" boolean NOT NULL DEFAULT true`);
|
||||
await queryRunner.query(`ALTER TABLE "StatusPageGroup" ADD "showUptimePercent" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "StatusPageGroup" ADD "uptimePercentPrecision" character varying`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "StatusPageGroup" DROP COLUMN "uptimePercentPrecision"`);
|
||||
await queryRunner.query(`ALTER TABLE "StatusPageGroup" DROP COLUMN "showUptimePercent"`);
|
||||
await queryRunner.query(`ALTER TABLE "StatusPageGroup" DROP COLUMN "showCurrentStatus"`);
|
||||
}
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPageGroup" ADD "showCurrentStatus" boolean NOT NULL DEFAULT true`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPageGroup" ADD "showUptimePercent" boolean NOT NULL DEFAULT false`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPageGroup" ADD "uptimePercentPrecision" character varying`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPageGroup" DROP COLUMN "uptimePercentPrecision"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPageGroup" DROP COLUMN "showUptimePercent"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "StatusPageGroup" DROP COLUMN "showCurrentStatus"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -125,5 +125,5 @@ export default [
|
||||
MigrationName1725901024444,
|
||||
MigrationName1725975175669,
|
||||
MigrationName1725976810107,
|
||||
MigrationName1726831037585
|
||||
MigrationName1726831037585,
|
||||
];
|
||||
|
@ -9,6 +9,9 @@ import FieldType from "Common/UI/Components/Types/FieldType";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
import StatusPageGroup from "Common/Models/DatabaseModels/StatusPageGroup";
|
||||
import React, { Fragment, FunctionComponent, ReactElement } from "react";
|
||||
import UptimePrecision from "Common/Types/StatusPage/UptimePrecision";
|
||||
import DropdownUtil from "Common/UI/Utils/Dropdown";
|
||||
import FormValues from "Common/UI/Components/Forms/Types/FormValues";
|
||||
|
||||
const StatusPageDelete: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@ -48,6 +51,16 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
|
||||
"Here are different groups for your status page resources.",
|
||||
}}
|
||||
noItemsMessage={"No status page group created for this status page."}
|
||||
formSteps={[
|
||||
{
|
||||
title: "Group Details",
|
||||
id: "group-details",
|
||||
},
|
||||
{
|
||||
title: "Advanced",
|
||||
id: "advanced",
|
||||
},
|
||||
]}
|
||||
formFields={[
|
||||
{
|
||||
field: {
|
||||
@ -57,6 +70,7 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: "Resource Group Name",
|
||||
stepId: "group-details",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@ -65,6 +79,7 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: "Group Description",
|
||||
fieldType: FormFieldSchemaType.Markdown,
|
||||
required: false,
|
||||
stepId: "group-details",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
@ -73,6 +88,46 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
|
||||
title: "Expand on Status Page by Default",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
stepId: "group-details",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showCurrentStatus: true,
|
||||
},
|
||||
title: "Show Current Group Status",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
defaultValue: true,
|
||||
description:
|
||||
"Current Status will be shown beside this group on your status page.",
|
||||
stepId: "advanced",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
showUptimePercent: true,
|
||||
},
|
||||
title: "Show Uptime %",
|
||||
fieldType: FormFieldSchemaType.Toggle,
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
description:
|
||||
"Show uptime percentage for the past 90 days beside this group on your status page.",
|
||||
stepId: "advanced",
|
||||
},
|
||||
{
|
||||
field: {
|
||||
uptimePercentPrecision: true,
|
||||
},
|
||||
stepId: "advanced",
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
dropdownOptions:
|
||||
DropdownUtil.getDropdownOptionsFromEnum(UptimePrecision),
|
||||
showIf: (item: FormValues<StatusPageGroup>): boolean => {
|
||||
return Boolean(item.showUptimePercent);
|
||||
},
|
||||
title: "Select Uptime Precision",
|
||||
defaultValue: UptimePrecision.ONE_DECIMAL,
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
showRefreshButton={true}
|
||||
|
@ -293,6 +293,118 @@ const Overview: FunctionComponent<PageComponentProps> = (
|
||||
StatusPageUtil.isPrivateStatusPage(),
|
||||
]);
|
||||
|
||||
type GetMonitorStatusTimelineForResourceFunction = (data: {
|
||||
resource: StatusPageResource;
|
||||
}) => Array<MonitorStatusTimeline>;
|
||||
|
||||
const getMonitorStatusTimelineForResource: GetMonitorStatusTimelineForResourceFunction =
|
||||
(data: { resource: StatusPageResource }): Array<MonitorStatusTimeline> => {
|
||||
return [...monitorStatusTimelines].filter(
|
||||
(timeline: MonitorStatusTimeline) => {
|
||||
// check monitor if first.
|
||||
|
||||
if (data.resource.monitorId) {
|
||||
return (
|
||||
timeline.monitorId?.toString() ===
|
||||
data.resource.monitorId?.toString()
|
||||
);
|
||||
}
|
||||
|
||||
if (data.resource.monitorGroupId) {
|
||||
const monitorsInThisGroup: Array<ObjectID> | undefined =
|
||||
monitorsInGroup[data.resource.monitorGroupId?.toString() || ""];
|
||||
|
||||
if (!monitorsInThisGroup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return monitorsInThisGroup.find((monitorId: ObjectID) => {
|
||||
return monitorId.toString() === timeline.monitorId?.toString();
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
type GetCurrentGroupStatusElementFunction = (data: {
|
||||
group: StatusPageGroup;
|
||||
}) => ReactElement;
|
||||
|
||||
const getCurrentGroupStatusElement: GetCurrentGroupStatusElementFunction =
|
||||
(data: { group: StatusPageGroup }): ReactElement => {
|
||||
const currentStatus: MonitorStatus = getCurrentGroupStatus(data.group);
|
||||
|
||||
const resourcesInGroup: Array<StatusPageResource> = getResourcesInGroup(
|
||||
data.group,
|
||||
);
|
||||
|
||||
const monitorStatusTimelines: Array<MonitorStatusTimeline> = [];
|
||||
|
||||
for (const resource of resourcesInGroup) {
|
||||
// get monitor status timeline.
|
||||
const monitorStatusTimelines: Array<MonitorStatusTimeline> =
|
||||
getMonitorStatusTimelineForResource({
|
||||
resource: resource,
|
||||
});
|
||||
|
||||
// add to the monitor status timelines.
|
||||
|
||||
monitorStatusTimelines.push(...monitorStatusTimelines);
|
||||
}
|
||||
|
||||
const downtimeMonitorStatuses: Array<MonitorStatus> =
|
||||
statusPage?.downtimeMonitorStatuses || [];
|
||||
|
||||
// if the current status is operational then show uptime Percent.
|
||||
|
||||
let precision: UptimePrecision = UptimePrecision.ONE_DECIMAL;
|
||||
|
||||
if (data.group.uptimePercentPrecision) {
|
||||
precision = data.group.uptimePercentPrecision;
|
||||
}
|
||||
|
||||
if (
|
||||
!downtimeMonitorStatuses.find((downtimeStatus: MonitorStatus) => {
|
||||
return currentStatus.id?.toString() === downtimeStatus.id?.toString();
|
||||
}) &&
|
||||
data.group.showUptimePercent
|
||||
) {
|
||||
const uptimePercent: number = UptimeUtil.calculateUptimePercentage(
|
||||
monitorStatusTimelines,
|
||||
precision,
|
||||
downtimeMonitorStatuses,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="font-medium"
|
||||
style={{
|
||||
color: currentStatus?.color?.toString() || Green.toString(),
|
||||
}}
|
||||
>
|
||||
{uptimePercent}% uptime
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (data.group.showCurrentStatus) {
|
||||
return (
|
||||
<div
|
||||
className=""
|
||||
style={{
|
||||
color: currentStatus?.color?.toString() || Green.toString(),
|
||||
}}
|
||||
>
|
||||
{currentStatus?.name || "Operational"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
type GetOverallMonitorStatusFunction = (
|
||||
statusPageResources: Array<StatusPageResource>,
|
||||
monitorStatuses: Array<MonitorStatus>,
|
||||
@ -416,14 +528,9 @@ const Overview: FunctionComponent<PageComponentProps> = (
|
||||
uptimePrecision={
|
||||
resource.uptimePercentPrecision || UptimePrecision.ONE_DECIMAL
|
||||
}
|
||||
monitorStatusTimeline={[...monitorStatusTimelines].filter(
|
||||
(timeline: MonitorStatusTimeline) => {
|
||||
return (
|
||||
timeline.monitorId?.toString() ===
|
||||
resource.monitorId?.toString()
|
||||
);
|
||||
},
|
||||
)}
|
||||
monitorStatusTimeline={getMonitorStatusTimelineForResource({
|
||||
resource: resource,
|
||||
})}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
showHistoryChart={resource.showStatusHistoryChart}
|
||||
@ -468,22 +575,9 @@ const Overview: FunctionComponent<PageComponentProps> = (
|
||||
description={resource.displayDescription || ""}
|
||||
tooltip={resource.displayTooltip || ""}
|
||||
currentStatus={currentStatus}
|
||||
monitorStatusTimeline={[...monitorStatusTimelines].filter(
|
||||
(timeline: MonitorStatusTimeline) => {
|
||||
const monitorsInThisGroup: Array<ObjectID> | undefined =
|
||||
monitorsInGroup[resource.monitorGroupId?.toString() || ""];
|
||||
|
||||
if (!monitorsInThisGroup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return monitorsInThisGroup.find((monitorId: ObjectID) => {
|
||||
return (
|
||||
monitorId.toString() === timeline.monitorId?.toString()
|
||||
);
|
||||
});
|
||||
},
|
||||
)}
|
||||
monitorStatusTimeline={getMonitorStatusTimelineForResource({
|
||||
resource: resource,
|
||||
})}
|
||||
downtimeMonitorStatuses={
|
||||
statusPage?.downtimeMonitorStatuses || []
|
||||
}
|
||||
@ -598,62 +692,52 @@ const Overview: FunctionComponent<PageComponentProps> = (
|
||||
return groups;
|
||||
};
|
||||
|
||||
type GetRightAccordionElementFunction = (
|
||||
type GetResourcesInGroupFunction = (
|
||||
group: StatusPageGroup,
|
||||
) => ReactElement;
|
||||
) => Array<StatusPageResource>;
|
||||
|
||||
const getRightAccordionElement: GetRightAccordionElementFunction = (
|
||||
const getResourcesInGroup: GetResourcesInGroupFunction = (
|
||||
group: StatusPageGroup,
|
||||
): ReactElement => {
|
||||
): Array<StatusPageResource> => {
|
||||
return statusPageResources.filter((resource: StatusPageResource) => {
|
||||
return resource.statusPageGroupId?.toString() === group._id?.toString();
|
||||
});
|
||||
};
|
||||
|
||||
type GetCurrentGroupStatus = (group: StatusPageGroup) => MonitorStatus;
|
||||
|
||||
const getCurrentGroupStatus: GetCurrentGroupStatus = (
|
||||
group: StatusPageGroup,
|
||||
): MonitorStatus => {
|
||||
let currentStatus: MonitorStatus = new MonitorStatus();
|
||||
currentStatus.name = "Operational";
|
||||
currentStatus.color = Green;
|
||||
let hasResource: boolean = false;
|
||||
|
||||
for (const resource of statusPageResources) {
|
||||
const resourcesInGroup: Array<StatusPageResource> =
|
||||
getResourcesInGroup(group);
|
||||
|
||||
for (const resource of resourcesInGroup) {
|
||||
const currentMonitorStatus: MonitorStatus | undefined =
|
||||
monitorStatuses.find((status: MonitorStatus) => {
|
||||
return (
|
||||
status._id?.toString() ===
|
||||
resource.monitor?.currentMonitorStatusId?.toString()
|
||||
);
|
||||
});
|
||||
|
||||
if (
|
||||
(resource.statusPageGroupId &&
|
||||
resource.statusPageGroupId.toString() &&
|
||||
group &&
|
||||
group._id?.toString() &&
|
||||
group._id?.toString() === resource.statusPageGroupId.toString()) ||
|
||||
(!resource.statusPageGroupId && !group)
|
||||
(currentStatus &&
|
||||
currentStatus.priority &&
|
||||
currentMonitorStatus?.priority &&
|
||||
currentMonitorStatus?.priority > currentStatus.priority) ||
|
||||
!currentStatus ||
|
||||
!currentStatus.priority
|
||||
) {
|
||||
hasResource = true;
|
||||
const currentMonitorStatus: MonitorStatus | undefined =
|
||||
monitorStatuses.find((status: MonitorStatus) => {
|
||||
return (
|
||||
status._id?.toString() ===
|
||||
resource.monitor?.currentMonitorStatusId?.toString()
|
||||
);
|
||||
});
|
||||
|
||||
if (
|
||||
(currentStatus &&
|
||||
currentStatus.priority &&
|
||||
currentMonitorStatus?.priority &&
|
||||
currentMonitorStatus?.priority > currentStatus.priority) ||
|
||||
!currentStatus ||
|
||||
!currentStatus.priority
|
||||
) {
|
||||
currentStatus = currentMonitorStatus!;
|
||||
}
|
||||
currentStatus = currentMonitorStatus!;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasResource) {
|
||||
return (
|
||||
<div
|
||||
className="bold font16"
|
||||
style={{
|
||||
color: currentStatus?.color?.toString() || Green.toString(),
|
||||
}}
|
||||
>
|
||||
{currentStatus?.name || "Operational"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
return currentStatus;
|
||||
};
|
||||
|
||||
const activeIncidentsInIncidentGroup: Array<IncidentGroup> =
|
||||
@ -759,9 +843,9 @@ const Overview: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<Accordion
|
||||
key={i}
|
||||
rightElement={getRightAccordionElement(
|
||||
resourceGroup,
|
||||
)}
|
||||
rightElement={getCurrentGroupStatusElement({
|
||||
group: resourceGroup,
|
||||
})}
|
||||
isInitiallyExpanded={
|
||||
resourceGroup.isExpandedByDefault
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user