diff --git a/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx b/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx
index cab7262337..aea2b3924e 100644
--- a/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx
+++ b/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx
@@ -16,6 +16,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import { UPDATE_APPLICATION } from 'component/providers/AccessProvider/permissions';
import { ApplicationView } from '../ApplicationView/ApplicationView';
import { ApplicationUpdate } from '../ApplicationUpdate/ApplicationUpdate';
+import { ConnectedInstances } from '../ConnectedInstances/ConnectedInstances';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
@@ -30,8 +31,10 @@ import { formatDateYMD } from 'utils/formatDate';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel';
+import { useUiFlag } from 'hooks/useUiFlag';
export const ApplicationEdit = () => {
+ const showAdvancedApplicationMetrics = useUiFlag('sdkReporting');
const navigate = useNavigate();
const name = useRequiredPathParam('name');
const { application, loading } = useApplication(name);
@@ -84,6 +87,13 @@ export const ApplicationEdit = () => {
},
];
+ if (showAdvancedApplicationMetrics) {
+ tabData.push({
+ label: 'Connected instances',
+ component: ,
+ });
+ }
+
if (loading) {
return (
diff --git a/frontend/src/component/application/ConnectedInstances/ConnectedInstances.tsx b/frontend/src/component/application/ConnectedInstances/ConnectedInstances.tsx
new file mode 100644
index 0000000000..22bb1718e2
--- /dev/null
+++ b/frontend/src/component/application/ConnectedInstances/ConnectedInstances.tsx
@@ -0,0 +1,53 @@
+import { useMemo } from 'react';
+import useApplication from 'hooks/api/getters/useApplication/useApplication';
+import { formatDateYMDHMS } from 'utils/formatDate';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import { useConnectedInstancesTable } from './useConnectedInstancesTable';
+import { ConnectedInstancesTable } from './ConnectedInstancesTable';
+
+export const ConnectedInstances = () => {
+ const name = useRequiredPathParam('name');
+ const { application } = useApplication(name);
+
+ const tableData = useMemo(() => {
+ return (
+ application.instances
+ // @ts-expect-error: the type definition here is incomplete. It
+ // should be updated as part of this project.
+ .filter((instance) => instance.environment === 'production')
+ .map(({ instanceId, sdkVersion, clientIp, lastSeen }) => {
+ return {
+ instanceId,
+ ip: clientIp,
+ sdkVersion,
+ lastSeen: formatDateYMDHMS(lastSeen),
+ };
+ })
+ );
+ }, [application]);
+
+ const {
+ getTableProps,
+ getTableBodyProps,
+ headerGroups,
+ rows,
+ prepareRow,
+ state: { globalFilter },
+ setHiddenColumns,
+ columns,
+ } = useConnectedInstancesTable(tableData);
+
+ return (
+
+ );
+};
diff --git a/frontend/src/component/application/ConnectedInstances/ConnectedInstancesTable.tsx b/frontend/src/component/application/ConnectedInstances/ConnectedInstancesTable.tsx
new file mode 100644
index 0000000000..0e0a995967
--- /dev/null
+++ b/frontend/src/component/application/ConnectedInstances/ConnectedInstancesTable.tsx
@@ -0,0 +1,109 @@
+import {
+ Row,
+ TablePropGetter,
+ TableProps,
+ TableBodyPropGetter,
+ TableBodyProps,
+ HeaderGroup,
+} from 'react-table';
+import {
+ SortableTableHeader,
+ TableCell,
+ TablePlaceholder,
+} from 'component/common/Table';
+import {
+ Box,
+ styled,
+ Table,
+ TableBody,
+ TableRow,
+ useMediaQuery,
+} from '@mui/material';
+import theme from 'themes/theme';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+
+import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
+
+const hiddenColumnsSmall = ['ip', 'sdkVersion'];
+const hiddenColumnsCompact = ['ip', 'sdkVersion', 'lastSeen'];
+
+type ConnectedInstancesTableProps = {
+ compact?: boolean;
+ loading: boolean;
+ setHiddenColumns: (param: any) => void;
+ columns: any[];
+ rows: Row