mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
refactor UI of header alerts
This commit is contained in:
parent
7eebd75088
commit
30db4e58ff
@ -1,6 +1,8 @@
|
||||
import Icon from "../Icon/Icon";
|
||||
import Icon, { ThickProp } from "../Icon/Icon";
|
||||
import IconProp from "Common/Types/Icon/IconProp";
|
||||
import React, { ReactElement } from "react";
|
||||
import Tooltip from "../Tooltip/Tooltip";
|
||||
import { GetReactElementFunction } from "../../Types/FunctionTypes";
|
||||
|
||||
export enum HeaderAlertType {
|
||||
SUCCESS = "Success",
|
||||
@ -15,45 +17,60 @@ export interface ComponentProps {
|
||||
title: string;
|
||||
className?: string | undefined;
|
||||
alertType: HeaderAlertType;
|
||||
tooltip?: string | undefined;
|
||||
}
|
||||
|
||||
const HeaderAlert: (props: ComponentProps) => ReactElement = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
let bgColor: string = "bg-indigo-500"; // default color info.
|
||||
let textColor: string = "text-indigo-500"; // default color info.
|
||||
|
||||
switch (props.alertType) {
|
||||
case HeaderAlertType.SUCCESS:
|
||||
bgColor = "bg-green-500 hover:bg-green-600";
|
||||
textColor = "text-green-500 hover:text-green-600";
|
||||
break;
|
||||
case HeaderAlertType.ERROR:
|
||||
bgColor = "bg-red-500 hover:bg-red-600";
|
||||
textColor = "text-red-500 hover:text-red-600";
|
||||
break;
|
||||
case HeaderAlertType.WARNING:
|
||||
bgColor = "bg-yellow-500 hover:bg-yellow-600";
|
||||
textColor = "text-yellow-500 hover:text-yellow-600";
|
||||
break;
|
||||
case HeaderAlertType.INFO:
|
||||
bgColor = "bg-indigo-500 hover:bg-indigo-600";
|
||||
textColor = "text-indigo-500 hover:text-indigo-600";
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`rounded-md m-3 p-3 ${bgColor} ${props.className} cursor-pointer ml-0 p-3 pr-4`}
|
||||
onClick={() => {
|
||||
props.onClick && props.onClick();
|
||||
}}
|
||||
>
|
||||
<div className="flex ">
|
||||
<div className="flex-shrink-0">
|
||||
<Icon icon={props.icon} className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div className="ml-1 flex-1 md:flex md:justify-between">
|
||||
<p className={`text-sm text-white`}>{props.title}</p>
|
||||
const getElement: GetReactElementFunction = (): ReactElement => {
|
||||
return (
|
||||
<div
|
||||
className={`cursor-pointer hover:bg-gray-100 p-1 h-7 pl-2 pr-2 -mt-2 -ml-7 mr-1 rounded-full ${props.className}`}
|
||||
onClick={() => {
|
||||
props.onClick && props.onClick();
|
||||
}}
|
||||
>
|
||||
<div className="flex ">
|
||||
<div className={`flex-shrink-0 mt-0.5 ${textColor}`}>
|
||||
<Icon
|
||||
icon={props.icon}
|
||||
thick={ThickProp.Thick}
|
||||
className={`h-4 w-4 stroke-[3px] font-bold ${textColor}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-1 flex-1 md:flex md:justify-between">
|
||||
<p className={`text-sm font-semibold ${textColor}`}>
|
||||
{props.title}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
if (props.tooltip) {
|
||||
return <Tooltip text={props.tooltip}>{getElement()}</Tooltip>;
|
||||
}
|
||||
|
||||
return getElement();
|
||||
};
|
||||
|
||||
export default HeaderAlert;
|
||||
|
28
Common/UI/Components/HeaderAlert/HeaderAlertGroup.tsx
Normal file
28
Common/UI/Components/HeaderAlert/HeaderAlertGroup.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React, { ReactElement } from "react";
|
||||
|
||||
export interface ComponentProps {
|
||||
children: Array<ReactElement>;
|
||||
}
|
||||
|
||||
const HeaderAlertGroup: (props: ComponentProps) => ReactElement = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div className="rounded-lg m-2 flex border-2 border-gray-200">
|
||||
{props.children.map((child: ReactElement, index: number) => {
|
||||
const isLastElement: boolean = index === props.children.length - 1;
|
||||
|
||||
return (
|
||||
<div key={index} className="p-4 flex">
|
||||
{child}
|
||||
{!isLastElement && (
|
||||
<div className="border-r-2 border-gray-200"></div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderAlertGroup;
|
@ -19,6 +19,7 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
refreshToggle?: string | undefined;
|
||||
className?: string | undefined;
|
||||
alertType: HeaderAlertType;
|
||||
tooltip?: string | undefined;
|
||||
}
|
||||
|
||||
const HeaderModelAlert: <TBaseModel extends BaseModel>(
|
||||
@ -86,6 +87,7 @@ const HeaderModelAlert: <TBaseModel extends BaseModel>(
|
||||
onClick={props.onClick}
|
||||
className={props.className}
|
||||
alertType={props.alertType}
|
||||
tooltip={props.tooltip}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -18,6 +18,7 @@ import HeaderAlert, {
|
||||
HeaderAlertType,
|
||||
} from "Common/UI/Components/HeaderAlert/HeaderAlert";
|
||||
import HeaderModelAlert from "Common/UI/Components/HeaderAlert/HeaderModelAlert";
|
||||
import HeaderAlertGroup from "Common/UI/Components/HeaderAlert/HeaderAlertGroup";
|
||||
import { SizeProp } from "Common/UI/Components/Icon/Icon";
|
||||
import { BILLING_ENABLED, getAllEnvVars } from "Common/UI/Config";
|
||||
import Navigation from "Common/UI/Utils/Navigation";
|
||||
@ -34,6 +35,7 @@ import React, {
|
||||
import Realtime from "Common/UI/Utils/Realtime";
|
||||
import DashboardNavigation from "../../Utils/Navigation";
|
||||
import ModelEventType from "Common/Types/Realtime/ModelEventType";
|
||||
import Alert from "Common/Models/DatabaseModels/Alert";
|
||||
|
||||
export interface ComponentProps {
|
||||
projects: Array<Project>;
|
||||
@ -50,11 +52,18 @@ const DashboardHeader: FunctionComponent<ComponentProps> = (
|
||||
const [activeIncidentToggleRefresh, setActiveIncidentToggleRefresh] =
|
||||
useState<string>(OneUptimeDate.getCurrentDate().toString());
|
||||
|
||||
const [activeAlertToggleRefresh, setActiveAlertToggleRefresh] =
|
||||
useState<string>(OneUptimeDate.getCurrentDate().toString());
|
||||
|
||||
const refreshIncidentCount: VoidFunction = () => {
|
||||
setActiveIncidentToggleRefresh(OneUptimeDate.getCurrentDate().toString());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const refreshAlertCount: VoidFunction = () => {
|
||||
setActiveAlertToggleRefresh(OneUptimeDate.getCurrentDate().toString());
|
||||
};
|
||||
|
||||
const realtimeIncidentCountRefresh: () => VoidFunction = (): VoidFunction => {
|
||||
const stopListeningOnCreate: VoidFunction =
|
||||
Realtime.listenToModelEvent<Incident>(
|
||||
{
|
||||
@ -91,12 +100,72 @@ const DashboardHeader: FunctionComponent<ComponentProps> = (
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
const stopListening: VoidFunction = () => {
|
||||
// on unmount.
|
||||
stopListeningOnCreate();
|
||||
stopListeningOnUpdate();
|
||||
stopListeningOnDelete();
|
||||
};
|
||||
|
||||
return stopListening;
|
||||
};
|
||||
|
||||
const realtimeAlertCountRefresh: () => VoidFunction = (): VoidFunction => {
|
||||
const stopListeningOnCreate: VoidFunction =
|
||||
Realtime.listenToModelEvent<Alert>(
|
||||
{
|
||||
eventType: ModelEventType.Create,
|
||||
modelType: Alert,
|
||||
tenantId: DashboardNavigation.getProjectId()!,
|
||||
},
|
||||
() => {
|
||||
refreshAlertCount();
|
||||
},
|
||||
);
|
||||
|
||||
const stopListeningOnUpdate: VoidFunction =
|
||||
Realtime.listenToModelEvent<Alert>(
|
||||
{
|
||||
eventType: ModelEventType.Update,
|
||||
modelType: Alert,
|
||||
tenantId: DashboardNavigation.getProjectId()!,
|
||||
},
|
||||
() => {
|
||||
refreshAlertCount();
|
||||
},
|
||||
);
|
||||
|
||||
const stopListeningOnDelete: VoidFunction =
|
||||
Realtime.listenToModelEvent<Alert>(
|
||||
{
|
||||
eventType: ModelEventType.Delete,
|
||||
modelType: Alert,
|
||||
tenantId: DashboardNavigation.getProjectId()!,
|
||||
},
|
||||
() => {
|
||||
refreshAlertCount();
|
||||
},
|
||||
);
|
||||
|
||||
const stopListening: VoidFunction = () => {
|
||||
// on unmount.
|
||||
stopListeningOnCreate();
|
||||
stopListeningOnUpdate();
|
||||
stopListeningOnDelete();
|
||||
};
|
||||
|
||||
return stopListening;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const realtimeIncidentStop: VoidFunction = realtimeIncidentCountRefresh();
|
||||
|
||||
const realtimeAlertStop: VoidFunction = realtimeAlertCountRefresh();
|
||||
|
||||
return () => {
|
||||
realtimeIncidentStop();
|
||||
realtimeAlertStop();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const showAddCardButton: boolean = Boolean(
|
||||
@ -143,61 +212,86 @@ const DashboardHeader: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
|
||||
<div className="flex ml-3">
|
||||
<HeaderModelAlert<TeamMember>
|
||||
icon={IconProp.Folder}
|
||||
modelType={TeamMember}
|
||||
query={{
|
||||
userId: User.getUserId(),
|
||||
hasAcceptedInvitation: false,
|
||||
}}
|
||||
alertType={HeaderAlertType.INFO}
|
||||
singularName="Project Invitation"
|
||||
pluralName="Project Invitations"
|
||||
requestOptions={{
|
||||
isMultiTenantRequest: true,
|
||||
}}
|
||||
onClick={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.PROJECT_INVITATIONS]!);
|
||||
}}
|
||||
/>
|
||||
|
||||
<HeaderModelAlert<Incident>
|
||||
icon={IconProp.Alert}
|
||||
modelType={Incident}
|
||||
alertType={HeaderAlertType.ERROR}
|
||||
query={{
|
||||
currentIncidentState: {
|
||||
order: 1,
|
||||
},
|
||||
}}
|
||||
refreshToggle={activeIncidentToggleRefresh}
|
||||
singularName="New Incident"
|
||||
pluralName="New Incidents"
|
||||
requestOptions={{
|
||||
isMultiTenantRequest: true,
|
||||
}}
|
||||
onClick={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.NEW_INCIDENTS]!);
|
||||
}}
|
||||
/>
|
||||
|
||||
{showTrialButton && (
|
||||
<HeaderAlert
|
||||
icon={IconProp.Clock}
|
||||
<HeaderAlertGroup>
|
||||
<HeaderModelAlert<TeamMember>
|
||||
icon={IconProp.Folder}
|
||||
modelType={TeamMember}
|
||||
query={{
|
||||
userId: User.getUserId(),
|
||||
hasAcceptedInvitation: false,
|
||||
}}
|
||||
alertType={HeaderAlertType.INFO}
|
||||
title={`Trial ends in ${OneUptimeDate.getNumberOfDaysBetweenDatesInclusive(
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
props.selectedProject!.trialEndsAt!,
|
||||
)} ${
|
||||
OneUptimeDate.getNumberOfDaysBetweenDatesInclusive(
|
||||
singularName=""
|
||||
pluralName=""
|
||||
tooltip="Looks like you have pending project invitations. Please click here to review and accept them."
|
||||
requestOptions={{
|
||||
isMultiTenantRequest: true,
|
||||
}}
|
||||
onClick={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.PROJECT_INVITATIONS]!);
|
||||
}}
|
||||
/>
|
||||
|
||||
<HeaderModelAlert<Incident>
|
||||
icon={IconProp.Alert}
|
||||
modelType={Incident}
|
||||
alertType={HeaderAlertType.ERROR}
|
||||
query={{
|
||||
currentIncidentState: {
|
||||
order: 1,
|
||||
},
|
||||
}}
|
||||
refreshToggle={activeIncidentToggleRefresh}
|
||||
singularName=""
|
||||
pluralName=""
|
||||
tooltip="View all active incidents"
|
||||
requestOptions={{
|
||||
isMultiTenantRequest: true,
|
||||
}}
|
||||
onClick={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.NEW_INCIDENTS]!);
|
||||
}}
|
||||
/>
|
||||
|
||||
<HeaderModelAlert<Alert>
|
||||
icon={IconProp.ExclaimationCircle}
|
||||
modelType={Alert}
|
||||
alertType={HeaderAlertType.ERROR}
|
||||
query={{
|
||||
currentAlertState: {
|
||||
order: 1,
|
||||
},
|
||||
}}
|
||||
refreshToggle={activeAlertToggleRefresh}
|
||||
singularName=""
|
||||
pluralName=""
|
||||
tooltip="View all active alerts"
|
||||
onClick={() => {
|
||||
Navigation.navigate(RouteMap[PageMap.ALERTS]!);
|
||||
}}
|
||||
/>
|
||||
|
||||
{showTrialButton ? (
|
||||
<HeaderAlert
|
||||
icon={IconProp.Clock}
|
||||
tooltip="Your trial ends soon"
|
||||
alertType={HeaderAlertType.INFO}
|
||||
title={`${OneUptimeDate.getNumberOfDaysBetweenDatesInclusive(
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
props.selectedProject!.trialEndsAt!,
|
||||
) > 1
|
||||
? "days"
|
||||
: "day"
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
)} ${
|
||||
OneUptimeDate.getNumberOfDaysBetweenDatesInclusive(
|
||||
OneUptimeDate.getCurrentDate(),
|
||||
props.selectedProject!.trialEndsAt!,
|
||||
) > 1
|
||||
? "days"
|
||||
: "day"
|
||||
}`}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</HeaderAlertGroup>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user