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>
|
||||
<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}}
|
||||
<button v-if="admin" @click="delete_update(update)" type="button" class="close">
|
||||
<span aria-hidden="true">×</span>
|
||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
|||
return "badge-success"
|
||||
case "update":
|
||||
return "badge-info"
|
||||
case "investigating":
|
||||
case "issue summary":
|
||||
return "badge-danger"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -12,9 +12,8 @@
|
|||
<form class="row" @submit.prevent="createIncidentUpdate">
|
||||
<div class="col-12 col-md-3 mb-3 mb-md-0">
|
||||
<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="Unknown">Unknown</option>
|
||||
<option value="Resolved">Resolved</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -53,7 +52,7 @@
|
|||
incident_update: {
|
||||
incident: this.incident.id,
|
||||
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 = {
|
||||
incident: this.incident.id,
|
||||
message: "",
|
||||
type: "Investigating"
|
||||
type: "Issue summary"
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from "react";
|
||||
// import { groups } from "../utils/data";
|
||||
import GroupItem from "./GroupItem";
|
||||
import { isObject, isObjectEmpty } from "../utils/helper";
|
||||
|
||||
|
@ -17,9 +16,6 @@ function showPlus(service) {
|
|||
}
|
||||
|
||||
const Group = ({ services }) => {
|
||||
// const data = groups.sort((a, b) => a.order_id - b.order_id);
|
||||
// if (!data.length > 0) return <></>;
|
||||
|
||||
return (
|
||||
<div className="list-group">
|
||||
{services?.map((service) => {
|
||||
|
|
|
@ -8,6 +8,7 @@ import GroupServiceFailures from "./GroupServiceFailures";
|
|||
import SubServiceCard from "./SubServiceCard";
|
||||
import infoIcon from "../static/info.svg";
|
||||
import { analyticsTrack } from "../utils/trackers";
|
||||
import IncidentsBlock from "./IncidentsBlock";
|
||||
|
||||
const GroupItem = ({ service, showPlusButton }) => {
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
|
@ -39,26 +40,26 @@ const GroupItem = ({ service, showPlusButton }) => {
|
|||
}
|
||||
|
||||
analyticsTrack({
|
||||
objectName: 'Service Expand',
|
||||
actionName: 'clicked',
|
||||
screen: 'Home page',
|
||||
properties:{
|
||||
objectName: "Service Expand",
|
||||
actionName: "clicked",
|
||||
screen: "Home page",
|
||||
properties: {
|
||||
serviceName: event.target.name,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const closeCollapse = (event) => {
|
||||
setCollapse(false);
|
||||
|
||||
analyticsTrack({
|
||||
objectName: 'Service Collapse',
|
||||
actionName: 'clicked',
|
||||
screen: 'Home page',
|
||||
properties:{
|
||||
objectName: "Service Collapse",
|
||||
actionName: "clicked",
|
||||
screen: "Home page",
|
||||
properties: {
|
||||
serviceName: event.target.name,
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleMouseOver = (service) => {
|
||||
|
@ -68,28 +69,31 @@ const GroupItem = ({ service, showPlusButton }) => {
|
|||
const handleMouseOut = () => setHoverText("");
|
||||
|
||||
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 */}
|
||||
<div className="service_item--header mb-3">
|
||||
<div className="service_item--right">
|
||||
{!loading && showPlusButton && (
|
||||
<>
|
||||
{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 />}
|
||||
|
||||
<span
|
||||
className="subtitle no-decoration font-14 mr-1"
|
||||
// to="/service/1"
|
||||
>
|
||||
{service.name}
|
||||
</span>
|
||||
<span className="subtitle no-decoration mr-1">{service.name}</span>
|
||||
{service?.description && (
|
||||
<>
|
||||
<ReactTooltip
|
||||
|
@ -110,89 +114,44 @@ const GroupItem = ({ service, showPlusButton }) => {
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="service_item--left">
|
||||
<span
|
||||
className={`badge float-right font-12 ${
|
||||
service.online ? "uptime" : "downtime"
|
||||
}`}
|
||||
style={{ display: collapse ? "none" : "block" }}
|
||||
>
|
||||
{service.online ? langs("online") : langs("offline")}
|
||||
</span>
|
||||
</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>
|
||||
{!collapse && (
|
||||
<div className="service_item--left">
|
||||
<span
|
||||
className={`badge float-right font-12 ${
|
||||
service.online ? "uptime" : "downtime"
|
||||
}`}>
|
||||
{service.online ? langs("online") : langs("offline")}
|
||||
</span>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
||||
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 [containerRef, isVisible] = useIntersectionObserver({
|
||||
// root: null,
|
||||
// rootMargin: "0px",
|
||||
// threshold: 1.0,
|
||||
// });
|
||||
|
||||
const [hoverText, setHoverText] = useState("");
|
||||
const [loaded, setLoaded] = useState(true);
|
||||
const [failureData, setFailureData] = useState([]);
|
||||
|
@ -98,7 +92,7 @@ const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
|||
}
|
||||
}
|
||||
fetchData();
|
||||
}, [service]);
|
||||
}, [service, group]);
|
||||
|
||||
const handleTooltip = (d) => {
|
||||
let txt = "";
|
||||
|
@ -133,7 +127,6 @@ const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
|||
if (loaded) return <ServiceLoader text="Loading series.." />;
|
||||
|
||||
return (
|
||||
// transition div
|
||||
<div name="fade" style={{ display: collapse ? "none" : "block" }}>
|
||||
<div className="block-chart">
|
||||
<ReactTooltip
|
||||
|
@ -150,8 +143,7 @@ const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
|||
onMouseOver={() => handleMouseOver(d)}
|
||||
onMouseOut={handleMouseOut}
|
||||
key={i}
|
||||
data-tip={hoverText}
|
||||
>
|
||||
data-tip={hoverText}>
|
||||
{d.status !== 0 && (
|
||||
<span className="d-none d-md-block text-center small"></span>
|
||||
)}
|
||||
|
|
|
@ -16,32 +16,31 @@ const IncidentUpdate = ({ update, admin }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="col-12 mb-3 pb-2 border-bottom" role="alert">
|
||||
<span
|
||||
className={`
|
||||
font-weight-bold text-capitalize
|
||||
${update.type.toLowerCase() === "resolved" ? "text-success" : ""}
|
||||
${update.type.toLowerCase() === "investigating" ? "text-danger" : ""}
|
||||
${update.type.toLowerCase() === "update" ? "text-warning" : ""}
|
||||
`}
|
||||
>
|
||||
{update.type}
|
||||
</span>
|
||||
<span className="text-muted">
|
||||
- {update.message}
|
||||
{admin && (
|
||||
<button
|
||||
onClick={deleteUpdate(update)}
|
||||
type="button"
|
||||
className="close"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
<span className="d-block small">
|
||||
{DateUtils.ago(update.created_at)} ago
|
||||
</span>
|
||||
<div className="incident-wrapper mb-3 pb-2 d-flex" role="alert">
|
||||
<div className="time-line mr-2">
|
||||
<span class="dot"></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span className="font-14">
|
||||
{update.message}
|
||||
{admin && (
|
||||
<button
|
||||
onClick={deleteUpdate(update)}
|
||||
type="button"
|
||||
className="close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
<span className="d-block small text-muted">
|
||||
Posted {DateUtils.ago(update.created_at)} ago.{" "}
|
||||
{DateUtils.format(
|
||||
DateUtils.parseISO(update.created_at),
|
||||
"MMM d, yyyy - HH:mm"
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,36 +3,96 @@ import API from "../config/API";
|
|||
import DateUtils from "../utils/DateUtils";
|
||||
import IncidentUpdate from "./IncidentUpdate";
|
||||
|
||||
const IncidentsBlock = ({ service }) => {
|
||||
const IncidentsBlock = ({ service, group }) => {
|
||||
const [incidents, setIncidents] = useState([]);
|
||||
const [incidentsShow, setIncidentsShow] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
const data = await API.incidents_service(service.id);
|
||||
setIncidents(data);
|
||||
let 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();
|
||||
}, [service.id]);
|
||||
}, [service.id, group?.id]);
|
||||
|
||||
const handleIncidentShow = (event) => {
|
||||
const { id } = event.target;
|
||||
|
||||
setIncidentsShow({ ...incidentsShow, [id]: !incidentsShow[id] });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
{incidents?.map((incident, i) => {
|
||||
return (
|
||||
<div className="col-12 mt-2" key={i}>
|
||||
<span className="braker mt-1 mb-3"></span>
|
||||
<h6>
|
||||
{incident.title}
|
||||
<span className="font-2 float-right">
|
||||
{DateUtils.niceDate(incident.created_at)}
|
||||
</span>
|
||||
</h6>
|
||||
<div className="font-2 mb-3" v-html="incident.description"></div>
|
||||
{incident.updates.map((update, i) => {
|
||||
return <IncidentUpdate key={i} update={update} admin={false} />;
|
||||
})}
|
||||
<div className="incidents-wrapper row">
|
||||
<div className="col-12 mt-2">
|
||||
{incidents?.length > 0 ? (
|
||||
incidents?.map((incident) => {
|
||||
const { id, title, description, updated_at } = incident;
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="braker mt-1 mb-3"></span>
|
||||
<div
|
||||
className={`incident-title col-12 ${
|
||||
incidentsShow[id] && "mb-3"
|
||||
}`}>
|
||||
{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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,36 +1,26 @@
|
|||
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 ContentHeader from "./ContentHeader";
|
||||
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 { STATUS_COLOR, STATUS_ICON, STATUS_TEXT } from "../utils/constants";
|
||||
import { findStatus } from "../utils/helper";
|
||||
import { analyticsTrack } from "../utils/trackers";
|
||||
|
||||
const ServicesPage = () => {
|
||||
// const data = messages.filter((m) => inRange(m) && m.service === 0);
|
||||
const [services, setServices] = useState([]);
|
||||
const [status, setStatus] = useState("uptime");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [poll, setPolling] = useState(1);
|
||||
const today = DateUtils.format(new Date(), "d MMMM yyyy, hh:mm aaa");
|
||||
|
||||
useEffect(() => {
|
||||
if(!loading) {
|
||||
if (!loading) {
|
||||
analyticsTrack({
|
||||
objectName: 'Status Page',
|
||||
actionName: 'displayed',
|
||||
screen: 'Home page'
|
||||
})
|
||||
objectName: "Status Page",
|
||||
actionName: "displayed",
|
||||
screen: "Home page",
|
||||
});
|
||||
}
|
||||
}, [loading])
|
||||
}, [loading]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
|
@ -59,47 +49,20 @@ const ServicesPage = () => {
|
|||
return (
|
||||
<div className="container col-md-7 col-sm-12 sm-container">
|
||||
<ContentHeader />
|
||||
|
||||
<div className="app-content">
|
||||
<div className="service">
|
||||
<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>
|
||||
|
||||
{loading && <ServiceLoader text="Loading Services" />}
|
||||
|
||||
{/* <ServicesList loading={loading} services={services} /> */}
|
||||
|
||||
{/* TODO --> Grouped Services to Accordian*/}
|
||||
{services && services.length > 0 ? (
|
||||
<Group services={services} />
|
||||
) : (
|
||||
<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="service-status">
|
||||
<span className="service-status-badge uptime"></span>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useState } from "react";
|
|||
import ReactTooltip from "react-tooltip";
|
||||
import langs from "../config/langs";
|
||||
import GroupServiceFailures from "./GroupServiceFailures";
|
||||
// import IncidentsBlock from "./IncidentsBlock";
|
||||
import IncidentsBlock from "./IncidentsBlock";
|
||||
import infoIcon from "../static/info.svg";
|
||||
|
||||
const SubServiceCard = ({ group, service }) => {
|
||||
|
@ -50,15 +50,14 @@ const SubServiceCard = ({ group, service }) => {
|
|||
<span
|
||||
className={`badge float-right font-12 ${
|
||||
service.online ? "status-green" : "status-red"
|
||||
}`}
|
||||
>
|
||||
}`}>
|
||||
{service.online ? langs("online") : langs("offline")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<GroupServiceFailures group={group} service={service} />
|
||||
{/* <IncidentsBlock service={service} /> */}
|
||||
<IncidentsBlock group={group} service={service} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -213,10 +213,15 @@ class Api {
|
|||
}
|
||||
|
||||
async incidents_service(id) {
|
||||
return incidents[id];
|
||||
// return axios
|
||||
// .get("api/services/" + id + "/incidents")
|
||||
// .then((response) => response.data);
|
||||
return axios
|
||||
.get("/services/" + id + "/active_incidents")
|
||||
.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) {
|
||||
|
|
|
@ -13,6 +13,48 @@ a {
|
|||
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 {
|
||||
background: $primary-bg;
|
||||
min-width: 100vw;
|
||||
|
@ -116,25 +158,73 @@ a {
|
|||
transition-duration: 300ms;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
position: relative;
|
||||
min-height: 130px;
|
||||
padding: 1.25rem 0;
|
||||
background-color: $white;
|
||||
.list-group .service-parent {
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1rem;
|
||||
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 {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
.service-card {
|
||||
position: relative;
|
||||
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 {
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-width: 1px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.service-card a {
|
||||
|
@ -157,40 +247,6 @@ a {
|
|||
&--right {
|
||||
display: flex;
|
||||
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;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 15px;
|
||||
|
||||
.service-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 1.875rem;
|
||||
|
||||
&-badge {
|
||||
width: 0.75rem;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// import DateUtils from "./DateUtils";
|
||||
|
||||
export function findStatus(data) {
|
||||
if (!Array.isArray(data)) return null;
|
||||
if (data.length === 0) return null;
|
||||
|
@ -12,15 +10,18 @@ export function findStatus(data) {
|
|||
return "";
|
||||
}
|
||||
|
||||
// export function inRange(message) {
|
||||
// return DateUtils.isBetween(
|
||||
// DateUtils.now(),
|
||||
// message.start_on,
|
||||
// message.start_on === message.end_on
|
||||
// ? DateUtils.maxDate().toISOString()
|
||||
// : message.end_on
|
||||
// );
|
||||
// }
|
||||
export function getIncidentTextType(type) {
|
||||
switch (type.toLowerCase()) {
|
||||
case "resolved":
|
||||
return "text-success";
|
||||
case "issue summary":
|
||||
return "text-danger";
|
||||
case "update":
|
||||
return "text-warning";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export const isObject = (obj) => {
|
||||
if (Object.prototype.toString.call(obj) === "[object Object]") {
|
||||
|
@ -59,13 +60,15 @@ export const calcPer = (uptime, downtime) => {
|
|||
// }
|
||||
|
||||
export const setUerId = (id) => {
|
||||
localStorage.setItem('stat_user_id',id);
|
||||
}
|
||||
localStorage.setItem("stat_user_id", id);
|
||||
};
|
||||
|
||||
export const getUserId = () => {
|
||||
return localStorage.getItem('stat_user_id');
|
||||
}
|
||||
return localStorage.getItem("stat_user_id");
|
||||
};
|
||||
|
||||
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