diff --git a/ui/app/api/mirrors/alerts/route.ts b/ui/app/api/mirrors/alerts/route.ts
index 1870ef89af..ecb9891cbd 100644
--- a/ui/app/api/mirrors/alerts/route.ts
+++ b/ui/app/api/mirrors/alerts/route.ts
@@ -4,13 +4,18 @@ export const dynamic = 'force-dynamic';
export async function POST(request: Request) {
const { flowName } = await request.json();
- const errs = await prisma.flow_errors.findMany({
+ const errCount = await prisma.flow_errors.count({
where: {
flow_name: flowName,
+ error_type: 'error',
+ ack: false,
},
});
-
- return new Response(
- JSON.stringify(errs, (_, v) => (typeof v === 'bigint' ? v.toString() : v))
- );
+ let mirrorStatus: 'healthy' | 'failed';
+ if (errCount > 0) {
+ mirrorStatus = 'failed';
+ } else {
+ mirrorStatus = 'healthy';
+ }
+ return new Response(JSON.stringify(mirrorStatus));
}
diff --git a/ui/app/dto/MirrorsDTO.ts b/ui/app/dto/MirrorsDTO.ts
index 4a76200fd4..977f7f353c 100644
--- a/ui/app/dto/MirrorsDTO.ts
+++ b/ui/app/dto/MirrorsDTO.ts
@@ -28,3 +28,12 @@ export type SyncStatusRow = {
endTime: Date | null;
numRows: number;
};
+
+export type AlertErr = {
+ id: bigint;
+ flow_name: string;
+ error_message: string;
+ error_type: string;
+ error_timestamp: Date;
+ ack: boolean;
+};
diff --git a/ui/app/mirrors/errors/[mirrorName]/page.tsx b/ui/app/mirrors/errors/[mirrorName]/page.tsx
new file mode 100644
index 0000000000..75e8c91d2b
--- /dev/null
+++ b/ui/app/mirrors/errors/[mirrorName]/page.tsx
@@ -0,0 +1,75 @@
+import { AlertErr } from '@/app/dto/MirrorsDTO';
+import prisma from '@/app/utils/prisma';
+import TimeLabel from '@/components/TimeComponent';
+import { Label } from '@/lib/Label';
+import { Table, TableCell, TableRow } from '@/lib/Table';
+
+type MirrorErrorProps = {
+ params: { mirrorName: string };
+};
+
+const MirrorError = async ({ params: { mirrorName } }: MirrorErrorProps) => {
+ const mirrorErrors: AlertErr[] = await prisma.flow_errors.findMany({
+ where: {
+ flow_name: mirrorName,
+ error_type: 'error',
+ },
+ distinct: ['error_message'],
+ });
+
+ return (
+
+
+
+
+
+
+
+
+ Type
+ Message
+
+
+
+
+ }
+ >
+ {mirrorErrors.map((mirrorError) => (
+
+
+ {mirrorError.error_type.toUpperCase()}
+
+
+ {mirrorError.error_message}
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default MirrorError;
diff --git a/ui/app/mirrors/mirror-error.tsx b/ui/app/mirrors/mirror-error.tsx
deleted file mode 100644
index 950ef6d500..0000000000
--- a/ui/app/mirrors/mirror-error.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-'use client';
-
-import { Icon } from "@/lib/Icon";
-import { Prisma } from "@prisma/client";
-import React, { useState, useEffect } from "react";
-import * as Popover from '@radix-ui/react-popover';
-import { ProgressCircle } from "@/lib/ProgressCircle";
-import styled, { css } from 'styled-components';
-
-
-// const NoErrorMirror = styled.div`
-// color: ${({ theme }) => theme.colors.positive.fill.normal};
-// `;
-
-// const ErroredMirror = styled.div`
-// color: ${({ theme }) => theme.colors.destructive.fill.normal};
-// `;
-
-
-export const ErrorModal = ({ flowErrors }: { flowErrors: Prisma.flow_errorsSelect[] }) => {
- return (
-
- );
-};
-
-
-export const MirrorError = ({ flowName }: { flowName: string }) => {
- const [flowErrors, setFlowErrors] = useState();
- const [isLoading, setIsLoading] = useState(true);
- const [error, setError] = useState(null);
-
- useEffect(() => {
- const fetchData = async () => {
- setIsLoading(true);
- try {
- const response = await fetch(`/api/mirrors/alerts`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ flowName }),
- });
-
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
-
- const data = await response.json();
- setFlowErrors(data.errors);
- } catch (err: any) {
- setError(err.message);
- } finally {
- setIsLoading(false);
- }
- };
-
- fetchData();
- }, [flowName]);
-
- if (isLoading) {
- return ;
- }
-
- if (error) {
- console.log(error);
- return
;
- }
-
- if (!flowErrors || flowErrors.length === 0) {
- return ;
- }
-
- return ;
-};
diff --git a/ui/app/mirrors/mirror-status.tsx b/ui/app/mirrors/mirror-status.tsx
new file mode 100644
index 0000000000..2a68b7b3d2
--- /dev/null
+++ b/ui/app/mirrors/mirror-status.tsx
@@ -0,0 +1,83 @@
+'use client';
+
+import { Button } from '@/lib/Button';
+import { Icon } from '@/lib/Icon';
+import { Label } from '@/lib/Label';
+import { ProgressCircle } from '@/lib/ProgressCircle';
+import Link from 'next/link';
+import { useRouter } from 'next/navigation';
+import { useEffect, useState } from 'react';
+export const ErrorModal = ({ flowName }: { flowName: string }) => {
+ const router = useRouter();
+ return (
+
+
+
+ );
+};
+
+export const MirrorError = ({ flowName }: { flowName: string }) => {
+ const [flowStatus, setFlowStatus] = useState();
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ setIsLoading(true);
+ try {
+ const response = await fetch(`/api/mirrors/alerts`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ flowName }),
+ });
+
+ if (!response.ok) {
+ throw new Error('Network response was not ok');
+ }
+
+ const flowStatus = await response.json();
+ setFlowStatus(flowStatus);
+ } catch (err: any) {
+ setError(err.message);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [flowName]);
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
+ );
+ }
+
+ if (flowStatus == 'healthy') {
+ return ;
+ }
+
+ return ;
+};
diff --git a/ui/app/mirrors/tables.tsx b/ui/app/mirrors/tables.tsx
index 133427ff43..6c1289befc 100644
--- a/ui/app/mirrors/tables.tsx
+++ b/ui/app/mirrors/tables.tsx
@@ -5,10 +5,9 @@ import TimeLabel from '@/components/TimeComponent';
import { Label } from '@/lib/Label';
import { SearchField } from '@/lib/SearchField';
import { Table, TableCell, TableRow } from '@/lib/Table';
-import { Tab } from '@tremor/react';
import Link from 'next/link';
import { useMemo, useState } from 'react';
-import { MirrorError } from './mirror-error';
+import { MirrorError } from './mirror-status';
export function CDCFlows({ cdcFlows }: { cdcFlows: any }) {
const [searchQuery, setSearchQuery] = useState('');
@@ -45,15 +44,26 @@ export function CDCFlows({ cdcFlows }: { cdcFlows: any }) {
}}
header={
- {['Name', 'Source', 'Destination', 'Start Time', 'Errors', ''].map(
- (heading, index) => (
-
-
-
- )
- )}
+ {[
+ 'Name',
+ 'Source',
+ 'Destination',
+ 'Start Time',
+ 'Status',
+ '',
+ ].map((heading, index) => (
+
+
+
+ ))}
}
>