mirror of https://github.com/statping/statping
PCORE-2143: Showing incidents for each service (#30)
* feat: added incidents for each service * feat: fixed linting * feat: fixed linting * feat: fixed linting * fix: style changes * fix: removed commented code * fix: removed commented code and issue summarypull/1113/head
parent
609796d782
commit
b86b998808
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="col-12 mb-3 pb-2 border-bottom" role="alert">
|
<div class="col-12 mb-3 pb-2 border-bottom" role="alert">
|
||||||
<span class="font-weight-bold text-capitalize" :class="{'text-success': update.type.toLowerCase()==='resolved', 'text-danger': update.type.toLowerCase()==='investigating', 'text-warning': update.type.toLowerCase()==='update'}">{{update.type}}</span>
|
<span class="font-weight-bold text-capitalize" :class="{'text-success': update.type.toLowerCase()==='resolved', 'text-danger': update.type.toLowerCase()==='issue summary', 'text-warning': update.type.toLowerCase()==='update'}">{{update.type}}</span>
|
||||||
<span class="text-muted">- {{update.message}}
|
<span class="text-muted">- {{update.message}}
|
||||||
<button v-if="admin" @click="delete_update(update)" type="button" class="close">
|
<button v-if="admin" @click="delete_update(update)" type="button" class="close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
||||||
return "badge-success"
|
return "badge-success"
|
||||||
case "update":
|
case "update":
|
||||||
return "badge-info"
|
return "badge-info"
|
||||||
case "investigating":
|
case "issue summary":
|
||||||
return "badge-danger"
|
return "badge-danger"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,9 +12,8 @@
|
||||||
<form class="row" @submit.prevent="createIncidentUpdate">
|
<form class="row" @submit.prevent="createIncidentUpdate">
|
||||||
<div class="col-12 col-md-3 mb-3 mb-md-0">
|
<div class="col-12 col-md-3 mb-3 mb-md-0">
|
||||||
<select v-model="incident_update.type" class="form-control">
|
<select v-model="incident_update.type" class="form-control">
|
||||||
<option value="Investigating">Investigating</option>
|
<option value="Issue summary">Issue summary</option>
|
||||||
<option value="Update">Update</option>
|
<option value="Update">Update</option>
|
||||||
<option value="Unknown">Unknown</option>
|
|
||||||
<option value="Resolved">Resolved</option>
|
<option value="Resolved">Resolved</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,7 +52,7 @@
|
||||||
incident_update: {
|
incident_update: {
|
||||||
incident: this.incident.id,
|
incident: this.incident.id,
|
||||||
message: "",
|
message: "",
|
||||||
type: "Investigating" // TODO: default to something.. theres is no error checking for blank submission...
|
type: "Issue summary"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -74,7 +73,7 @@
|
||||||
this.incident_update = {
|
this.incident_update = {
|
||||||
incident: this.incident.id,
|
incident: this.incident.id,
|
||||||
message: "",
|
message: "",
|
||||||
type: "Investigating"
|
type: "Issue summary"
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
// import { groups } from "../utils/data";
|
|
||||||
import GroupItem from "./GroupItem";
|
import GroupItem from "./GroupItem";
|
||||||
import { isObject, isObjectEmpty } from "../utils/helper";
|
import { isObject, isObjectEmpty } from "../utils/helper";
|
||||||
|
|
||||||
|
@ -17,9 +16,6 @@ function showPlus(service) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Group = ({ services }) => {
|
const Group = ({ services }) => {
|
||||||
// const data = groups.sort((a, b) => a.order_id - b.order_id);
|
|
||||||
// if (!data.length > 0) return <></>;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="list-group">
|
<div className="list-group">
|
||||||
{services?.map((service) => {
|
{services?.map((service) => {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import GroupServiceFailures from "./GroupServiceFailures";
|
||||||
import SubServiceCard from "./SubServiceCard";
|
import SubServiceCard from "./SubServiceCard";
|
||||||
import infoIcon from "../static/info.svg";
|
import infoIcon from "../static/info.svg";
|
||||||
import { analyticsTrack } from "../utils/trackers";
|
import { analyticsTrack } from "../utils/trackers";
|
||||||
|
import IncidentsBlock from "./IncidentsBlock";
|
||||||
|
|
||||||
const GroupItem = ({ service, showPlusButton }) => {
|
const GroupItem = ({ service, showPlusButton }) => {
|
||||||
const [collapse, setCollapse] = useState(false);
|
const [collapse, setCollapse] = useState(false);
|
||||||
|
@ -39,26 +40,26 @@ const GroupItem = ({ service, showPlusButton }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsTrack({
|
analyticsTrack({
|
||||||
objectName: 'Service Expand',
|
objectName: "Service Expand",
|
||||||
actionName: 'clicked',
|
actionName: "clicked",
|
||||||
screen: 'Home page',
|
screen: "Home page",
|
||||||
properties:{
|
properties: {
|
||||||
serviceName: event.target.name,
|
serviceName: event.target.name,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeCollapse = (event) => {
|
const closeCollapse = (event) => {
|
||||||
setCollapse(false);
|
setCollapse(false);
|
||||||
|
|
||||||
analyticsTrack({
|
analyticsTrack({
|
||||||
objectName: 'Service Collapse',
|
objectName: "Service Collapse",
|
||||||
actionName: 'clicked',
|
actionName: "clicked",
|
||||||
screen: 'Home page',
|
screen: "Home page",
|
||||||
properties:{
|
properties: {
|
||||||
serviceName: event.target.name,
|
serviceName: event.target.name,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseOver = (service) => {
|
const handleMouseOver = (service) => {
|
||||||
|
@ -68,28 +69,31 @@ const GroupItem = ({ service, showPlusButton }) => {
|
||||||
const handleMouseOut = () => setHoverText("");
|
const handleMouseOut = () => setHoverText("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="service-card service_item card-bg pb-0">
|
<div className="service-parent service-card service_item card-bg">
|
||||||
{/** TODO: change span to navlink */}
|
{/** TODO: change span to navlink */}
|
||||||
<div className="service_item--header mb-3">
|
<div className="service_item--header mb-3">
|
||||||
<div className="service_item--right">
|
<div className="service_item--right">
|
||||||
{!loading && showPlusButton && (
|
{!loading && showPlusButton && (
|
||||||
<>
|
<>
|
||||||
{collapse ? (
|
{collapse ? (
|
||||||
<button className="square-minus" name={service.name} onClick={closeCollapse} />
|
<button
|
||||||
|
className="square-minus"
|
||||||
|
name={service.name}
|
||||||
|
onClick={closeCollapse}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<button className="square-plus" name={service.name} onClick={openCollapse} />
|
<button
|
||||||
|
className="square-plus"
|
||||||
|
name={service.name}
|
||||||
|
onClick={openCollapse}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{loading && <FontAwesomeIcon icon={faCircleNotch} spin />}
|
{loading && <FontAwesomeIcon icon={faCircleNotch} spin />}
|
||||||
|
|
||||||
<span
|
<span className="subtitle no-decoration mr-1">{service.name}</span>
|
||||||
className="subtitle no-decoration font-14 mr-1"
|
|
||||||
// to="/service/1"
|
|
||||||
>
|
|
||||||
{service.name}
|
|
||||||
</span>
|
|
||||||
{service?.description && (
|
{service?.description && (
|
||||||
<>
|
<>
|
||||||
<ReactTooltip
|
<ReactTooltip
|
||||||
|
@ -110,89 +114,44 @@ const GroupItem = ({ service, showPlusButton }) => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="service_item--left">
|
{!collapse && (
|
||||||
<span
|
<div className="service_item--left">
|
||||||
className={`badge float-right font-12 ${
|
<span
|
||||||
service.online ? "uptime" : "downtime"
|
className={`badge float-right font-12 ${
|
||||||
}`}
|
service.online ? "uptime" : "downtime"
|
||||||
style={{ display: collapse ? "none" : "block" }}
|
}`}>
|
||||||
>
|
{service.online ? langs("online") : langs("offline")}
|
||||||
{service.online ? langs("online") : langs("offline")}
|
</span>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<GroupServiceFailures service={service} collapse={collapse} />
|
|
||||||
{/*<IncidentsBlock service={service} /> */}
|
|
||||||
<div
|
|
||||||
className="list-group online_list"
|
|
||||||
style={{ display: collapse ? "block" : "none" }}
|
|
||||||
>
|
|
||||||
{subServices && subServices?.length > 0 ? (
|
|
||||||
subServices.map((sub_service, i) => {
|
|
||||||
return (
|
|
||||||
<SubServiceCard
|
|
||||||
key={i}
|
|
||||||
group={service}
|
|
||||||
service={sub_service}
|
|
||||||
collapse={collapse}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<div className="subtitle text-align-center">No Services</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!collapse && (
|
||||||
|
<GroupServiceFailures service={service} collapse={collapse} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!collapse && <IncidentsBlock service={service} />}
|
||||||
|
|
||||||
|
{collapse && (
|
||||||
|
<div className="sub-service-wrapper list-group online_list">
|
||||||
|
{subServices && subServices?.length > 0 ? (
|
||||||
|
subServices.map((sub_service, i) => {
|
||||||
|
return (
|
||||||
|
<SubServiceCard
|
||||||
|
key={i}
|
||||||
|
group={service}
|
||||||
|
service={sub_service}
|
||||||
|
collapse={collapse}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<div className="subtitle text-align-center">No Services</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(GroupItem);
|
export default React.memo(GroupItem);
|
||||||
|
|
||||||
// import React from "react";
|
|
||||||
// import langs from "../config/langs";
|
|
||||||
// import { services } from "../data";
|
|
||||||
// import GroupServiceFailures from "./GroupServiceFailures";
|
|
||||||
// import IncidentsBlock from "./IncidentsBlock";
|
|
||||||
// // import DateUtils from "../utils/DateUtils";
|
|
||||||
|
|
||||||
// const GroupItem = ({ group }) => {
|
|
||||||
// const groupServices = services
|
|
||||||
// .filter((s) => s.group_id === group.id)
|
|
||||||
// .sort((a, b) => a.order_id - b.order_id);
|
|
||||||
|
|
||||||
// if (!groupServices.length > 0) return null;
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <div className="col-12 full-col-12">
|
|
||||||
// {group.name !== "Empty Group" && (
|
|
||||||
// <h4 className="group_header mb-3 mt-4">{group.name}</h4>
|
|
||||||
// )}
|
|
||||||
// <div className="list-group online_list mb-4">
|
|
||||||
// {groupServices.map((service, i) => {
|
|
||||||
// return (
|
|
||||||
// <div key={i} className="service-card service-card-action">
|
|
||||||
// <span
|
|
||||||
// className="no-decoration font-3"
|
|
||||||
// // to={DateUtils.serviceLink(service)}
|
|
||||||
// >
|
|
||||||
// {service.name}
|
|
||||||
// </span>
|
|
||||||
|
|
||||||
// <span
|
|
||||||
// className={`badge text-uppercase float-right ${
|
|
||||||
// service.online ? "bg-success" : "bg-danger"
|
|
||||||
// }`}
|
|
||||||
// >
|
|
||||||
// {service.online ? langs("online") : langs("offline")}
|
|
||||||
// </span>
|
|
||||||
// <GroupServiceFailures service={service} />
|
|
||||||
// <IncidentsBlock service={service} />
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// })}
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export default GroupItem;
|
|
||||||
|
|
|
@ -57,12 +57,6 @@ async function fetchFailureSeries(url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
||||||
// const [containerRef, isVisible] = useIntersectionObserver({
|
|
||||||
// root: null,
|
|
||||||
// rootMargin: "0px",
|
|
||||||
// threshold: 1.0,
|
|
||||||
// });
|
|
||||||
|
|
||||||
const [hoverText, setHoverText] = useState("");
|
const [hoverText, setHoverText] = useState("");
|
||||||
const [loaded, setLoaded] = useState(true);
|
const [loaded, setLoaded] = useState(true);
|
||||||
const [failureData, setFailureData] = useState([]);
|
const [failureData, setFailureData] = useState([]);
|
||||||
|
@ -98,7 +92,7 @@ const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [service]);
|
}, [service, group]);
|
||||||
|
|
||||||
const handleTooltip = (d) => {
|
const handleTooltip = (d) => {
|
||||||
let txt = "";
|
let txt = "";
|
||||||
|
@ -133,7 +127,6 @@ const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
||||||
if (loaded) return <ServiceLoader text="Loading series.." />;
|
if (loaded) return <ServiceLoader text="Loading series.." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// transition div
|
|
||||||
<div name="fade" style={{ display: collapse ? "none" : "block" }}>
|
<div name="fade" style={{ display: collapse ? "none" : "block" }}>
|
||||||
<div className="block-chart">
|
<div className="block-chart">
|
||||||
<ReactTooltip
|
<ReactTooltip
|
||||||
|
@ -150,8 +143,7 @@ const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
||||||
onMouseOver={() => handleMouseOver(d)}
|
onMouseOver={() => handleMouseOver(d)}
|
||||||
onMouseOut={handleMouseOut}
|
onMouseOut={handleMouseOut}
|
||||||
key={i}
|
key={i}
|
||||||
data-tip={hoverText}
|
data-tip={hoverText}>
|
||||||
>
|
|
||||||
{d.status !== 0 && (
|
{d.status !== 0 && (
|
||||||
<span className="d-none d-md-block text-center small"></span>
|
<span className="d-none d-md-block text-center small"></span>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -16,32 +16,31 @@ const IncidentUpdate = ({ update, admin }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="col-12 mb-3 pb-2 border-bottom" role="alert">
|
<div className="incident-wrapper mb-3 pb-2 d-flex" role="alert">
|
||||||
<span
|
<div className="time-line mr-2">
|
||||||
className={`
|
<span class="dot"></span>
|
||||||
font-weight-bold text-capitalize
|
</div>
|
||||||
${update.type.toLowerCase() === "resolved" ? "text-success" : ""}
|
|
||||||
${update.type.toLowerCase() === "investigating" ? "text-danger" : ""}
|
<div>
|
||||||
${update.type.toLowerCase() === "update" ? "text-warning" : ""}
|
<span className="font-14">
|
||||||
`}
|
{update.message}
|
||||||
>
|
{admin && (
|
||||||
{update.type}
|
<button
|
||||||
</span>
|
onClick={deleteUpdate(update)}
|
||||||
<span className="text-muted">
|
type="button"
|
||||||
- {update.message}
|
className="close">
|
||||||
{admin && (
|
<span aria-hidden="true">×</span>
|
||||||
<button
|
</button>
|
||||||
onClick={deleteUpdate(update)}
|
)}
|
||||||
type="button"
|
</span>
|
||||||
className="close"
|
<span className="d-block small text-muted">
|
||||||
>
|
Posted {DateUtils.ago(update.created_at)} ago.{" "}
|
||||||
<span aria-hidden="true">×</span>
|
{DateUtils.format(
|
||||||
</button>
|
DateUtils.parseISO(update.created_at),
|
||||||
)}
|
"MMM d, yyyy - HH:mm"
|
||||||
</span>
|
)}
|
||||||
<span className="d-block small">
|
</span>
|
||||||
{DateUtils.ago(update.created_at)} ago
|
</div>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,36 +3,96 @@ import API from "../config/API";
|
||||||
import DateUtils from "../utils/DateUtils";
|
import DateUtils from "../utils/DateUtils";
|
||||||
import IncidentUpdate from "./IncidentUpdate";
|
import IncidentUpdate from "./IncidentUpdate";
|
||||||
|
|
||||||
const IncidentsBlock = ({ service }) => {
|
const IncidentsBlock = ({ service, group }) => {
|
||||||
const [incidents, setIncidents] = useState([]);
|
const [incidents, setIncidents] = useState([]);
|
||||||
|
const [incidentsShow, setIncidentsShow] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const data = await API.incidents_service(service.id);
|
let data = [];
|
||||||
setIncidents(data);
|
|
||||||
|
if (group?.id) {
|
||||||
|
data = await API.sub_incidents_service(group.id, service.id);
|
||||||
|
} else {
|
||||||
|
data = await API.incidents_service(service.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIncidents(data || []);
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [service.id]);
|
}, [service.id, group?.id]);
|
||||||
|
|
||||||
|
const handleIncidentShow = (event) => {
|
||||||
|
const { id } = event.target;
|
||||||
|
|
||||||
|
setIncidentsShow({ ...incidentsShow, [id]: !incidentsShow[id] });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="row">
|
<div className="incidents-wrapper row">
|
||||||
{incidents?.map((incident, i) => {
|
<div className="col-12 mt-2">
|
||||||
return (
|
{incidents?.length > 0 ? (
|
||||||
<div className="col-12 mt-2" key={i}>
|
incidents?.map((incident) => {
|
||||||
<span className="braker mt-1 mb-3"></span>
|
const { id, title, description, updated_at } = incident;
|
||||||
<h6>
|
|
||||||
{incident.title}
|
return (
|
||||||
<span className="font-2 float-right">
|
<>
|
||||||
{DateUtils.niceDate(incident.created_at)}
|
<span className="braker mt-1 mb-3"></span>
|
||||||
</span>
|
<div
|
||||||
</h6>
|
className={`incident-title col-12 ${
|
||||||
<div className="font-2 mb-3" v-html="incident.description"></div>
|
incidentsShow[id] && "mb-3"
|
||||||
{incident.updates.map((update, i) => {
|
}`}>
|
||||||
return <IncidentUpdate key={i} update={update} admin={false} />;
|
{incidentsShow[id] ? (
|
||||||
})}
|
<button
|
||||||
|
className="square-minus"
|
||||||
|
type="button"
|
||||||
|
id={id}
|
||||||
|
onClick={handleIncidentShow}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
className="square-plus"
|
||||||
|
type="button"
|
||||||
|
id={id}
|
||||||
|
onClick={handleIncidentShow}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="title-wrapper">
|
||||||
|
<span class="subtitle no-decoration">{title}</span>
|
||||||
|
<span className="d-block small text-dark">
|
||||||
|
{description}
|
||||||
|
</span>
|
||||||
|
<span className="d-block small text-muted">
|
||||||
|
Updated {DateUtils.ago(updated_at)} ago.{" "}
|
||||||
|
{DateUtils.format(
|
||||||
|
DateUtils.parseISO(updated_at),
|
||||||
|
"MMM d, yyyy - HH:mm"
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{incidentsShow[id] && (
|
||||||
|
<div className="incident-updates-wrapper col-12">
|
||||||
|
{incident?.updates.map((update) => {
|
||||||
|
return (
|
||||||
|
<IncidentUpdate
|
||||||
|
key={update.id}
|
||||||
|
update={update}
|
||||||
|
admin={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<div className="col-12">
|
||||||
|
<span class="font-14 text-muted">No recent incidents</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
)}
|
||||||
})}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,36 +1,26 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
// import { NavLink } from "react-router-dom";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import DateUtils from "../utils/DateUtils";
|
|
||||||
import Group from "./Group";
|
import Group from "./Group";
|
||||||
import ContentHeader from "./ContentHeader";
|
import ContentHeader from "./ContentHeader";
|
||||||
import ServiceLoader from "./ServiceLoader";
|
import ServiceLoader from "./ServiceLoader";
|
||||||
// import IncidentService from "./IncidentService";
|
|
||||||
// import MessageBlock from "./MessageBlock";
|
|
||||||
// import ServiceBlock from "./ServiceBlock";
|
|
||||||
// import ServicesList from "./ServicesList";
|
|
||||||
import API from "../config/API";
|
import API from "../config/API";
|
||||||
import { STATUS_COLOR, STATUS_ICON, STATUS_TEXT } from "../utils/constants";
|
|
||||||
import { findStatus } from "../utils/helper";
|
import { findStatus } from "../utils/helper";
|
||||||
import { analyticsTrack } from "../utils/trackers";
|
import { analyticsTrack } from "../utils/trackers";
|
||||||
|
|
||||||
const ServicesPage = () => {
|
const ServicesPage = () => {
|
||||||
// const data = messages.filter((m) => inRange(m) && m.service === 0);
|
|
||||||
const [services, setServices] = useState([]);
|
const [services, setServices] = useState([]);
|
||||||
const [status, setStatus] = useState("uptime");
|
const [status, setStatus] = useState("uptime");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [poll, setPolling] = useState(1);
|
const [poll, setPolling] = useState(1);
|
||||||
const today = DateUtils.format(new Date(), "d MMMM yyyy, hh:mm aaa");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!loading) {
|
if (!loading) {
|
||||||
analyticsTrack({
|
analyticsTrack({
|
||||||
objectName: 'Status Page',
|
objectName: "Status Page",
|
||||||
actionName: 'displayed',
|
actionName: "displayed",
|
||||||
screen: 'Home page'
|
screen: "Home page",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}, [loading])
|
}, [loading]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
|
@ -59,47 +49,20 @@ const ServicesPage = () => {
|
||||||
return (
|
return (
|
||||||
<div className="container col-md-7 col-sm-12 sm-container">
|
<div className="container col-md-7 col-sm-12 sm-container">
|
||||||
<ContentHeader />
|
<ContentHeader />
|
||||||
|
|
||||||
<div className="app-content">
|
<div className="app-content">
|
||||||
<div className="service">
|
<div className="service">
|
||||||
<h2 className="title font-20 fw-700">Razorpay Payments</h2>
|
<h2 className="title font-20 fw-700">Razorpay Payments</h2>
|
||||||
<div className="d-flex align-items-center subtitle font-12 mt-2">
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={STATUS_ICON[status]}
|
|
||||||
style={{
|
|
||||||
fontSize: "16px",
|
|
||||||
color: STATUS_COLOR[status],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span className="mx-1">{STATUS_TEXT[status]}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="date font-12">{today}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading && <ServiceLoader text="Loading Services" />}
|
{loading && <ServiceLoader text="Loading Services" />}
|
||||||
|
|
||||||
{/* <ServicesList loading={loading} services={services} /> */}
|
|
||||||
|
|
||||||
{/* TODO --> Grouped Services to Accordian*/}
|
|
||||||
{services && services.length > 0 ? (
|
{services && services.length > 0 ? (
|
||||||
<Group services={services} />
|
<Group services={services} />
|
||||||
) : (
|
) : (
|
||||||
<div className="description text-align-center">No Services</div>
|
<div className="description text-align-center">No Services</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* <div>
|
|
||||||
{data.map((message) => {
|
|
||||||
return <MessageBlock key={message.id} message={message} />;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{services.map((service) => {
|
|
||||||
return <ServiceBlock key={service.id} service={service} />;
|
|
||||||
})}
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<div className="app-footer">
|
<div className="app-footer">
|
||||||
<div className="service-status">
|
<div className="service-status">
|
||||||
<span className="service-status-badge uptime"></span>
|
<span className="service-status-badge uptime"></span>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useState } from "react";
|
||||||
import ReactTooltip from "react-tooltip";
|
import ReactTooltip from "react-tooltip";
|
||||||
import langs from "../config/langs";
|
import langs from "../config/langs";
|
||||||
import GroupServiceFailures from "./GroupServiceFailures";
|
import GroupServiceFailures from "./GroupServiceFailures";
|
||||||
// import IncidentsBlock from "./IncidentsBlock";
|
import IncidentsBlock from "./IncidentsBlock";
|
||||||
import infoIcon from "../static/info.svg";
|
import infoIcon from "../static/info.svg";
|
||||||
|
|
||||||
const SubServiceCard = ({ group, service }) => {
|
const SubServiceCard = ({ group, service }) => {
|
||||||
|
@ -50,15 +50,14 @@ const SubServiceCard = ({ group, service }) => {
|
||||||
<span
|
<span
|
||||||
className={`badge float-right font-12 ${
|
className={`badge float-right font-12 ${
|
||||||
service.online ? "status-green" : "status-red"
|
service.online ? "status-green" : "status-red"
|
||||||
}`}
|
}`}>
|
||||||
>
|
|
||||||
{service.online ? langs("online") : langs("offline")}
|
{service.online ? langs("online") : langs("offline")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<GroupServiceFailures group={group} service={service} />
|
<GroupServiceFailures group={group} service={service} />
|
||||||
{/* <IncidentsBlock service={service} /> */}
|
<IncidentsBlock group={group} service={service} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -213,10 +213,15 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
async incidents_service(id) {
|
async incidents_service(id) {
|
||||||
return incidents[id];
|
return axios
|
||||||
// return axios
|
.get("/services/" + id + "/active_incidents")
|
||||||
// .get("api/services/" + id + "/incidents")
|
.then((response) => response.data);
|
||||||
// .then((response) => response.data);
|
}
|
||||||
|
|
||||||
|
async sub_incidents_service(id, sub_id) {
|
||||||
|
return axios
|
||||||
|
.get(`/services/${id}/sub_services/${sub_id}/active_incidents`)
|
||||||
|
.then((response) => response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async incident_create(service_id, data) {
|
async incident_create(service_id, data) {
|
||||||
|
|
|
@ -13,6 +13,48 @@ a {
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
height: 8px;
|
||||||
|
width: 8px;
|
||||||
|
background-color: #bbb;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-plus,
|
||||||
|
.square-minus {
|
||||||
|
color: $blue;
|
||||||
|
border: 2px solid $blue;
|
||||||
|
border-radius: 2px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
font-size: 8px;
|
||||||
|
margin-right: 5px;
|
||||||
|
position: relative;
|
||||||
|
background-color: $white;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-plus:after {
|
||||||
|
content: "+";
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-minus:after {
|
||||||
|
content: "-";
|
||||||
|
}
|
||||||
|
|
||||||
.app-layout {
|
.app-layout {
|
||||||
background: $primary-bg;
|
background: $primary-bg;
|
||||||
min-width: 100vw;
|
min-width: 100vw;
|
||||||
|
@ -116,25 +158,73 @@ a {
|
||||||
transition-duration: 300ms;
|
transition-duration: 300ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-card {
|
.list-group .service-parent {
|
||||||
position: relative;
|
border-radius: 4px;
|
||||||
min-height: 130px;
|
margin-bottom: 1rem;
|
||||||
padding: 1.25rem 0;
|
|
||||||
background-color: $white;
|
|
||||||
border: 1px solid #eff2f7;
|
border: 1px solid #eff2f7;
|
||||||
border-bottom-width: 0;
|
|
||||||
overflow: hidden;
|
.sub-service-wrapper {
|
||||||
|
border-top: 1px solid #eff2f7;
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
&:last-child {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group .service-card:first-child {
|
.service-card {
|
||||||
border-top-left-radius: 4px;
|
position: relative;
|
||||||
border-top-right-radius: 4px;
|
padding: 1.25rem 0;
|
||||||
|
background-color: $white;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #eff2f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incidents-wrapper {
|
||||||
|
.incident-title {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.title-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-plus,
|
||||||
|
.square-minus {
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.incident-updates-wrapper {
|
||||||
|
margin-left: 3px;
|
||||||
|
|
||||||
|
.incident-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 3px;
|
||||||
|
|
||||||
|
&:not(:last-child):after {
|
||||||
|
content: "";
|
||||||
|
height: 100%;
|
||||||
|
width: 1px;
|
||||||
|
top: 21px;
|
||||||
|
left: 3px;
|
||||||
|
background-color: #bbb;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group .service-card:last-child {
|
.list-group .service-card:last-child {
|
||||||
border-bottom-right-radius: 4px;
|
margin-bottom: 0;
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-card a {
|
.service-card a {
|
||||||
|
@ -157,40 +247,6 @@ a {
|
||||||
&--right {
|
&--right {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.square-plus,
|
|
||||||
.square-minus {
|
|
||||||
color: $blue;
|
|
||||||
border: 2px solid $blue;
|
|
||||||
border-radius: 2px;
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
font-size: 8px;
|
|
||||||
margin-right: 5px;
|
|
||||||
position: relative;
|
|
||||||
background-color: $white;
|
|
||||||
outline: none;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 10px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.square-plus:after {
|
|
||||||
content: "+";
|
|
||||||
}
|
|
||||||
|
|
||||||
.square-minus:after {
|
|
||||||
content: "-";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,10 +409,12 @@ a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 15px;
|
||||||
|
|
||||||
.service-status {
|
.service-status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-right: 1.875rem;
|
|
||||||
|
|
||||||
&-badge {
|
&-badge {
|
||||||
width: 0.75rem;
|
width: 0.75rem;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// import DateUtils from "./DateUtils";
|
|
||||||
|
|
||||||
export function findStatus(data) {
|
export function findStatus(data) {
|
||||||
if (!Array.isArray(data)) return null;
|
if (!Array.isArray(data)) return null;
|
||||||
if (data.length === 0) return null;
|
if (data.length === 0) return null;
|
||||||
|
@ -12,15 +10,18 @@ export function findStatus(data) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// export function inRange(message) {
|
export function getIncidentTextType(type) {
|
||||||
// return DateUtils.isBetween(
|
switch (type.toLowerCase()) {
|
||||||
// DateUtils.now(),
|
case "resolved":
|
||||||
// message.start_on,
|
return "text-success";
|
||||||
// message.start_on === message.end_on
|
case "issue summary":
|
||||||
// ? DateUtils.maxDate().toISOString()
|
return "text-danger";
|
||||||
// : message.end_on
|
case "update":
|
||||||
// );
|
return "text-warning";
|
||||||
// }
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const isObject = (obj) => {
|
export const isObject = (obj) => {
|
||||||
if (Object.prototype.toString.call(obj) === "[object Object]") {
|
if (Object.prototype.toString.call(obj) === "[object Object]") {
|
||||||
|
@ -59,13 +60,15 @@ export const calcPer = (uptime, downtime) => {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
export const setUerId = (id) => {
|
export const setUerId = (id) => {
|
||||||
localStorage.setItem('stat_user_id',id);
|
localStorage.setItem("stat_user_id", id);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getUserId = () => {
|
export const getUserId = () => {
|
||||||
return localStorage.getItem('stat_user_id');
|
return localStorage.getItem("stat_user_id");
|
||||||
}
|
};
|
||||||
|
|
||||||
export const generateUUID = (length) => {
|
export const generateUUID = (length) => {
|
||||||
return Array.from(Array(length), () => Math.floor(Math.random() * 36).toString(36)).join('')
|
return Array.from(Array(length), () =>
|
||||||
}
|
Math.floor(Math.random() * 36).toString(36)
|
||||||
|
).join("");
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue