mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: Update no items message in Notification Logs tables to include pluralization
This commit is contained in:
2
Common/Typings/elkjs.d.ts
vendored
2
Common/Typings/elkjs.d.ts
vendored
@@ -25,6 +25,6 @@ declare module "elkjs/lib/elk.bundled.js" {
|
||||
}
|
||||
|
||||
export default class ELK {
|
||||
layout(graph: ElkNode): Promise<ElkNode>;
|
||||
public layout(graph: ElkNode): Promise<ElkNode>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,13 @@ export interface ServiceDependencyGraphProps {
|
||||
const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
props: ServiceDependencyGraphProps,
|
||||
): ReactElement => {
|
||||
const computeLuminance = (r: number, g: number, b: number): number => {
|
||||
const transform = (v: number): number => {
|
||||
const c = v / 255;
|
||||
const computeLuminance: (r: number, g: number, b: number) => number = (
|
||||
r: number,
|
||||
g: number,
|
||||
b: number,
|
||||
): number => {
|
||||
const transform: (v: number) => number = (v: number): number => {
|
||||
const c: number = v / 255;
|
||||
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
||||
};
|
||||
const R: number = transform(r);
|
||||
@@ -47,7 +51,7 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
||||
};
|
||||
|
||||
const getContrastText = (bg?: string): string => {
|
||||
const getContrastText: (bg?: string) => string = (bg?: string): string => {
|
||||
if (!bg) {
|
||||
return "#111827"; // gray-900
|
||||
}
|
||||
@@ -73,7 +77,7 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
if (hex.length === 3) {
|
||||
hex = hex
|
||||
.split("")
|
||||
.map((c) => {
|
||||
.map((c: string): string => {
|
||||
return c + c;
|
||||
})
|
||||
.join("");
|
||||
@@ -91,21 +95,25 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
const [rfNodes, setRfNodes] = useState<Node[]>([]);
|
||||
const [rfEdges, setRfEdges] = useState<Edge[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const elk = new ELK();
|
||||
useEffect((): void => {
|
||||
const elk: ELK = new ELK();
|
||||
// fixed node dimensions for layout (px)
|
||||
const NODE_WIDTH = 220;
|
||||
const NODE_HEIGHT = 56;
|
||||
const NODE_WIDTH: number = 220;
|
||||
const NODE_HEIGHT: number = 56;
|
||||
|
||||
const sortedServices = [...props.services].sort((a, b) => {
|
||||
return a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
|
||||
});
|
||||
const sortedDeps = [...props.dependencies].sort((a, b) => {
|
||||
if (a.fromServiceId === b.fromServiceId) {
|
||||
return a.toServiceId.localeCompare(b.toServiceId);
|
||||
}
|
||||
return a.fromServiceId.localeCompare(b.fromServiceId);
|
||||
});
|
||||
const sortedServices: Array<ServiceNodeData> = [...props.services].sort(
|
||||
(a: ServiceNodeData, b: ServiceNodeData): number => {
|
||||
return a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
|
||||
},
|
||||
);
|
||||
const sortedDeps: Array<ServiceEdgeData> = [...props.dependencies].sort(
|
||||
(a: ServiceEdgeData, b: ServiceEdgeData): number => {
|
||||
if (a.fromServiceId === b.fromServiceId) {
|
||||
return a.toServiceId.localeCompare(b.toServiceId);
|
||||
}
|
||||
return a.fromServiceId.localeCompare(b.fromServiceId);
|
||||
},
|
||||
);
|
||||
|
||||
const elkGraph: ElkNode = {
|
||||
id: "root",
|
||||
@@ -116,7 +124,7 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
"elk.spacing.nodeNode": "60",
|
||||
"elk.edgeRouting": "POLYLINE",
|
||||
},
|
||||
children: sortedServices.map((svc: ServiceNodeData) => {
|
||||
children: sortedServices.map((svc: ServiceNodeData): ElkNode => {
|
||||
return {
|
||||
id: svc.id,
|
||||
width: NODE_WIDTH,
|
||||
@@ -132,35 +140,39 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
}),
|
||||
};
|
||||
|
||||
const layout = async (): Promise<void> => {
|
||||
const layout: () => Promise<void> = async (): Promise<void> => {
|
||||
try {
|
||||
const res: any = await elk.layout(elkGraph as any);
|
||||
const placedNodes: Node[] = (res.children || []).map((child: any) => {
|
||||
const svc: ServiceNodeData | undefined = sortedServices.find((s) => {
|
||||
return s.id === child.id;
|
||||
});
|
||||
const background: string = svc?.color || "#ffffff";
|
||||
const textColor: string = getContrastText(background);
|
||||
return {
|
||||
id: child.id || "",
|
||||
data: { label: svc?.name || "" },
|
||||
position: { x: child.x || 0, y: child.y || 0 },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
style: {
|
||||
borderRadius: 8,
|
||||
padding: 8,
|
||||
border: "1px solid rgba(0,0,0,0.08)",
|
||||
background,
|
||||
color: textColor,
|
||||
boxShadow: "0 1px 2px rgba(16,24,40,.05)",
|
||||
width: NODE_WIDTH,
|
||||
height: NODE_HEIGHT,
|
||||
},
|
||||
} as Node;
|
||||
});
|
||||
const placedNodes: Node[] = (res.children || []).map(
|
||||
(child: any): Node => {
|
||||
const svc: ServiceNodeData | undefined = sortedServices.find(
|
||||
(s: ServiceNodeData): boolean => {
|
||||
return s.id === child.id;
|
||||
},
|
||||
);
|
||||
const background: string = svc?.color || "#ffffff";
|
||||
const textColor: string = getContrastText(background);
|
||||
return {
|
||||
id: child.id || "",
|
||||
data: { label: svc?.name || "" },
|
||||
position: { x: child.x || 0, y: child.y || 0 },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
style: {
|
||||
borderRadius: 8,
|
||||
padding: 8,
|
||||
border: "1px solid rgba(0,0,0,0.08)",
|
||||
background,
|
||||
color: textColor,
|
||||
boxShadow: "0 1px 2px rgba(16,24,40,.05)",
|
||||
width: NODE_WIDTH,
|
||||
height: NODE_HEIGHT,
|
||||
},
|
||||
} as Node;
|
||||
},
|
||||
);
|
||||
|
||||
const stroke = "#94a3b8"; // slate-400
|
||||
const stroke: string = "#94a3b8"; // slate-400
|
||||
const placedEdges: Edge[] = sortedDeps.map(
|
||||
(dep: ServiceEdgeData): Edge => {
|
||||
return {
|
||||
@@ -177,39 +189,41 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
|
||||
setRfNodes(placedNodes);
|
||||
setRfEdges(placedEdges);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Fallback: deterministic grid by name
|
||||
const sorted = sortedServices;
|
||||
const COLS = 4;
|
||||
const GAP_X = 260;
|
||||
const GAP_Y = 120;
|
||||
const nodes: Node[] = sorted.map((svc: ServiceNodeData, i: number) => {
|
||||
const col = i % COLS;
|
||||
const row = Math.floor(i / COLS);
|
||||
const x = col * GAP_X;
|
||||
const y = row * GAP_Y;
|
||||
const background: string = svc.color || "#ffffff";
|
||||
const textColor: string = getContrastText(background);
|
||||
return {
|
||||
id: svc.id,
|
||||
data: { label: svc.name },
|
||||
position: { x, y },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
style: {
|
||||
borderRadius: 8,
|
||||
padding: 8,
|
||||
border: "1px solid rgba(0,0,0,0.08)",
|
||||
background,
|
||||
color: textColor,
|
||||
boxShadow: "0 1px 2px rgba(16,24,40,.05)",
|
||||
width: NODE_WIDTH,
|
||||
height: NODE_HEIGHT,
|
||||
},
|
||||
};
|
||||
});
|
||||
const stroke = "#94a3b8";
|
||||
const edges: Edge[] = sortedDeps.map((dep: ServiceEdgeData) => {
|
||||
const sorted: Array<ServiceNodeData> = sortedServices;
|
||||
const COLS: number = 4;
|
||||
const GAP_X: number = 260;
|
||||
const GAP_Y: number = 120;
|
||||
const nodes: Node[] = sorted.map(
|
||||
(svc: ServiceNodeData, i: number): Node => {
|
||||
const col: number = i % COLS;
|
||||
const row: number = Math.floor(i / COLS);
|
||||
const x: number = col * GAP_X;
|
||||
const y: number = row * GAP_Y;
|
||||
const background: string = svc.color || "#ffffff";
|
||||
const textColor: string = getContrastText(background);
|
||||
return {
|
||||
id: svc.id,
|
||||
data: { label: svc.name },
|
||||
position: { x, y },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
style: {
|
||||
borderRadius: 8,
|
||||
padding: 8,
|
||||
border: "1px solid rgba(0,0,0,0.08)",
|
||||
background,
|
||||
color: textColor,
|
||||
boxShadow: "0 1px 2px rgba(16,24,40,.05)",
|
||||
width: NODE_WIDTH,
|
||||
height: NODE_HEIGHT,
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
const stroke: string = "#94a3b8";
|
||||
const edges: Edge[] = sortedDeps.map((dep: ServiceEdgeData): Edge => {
|
||||
return {
|
||||
id: `e-${dep.fromServiceId}-${dep.toServiceId}`,
|
||||
source: dep.fromServiceId,
|
||||
@@ -226,7 +240,6 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
};
|
||||
|
||||
layout();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.services, props.dependencies]);
|
||||
|
||||
return (
|
||||
@@ -250,7 +263,7 @@ const ServiceDependencyGraph: FunctionComponent<ServiceDependencyGraphProps> = (
|
||||
connectOnClick={false}
|
||||
>
|
||||
<MiniMap
|
||||
nodeColor={(n) => {
|
||||
nodeColor={(n: Node): string => {
|
||||
return (
|
||||
(n.style as any)?.background ||
|
||||
(n.data as any)?.color ||
|
||||
|
||||
@@ -112,7 +112,9 @@ const CallLogsTable: FunctionComponent<CallLogsTableProps> = (
|
||||
props.noItemsMessage ||
|
||||
(props.singularName
|
||||
? `No call logs for this ${props.singularName}.`
|
||||
: "No call logs.")
|
||||
: props.pluralName
|
||||
? `No ${props.pluralName.toLowerCase()} call logs.`
|
||||
: "No call logs.")
|
||||
}
|
||||
showRefreshButton={true}
|
||||
columns={props.columns || defaultColumns}
|
||||
|
||||
@@ -113,7 +113,9 @@ const EmailLogsTable: FunctionComponent<EmailLogsTableProps> = (
|
||||
props.noItemsMessage ||
|
||||
(props.singularName
|
||||
? `No email logs for this ${props.singularName}.`
|
||||
: "No email logs.")
|
||||
: props.pluralName
|
||||
? `No ${props.pluralName.toLowerCase()} email logs.`
|
||||
: "No email logs.")
|
||||
}
|
||||
showRefreshButton={true}
|
||||
columns={props.columns || defaultColumns}
|
||||
|
||||
@@ -119,7 +119,9 @@ const PushLogsTable: FunctionComponent<PushLogsTableProps> = (
|
||||
props.noItemsMessage ||
|
||||
(props.singularName
|
||||
? `No Push logs for this ${props.singularName}.`
|
||||
: "No Push logs.")
|
||||
: props.pluralName
|
||||
? `No ${props.pluralName.toLowerCase()} Push logs.`
|
||||
: "No Push logs.")
|
||||
}
|
||||
showRefreshButton={true}
|
||||
columns={props.columns || defaultColumns}
|
||||
|
||||
@@ -112,7 +112,9 @@ const SmsLogsTable: FunctionComponent<SmsLogsTableProps> = (
|
||||
props.noItemsMessage ||
|
||||
(props.singularName
|
||||
? `No SMS logs for this ${props.singularName}.`
|
||||
: "No SMS logs.")
|
||||
: props.pluralName
|
||||
? `No ${props.pluralName.toLowerCase()} SMS logs.`
|
||||
: "No SMS logs.")
|
||||
}
|
||||
showRefreshButton={true}
|
||||
columns={props.columns || defaultColumns}
|
||||
|
||||
@@ -138,7 +138,9 @@ const WorkspaceLogsTable: FunctionComponent<WorkspaceLogsTableProps> = (
|
||||
props.noItemsMessage ||
|
||||
(props.singularName
|
||||
? `No Workspace logs for this ${props.singularName}.`
|
||||
: "No Workspace logs.")
|
||||
: props.pluralName
|
||||
? `No ${props.pluralName.toLowerCase()} Workspace logs.`
|
||||
: "No Workspace logs.")
|
||||
}
|
||||
showRefreshButton={true}
|
||||
columns={props.columns || defaultColumns}
|
||||
|
||||
Reference in New Issue
Block a user