You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by pp...@apache.org on 2026/08/21 15:57:24 UTC

(atlas) 01/02: ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI (#708)

This is an automated email from the ASF dual-hosted git repository.

pawarprasad123 pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit 9a2cd9fa49076572d0f3466209839e4f8ab5eeab
Author: Brijesh Bhalala <32...@users.noreply.github.com>
AuthorDate: Tue Aug 11 15:16:27 2026 +0530

    ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI (#708)
    
    ( cherry-picked form the commit fba3527e34452ac1e103ca9ce75be43064ddd211)
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
    
    * ATLAS-5350: Atlas UI: Enhance Purge Audit Results UI
---
 .../src/hooks/__tests__/useVirtualization.test.ts  |  89 ++
 dashboard/src/hooks/useVirtualization.ts           |  33 +
 dashboard/src/utils/Enum.ts                        |  14 +
 dashboard/src/utils/Utils.ts                       |  43 +-
 .../views/Administrator/Audits/AdminAuditTable.tsx |  35 +-
 .../views/Administrator/Audits/AuditResults.scss   | 309 +++++++
 .../views/Administrator/Audits/AuditResults.tsx    | 857 ++++++++++++++----
 .../Audits/AuditsFilter/AuditFiltersFields.tsx     |   4 +
 .../Audits/__tests__/AdminAuditTable.test.tsx      | 122 ++-
 .../Audits/__tests__/AuditResults.test.tsx         | 975 +++++++++++++++++++--
 .../__tests__/AdministratorLayout.test.tsx         | 180 ++--
 dashboardv2/public/css/scss/drawer.scss            | 417 +++++++++
 dashboardv2/public/css/scss/style.scss             |   3 +-
 .../public/js/templates/audit/DrawerView_tmpl.html |  66 ++
 dashboardv2/public/js/utils/Utils.js               |  33 +
 .../js/views/audit/AdminAuditTableLayoutView.js    | 356 ++++++--
 dashboardv2/public/js/views/audit/DrawerView.js    | 298 +++++++
 .../public/js/views/search/QueryBuilderView.js     |   3 +
 18 files changed, 3418 insertions(+), 419 deletions(-)

diff --git a/dashboard/src/hooks/__tests__/useVirtualization.test.ts b/dashboard/src/hooks/__tests__/useVirtualization.test.ts
new file mode 100644
index 0000000000..62ce583568
--- /dev/null
+++ b/dashboard/src/hooks/__tests__/useVirtualization.test.ts
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { renderHook } from '@testing-library/react';
+import { useVirtualization } from '../useVirtualization';
+
+describe('useVirtualization', () => {
+  it('should return empty values when items array is empty', () => {
+    const { result } = renderHook(() =>
+      useVirtualization({ items: [], scrollTop: 0 })
+    );
+
+    expect(result.current).toEqual({
+      visibleItems: [],
+      paddingTop: 0,
+      paddingBottom: 0,
+      startIndex: 0,
+    });
+  });
+
+  it('should calculate visible items and padding correctly for initial state', () => {
+    const items = Array.from({ length: 100 }, (_, i) => i);
+    const { result } = renderHook(() =>
+      useVirtualization({ items, scrollTop: 0, itemHeight: 37, overscan: 10, visibleCount: 40 })
+    );
+
+    // Initial state (scrollTop = 0)
+    // startIndex = max(0, 0 - 10) = 0
+    // endIndex = min(99, 0 + 40 + 10) = 50
+    // visibleItems = items.slice(0, 51)
+
+    expect(result.current.startIndex).toBe(0);
+    expect(result.current.visibleItems.length).toBe(51);
+    expect(result.current.paddingTop).toBe(0);
+
+    // paddingBottom = (100 - 1 - 50) * 37 = 49 * 37 = 1813
+    expect(result.current.paddingBottom).toBe(1813);
+  });
+
+  it('should calculate visible items correctly when scrolled down', () => {
+    const items = Array.from({ length: 100 }, (_, i) => i);
+    // Scrolled 20 items down: 20 * 37 = 740
+    const { result } = renderHook(() =>
+      useVirtualization({ items, scrollTop: 740, itemHeight: 37, overscan: 10, visibleCount: 40 })
+    );
+
+    // startIndex = max(0, 20 - 10) = 10
+    // endIndex = min(99, 20 + 40 + 10) = 70
+
+    expect(result.current.startIndex).toBe(10);
+    expect(result.current.visibleItems.length).toBe(61); // 70 - 10 + 1
+
+    // paddingTop = 10 * 37 = 370
+    expect(result.current.paddingTop).toBe(370);
+
+    // paddingBottom = (100 - 1 - 70) * 37 = 29 * 37 = 1073
+    expect(result.current.paddingBottom).toBe(1073);
+  });
+
+  it('should cap end index at total items length', () => {
+    const items = Array.from({ length: 50 }, (_, i) => i);
+    // Scrolled way past the bottom
+    const { result } = renderHook(() =>
+      useVirtualization({ items, scrollTop: 5000, itemHeight: 37, overscan: 10, visibleCount: 40 })
+    );
+
+    // startIndex = max(0, 135 - 10) = 125
+    // endIndex = min(49, 135 + 40 + 10) = 49
+    // Wait, if startIndex > endIndex, slice will return empty array
+
+    expect(result.current.startIndex).toBe(125);
+    expect(result.current.visibleItems.length).toBe(0);
+    expect(result.current.paddingBottom).toBe(0);
+  });
+});
diff --git a/dashboard/src/hooks/useVirtualization.ts b/dashboard/src/hooks/useVirtualization.ts
new file mode 100644
index 0000000000..c518fd81cb
--- /dev/null
+++ b/dashboard/src/hooks/useVirtualization.ts
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useMemo } from 'react';
+import { virtualizeList } from '../utils/Utils';
+
+interface UseVirtualizationProps<T> {
+  items: T[];
+  scrollTop: number;
+  itemHeight?: number;
+  overscan?: number;
+  visibleCount?: number;
+}
+
+export const useVirtualization = <T>(options: UseVirtualizationProps<T>) => {
+  return useMemo(() => {
+    return virtualizeList(options);
+  }, [options.items, options.scrollTop, options.itemHeight, options.overscan, options.visibleCount]);
+};
diff --git a/dashboard/src/utils/Enum.ts b/dashboard/src/utils/Enum.ts
index 50644400b8..a67463a1f4 100644
--- a/dashboard/src/utils/Enum.ts
+++ b/dashboard/src/utils/Enum.ts
@@ -111,6 +111,20 @@ export const auditAction: { [key: string]: string } = {
   AUTO_PURGE : "Auto Purged Entities"
 };
 
+
+export enum AuditOperation {
+  PURGE = "PURGE",
+  AUTO_PURGE = "AUTO_PURGE",
+  IMPORT = "IMPORT",
+  EXPORT = "EXPORT"
+}
+
+export enum PurgeActiveView {
+  NONE = "none",
+  REQUESTED = "requested",
+  PURGED = "purged"
+}
+
 export const stats: any = {
   generalData: {
     collectionTime: "day"
diff --git a/dashboard/src/utils/Utils.ts b/dashboard/src/utils/Utils.ts
index 38556b9b54..3d0b0d7d69 100644
--- a/dashboard/src/utils/Utils.ts
+++ b/dashboard/src/utils/Utils.ts
@@ -801,6 +801,45 @@ const getNavigate = () => {
   return backNavigate;
 };
 
+const virtualizeList = (options: {
+  items: any[];
+  scrollTop: number;
+  itemHeight?: number;
+  overscan?: number;
+  visibleCount?: number;
+}) => {
+  const items = options.items || [];
+  const scrollTop = options.scrollTop || 0;
+  const itemHeight = options.itemHeight || 37;
+  const overscan = options.overscan || 10;
+  const visibleCount = options.visibleCount || 40;
+
+  const totalItems = items.length;
+  if (totalItems === 0) {
+    return {
+      visibleItems: [],
+      paddingTop: 0,
+      paddingBottom: 0,
+      startIndex: 0,
+    };
+  }
+
+  const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
+  const endIndex = Math.min(totalItems - 1, Math.floor(scrollTop / itemHeight) + visibleCount + overscan);
+
+  const visibleItems = items.slice(startIndex, endIndex + 1);
+
+  const paddingTop = startIndex * itemHeight;
+  const paddingBottom = Math.max(0, (totalItems - 1 - endIndex) * itemHeight);
+
+  return {
+    visibleItems,
+    paddingTop,
+    paddingBottom,
+    startIndex,
+  };
+};
+
 const globalSearchFilterInitialQuery: any = {
   query: {},
   setQuery: (newQuery: any) => {
@@ -857,5 +896,7 @@ export {
   setNavigate,
   getNavigate,
   globalSearchParams,
-  globalSearchFilterInitialQuery
+  globalSearchFilterInitialQuery,
+  virtualizeList
 };
+
diff --git a/dashboard/src/views/Administrator/Audits/AdminAuditTable.tsx b/dashboard/src/views/Administrator/Audits/AdminAuditTable.tsx
index 8673283228..ed3a2cb95c 100644
--- a/dashboard/src/views/Administrator/Audits/AdminAuditTable.tsx
+++ b/dashboard/src/views/Administrator/Audits/AdminAuditTable.tsx
@@ -54,8 +54,33 @@ const AdminAuditTable = () => {
       const limit = pageSize || 25;
       const offset = (pageIndex || 0) * limit;
 
-      let params: any = {
-        auditFilters: !isEmpty(queryApiObj) ? queryApiObj : null,
+      let auditFilters = !isEmpty(queryApiObj) ? JSON.parse(JSON.stringify(queryApiObj)) : null;
+
+      if (auditFilters) {
+        const filtersStr = JSON.stringify(auditFilters);
+        if (filtersStr.includes('"attributeName":"runId"')) {
+          // Remove any existing auditRowKind to prevent duplicates/conflicts
+          const removeAuditRowKind = (node: Record<string, any>) => {
+            if (node && node.criterion) {
+              node.criterion = node.criterion.filter((c: Record<string, any>) => c.attributeName !== 'auditRowKind');
+              node.criterion.forEach(removeAuditRowKind);
+            }
+          };
+          removeAuditRowKind(auditFilters);
+
+          // Force append SUMMARY by wrapping the existing filter
+          auditFilters = {
+            condition: "AND",
+            criterion: [
+              auditFilters,
+              { attributeName: "auditRowKind", operator: "eq", attributeValue: "SUMMARY" }
+            ]
+          };
+        }
+      }
+
+      let params: Record<string, unknown> = {
+        auditFilters: auditFilters,
         limit: limit,
         sortOrder: "DESCENDING",
         offset: offset,
@@ -65,10 +90,10 @@ const AdminAuditTable = () => {
       try {
         setLoader(true);
         let searchResp = await getAuditData(params);
-        setAuditData(searchResp.data);
+        setAuditData(searchResp.data || []);
         setLoader(false);
-      } catch (error: any) {
-        console.error("Error fetching data:", error.response.data.errorMessage);
+      } catch (error: unknown) {
+        console.error("Error fetching data:", (error as any)?.response?.data?.errorMessage || (error as any)?.message);
         toast.dismiss(toastId.current);
         serverError(error, toastId);
         setLoader(false);
diff --git a/dashboard/src/views/Administrator/Audits/AuditResults.scss b/dashboard/src/views/Administrator/Audits/AuditResults.scss
new file mode 100644
index 0000000000..65e14ad35a
--- /dev/null
+++ b/dashboard/src/views/Administrator/Audits/AuditResults.scss
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.purge-audit-view {
+    margin-top: 16px;
+    margin-bottom: 16px;
+}
+
+.purge-summary-container {
+    padding: 20px;
+    border-radius: 8px;
+    background-color: #f0f4f8;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02);
+}
+
+.purge-runid-header {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin-bottom: 16px;
+}
+
+.runid-text {
+    font-family: monospace;
+}
+
+.card-title {
+    text-transform: uppercase;
+    font-weight: bold;
+    letter-spacing: 0.5px;
+    font-size: 11px;
+    display: block;
+}
+
+.card-count {
+    font-weight: bold;
+    margin-top: 4px;
+}
+
+.purge-card {
+    padding: 12px;
+    border-radius: 8px;
+    transition: all 0.2s ease-in-out;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
+}
+
+.purge-card-requested {
+    background-color: #eff6ff;
+    border: 1px solid #bfdbfe;
+    cursor: pointer;
+
+    &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
+        border-color: #60a5fa;
+    }
+}
+
+.purge-card-purged {
+    background-color: #f0fdf4;
+    border: 1px solid #bbf7d0;
+
+    &.clickable {
+        cursor: pointer;
+
+        &:hover {
+            transform: translateY(-2px);
+            box-shadow: 0 4px 12px rgba(34, 197, 94, 0.15);
+            border-color: #4ade80;
+        }
+    }
+}
+
+.purge-card-failed {
+    background-color: #fef2f2;
+    border: 1px solid #fecaca;
+    cursor: default;
+}
+
+.purge-card-failed-empty {
+    background-color: #fafafa;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    cursor: default;
+}
+
+.purge-card-skipped {
+    background-color: #fffbeb;
+    border: 1px solid #fef08a;
+    cursor: default;
+}
+
+.purge-card-skipped-empty {
+    background-color: #fafafa;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    cursor: default;
+}
+
+.purge-alert-container {
+    margin-top: 16px;
+}
+
+.purge-alert-title {
+    font-weight: bold;
+    font-size: 13px;
+}
+
+.drawer-paper {
+    width: 400px;
+}
+
+.drawer-content-wrapper {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    overflow: hidden;
+    box-sizing: border-box;
+}
+
+.drawer-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 8px 12px;
+}
+
+.drawer-title {
+    font-weight: 600;
+    color: rgba(0, 0, 0, 0.87);
+    font-size: 16px;
+}
+
+.drawer-runid-container {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin: 4px 12px;
+    padding: 4px 0;
+}
+
+.drawer-runid-text {
+    font-size: 12px;
+}
+
+.drawer-search-container {
+    margin: 4px 12px;
+    padding-bottom: 8px;
+}
+
+.drawer-search-input {
+    width: 100%;
+    border-bottom: 1px solid #d1d5db;
+    padding-bottom: 4px;
+}
+
+.drawer-loader {
+    display: flex;
+    justify-content: center;
+    padding: 40px 0;
+}
+
+.drawer-list-container {
+    overflow-y: scroll;
+    flex-grow: 1;
+    min-height: 0;
+    margin: 0 15px;
+    padding-right: 5px;
+}
+
+.drawer-list-empty {
+    padding: 24px 0;
+    text-align: center;
+}
+
+.drawer-list-item {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.04);
+    padding: 0 0 0 10px;
+    height: 24px;
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+}
+
+.drawer-list-index {
+    margin-right: 8px;
+    min-width: 24px;
+    color: rgba(0, 0, 0, 0.6);
+    text-align: right;
+}
+
+.drawer-list-link {
+    display: inline-block;
+    max-width: 100%;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    text-align: left;
+    margin-left: 12px;
+}
+
+.drawer-footer {
+    margin-top: auto;
+    padding: 8px 12px;
+    border-top: 1px solid #e2e8f0;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    flex-wrap: nowrap;
+    flex-shrink: 0;
+    background-color: #fff;
+}
+
+.drawer-footer-count {
+    white-space: nowrap;
+    margin-right: 8px;
+    font-size: 13px;
+}
+
+.drawer-pagination-container {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+}
+
+.drawer-limit-container {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+}
+
+.drawer-limit-label {
+    font-size: 13px;
+}
+
+.drawer-limit-input {
+    width: 48px;
+    height: 24px;
+    box-sizing: border-box;
+    font-size: 13px;
+    border: 1px solid #cbd5e1;
+    border-radius: 4px;
+    padding: 0 4px;
+    text-align: center;
+    outline: none;
+
+    &:focus {
+        border-color: #90caf9;
+    }
+}
+
+.audit-list-header {
+    padding: 16px 0 0 16px;
+    text-align: left;
+}
+
+.audit-list-link {
+    display: inline-block;
+    max-width: 100%;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    text-align: left;
+    vertical-align: bottom;
+}
+
+.copy-icon {
+    font-size: 15px;
+    color: rgba(0, 0, 0, 0.6);
+
+    &.copied {
+        color: #2e7d32;
+    }
+}
+
+.drawer-search-icon {
+    font-size: 16px;
+    color: rgba(0, 0, 0, 0.6);
+}
+
+.purge-pagination {
+    .MuiPaginationItem-root {
+        min-width: 24px;
+        height: 24px;
+        margin: 0 2px;
+        font-size: 13px;
+    }
+
+    .MuiPaginationItem-ellipsis {
+        display: none;
+    }
+
+    .MuiPaginationItem-page:not(.Mui-selected) {
+        display: none;
+    }
+}
\ No newline at end of file
diff --git a/dashboard/src/views/Administrator/Audits/AuditResults.tsx b/dashboard/src/views/Administrator/Audits/AuditResults.tsx
index acde90ff39..42677ad408 100644
--- a/dashboard/src/views/Administrator/Audits/AuditResults.tsx
+++ b/dashboard/src/views/Administrator/Audits/AuditResults.tsx
@@ -15,227 +15,730 @@
  * limitations under the License.
  */
 
-import { Grid, Link, List, ListItem, ListItemText, Typography } from "@mui/material";
-import { auditAction, category } from "@utils/Enum";
+import { Grid, Link, List, ListItem, ListItemText, Typography, Box, Drawer, IconButton, Stack, Tooltip, TextField, InputAdornment, Pagination, PaginationItem, Skeleton } from "@mui/material";
+import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
+import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
+import ContentCopyIcon from "@mui/icons-material/ContentCopy";
+import SearchIcon from "@mui/icons-material/Search";
+import { auditAction, category, AuditOperation, PurgeActiveView } from "@utils/Enum";
 import { isEmpty, jsonParse } from "@utils/Utils";
+import { useVirtualization } from "@hooks/useVirtualization";
 import CustomModal from "@components/Modal";
 import TypeDefAuditDetailModal from "@components/TypeDefAuditDetailModal";
-import { useState } from "react";
-import { Item } from "@utils/Muiutils";
+import { useRef, useState, useEffect } from "react";
 import AuditsTab from "@views/DetailPage/EntityDetailTabs/AuditsTab";
 import ImportExportAudits from "./ImportExportAudits";
+import { LightTooltip } from "@components/muiComponents";
+import { fetchApi } from "@api/apiMethods/fetchApi";
+import "./AuditResults.scss";
+interface AuditEntry {
+  guid: string;
+  operation: string;
+  params?: string;
+  result?: string;
+  runId?: string;
+}
 
-const AuditResults = ({ componentProps, row }: any) => {
+interface PurgeSummary {
+  runId?: string;
+  requestedCount?: number;
+  purgedCount?: number;
+  purgedDependenciesCount?: number;
+  failedCount?: number;
+  failedDependenciesCount?: number;
+  skippedCount?: number;
+  executionFailed?: boolean;
+}
+
+interface AuditResultsProps {
+  componentProps?: {
+    auditData?: AuditEntry[];
+  };
+  row: {
+    original: {
+      guid: string;
+      runId?: string;
+    };
+  };
+}
+
+const AuditResults: React.FC<AuditResultsProps> = ({ componentProps, row }) => {
   const { auditData } = componentProps || {};
   const [openModal, setOpenModal] = useState<boolean>(false);
   const [openPurgeModal, setOpenPurgeModal] = useState<boolean>(false);
-  const [currentResultObj, setCurrentObj] = useState<any>({});
-  const [currentPurgeResultObj, setCurrentPurgeResultObj] = useState<any>("");
+  const [currentResultObj, setCurrentObj] = useState<Record<string, unknown> | undefined>();
+  // Stores the guid of the clicked purged entity
+  const [currentPurgeResultObj, setCurrentPurgeResultObj] = useState<string | undefined>();
+  const [activePurgeView, setActivePurgeView] = useState<PurgeActiveView>(PurgeActiveView.NONE);
+  const [drawerSearchText, setDrawerSearchText] = useState<string>('');
+  const [drawerPage, setDrawerPage] = useState<number>(1);
+  const [drawerPageSize, setDrawerPageSize] = useState<number>(25);
+  const [drawerPageSizeInput, setDrawerPageSizeInput] = useState<string>('25');
+  const [scrollTop, setScrollTop] = useState<number>(0);
+  const [copiedRunId, setCopiedRunId] = useState<boolean>(false);
+  const [purgedApiGuids, setPurgedApiGuids] = useState<string[]>([]);
+  const [summaryData, setSummaryData] = useState<Record<string, unknown> | null>(null);
+  const [loadingSummary, setLoadingSummary] = useState<boolean>(false);
+
+
   const handleCloseModal = () => {
     setOpenModal(false);
   };
   const handleClosePurgeModal = () => {
     setOpenPurgeModal(false);
   };
-  const auditObj = !isEmpty(auditData)
-    ? auditData.find((obj: { guid: string }) => obj.guid == row.original.guid)
-    : {};
 
-  const { operation, params, result } = auditObj;
+  const handleCopyRunId = (idToCopy: string, setCopied: (val: boolean) => void) => {
+    if (navigator.clipboard) {
+      navigator.clipboard.writeText(idToCopy);
+    } else {
+      const textField = document.createElement('textarea');
+      textField.innerText = idToCopy;
+      document.body.appendChild(textField);
+      textField.select();
+      document.execCommand('copy');
+      textField.remove();
+    }
+    setCopied(true);
+    setTimeout(() => setCopied(false), 2000);
+  };
+
+  const auditObj: AuditEntry | undefined = !isEmpty(auditData)
+    ? (auditData as AuditEntry[]).find((obj) => obj.guid === row.original.guid)
+    : undefined;
+
+  const operation = auditObj?.operation ?? '';
+  const params = auditObj?.params;
+  const result = auditObj?.result;
+
+  let isPurgeOperation = operation === AuditOperation.PURGE || operation === AuditOperation.AUTO_PURGE;
+  const summaryGuid = auditObj?.guid ?? row.original.guid;
+
+  useEffect(() => {
+    const controller = new AbortController();
+
+    if (isPurgeOperation && summaryGuid) {
+      setLoadingSummary(true);
+      fetchApi(`/api/atlas/admin/audit/${summaryGuid}/summary`, {
+        method: "GET",
+        headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
+        signal: controller.signal
+      })
+        .then(res => {
+          if (!controller.signal.aborted) {
+            if (res.data && !Array.isArray(res.data) && typeof res.data === 'object') {
+              setSummaryData(res.data);
+            }
+          }
+        })
+        .catch(err => {
+          if (!controller.signal.aborted && err.name !== 'AbortError' && err.name !== 'CanceledError') {
+            console.error("Failed to fetch purge summary", err);
+          }
+        })
+        .finally(() => {
+          if (!controller.signal.aborted) {
+            setLoadingSummary(false);
+          }
+        });
+    }
 
-  const resultObj =
-    (operation == "PURGE" || operation == "AUTO_PURGE")
-      ? result.replace("[", "").replace("]", "").split(",")
-      : jsonParse(result);
+    return () => {
+      controller.abort();
+    };
+  }, [isPurgeOperation, summaryGuid]);
+
+  let summary: Record<string, unknown> = summaryData || {};
+  let requestedEntitiesList: string[] = [];
+  let legacyPurgedList: string[] = [];
+
+  if (isPurgeOperation) {
+    if (!summaryData) {
+      try {
+        const parsed = typeof result === "string" ? JSON.parse(result) : result;
+        if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
+          summary = (parsed as Record<string, unknown>).summary
+            ? (parsed as Record<string, unknown>).summary as Record<string, unknown>
+            : parsed as Record<string, unknown>;
+        } else if (Array.isArray(parsed)) {
+          legacyPurgedList = (parsed as unknown[]).map((item) =>
+            typeof item === "string" ? item : (item as { guid?: string }).guid || String(item)
+          );
+        }
+      } catch (_e) {
+        if (typeof result === "string" && !result.startsWith("{")) {
+          legacyPurgedList = result.replace(/^\[|\]$/g, "").split(",").map(s => s.trim()).filter(Boolean);
+        }
+      }
+    } else {
+      if (typeof result === "string" && !result.startsWith("{")) {
+        legacyPurgedList = result.replace(/^\[|\]$/g, "").split(",").map(s => s.trim()).filter(Boolean);
+      }
+    }
+
+    if (params) {
+      try {
+        const parsedParams = JSON.parse(params);
+        if (Array.isArray(parsedParams)) {
+          requestedEntitiesList = parsedParams as string[];
+        } else if (typeof params === "string") {
+          requestedEntitiesList = params.replace(/^\[|\]$/g, "").split(",").map(s => s.trim()).filter(Boolean);
+        }
+      } catch (_e) {
+        requestedEntitiesList = typeof params === "string"
+          ? params.replace(/^\[|\]$/g, "").split(",").map(s => s.trim()).filter(Boolean)
+          : [];
+      }
+    }
+  } else {
+    try {
+      summary = jsonParse(result) as Record<string, unknown>;
+    } catch (_e) {
+      summary = {};
+    }
+  }
+
+  const purgeSummary = summary as PurgeSummary;
+  const runId = row.original.runId
+    ?? purgeSummary?.runId
+    ?? auditObj?.runId
+    ?? 'N/A';
+
+  const isSummaryRow = (runId !== 'N/A') && isPurgeOperation;
+
+  const requestedCount = purgeSummary?.requestedCount ?? requestedEntitiesList.length;
+  const purgedCount = purgeSummary?.purgedCount ?? legacyPurgedList.length;
+  const purgedDependenciesCount = purgeSummary?.purgedDependenciesCount ?? 0;
+  const totalPurgedCount = purgedCount + purgedDependenciesCount;
+  const failedCount = purgeSummary?.failedCount ?? 0;
+  const failedDependenciesCount = purgeSummary?.failedDependenciesCount ?? 0;
+  const totalFailedCount = failedCount + failedDependenciesCount;
+  const skippedCount = purgeSummary?.skippedCount ?? 0;
+  const executionFailed = !!purgeSummary?.executionFailed || totalFailedCount > 0;
+
+  const handleOpenPurgedDrawer = () => {
+    if (totalPurgedCount === 0) return;
+    setActivePurgeView(PurgeActiveView.PURGED);
+    setDrawerPage(1);
+    setScrollTop(0);
+    // As requested, Total Purged simply uses the raw `result` object string (legacyPurgedList)
+    setPurgedApiGuids(legacyPurgedList);
+  };
 
   return (
     <>
-      {operation != "PURGE" &&
-        operation != "AUTO_PURGE" &&
-        operation != "IMPORT" &&
-        operation != "EXPORT" &&
-        !isEmpty(resultObj) ? (
-        <Grid container spacing={2}>
-          {params.split(",").length > 1 ? (
-            <>
-              {params.split(",")?.map((param: { param: string }) => {
-                return (
-                  <Grid item md={4}>
-                    <Item
-                      sx={{
-                        height: "100%",
-                        maxHeight: "300px",
-                        overflow: "auto",
-                      }}
-                    >
-                      <Typography
-                        sx={{ padding: "1rem 0 0 1rem", textAlign: "left" }}
-                      >{`${category[param as any]} ${auditAction[operation]
-                        }`}</Typography>
-
-                      <List className="audit-results-list">
-                        {resultObj[param as any].map(
-                          (obj: { name: string }) => {
-                            const { name } = obj;
-                            return (
-                              <>
-                                <ListItem className="audit-results-list-item">
-                                  <Link
-                                    className="audit-results-entityid"
-                                    component="button"
-                                    variant="body2"
-                                    onClick={() => {
-                                      setOpenModal(true);
-                                      setCurrentObj(obj);
-                                    }}
-                                    title={name}
-                                    sx={{
-                                      display: "inline-block",
-                                      maxWidth: "100%",
-                                      textOverflow: "ellipsis",
-                                      overflow: "hidden",
-                                      whiteSpace: "nowrap",
-                                      textAlign: "left",
-                                      verticalAlign: "bottom"
-                                    }}
-                                  >
-                                    {name}
-                                  </Link>
-                                </ListItem>
-                              </>
-                            );
-                          }
-                        )}
-                      </List>
-                    </Item>
-                  </Grid>
-                );
-              })}
-            </>
-          ) : (
-            <>
-              <Grid item md={4}>
-                <Item
-                  sx={{
-                    height: "100%",
-                    maxHeight: "300px",
-                    overflow: "auto",
-                  }}
-                >
-                  <Typography
-                    sx={{ padding: "1rem 0 0 1rem", textAlign: "left" }}
-                  >{`${category[params as any]} ${auditAction[operation]
-                    }`}</Typography>
-                  <List className="audit-results-list">
-                    {resultObj[params].map((obj: { name: string }) => {
-                      const { name } = obj;
-                      return (
-                        <>
-                          <ListItem className="audit-results-list-item">
+      <TypeDefAuditDetailModal
+        open={openModal}
+        onClose={handleCloseModal}
+        detailObject={currentResultObj ?? null}
+        maxWidth="md"
+      />
+
+      <CustomModal
+        open={openPurgeModal}
+        onClose={handleClosePurgeModal}
+        title={operation === "AUTO_PURGE" ? `Auto Purge Entity Details: ${currentPurgeResultObj}` : `Purged Entity Details: ${currentPurgeResultObj}`}
+        button1Handler={undefined}
+        button2Handler={undefined}
+        maxWidth="md"
+        footer={false}
+      >
+        <AuditsTab auditResultGuid={currentPurgeResultObj} />
+      </CustomModal>
+
+      {operation === "TYPE_DEF_CREATE" ||
+        operation === "TYPE_DEF_UPDATE" ||
+        operation === "TYPE_DEF_DELETE" ? (
+        <List className="audit-results-list">
+          {summary &&
+            Object.keys(summary).map((key: string) => {
+              const rawItems = summary[key];
+              const items: Array<Record<string, unknown> | string> = Array.isArray(rawItems)
+                ? (rawItems as Array<Record<string, unknown> | string>)
+                : [];
+              return (
+                <div key={key}>
+                  <Typography className="audit-list-header">
+                    {`${category[key as keyof typeof category] || key} ${auditAction[operation as keyof typeof auditAction] || operation}`}
+                  </Typography>
+                  {items.map((obj: Record<string, unknown> | string, idx: number) => {
+                    const name = typeof obj === 'object' && obj !== null
+                      ? (obj.name as string) || String(obj)
+                      : String(obj);
+                    return (
+                      <ListItem key={name + idx} className="audit-results-list-item">
+                        <ListItemText
+                          primary={
                             <Link
-                              className="audit-results-entityid"
+                              className="audit-results-entityid audit-list-link"
                               component="button"
                               variant="body2"
                               onClick={() => {
                                 setOpenModal(true);
-                                setCurrentObj(obj);
+                                setCurrentObj(typeof obj === "object" ? obj : { name: obj });
                               }}
                               title={name}
-                              sx={{
-                                display: "inline-block",
-                                maxWidth: "100%",
-                                textOverflow: "ellipsis",
-                                overflow: "hidden",
-                                whiteSpace: "nowrap",
-                                textAlign: "left",
-                                verticalAlign: "bottom"
-                              }}
                             >
                               {name}
                             </Link>
-                          </ListItem>
-                        </>
-                      );
-                    })}
-                  </List>
-                </Item>
+                          }
+                        />
+                      </ListItem>
+                    );
+                  })}
+                </div>
+              );
+            })}
+        </List>
+      ) : operation === "IMPORT" || operation === "EXPORT" ? (
+        <ImportExportAudits auditObj={auditObj} />
+      ) : !isPurgeOperation ? (
+        <Typography>No Results Found</Typography>
+      ) : null}
+
+      {/* Purge Audit View */}
+      {isPurgeOperation ? (
+        <Box className="purge-audit-view">
+          {loadingSummary && Object.keys(summary).length === 0 && legacyPurgedList.length === 0 && !result ? (
+            <Box sx={{ p: 2 }}>
+              <Skeleton variant="text" width="40%" height={30} sx={{ mb: 2 }} />
+              <Grid container spacing={2}>
+                <Grid item xs={6} sm={3}><Skeleton variant="rectangular" height={70} sx={{ borderRadius: 1 }} /></Grid>
+                <Grid item xs={6} sm={3}><Skeleton variant="rectangular" height={70} sx={{ borderRadius: 1 }} /></Grid>
+                <Grid item xs={6} sm={3}><Skeleton variant="rectangular" height={70} sx={{ borderRadius: 1 }} /></Grid>
+                <Grid item xs={6} sm={3}><Skeleton variant="rectangular" height={70} sx={{ borderRadius: 1 }} /></Grid>
               </Grid>
-            </>
+            </Box>
+          ) : (
+            <Box className="purge-summary-container">
+
+              {/* Run Id Header with Copy Action */}
+              {runId !== 'N/A' && (
+                <Box className="purge-runid-header">
+                  <Typography variant="body2" color="textSecondary" className="runid-text">
+                    <strong>Run Id:</strong> {runId}
+                  </Typography>
+                  <Tooltip title={copiedRunId ? "Copied!" : "Copy Run Id"}>
+                    <IconButton
+                      size="small"
+                      onClick={() => handleCopyRunId(runId, setCopiedRunId)}
+                      className="purge-runid-copy"
+                    >
+                      <ContentCopyIcon className={`copy-icon ${copiedRunId ? "copied" : ""}`} />
+                    </IconButton>
+                  </Tooltip>
+                </Box>
+              )}
+
+              {/* 4 Cards Grid: Requested, Total Purged, Failed (Display Only), Skipped (Display Only) */}
+              <Grid container spacing={2}>
+                {/* 1. Clickable Requested Card */}
+                {isSummaryRow && (
+                  <Grid item xs={6} sm={3}>
+                    <Box
+                      role="button"
+                      tabIndex={0}
+                      aria-label="View Requested Entities"
+                      onClick={() => {
+                        setActivePurgeView(PurgeActiveView.REQUESTED);
+                        setDrawerPage(1);
+                        setScrollTop(0);
+                      }}
+                      onKeyDown={(e) => {
+                        if (e.key === 'Enter' || e.key === ' ') {
+                          e.preventDefault();
+                          setActivePurgeView(PurgeActiveView.REQUESTED);
+                          setDrawerPage(1);
+                          setScrollTop(0);
+                        }
+                      }}
+                      className="purge-card purge-card-requested"
+                    >
+                      <Typography variant="caption" color="primary.main" display="block" className="card-title">
+                        Requested
+                      </Typography>
+                      <Typography variant="h5" color="primary.main" className="card-count">
+                        {requestedCount}
+                      </Typography>
+                    </Box>
+                  </Grid>
+                )}
+
+                {/* 2. Clickable Total Purged Card */}
+                <Grid item xs={isSummaryRow ? 6 : 12} sm={isSummaryRow ? 3 : 4}>
+                  <Box
+                    role={totalPurgedCount > 0 ? "button" : undefined}
+                    tabIndex={totalPurgedCount > 0 ? 0 : undefined}
+                    aria-label="View Purged Entities"
+                    onClick={handleOpenPurgedDrawer}
+                    onKeyDown={(e) => {
+                      if (totalPurgedCount > 0 && (e.key === 'Enter' || e.key === ' ')) {
+                        e.preventDefault();
+                        handleOpenPurgedDrawer();
+                      }
+                    }}
+                    className={`purge-card purge-card-purged ${totalPurgedCount > 0 ? "clickable" : ""}`}
+                  >
+                    <Typography variant="caption" color="success.main" display="block" className="card-title">
+                      PURGED
+                    </Typography>
+                    <Typography variant="h5" color="success.main" className="card-count">
+                      {totalPurgedCount}
+                    </Typography>
+                  </Box>
+                </Grid>
+
+                {/* 3 & 4. Display-Only Failed and Skipped Cards */}
+                {isSummaryRow && (
+                  <>
+                    <Grid item xs={6} sm={3}>
+                      <LightTooltip
+                        title={
+                          totalFailedCount > 0 || executionFailed
+                            ? "Some entities failed to purge. Please check purgefailure.log for details."
+                            : "No failed entities during this purge operation."
+                        }
+                        arrow
+                        placement="top"
+                      >
+                        <Box
+                          className={`purge-card ${totalFailedCount > 0 ? "purge-card-failed" : "purge-card-failed-empty"}`}
+                        >
+                          <Typography variant="caption" color={totalFailedCount > 0 ? "error.main" : "textSecondary"} display="block" className="card-title">
+                            Failed
+                          </Typography>
+                          <Typography variant="h5" color={totalFailedCount > 0 ? "error.main" : "textPrimary"} className="card-count">
+                            {totalFailedCount}
+                          </Typography>
+                        </Box>
+                      </LightTooltip>
+                    </Grid>
+
+                    {/* 4. Display-Only Skipped Card */}
+                    <Grid item xs={6} sm={3}>
+                      <LightTooltip
+                        title={
+                          skippedCount > 0 || executionFailed
+                            ? "Some entities were skipped during purge. Please check purgefailure.log for details."
+                            : "No skipped entities during this purge operation."
+                        }
+                        arrow
+                        placement="top"
+                      >
+                        <Box
+                          className={`purge-card ${skippedCount > 0 ? "purge-card-skipped" : "purge-card-skipped-empty"}`}
+                        >
+                          <Typography variant="caption" color={skippedCount > 0 ? "warning.main" : "textSecondary"} display="block" className="card-title">
+                            Skipped
+                          </Typography>
+                          <Typography variant="h5" color={skippedCount > 0 ? "warning.main" : "textPrimary"} className="card-count">
+                            {skippedCount}
+                          </Typography>
+                        </Box>
+                      </LightTooltip>
+                    </Grid>
+                  </>
+                )}
+              </Grid>
+            </Box>
           )}
-        </Grid>
-      ) : (
-        operation != "PURGE" &&
-        operation != "AUTO_PURGE" &&
-        operation != "IMPORT" &&
-        operation != "EXPORT" && <Typography>No Results Found</Typography>
-      )}
-
-      {(operation == "PURGE" || operation == "AUTO_PURGE") && !isEmpty(resultObj) ? (
-        <>
-          <Typography>{`${category[operation]}`}</Typography>
-          <List className="audit-results-list">
-            {resultObj.map((obj: string) => {
+
+          {/* Right Side Drawer — client-side pagination for both Purged and Requested */}
+          <PurgeEntitiesDrawer
+            activePurgeView={activePurgeView}
+            setActivePurgeView={setActivePurgeView}
+            requestedEntitiesList={requestedEntitiesList}
+            purgedApiGuids={purgedApiGuids}
+            drawerSearchText={drawerSearchText}
+            setDrawerSearchText={setDrawerSearchText}
+            drawerPage={drawerPage}
+            setDrawerPage={setDrawerPage}
+            drawerPageSize={drawerPageSize}
+            setDrawerPageSize={setDrawerPageSize}
+            scrollTop={scrollTop}
+            setScrollTop={setScrollTop}
+            runId={runId}
+            handleCopyRunId={handleCopyRunId}
+            setOpenPurgeModal={setOpenPurgeModal}
+            setCurrentPurgeResultObj={setCurrentPurgeResultObj}
+            drawerPageSizeInput={drawerPageSizeInput}
+            setDrawerPageSizeInput={setDrawerPageSizeInput}
+          />
+        </Box>
+      ) : null}
+    </>
+  );
+};
+
+
+interface PurgeEntitiesDrawerProps {
+  activePurgeView: PurgeActiveView;
+  setActivePurgeView: (view: PurgeActiveView) => void;
+  requestedEntitiesList: string[];
+  purgedApiGuids: string[];
+  drawerSearchText: string;
+  setDrawerSearchText: (text: string) => void;
+  drawerPage: number;
+  setDrawerPage: React.Dispatch<React.SetStateAction<number>>;
+  drawerPageSize: number;
+  setDrawerPageSize: React.Dispatch<React.SetStateAction<number>>;
+  scrollTop: number;
+  setScrollTop: React.Dispatch<React.SetStateAction<number>>;
+  runId: string;
+  handleCopyRunId: (id: string, setCopied: (val: boolean) => void) => void;
+  setOpenPurgeModal: (open: boolean) => void;
+  setCurrentPurgeResultObj: (guid: string) => void;
+  drawerPageSizeInput: string;
+  setDrawerPageSizeInput: (input: string) => void;
+}
+
+const PurgeEntitiesDrawer: React.FC<PurgeEntitiesDrawerProps> = ({
+  activePurgeView,
+  setActivePurgeView,
+  requestedEntitiesList,
+  purgedApiGuids,
+  drawerSearchText,
+  setDrawerSearchText,
+  drawerPage,
+  setDrawerPage,
+  drawerPageSize,
+  setDrawerPageSize,
+  scrollTop,
+  setScrollTop,
+  runId,
+  handleCopyRunId,
+  setOpenPurgeModal,
+  setCurrentPurgeResultObj,
+  drawerPageSizeInput,
+  setDrawerPageSizeInput,
+}) => {
+  const [drawerCopiedRunId, setDrawerCopiedRunId] = useState<boolean>(false);
+  const listRef = useRef<HTMLUListElement | null>(null);
+
+  useEffect(() => {
+    if (listRef.current) {
+      listRef.current.scrollTop = 0;
+    }
+  }, [drawerPage, drawerSearchText, activePurgeView]);
+
+  const rawListForView: (string | { guid: string; attributes?: { name?: string } })[] =
+    activePurgeView === PurgeActiveView.REQUESTED ? requestedEntitiesList : purgedApiGuids;
+
+  const filteredList = rawListForView.filter((item: string | { guid: string; attributes?: { name?: string } }) => {
+    if (!drawerSearchText) return true;
+    const guidStr = typeof item === 'object' && item !== null ? item.guid : item;
+    const nameStr = typeof item === 'object' && item !== null ? item.attributes?.name : '';
+    const searchLower = drawerSearchText.trim().toLowerCase();
+    return (guidStr && guidStr.toLowerCase().includes(searchLower)) ||
+      (nameStr && nameStr.toLowerCase().includes(searchLower));
+  });
+
+  const displayItems = filteredList.slice((drawerPage - 1) * drawerPageSize, drawerPage * drawerPageSize);
+
+  const { visibleItems, paddingTop, paddingBottom, startIndex } = useVirtualization({
+    items: displayItems,
+    scrollTop,
+    itemHeight: 24
+  });
+
+  const displayTotal = filteredList.length;
+
+  const handleDrawerScroll = (e: React.UIEvent<HTMLUListElement>) => {
+    setScrollTop(e.currentTarget.scrollTop);
+  };
+
+  return (
+    <Drawer
+      anchor="right"
+      open={activePurgeView !== PurgeActiveView.NONE}
+      onClose={() => {
+        setActivePurgeView(PurgeActiveView.NONE);
+        setDrawerSearchText('');
+        setDrawerPage(1);
+        setScrollTop(0);
+      }}
+      PaperProps={{ className: "drawer-paper" }}
+    >
+      <Box className="drawer-content-wrapper">
+        <Stack direction="row" justifyContent="space-between" alignItems="center" className="drawer-search-container">
+          <Typography className="drawer-title">
+            {activePurgeView === PurgeActiveView.REQUESTED ? 'Requested Entities' : 'Purged Entities'}
+          </Typography>
+          <IconButton
+            aria-label="Close drawer"
+            onClick={() => {
+              setActivePurgeView(PurgeActiveView.NONE);
+              setDrawerSearchText('');
+              setDrawerPage(1);
+              setScrollTop(0);
+            }}
+            size="small"
+          >
+            ✕
+          </IconButton>
+        </Stack>
+
+        {runId !== 'N/A' && (
+          <Box className="drawer-runid-container">
+            <Typography variant="caption" color="textSecondary" className="drawer-runid-text">
+              <strong>Run Id:</strong> {runId}
+            </Typography>
+            <Tooltip title={drawerCopiedRunId ? "Copied!" : "Copy Run Id"}>
+              <IconButton
+                size="small"
+                onClick={() => handleCopyRunId(runId, setDrawerCopiedRunId)}
+                className="drawer-runid-copy"
+              >
+                <ContentCopyIcon className={`copy-icon ${drawerCopiedRunId ? "copied" : ""}`} />
+              </IconButton>
+            </Tooltip>
+          </Box>
+        )}
+
+        {(activePurgeView === PurgeActiveView.REQUESTED ? requestedEntitiesList.length > 0 : purgedApiGuids.length > 0) && (
+          <Box className="drawer-search-container">
+            <TextField
+              fullWidth
+              size="small"
+              variant="standard"
+              placeholder="Search GUIDs..."
+              value={drawerSearchText}
+              onChange={(e) => {
+                setDrawerSearchText(e.target.value);
+                setDrawerPage(1);
+                setScrollTop(0);
+              }}
+              InputProps={{
+                disableUnderline: true,
+                startAdornment: (
+                  <InputAdornment position="start">
+                    <SearchIcon className="drawer-search-icon" />
+                  </InputAdornment>
+                ),
+                endAdornment: drawerSearchText ? (
+                  <InputAdornment position="end">
+                    <IconButton size="small" onClick={() => {
+                      setDrawerSearchText('');
+                      setDrawerPage(1);
+                      setScrollTop(0);
+                    }}>
+                      ✕
+                    </IconButton>
+                  </InputAdornment>
+                ) : null
+              }}
+              className="drawer-search-input"
+            />
+          </Box>
+        )}
+
+
+
+        <List dense className="drawer-list-container" onScroll={handleDrawerScroll} onWheel={handleDrawerScroll} ref={listRef}>
+          {(() => {
+            if (displayItems.length === 0) {
               return (
-                <ListItem className="audit-results-list-item">
-                  <ListItemText
-                    primary={
+                <Typography variant="body2" color="textSecondary" className="drawer-list-empty">
+                  {"No matching GUIDs found"}
+                </Typography>
+              );
+            }
+
+            return (
+              <>
+                {paddingTop > 0 && <div style={{ height: paddingTop }} />}
+                {visibleItems.map((item: string | { guid: string; attributes?: { name?: string } }, localIndex: number) => {
+                  const index = startIndex + localIndex;
+                  const globalIndex = (drawerPage - 1) * drawerPageSize + index + 1;
+                  const isObj = typeof item === 'object' && item !== null;
+                  const guidStr = isObj ? item.guid : item;
+                  return (
+                    <ListItem key={guidStr + index} className="drawer-list-item">
+                      <Typography variant="body2" className="drawer-list-index">{globalIndex}.</Typography>
                       <Link
-                        className="audit-results-entityid"
                         component="button"
                         variant="body2"
+                        underline="hover"
                         onClick={() => {
                           setOpenPurgeModal(true);
-                          setCurrentPurgeResultObj(obj);
-                        }}
-                        title={obj}
-                        sx={{
-                          display: "inline-block",
-                          maxWidth: "100%",
-                          textOverflow: "ellipsis",
-                          overflow: "hidden",
-                          whiteSpace: "nowrap",
-                          textAlign: "left",
-                          verticalAlign: "bottom"
+                          setCurrentPurgeResultObj(guidStr);
                         }}
+                        title={guidStr}
+                        className="drawer-list-link"
                       >
-                        {obj}
+                        {guidStr}
                       </Link>
-                    }
-                  />
-                </ListItem>
-              );
-            })}
-          </List>
-        </>
-      ) : (
-        (operation == "PURGE" || operation == "AUTO_PURGE") && <Typography>No Results Found</Typography>
-      )}
+                    </ListItem>
+                  );
+                })}
+                {paddingBottom > 0 && <div style={{ height: paddingBottom }} />}
+              </>
+            );
+          })()}
 
-      {(operation == "IMPORT" || operation == "EXPORT") && (
-        <ImportExportAudits auditObj={auditObj} />
-      )}
 
-      <TypeDefAuditDetailModal
-        open={openModal}
-        onClose={handleCloseModal}
-        detailObject={currentResultObj}
-        maxWidth="sm"
-      />
+        </List>
 
-      {(operation == "PURGE" || operation == "AUTO_PURGE") && (
-        <CustomModal
-          open={openPurgeModal}
-          onClose={handleClosePurgeModal}
-          title={operation == "PURGE" ? `Purged Entity Details: ${currentPurgeResultObj}` : `Auto Purged Entity Details: ${currentPurgeResultObj}`}
-          footer={false}
-          button1Handler={undefined}
-          button2Handler={undefined}
-          maxWidth="lg"
-        >
-          <AuditsTab auditResultGuid={currentPurgeResultObj} loading={false} />
-        </CustomModal>
-      )}
-    </>
+        {displayTotal > 0 && (
+          <Box className="drawer-footer">
+            <Typography variant="caption" color="textSecondary" className="drawer-footer-count">
+              {Math.min((drawerPage - 1) * drawerPageSize + 1, displayTotal)}-{Math.min(drawerPage * drawerPageSize, displayTotal)} of {displayTotal}
+            </Typography>
+
+            <Box className="drawer-pagination-container">
+              <Pagination
+                count={Math.ceil(displayTotal / drawerPageSize) || 1}
+                page={drawerPage}
+                onChange={(_e, val) => {
+                  setDrawerPage(val);
+                  setScrollTop(0);
+                }}
+                size="small"
+                color="primary"
+                siblingCount={0}
+                boundaryCount={0}
+                showFirstButton
+                showLastButton
+                className="purge-pagination"
+                renderItem={(item) => (
+                  <PaginationItem
+                    slots={{ first: KeyboardDoubleArrowLeftIcon, last: KeyboardDoubleArrowRightIcon }}
+                    {...item}
+                  />
+                )}
+              />
+
+              <Box className="drawer-limit-container">
+                <Typography variant="caption" color="textSecondary" className="drawer-limit-label">Limit</Typography>
+                <Box
+                  component="input"
+                  type="number"
+                  min={1}
+                  value={drawerPageSizeInput}
+                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                    setDrawerPageSizeInput(e.target.value);
+                  }}
+                  onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
+                    if (e.key === 'Enter') {
+                      const parsed = parseInt((e.target as HTMLInputElement).value, 10);
+                      if (Number.isFinite(parsed) && parsed > 0) {
+                        const clamped = Math.min(parsed, displayTotal);
+                        setDrawerPageSize(clamped);
+                        setDrawerPageSizeInput(String(clamped));
+                        setDrawerPage(1);
+                        setScrollTop(0);
+                      }
+                    }
+                  }}
+                  className="drawer-limit-input"
+                />
+              </Box>
+            </Box>
+          </Box>
+        )}
+      </Box>
+    </Drawer>
   );
 };
+
 export default AuditResults;
diff --git a/dashboard/src/views/Administrator/Audits/AuditsFilter/AuditFiltersFields.tsx b/dashboard/src/views/Administrator/Audits/AuditsFilter/AuditFiltersFields.tsx
index b479a4be39..08a5976692 100644
--- a/dashboard/src/views/Administrator/Audits/AuditsFilter/AuditFiltersFields.tsx
+++ b/dashboard/src/views/Administrator/Audits/AuditsFilter/AuditFiltersFields.tsx
@@ -308,6 +308,10 @@ export const fields = (allDataObj) => {
   }
   if (!isEmpty(auditEntryAttributeDefs)) {
     for (const attributes in auditEntryAttributeDefs) {
+      if (auditEntryAttributeDefs[attributes]?.name === "auditRowKind") {
+        continue; // Backend-only field, do not display in UI filter
+      }
+
       let returnObj: any = getObjDef(
         allDataObj,
         auditEntryAttributeDefs[attributes],
diff --git a/dashboard/src/views/Administrator/Audits/__tests__/AdminAuditTable.test.tsx b/dashboard/src/views/Administrator/Audits/__tests__/AdminAuditTable.test.tsx
index dbd844cd28..9756bdd73c 100644
--- a/dashboard/src/views/Administrator/Audits/__tests__/AdminAuditTable.test.tsx
+++ b/dashboard/src/views/Administrator/Audits/__tests__/AdminAuditTable.test.tsx
@@ -97,7 +97,18 @@ jest.mock('../AuditsFilter/AuditFilters', () => ({
       </button>
       <button
         onClick={() => {
-          setQueryApiObj({ userName: 'testUser' });
+          // If we add a window.mockRunId it will set runId
+          if ((window as any).mockRunId) {
+             setQueryApiObj({ 
+                condition: "AND",
+                criterion: [
+                  { attributeName: "runId", operator: "eq", attributeValue: "test-run-id" },
+                  { attributeName: "auditRowKind", operator: "eq", attributeValue: "DETAIL" }
+                ] 
+             });
+          } else {
+             setQueryApiObj({ userName: 'testUser' });
+          }
           setupdateTable(moment.now());
         }}
         data-testid="apply-filters"
@@ -905,4 +916,113 @@ describe('AdminAuditTable Component', () => {
       }, { timeout: 5000 });
     });
   });
+
+  describe('RunId Filtering Combinations', () => {
+    afterEach(() => {
+      delete (window as any).mockRunId;
+    });
+
+    it('should inject auditRowKind=SUMMARY when runId filter is present ', async () => {
+      mockGetAuditData.mockResolvedValue({ data: [] });
+      mockIsEmpty.mockImplementation((val: any) => {
+        if (Array.isArray(val)) return val.length === 0;
+        if (typeof val === 'object' && val !== null) return false;
+        return true;
+      });
+
+      render(<AdminAuditTable />);
+      
+      await waitFor(() => {
+        expect(capturedFetchData).toBeDefined();
+      });
+
+      // Enable the mock runId logic
+      (window as any).mockRunId = true;
+
+      fireEvent.click(screen.getByTestId('custom-button')); // open filters
+      
+      await waitFor(() => {
+        expect(screen.getByTestId('audit-filters')).toBeInTheDocument();
+      });
+
+      fireEvent.click(screen.getByTestId('apply-filters')); // apply filters with runId
+
+      // Component re-renders and should trigger fetchData
+      // capturedFetchData is called automatically when queryApiObj changes because of the mock TableLayout
+      // wait, our mock TableLayout only calls fetchData on mount! 
+      // But AdminAuditTable passes `tableFilters` as queryApiObj. 
+      // AdminAuditTable has a `fetchAuditResult` function that is passed to TableLayout as `fetchData`.
+      // We can directly invoke `capturedFetchData` again!
+      
+      await capturedFetchData({ pagination: { pageSize: 25, pageIndex: 0 } });
+
+      await waitFor(() => {
+        expect(mockGetAuditData).toHaveBeenCalled();
+        const callArgs = mockGetAuditData.mock.calls[mockGetAuditData.mock.calls.length - 1][0];
+        
+        // Assert that the filter contains the injected SUMMARY condition
+        const filtersStr = JSON.stringify(callArgs.auditFilters);
+        expect(filtersStr).toContain('"attributeValue":"SUMMARY"');
+        
+        // And assert it removed the old auditRowKind (DETAIL)
+        expect(filtersStr).not.toContain('"attributeValue":"DETAIL"');
+      });
+    });
+
+    it('should NOT inject auditRowKind=SUMMARY when runId filter is NOT present ', async () => {
+      mockGetAuditData.mockResolvedValue({ data: [] });
+      mockIsEmpty.mockImplementation((val: any) => {
+        if (Array.isArray(val)) return val.length === 0;
+        if (typeof val === 'object' && val !== null) return false;
+        return true;
+      });
+
+      render(<AdminAuditTable />);
+      
+      await waitFor(() => {
+        expect(capturedFetchData).toBeDefined();
+      });
+
+      // NO mockRunId here
+      
+      fireEvent.click(screen.getByTestId('custom-button'));
+      
+      await waitFor(() => {
+        expect(screen.getByTestId('audit-filters')).toBeInTheDocument();
+      });
+
+      fireEvent.click(screen.getByTestId('apply-filters')); // apply normal filter (userName)
+
+      await capturedFetchData({ pagination: { pageSize: 25, pageIndex: 0 } });
+
+      await waitFor(() => {
+        expect(mockGetAuditData).toHaveBeenCalled();
+        const callArgs = mockGetAuditData.mock.calls[mockGetAuditData.mock.calls.length - 1][0];
+        
+        // Assert that the filter does NOT contain the injected SUMMARY condition
+        const filtersStr = JSON.stringify(callArgs.auditFilters);
+        expect(filtersStr).not.toContain('"attributeValue":"SUMMARY"');
+        expect(filtersStr).toContain('userName');
+      });
+    });
+
+    it('should handle API failure gracefully', async () => {
+      mockGetAuditData.mockRejectedValue({ response: { data: { errorMessage: 'Internal Server Error' } } });
+      
+      render(<AdminAuditTable />);
+      
+      await waitFor(() => {
+        expect(capturedFetchData).toBeDefined();
+      });
+      
+      // Trigger fetch
+      await capturedFetchData({ pagination: { pageSize: 25, pageIndex: 0 } });
+      
+      // We are just making sure it doesn't crash and console.error is called
+      await waitFor(() => {
+        expect(mockGetAuditData).toHaveBeenCalled();
+      });
+    });
+  });
+
 });
diff --git a/dashboard/src/views/Administrator/Audits/__tests__/AuditResults.test.tsx b/dashboard/src/views/Administrator/Audits/__tests__/AuditResults.test.tsx
index 1896838f7d..ed4415bfe8 100644
--- a/dashboard/src/views/Administrator/Audits/__tests__/AuditResults.test.tsx
+++ b/dashboard/src/views/Administrator/Audits/__tests__/AuditResults.test.tsx
@@ -28,6 +28,8 @@
 import React from 'react';
 import { render, screen, fireEvent, waitFor } from '@testing-library/react';
 import AuditResults from '../AuditResults';
+import { toast } from 'react-toastify';
+jest.mock('react-toastify', () => ({ toast: { error: jest.fn() } }));
 
 // Mock dependencies
 const mockIsEmpty = jest.fn((val: any) => {
@@ -39,21 +41,33 @@ const mockIsEmpty = jest.fn((val: any) => {
 
 const mockIsArray = jest.fn((val: any) => Array.isArray(val));
 const mockJsonParse = jest.fn((val: any) => {
-  try {
-    return JSON.parse(val);
-  } catch {
-    return {};
-  }
+  if (!val) return [];
+  // Real jsonParse throws if the outer JSON is invalid
+  return JSON.parse(val, (_key, value) => {
+    try {
+      return typeof value === 'string' ? JSON.parse(value) : value;
+    } catch {
+      return value;
+    }
+  });
 });
 
 jest.mock('@utils/Utils', () => ({
+  ...jest.requireActual('@utils/Utils'),
   isArray: (...args: any[]) => mockIsArray(...args),
   isEmpty: (...args: any[]) => mockIsEmpty(...args),
   jsonParse: (...args: any[]) => mockJsonParse(...args)
 }));
 
+// Mock fetchApi so tests that trigger API calls don't crash
+jest.mock('@api/apiMethods/fetchApi', () => ({
+  fetchApi: jest.fn(() => Promise.resolve({ data: [] }))
+}));
+import { fetchApi } from '@api/apiMethods/fetchApi';
+
 // Mock Enum
 jest.mock('@utils/Enum', () => ({
+  ...jest.requireActual('@utils/Enum'),
   auditAction: {
     CREATE: 'Created',
     UPDATE: 'Updated',
@@ -139,6 +153,7 @@ jest.mock('@mui/material', () => {
         {children}
       </button>
     ),
+    Drawer: ({ children, open, PaperProps, ...props }: any) => open ? <div data-testid="drawer" {...props}>{children}</div> : null,
     List: ({ children, ...props }: any) => <ul data-testid="list" {...props}>{children}</ul>,
     ListItem: ({ children, ...props }: any) => <li data-testid="list-item" {...props}>{children}</li>,
     ListItemText: ({ primary, ...props }: any) => <div data-testid="list-item-text" {...props}>{primary}</div>,
@@ -146,11 +161,18 @@ jest.mock('@mui/material', () => {
   };
 });
 
+
+Object.assign(navigator, {
+  clipboard: {
+    writeText: jest.fn().mockImplementation(() => Promise.resolve()),
+  },
+});
+
 describe('AuditResults Component', () => {
   const mockAuditData = [
     {
       guid: 'audit-1',
-      operation: 'CREATE',
+      operation: 'TYPE_DEF_CREATE',
       params: 'entityDefs',
       result: JSON.stringify({
         entityDefs: [
@@ -161,7 +183,7 @@ describe('AuditResults Component', () => {
     },
     {
       guid: 'audit-2',
-      operation: 'UPDATE',
+      operation: 'TYPE_DEF_UPDATE',
       params: 'classificationDefs,enumDefs',
       result: JSON.stringify({
         classificationDefs: [{ name: 'Classification1', category: 'classificationDefs' }],
@@ -202,6 +224,10 @@ describe('AuditResults Component', () => {
 
   beforeEach(() => {
     jest.clearAllMocks();
+    // Reset fetchApi mock before each test
+    (fetchApi as jest.Mock).mockImplementation(() =>
+      Promise.resolve({ data: [] })
+    );
     mockIsEmpty.mockImplementation((val: any) => {
       if (val === null || val === undefined || val === '') return true;
       if (Array.isArray(val) && val.length === 0) return true;
@@ -210,11 +236,14 @@ describe('AuditResults Component', () => {
     });
     mockIsArray.mockImplementation((val: any) => Array.isArray(val));
     mockJsonParse.mockImplementation((val: any) => {
-      try {
-        return JSON.parse(val);
-      } catch {
-        return {};
-      }
+      if (!val) return [];
+      return JSON.parse(val, (_key, value) => {
+        try {
+          return typeof value === 'string' ? JSON.parse(value) : value;
+        } catch {
+          return value;
+        }
+      });
     });
   });
 
@@ -223,8 +252,8 @@ describe('AuditResults Component', () => {
       const componentProps = { auditData: mockAuditData };
       render(<AuditResults componentProps={componentProps} row={mockRow} />);
 
-      const grids = screen.getAllByTestId('grid');
-      expect(grids.length).toBeGreaterThan(0);
+      const lists = screen.getAllByTestId('list');
+      expect(lists.length).toBeGreaterThan(0);
     });
 
     it('should find audit object by guid', () => {
@@ -232,7 +261,7 @@ describe('AuditResults Component', () => {
       render(<AuditResults componentProps={componentProps} row={mockRow} />);
 
       // Should render results for audit-1
-      expect(screen.getAllByTestId('item').length).toBeGreaterThan(0);
+      expect(screen.getAllByTestId('list-item').length).toBeGreaterThan(0);
     });
 
     it('should handle empty auditData', () => {
@@ -247,7 +276,7 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={mockRow} />);
 
-      // When auditData is empty, auditObj is {}, and the component shows "No Results Found"
+      // When auditData is empty, auditObj is {}, and the component shows "No matching GUIDs found"
       const typographies = screen.getAllByTestId('typography');
       expect(typographies.length).toBeGreaterThan(0);
     });
@@ -264,14 +293,14 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={mockRow} />);
 
-      // When auditData is undefined, auditObj is {}, and the component shows "No Results Found"
+      // When auditData is undefined, auditObj is {}, and the component shows "No matching GUIDs found"
       const typographies = screen.getAllByTestId('typography');
       expect(typographies.length).toBeGreaterThan(0);
     });
   });
 
-  describe('CREATE/UPDATE/DELETE Operations', () => {
-    it('should render results for CREATE operation with single param', () => {
+  describe('TYPE_DEF_CREATE/UPDATE/DELETE Operations', () => {
+    it('should render results for TYPE_DEF_CREATE operation with single param', () => {
       const componentProps = { auditData: mockAuditData };
       const row = { original: { guid: 'audit-1' } };
 
@@ -283,7 +312,7 @@ describe('AuditResults Component', () => {
       expect(screen.getByText('Entity2')).toBeInTheDocument();
     });
 
-    it('should render results for UPDATE operation with multiple params', () => {
+    it('should render results for TYPE_DEF_UPDATE operation with multiple params', () => {
       const componentProps = { auditData: mockAuditData };
       const row = { original: { guid: 'audit-2' } };
 
@@ -363,24 +392,15 @@ describe('AuditResults Component', () => {
       });
     });
 
-    it('should show "No Record Found" when current object is empty', async () => {
-      const componentProps = {
-        auditData: [
-          {
-            guid: 'audit-empty',
-            operation: 'CREATE',
-            params: 'entityDefs',
-            result: JSON.stringify({ entityDefs: [{}] })
-          }
-        ]
-      };
-      const row = { original: { guid: 'audit-empty' } };
+    it('should show "No Record Found" when current object is empty', () => {
+      mockIsEmpty.mockReturnValue(true); // Force isEmpty to return true
+      const componentProps = { auditData: mockAuditData };
+      const row = { original: { guid: 'audit-1' } };
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
       // The component should still render but with empty object
-      const grids = screen.getAllByTestId('grid');
-      expect(grids.length).toBeGreaterThan(0);
+      expect(screen.getByText('No Results Found')).toBeInTheDocument();
     });
   });
 
@@ -391,6 +411,8 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
       const typographies = screen.getAllByTestId('typography');
       expect(typographies.length).toBeGreaterThan(0);
       expect(screen.getByText('guid-1')).toBeInTheDocument();
@@ -404,6 +426,8 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
       const typographies = screen.getAllByTestId('typography');
       expect(typographies.length).toBeGreaterThan(0);
       expect(screen.getByText('guid-4')).toBeInTheDocument();
@@ -416,6 +440,8 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
       const purgeLink = screen.getByText('guid-1');
       fireEvent.click(purgeLink);
 
@@ -425,7 +451,6 @@ describe('AuditResults Component', () => {
 
       expect(screen.getByTestId('modal-title')).toHaveTextContent('Purged Entity Details: guid-1');
       expect(screen.getByTestId('audits-tab')).toBeInTheDocument();
-      expect(screen.getByTestId('audits-tab')).toHaveAttribute('data-loading', 'false');
     });
 
     it('should open auto purge modal with correct title', async () => {
@@ -434,6 +459,9 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
+      // Click to open drawer first
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
       const purgeLink = screen.getByText('guid-4');
       fireEvent.click(purgeLink);
 
@@ -441,7 +469,7 @@ describe('AuditResults Component', () => {
         expect(screen.getByTestId('custom-modal')).toBeInTheDocument();
       });
 
-      expect(screen.getByTestId('modal-title')).toHaveTextContent('Auto Purged Entity Details: guid-4');
+      expect(screen.getByTestId('modal-title')).toHaveTextContent('Auto Purge Entity Details: guid-4');
     });
 
     it('should close purge modal when close button is clicked', async () => {
@@ -450,6 +478,9 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
+      // Click to open drawer first
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
       // Open modal
       const purgeLink = screen.getByText('guid-1');
       fireEvent.click(purgeLink);
@@ -466,7 +497,7 @@ describe('AuditResults Component', () => {
       });
     });
 
-    it('should show "No Results Found" for empty PURGE result', () => {
+    it('should show "No matching GUIDs found" for empty PURGE result', () => {
       const componentProps = {
         auditData: [
           {
@@ -481,13 +512,11 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
-      // After removing brackets and splitting, '[]' becomes ['']
-      // The component will render this as a single empty item, not "No Results Found"
-      const typographies = screen.getAllByTestId('typography');
-      expect(typographies.length).toBeGreaterThan(0);
+      const countElement = screen.getByText('0', { selector: '.card-count' });
+      expect(countElement).toBeInTheDocument();
     });
 
-    it('should show "No Results Found" for empty AUTO_PURGE result', () => {
+    it('should show "No matching GUIDs found" for empty AUTO_PURGE result', () => {
       const componentProps = {
         auditData: [
           {
@@ -502,10 +531,8 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
-      // After removing brackets and splitting, '[]' becomes ['']
-      // The component will render this as a single empty item, not "No Results Found"
-      const typographies = screen.getAllByTestId('typography');
-      expect(typographies.length).toBeGreaterThan(0);
+      const countElement = screen.getByText('0', { selector: '.card-count' });
+      expect(countElement).toBeInTheDocument();
     });
   });
 
@@ -533,19 +560,11 @@ describe('AuditResults Component', () => {
 
   describe('Edge Cases', () => {
     it('should handle empty result object for non-PURGE operations', () => {
-      mockJsonParse.mockReturnValue({});
-      mockIsEmpty.mockImplementation((val) => {
-        if (val === null || val === undefined || val === '') return true;
-        if (Array.isArray(val) && val.length === 0) return true;
-        if (typeof val === 'object' && Object.keys(val).length === 0) return true;
-        return false;
-      });
-
       const componentProps = {
         auditData: [
           {
             guid: 'audit-empty-result',
-            operation: 'CREATE',
+            operation: 'TYPE_DEF_CREATE',
             params: 'entityDefs',
             result: '{}'
           }
@@ -555,24 +574,18 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
-      const typographies = screen.getAllByTestId('typography');
-      expect(typographies.length).toBeGreaterThan(0);
+      const list = screen.getByTestId('list');
+      expect(list).toBeInTheDocument();
+      const listItems = screen.queryAllByTestId('list-item');
+      expect(listItems.length).toBe(0);
     });
 
     it('should handle malformed JSON in result', () => {
-      mockJsonParse.mockReturnValue({});
-      mockIsEmpty.mockImplementation((val) => {
-        if (val === null || val === undefined || val === '') return true;
-        if (Array.isArray(val) && val.length === 0) return true;
-        if (typeof val === 'object' && Object.keys(val).length === 0) return true;
-        return false;
-      });
-
       const componentProps = {
         auditData: [
           {
             guid: 'audit-malformed',
-            operation: 'CREATE',
+            operation: 'TYPE_DEF_CREATE',
             params: 'entityDefs',
             result: 'malformed json'
           }
@@ -582,31 +595,35 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
-      const typographies = screen.getAllByTestId('typography');
-      expect(typographies.length).toBeGreaterThan(0);
+      const list = screen.getByTestId('list');
+      expect(list).toBeInTheDocument();
+      const listItems = screen.queryAllByTestId('list-item');
+      expect(listItems.length).toBe(0);
     });
 
     it('should handle audit object not found', () => {
       const componentProps = { auditData: mockAuditData };
       const row = { original: { guid: 'non-existent-guid' } };
 
-      const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
-      
-      // This will cause an error because auditObj will be undefined and result will be undefined
-      expect(() => render(<AuditResults componentProps={componentProps} row={row} />)).toThrow();
-      
-      consoleSpy.mockRestore();
+      // With proper TypeScript types, auditObj is undefined when guid is not found.
+      // The component now handles this gracefully by rendering "No matching GUIDs found"
+      // instead of crashing (improved behavior from the TS fix).
+      render(<AuditResults componentProps={componentProps} row={row} />);
+
+      // Component renders without throwing and shows a default "No matching GUIDs found" state
+      const typographies = screen.getAllByTestId('typography');
+      expect(typographies.length).toBeGreaterThan(0);
     });
 
     it('should handle params with comma-separated values', () => {
       const componentProps = { auditData: mockAuditData };
-      const row = { original: { guid: 'audit-2' } };
+      const row = { original: { guid: 'audit-2' } }; // This has params 'classificationDefs,enumDefs'
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
-      // Should render multiple grids for each param
-      const grids = screen.getAllByTestId('grid');
-      expect(grids.length).toBeGreaterThan(1);
+      // Should render multiple list items for each param
+      const listItems = screen.getAllByTestId('list-item');
+      expect(listItems.length).toBeGreaterThan(1);
     });
 
     it('should handle single param without comma', () => {
@@ -615,9 +632,9 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
-      // Should render grids
-      const grids = screen.getAllByTestId('grid');
-      expect(grids.length).toBeGreaterThan(0);
+      // Should render list items
+      const listItems = screen.getAllByTestId('list-item');
+      expect(listItems.length).toBeGreaterThan(0);
     });
 
     it('should display array length in modal when value is array', async () => {
@@ -625,7 +642,7 @@ describe('AuditResults Component', () => {
         auditData: [
           {
             guid: 'audit-array',
-            operation: 'CREATE',
+            operation: 'TYPE_DEF_CREATE',
             params: 'entityDefs',
             result: JSON.stringify({
               entityDefs: [
@@ -677,6 +694,9 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
+      // Click the "Purged Entities" summary card to open the drawer
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
       // Should split "[guid-1,guid-2,guid-3]" into array
       expect(screen.getByText('guid-1')).toBeInTheDocument();
       expect(screen.getByText('guid-2')).toBeInTheDocument();
@@ -689,6 +709,8 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
       // Should split "[guid-4,guid-5]" into array
       expect(screen.getByText('guid-4')).toBeInTheDocument();
       expect(screen.getByText('guid-5')).toBeInTheDocument();
@@ -739,7 +761,7 @@ describe('AuditResults Component', () => {
   });
 
   describe('PURGE Operations - Empty Results Branches', () => {
-    it('should show "No Results Found" for PURGE with truly empty result', () => {
+    it('should show "No matching GUIDs found" for PURGE with truly empty result', () => {
       mockIsEmpty.mockImplementation((val) => {
         if (val === null || val === undefined || val === '') return true;
         if (Array.isArray(val) && val.length === 0) return true;
@@ -762,12 +784,11 @@ describe('AuditResults Component', () => {
       const row = { original: { guid: 'audit-purge-truly-empty' } };
 
       render(<AuditResults componentProps={componentProps} row={row} />);
-
-      const typographies = screen.getAllByTestId('typography');
-      expect(typographies.length).toBeGreaterThan(0);
+      const countElement = screen.getByText('0', { selector: '.card-count' });
+      expect(countElement).toBeInTheDocument();
     });
 
-    it('should show "No Results Found" for AUTO_PURGE with truly empty result', () => {
+    it('should show "No matching GUIDs found" for AUTO_PURGE with truly empty result', () => {
       mockIsEmpty.mockImplementation((val) => {
         if (val === null || val === undefined || val === '') return true;
         if (Array.isArray(val) && val.length === 0) return true;
@@ -791,8 +812,784 @@ describe('AuditResults Component', () => {
 
       render(<AuditResults componentProps={componentProps} row={row} />);
 
-      const typographies = screen.getAllByTestId('typography');
-      expect(typographies.length).toBeGreaterThan(0);
+      const countElement = screen.getByText('0', { selector: '.card-count' });
+      expect(countElement).toBeInTheDocument();
+    });
+  });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // TYPE_DEF_DELETE Operation
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('TYPE_DEF_DELETE Operation', () => {
+    it('should render results for TYPE_DEF_DELETE operation', () => {
+      const auditData = [
+        {
+          guid: 'audit-del',
+          operation: 'TYPE_DEF_DELETE',
+          params: 'entityDefs',
+          result: JSON.stringify({
+            entityDefs: [{ name: 'DeletedEntity', category: 'entityDefs' }]
+          })
+        }
+      ];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'audit-del' } }} />);
+
+      expect(screen.getByText('DeletedEntity')).toBeInTheDocument();
+      expect(screen.getByText(/TYPE_DEF_DELETE/)).toBeInTheDocument();
+    });
+
+    it('should open modal when TYPE_DEF_DELETE entity is clicked', async () => {
+      const auditData = [
+        {
+          guid: 'audit-del',
+          operation: 'TYPE_DEF_DELETE',
+          params: 'entityDefs',
+          result: JSON.stringify({
+            entityDefs: [{ name: 'DeletedEntity', category: 'entityDefs' }]
+          })
+        }
+      ];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'audit-del' } }} />);
+
+      fireEvent.click(screen.getByText('DeletedEntity'));
+      await waitFor(() => {
+        expect(screen.getByTestId('custom-modal')).toBeInTheDocument();
+      });
     });
   });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // PURGE — JSON Summary Format (new structured format)
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('PURGE Operations — JSON Summary format', () => {
+    const summaryResult = JSON.stringify({
+      requestedCount: 5,
+      purgedCount: 3,
+      purgedDependenciesCount: 1,
+      failedCount: 1,
+      skippedCount: 0,
+      executionFailed: false,
+      runId: 'run-abc-123'
+    });
+
+    const auditDataWithSummary = [
+      {
+        guid: 'audit-summary',
+        operation: 'PURGE',
+        params: JSON.stringify(['g1', 'g2', 'g3', 'g4', 'g5']),
+        result: summaryResult
+      }
+    ];
+
+    it('should display purgedCount from JSON summary', () => {
+      render(<AuditResults componentProps={{ auditData: auditDataWithSummary }} row={{ original: { guid: 'audit-summary' } }} />);
+      // Total Purged = purgedCount(3) + purgedDependenciesCount(1) = 4
+      expect(screen.getByText('4')).toBeInTheDocument();
+    });
+
+    it('should display failedCount from JSON summary', () => {
+      render(<AuditResults componentProps={{ auditData: auditDataWithSummary }} row={{ original: { guid: 'audit-summary' } }} />);
+      expect(screen.getByText('Failed')).toBeInTheDocument();
+      expect(screen.getByText('1')).toBeInTheDocument();
+    });
+
+    it('should display skippedCount from JSON summary', () => {
+      render(<AuditResults componentProps={{ auditData: auditDataWithSummary }} row={{ original: { guid: 'audit-summary' } }} />);
+      expect(screen.getByText('Skipped')).toBeInTheDocument();
+      expect(screen.getByText('0')).toBeInTheDocument();
+    });
+
+    it('should display Requested count from JSON summary', () => {
+      render(<AuditResults componentProps={{ auditData: auditDataWithSummary }} row={{ original: { guid: 'audit-summary' } }} />);
+      expect(screen.getByText('Requested')).toBeInTheDocument();
+      expect(screen.getByText('5')).toBeInTheDocument();
+    });
+
+    describe('Purge Failure States', () => {
+      it('should apply failure styling when failedCount > 0 or executionFailed is true', () => {
+        const failedResult = JSON.stringify({
+          requestedCount: 3, purgedCount: 1, purgedDependenciesCount: 0,
+          failedCount: 2, skippedCount: 0, executionFailed: true, runId: 'test'
+        });
+        const auditData = [{ guid: 'a-fail', operation: 'PURGE', params: '', result: failedResult }];
+        const { container } = render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'a-fail' } }} />);
+
+        expect(container.querySelector('.purge-card-failed')).toBeInTheDocument();
+      });
+
+      it('should NOT apply failure styling when failedCount is 0 and executionFailed is false', () => {
+        const okResult = JSON.stringify({
+          requestedCount: 3, purgedCount: 3, purgedDependenciesCount: 0,
+          failedCount: 0, skippedCount: 0, executionFailed: false, runId: 'test'
+        });
+        const auditData = [{ guid: 'a-ok', operation: 'PURGE', params: '', result: okResult }];
+        const { container } = render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'a-ok' } }} />);
+
+        expect(container.querySelector('.purge-card-failed')).not.toBeInTheDocument();
+        expect(container.querySelector('.purge-card-failed-empty')).toBeInTheDocument();
+      });
+    });
+
+    it('should show PURGE with JSON array result (not object)', () => {
+      const arrayResult = JSON.stringify(['arr-guid-1', 'arr-guid-2']);
+      const auditData = [{ guid: 'a-arr', operation: 'PURGE', params: '', result: arrayResult }];
+
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'a-arr' } }} />);
+
+      // Open drawer
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      expect(screen.getByText('arr-guid-1')).toBeInTheDocument();
+      expect(screen.getByText('arr-guid-2')).toBeInTheDocument();
+    });
+
+    it('should handle PURGE with JSON params array', () => {
+      const auditData = [
+        {
+          guid: 'a-params-arr',
+          operation: 'PURGE',
+          params: JSON.stringify(['req-guid-1', 'req-guid-2']),
+          result: '[purged-guid-1]'
+        }
+      ];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'a-params-arr' } }} />);
+
+      // The component should render the purge UI — the card label
+      const allPurgedEntities = screen.getAllByText('PURGED');
+      expect(allPurgedEntities.length).toBeGreaterThan(0);
+    });
+  });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // Run ID Display & Copy
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('Run ID display', () => {
+    // runId is sourced from row.original.runId first, then summary.runId, then auditObj.runId
+    it('should display Run ID when present on row.original', () => {
+      const auditData = [{ guid: 'a-rid', operation: 'PURGE', params: '', result: '[guid-1]' }];
+
+      render(
+        <AuditResults
+          componentProps={{ auditData }}
+          row={{ original: { guid: 'a-rid', runId: 'run-row-999' } }}
+        />
+      );
+
+      // Run Id appears in the main card header (may also appear in drawer header)
+      const runIdElements = screen.getAllByText(/run-row-999/);
+      expect(runIdElements.length).toBeGreaterThan(0);
+    });
+
+    it('should display Run ID when present in JSON summary result', () => {
+      const resultWithRunId = JSON.stringify({
+        requestedCount: 2, purgedCount: 2, purgedDependenciesCount: 0,
+        failedCount: 0, skippedCount: 0, executionFailed: false, runId: 'run-summary-888'
+      });
+      const auditData = [{ guid: 'a-rid2', operation: 'PURGE', params: '', result: resultWithRunId }];
+
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'a-rid2', runId: 'run-summary-888' } }} />);
+
+      const runIdElements = screen.getAllByText(/run-summary-888/);
+      expect(runIdElements.length).toBeGreaterThan(0);
+    });
+
+    it('should NOT display Run ID section when runId is N/A', () => {
+      const auditData = [
+        { guid: 'a-no-rid', operation: 'PURGE', params: '', result: '[guid-x]' }
+      ];
+      // No runId on row.original, no runId in summary → defaults to 'N/A'
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'a-no-rid' } }} />);
+
+      expect(screen.queryByText(/Run Id:/)).not.toBeInTheDocument();
+    });
+
+    it('should show Run Id label and value when runId is present on row', () => {
+      const auditData = [{ guid: 'a-copy', operation: 'PURGE', params: '', result: '[guid-1]' }];
+
+      render(
+        <AuditResults
+          componentProps={{ auditData }}
+          row={{ original: { guid: 'a-copy', runId: 'copy-test-run' } }}
+        />
+      );
+
+      const runIdElements = screen.getAllByText(/copy-test-run/);
+      expect(runIdElements.length).toBeGreaterThan(0);
+    });
+  });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // Drawer — open, close, search, clear, "Showing X of Y" footer
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('Drawer interactions', () => {
+    it('should open drawer when Purged Entities card is clicked', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      expect(screen.getByTestId('drawer')).toBeInTheDocument();
+    });
+
+
+
+    it('should show "No matching GUIDs found" when search has no match', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      // Search for something that doesn't match
+      const searchInput = screen.getByPlaceholderText('Search GUIDs...');
+      fireEvent.change(searchInput, { target: { value: 'no-such-guid' } });
+
+      expect(screen.getByText('No matching GUIDs found')).toBeInTheDocument();
+    });
+
+    it('should filter GUIDs by search text', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      // All 3 GUIDs are initially shown
+      expect(screen.getByText('guid-1')).toBeInTheDocument();
+      expect(screen.getByText('guid-2')).toBeInTheDocument();
+      expect(screen.getByText('guid-3')).toBeInTheDocument();
+
+      // Now search for "guid-1"
+      const searchInput = screen.getByPlaceholderText('Search GUIDs...');
+      fireEvent.change(searchInput, { target: { value: 'guid-1' } });
+
+      expect(screen.getByText('guid-1')).toBeInTheDocument();
+      expect(screen.queryByText('guid-2')).not.toBeInTheDocument();
+      expect(screen.queryByText('guid-3')).not.toBeInTheDocument();
+    });
+
+    it('should clear search when clear button is clicked', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      const searchInput = screen.getByPlaceholderText('Search GUIDs...');
+
+      // Type into the search to filter down to guid-1
+      fireEvent.change(searchInput, { target: { value: 'guid-1' } });
+      expect(screen.getByText('guid-1')).toBeInTheDocument();
+      expect(screen.queryByText('guid-2')).not.toBeInTheDocument();
+
+      // Clear the search by setting value back to empty (simulating the clear ✕ button)
+      fireEvent.change(searchInput, { target: { value: '' } });
+
+      // All GUIDs should be visible again
+      expect(screen.getByText('guid-1')).toBeInTheDocument();
+      expect(screen.getByText('guid-2')).toBeInTheDocument();
+      expect(screen.getByText('guid-3')).toBeInTheDocument();
+    });
+
+
+
+    it('should show "Limit" label in drawer footer', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      expect(screen.getByText('Limit')).toBeInTheDocument();
+    });
+
+
+
+    it('should display GUID index numbers in the drawer list', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      // Should show "1.", "2.", "3."
+      expect(screen.getByText('1.')).toBeInTheDocument();
+      expect(screen.getByText('2.')).toBeInTheDocument();
+      expect(screen.getByText('3.')).toBeInTheDocument();
+    });
+
+    it('should handle pagination correctly in the drawer', () => {
+      const largeList = Array.from({ length: 30 }, (_, i) => `guid-page-${i + 1}`);
+      const largeAuditData = [
+        { guid: 'audit-large', operation: 'PURGE', params: '', result: JSON.stringify(largeList) }
+      ];
+      render(<AuditResults componentProps={{ auditData: largeAuditData }} row={{ original: { guid: 'audit-large' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      expect(screen.getByText('guid-page-1')).toBeInTheDocument();
+      expect(screen.queryByText('guid-page-30')).not.toBeInTheDocument();
+
+      const nextPageBtn = screen.getByRole('button', { name: /Go to next page/i });
+      fireEvent.click(nextPageBtn);
+
+      expect(screen.queryByText('guid-page-1')).not.toBeInTheDocument();
+      expect(screen.getByText('guid-page-30')).toBeInTheDocument();
+    });
+  });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // Drawer — Purge modal on GUID click
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('Drawer — Purge entity detail modal', () => {
+    it('should show AuditsTab in modal when a GUID is clicked', async () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+      fireEvent.click(screen.getByText('guid-2'));
+
+      await waitFor(() => {
+        expect(screen.getByTestId('audits-tab')).toBeInTheDocument();
+        expect(screen.getByText('AuditsTab - guid-2')).toBeInTheDocument();
+      });
+    });
+
+    it('should update modal title when different GUID is clicked', async () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+      fireEvent.click(screen.getByText('guid-3'));
+
+      await waitFor(() => {
+        expect(screen.getByTestId('modal-title')).toHaveTextContent('Purged Entity Details: guid-3');
+      });
+    });
+  });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // PURGE — handleOpenPurgedDrawer guard: totalPurgedCount === 0
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('PURGE — empty result, no drawer opens', () => {
+    it('should NOT open drawer when totalPurgedCount is 0', () => {
+      const auditData = [
+        { guid: 'a-zero', operation: 'PURGE', params: '', result: '[]' }
+      ];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'a-zero' } }} />);
+
+      // Click the Purged Entities card — should not open a drawer with items
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      // No GUIDs shown since total is 0
+      expect(screen.queryByText('1.')).not.toBeInTheDocument();
+    });
+  });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // PURGE — Limit input (change page size)
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('Drawer — Limit input behaviour', () => {
+    it('should render the limit input with default value of 25', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      const limitInput = screen.getByDisplayValue('25');
+      expect(limitInput).toBeInTheDocument();
+    });
+
+    it('should update limit input value when user types', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      const limitInput = screen.getByDisplayValue('25');
+      fireEvent.change(limitInput, { target: { value: '5' } });
+
+      expect(screen.getByDisplayValue('5')).toBeInTheDocument();
+    });
+
+    it('should apply new limit when Enter is pressed', () => {
+      const componentProps = { auditData: mockAuditData };
+      render(<AuditResults componentProps={componentProps} row={{ original: { guid: 'audit-3' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      const limitInput = screen.getByDisplayValue('25');
+      fireEvent.change(limitInput, { target: { value: '2' } });
+      fireEvent.keyDown(limitInput, { key: 'Enter', code: 'Enter' });
+
+      // Input should be updated (clamped to min of entered value and total)
+      expect(screen.getByDisplayValue('2')).toBeInTheDocument();
+    });
+  });
+
+  // ─────────────────────────────────────────────────────────────────────────────
+  // Summary Row — shows Requested card, Total Purged, Failed, Skipped
+  // ─────────────────────────────────────────────────────────────────────────────
+  describe('SUMMARY row', () => {
+    const summaryAuditData = [
+      {
+        guid: 'audit-sum',
+        operation: 'PURGE',
+        params: JSON.stringify(['req-1', 'req-2']),
+        result: JSON.stringify({
+          requestedCount: 2,
+          purgedCount: 2,
+          purgedDependenciesCount: 0,
+          failedCount: 0,
+          skippedCount: 0,
+          executionFailed: false,
+          runId: 'test'
+        })
+      }
+    ];
+
+    it('should render Requested, Total Purged, Failed, Skipped cards for SUMMARY row', () => {
+      render(<AuditResults componentProps={{ auditData: summaryAuditData }} row={{ original: { guid: 'audit-sum' } }} />);
+
+      expect(screen.getByText('Requested')).toBeInTheDocument();
+      expect(screen.getByText('PURGED')).toBeInTheDocument();
+      expect(screen.getByText('Failed')).toBeInTheDocument();
+      expect(screen.getByText('Skipped')).toBeInTheDocument();
+    });
+
+    it('should NOT show Requested card for non-SUMMARY row', () => {
+      const nonSummaryData = [
+        { guid: 'ns-1', operation: 'PURGE', params: '', result: '[guid-a]' }
+      ];
+      render(<AuditResults componentProps={{ auditData: nonSummaryData }} row={{ original: { guid: 'ns-1' } }} />);
+
+      expect(screen.queryByText('Requested')).not.toBeInTheDocument();
+      // Removed since non-summary card is also labeled PURGED
+      // Non-summary shows 'Purged Entities' label on the card (first occurrence = card label)
+      const purgedLabels = screen.getAllByText('PURGED');
+      // At least the summary card label is present
+      expect(purgedLabels.length).toBeGreaterThan(0);
+    });
+
+    it('should open Requested Entities drawer when Requested card is clicked (SUMMARY row)', () => {
+      render(<AuditResults componentProps={{ auditData: summaryAuditData }} row={{ original: { guid: 'audit-sum' } }} />);
+
+      // Click the Requested card
+      fireEvent.click(screen.getByText('Requested'));
+
+      // The drawer should now show "Requested Entities" as its heading
+      expect(screen.getByText('Requested Entities')).toBeInTheDocument();
+    });
+
+    it('should open Total Purged drawer when Total Purged card is clicked (SUMMARY row)', async () => {
+      // SUMMARY row click triggers fetchPurged which calls fetch API
+      // fetch is mocked globally at top of file to return []
+      const auditDataForSummary = [{
+        guid: 'audit-sum2',
+        operation: 'PURGE',
+        params: JSON.stringify(['req-1']),
+        result: JSON.stringify({
+          requestedCount: 1, purgedCount: 1, purgedDependenciesCount: 0,
+          failedCount: 0, skippedCount: 0, executionFailed: false, runId: 'test'
+        })
+      }];
+      render(<AuditResults componentProps={{ auditData: auditDataForSummary }} row={{ original: { guid: 'audit-sum2' } }} />);
+
+      // Click the Total Purged card — triggers drawer + fetch
+      fireEvent.click(screen.getByText('PURGED'));
+
+      // The drawer header should show "Purged Entities" title
+      await waitFor(() => {
+        const purgedTitles = screen.getAllByText('PURGED');
+        expect(purgedTitles.length).toBeGreaterThan(0);
+      });
+    });
+
+    it('should show "No matching GUIDs found" when PURGED count > 0 but GUID list is empty', async () => {
+      const auditDataForSummary = [{
+        guid: 'audit-empty-sum',
+        operation: 'PURGE',
+        params: JSON.stringify(['req-1']),
+        result: JSON.stringify({
+          requestedCount: 1, purgedCount: 5, purgedDependenciesCount: 0,
+          failedCount: 0, skippedCount: 0, executionFailed: false, runId: 'test-empty'
+        })
+      }];
+      render(<AuditResults componentProps={{ auditData: auditDataForSummary }} row={{ original: { guid: 'audit-empty-sum' } }} />);
+
+      fireEvent.click(screen.getByText('PURGED'));
+
+      await waitFor(() => {
+        expect(screen.getByText('No matching GUIDs found')).toBeInTheDocument();
+      });
+    });
+
+    it('should NOT trigger action when Failed card is clicked (display only)', () => {
+      render(<AuditResults componentProps={{ auditData: summaryAuditData }} row={{ original: { guid: 'audit-sum' } }} />);
+
+      // Click the Failed card — it is display-only (cursor: default)
+      fireEvent.click(screen.getByText('Failed'));
+
+      // No drawer should open showing Requested or Purged Entities
+      expect(screen.queryByText('Requested Entities')).not.toBeInTheDocument();
+    });
+
+    it('should NOT trigger action when Skipped card is clicked (display only)', () => {
+      render(<AuditResults componentProps={{ auditData: summaryAuditData }} row={{ original: { guid: 'audit-sum' } }} />);
+
+      fireEvent.click(screen.getByText('Skipped'));
+
+      expect(screen.queryByText('Requested Entities')).not.toBeInTheDocument();
+    });
+
+    it('should show AUTO_PURGE SUMMARY row with all 4 cards', () => {
+      const autoPurgeSummary = [{
+        guid: 'audit-ap-sum',
+        operation: 'AUTO_PURGE',
+        params: JSON.stringify(['r1', 'r2', 'r3']),
+        result: JSON.stringify({
+          requestedCount: 3, purgedCount: 2, purgedDependenciesCount: 1,
+          failedCount: 1, skippedCount: 1, executionFailed: true, runId: 'test'
+        })
+      }];
+      render(<AuditResults componentProps={{ auditData: autoPurgeSummary }} row={{ original: { guid: 'audit-ap-sum' } }} />);
+
+      expect(screen.getByText('Requested')).toBeInTheDocument();
+      expect(screen.getByText('PURGED')).toBeInTheDocument();
+      expect(screen.getByText('Failed')).toBeInTheDocument();
+      expect(screen.getByText('Skipped')).toBeInTheDocument();
+    });
+
+    it('should show correct count on Total Purged card (purgedCount + purgedDependenciesCount)', () => {
+      const data = [{
+        guid: 'audit-count',
+        operation: 'PURGE',
+        params: '',
+        result: JSON.stringify({
+          requestedCount: 10, purgedCount: 6, purgedDependenciesCount: 2,
+          failedCount: 0, skippedCount: 2, executionFailed: false, runId: 'test'
+        })
+      }];
+      render(<AuditResults componentProps={{ auditData: data }} row={{ original: { guid: 'audit-count' } }} />);
+
+      // Total Purged = 6 + 2 = 8
+      expect(screen.getByText('8')).toBeInTheDocument();
+      // Skipped = 2
+      expect(screen.getByText('2')).toBeInTheDocument();
+    });
+  });
+
+
+  describe('Purge Drawer - Pagination Combinations', () => {
+    it('should gracefully handle invalid limit inputs', () => {
+      const auditData = [{ guid: 'test-limit', operation: 'PURGE', params: '', result: '["a", "b"]' }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'test-limit', runId: 'N/A' } }} />);
+      
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+      
+      const limitInput = screen.getByDisplayValue('25');
+      
+      // Invalid input (empty)
+      fireEvent.change(limitInput, { target: { value: '' } });
+      fireEvent.keyDown(limitInput, { key: 'Enter', code: 'Enter' });
+      // Usually it defaults to something or ignores, let's just make sure it doesn't crash
+      expect(screen.getByText('a')).toBeInTheDocument();
+
+      // Negative input
+      fireEvent.change(limitInput, { target: { value: '-10' } });
+      fireEvent.keyDown(limitInput, { key: 'Enter', code: 'Enter' });
+      expect(screen.getByText('a')).toBeInTheDocument();
+    });
+
+    it('should disable prev page button on first page ', () => {
+      const mockData = Array.from({ length: 30 }, (_, i) => `guid-${i}`);
+      const auditData = [{ guid: 'test', operation: 'PURGE', params: '', result: JSON.stringify(mockData) }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'test' } }} />);
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      const prevButton = screen.getByRole('button', { name: /Go to previous page/i });
+      expect(prevButton).toBeDisabled();
+    });
+
+    it('should change to next page when next button is clicked ', () => {
+      const mockData = Array.from({ length: 30 }, (_, i) => `guid-${i}`);
+      const auditData = [{ guid: 'test', operation: 'PURGE', params: '', result: JSON.stringify(mockData) }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'test' } }} />);
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      const nextButton = screen.getByRole('button', { name: /Go to next page/i });
+      fireEvent.click(nextButton);
+
+      // Should show item from next page
+      expect(screen.getByText('guid-25')).toBeInTheDocument();
+    });
+
+    it('should change page size when Limit input is updated ', () => {
+      const mockData = Array.from({ length: 30 }, (_, i) => `guid-${i}`);
+      const auditData = [{ guid: 'test', operation: 'PURGE', params: '', result: JSON.stringify(mockData) }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'test' } }} />);
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      const limitInput = screen.getByDisplayValue('25');
+      fireEvent.change(limitInput, { target: { value: '10' } });
+      fireEvent.keyDown(limitInput, { key: 'Enter', code: 'Enter' });
+
+      // Page size is now 10, so guid-10 should NOT be on the first page
+      expect(screen.queryByText('guid-10')).not.toBeInTheDocument();
+      expect(screen.getByText('guid-9')).toBeInTheDocument();
+    });
+  });
+
+  describe('Purge UI Combinations - Combinations', () => {
+    it('should correctly render legacy audit purge with array result ', () => {
+      const auditData = [{ guid: 'legacy1', operation: 'PURGE', params: '', result: '[legacy-guid-1, legacy-guid-2]' }];
+      const { container } = render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'legacy1', runId: 'N/A' } }} />);
+
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+      expect(screen.getByText('legacy-guid-1')).toBeInTheDocument();
+      expect(screen.getByText('legacy-guid-2')).toBeInTheDocument();
+    });
+
+    it('should show 0 count and not open drawer for legacy purge with empty array ', () => {
+      mockIsEmpty.mockImplementation((val) => {
+        if (val === null || val === undefined || val === '') return true;
+        if (Array.isArray(val) && val.length === 0) return true;
+        if (typeof val === 'object' && Object.keys(val).length === 0) return true;
+        if (Array.isArray(val) && val.length === 1 && val[0] === '') return true;
+        return false;
+      });
+      const auditData = [{ guid: 'legacy2', operation: 'PURGE', params: '', result: '[]' }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'legacy2', runId: 'N/A' } }} />);
+
+      expect(screen.queryByText('Requested')).not.toBeInTheDocument();
+
+      // Card displays 0
+      const countEl = screen.getByText('0');
+      expect(countEl).toBeInTheDocument();
+
+      // Drawer does not open
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+      expect(screen.queryByTestId('drawer')).not.toBeInTheDocument();
+    });
+
+    it('should correctly render new audit summary cards and open drawer for Requested ', () => {
+      // New audit summary has runId and result is JSON object
+      const summaryResult = JSON.stringify({
+        purgedCount: 10,
+        failedCount: 0,
+        skippedCount: 0,
+        runId: 'run-123'
+      });
+      const auditData = [{ guid: 'new1', operation: 'PURGE', params: '["req-1","req-2"]', result: summaryResult }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'new1', runId: 'run-123' } }} />);
+
+      // Should render summary cards
+      expect(screen.getByText('Requested')).toBeInTheDocument();
+      expect(screen.getByText('PURGED')).toBeInTheDocument();
+
+      // Click Requested card
+      fireEvent.click(screen.getByText('Requested'));
+
+      // Drawer should open showing requested guids
+      expect(screen.getByText('Requested Entities')).toBeInTheDocument();
+      expect(screen.getByText('req-1')).toBeInTheDocument();
+      expect(screen.getByText('req-2')).toBeInTheDocument();
+    });
+
+    it('should NOT open drawer when Total Purged card is clicked and count is 0 ', () => {
+      const summaryResult = JSON.stringify({
+        purgedCount: 0,
+        failedCount: 0,
+        skippedCount: 0,
+        runId: 'run-456'
+      });
+      const auditData = [{ guid: 'new2', operation: 'PURGE', params: '', result: summaryResult }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'new2', runId: 'run-456' } }} />);
+
+      // Click PURGED card
+      fireEvent.click(screen.getAllByText('PURGED')[0]);
+
+      // Drawer should NOT open
+      expect(screen.queryByText('Purged Entities')).not.toBeInTheDocument();
+    });
+  });
+
+
+  describe('Summary fetch logic (useEffect)', () => {
+    afterEach(() => {
+      jest.clearAllMocks();
+    });
+
+    it('(1) successful summary fetch populates cards', async () => {
+      (fetchApi as jest.Mock).mockResolvedValueOnce({
+        data: {
+          requestedCount: 10, purgedCount: 8, purgedDependenciesCount: 2, failedCount: 0, skippedCount: 0, executionFailed: false, runId: 'run-fetch-1'
+        }
+      });
+
+      const auditData = [{ guid: 'fetch-test-1', operation: 'PURGE', params: '[]' }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'fetch-test-1' } }} />);
+
+      await waitFor(() => {
+        expect(screen.getAllByText('10').length).toBeGreaterThan(0);
+      });
+    });
+
+    it('(2) fetch failure falls back to parsed result', async () => {
+      (fetchApi as jest.Mock).mockRejectedValueOnce(new Error('Fetch failed'));
+
+      const fallbackResult = JSON.stringify({
+        requestedCount: 5, purgedCount: 5, purgedDependenciesCount: 0,
+        failedCount: 0, skippedCount: 0, executionFailed: false, runId: 'run-fallback'
+      });
+      const auditData = [{ guid: 'fetch-test-2', operation: 'PURGE', params: '[]', result: fallbackResult }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'fetch-test-2' } }} />);
+
+      await waitFor(() => {
+        expect(screen.getAllByText('5').length).toBeGreaterThan(0);
+      });
+    });
+
+    it('(3) loading skeleton shows while fetching', async () => {
+      let resolvePromise: any;
+      const promise = new Promise((resolve) => {
+        resolvePromise = resolve;
+      });
+      (fetchApi as jest.Mock).mockReturnValueOnce(promise);
+
+      const auditData = [{ guid: 'fetch-test-3', operation: 'PURGE', params: '[]' }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'fetch-test-3' } }} />);
+
+      expect(document.querySelector('.MuiSkeleton-root')).not.toBeNull();
+
+      resolvePromise({ data: {} });
+      await waitFor(() => {
+        expect(document.querySelector('.MuiSkeleton-root')).toBeNull();
+      });
+    });
+
+    it('(4) AbortController cancels on unmount', () => {
+      const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
+
+      const auditData = [{ guid: 'fetch-test-4', operation: 'PURGE', params: '[]', result: 'run-fetch-4' }];
+      const { unmount } = render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'fetch-test-4' } }} />);
+
+      unmount();
+
+      expect(abortSpy).toHaveBeenCalled();
+      abortSpy.mockRestore();
+    });
+  });
+
+  describe('Copy Run ID', () => {
+    it('should copy Run ID to clipboard and show Copied tooltip', async () => {
+      const auditData = [{ guid: 'audit-1', operation: 'PURGE', params: '["a"]', result: '["a"]', runId: 'test-run-1' }];
+      render(<AuditResults componentProps={{ auditData }} row={{ original: { guid: 'audit-1' } }} />);
+
+      const copyBtn = screen.getByRole('button', { name: /Copy Run Id/i });
+      fireEvent.click(copyBtn);
+
+      expect(navigator.clipboard.writeText).toHaveBeenCalledWith('test-run-1');
+
+      await waitFor(() => {
+        expect(screen.getByRole('button', { name: /Copied!/i })).toBeInTheDocument();
+      });
+    });
+  });
+
 });
diff --git a/dashboard/src/views/Administrator/__tests__/AdministratorLayout.test.tsx b/dashboard/src/views/Administrator/__tests__/AdministratorLayout.test.tsx
index f2067c7a03..a5b04e440a 100644
--- a/dashboard/src/views/Administrator/__tests__/AdministratorLayout.test.tsx
+++ b/dashboard/src/views/Administrator/__tests__/AdministratorLayout.test.tsx
@@ -157,7 +157,7 @@ describe('AdministratorLayout Component', () => {
 		mockLocation.pathname = '/administrator';
 		mockLocation.search = '';
 		mockNavigate.mockClear();
-		
+
 		// Default Redux state
 		mockUseAppSelector.mockImplementation((selector: any) => {
 			const state = {
@@ -174,14 +174,14 @@ describe('AdministratorLayout Component', () => {
 	describe('Component Rendering', () => {
 		it('should render AdministratorLayout component', async () => {
 			renderComponent();
-			
+
 			expect(screen.getByTestId('item')).toBeInTheDocument();
 			await waitForLazyTab('business-metadata-tab');
 		});
 
 		it('should render all tabs', () => {
 			renderComponent();
-			
+
 			expect(screen.getByTestId('link-tab-Business Metadata')).toBeInTheDocument();
 			expect(screen.getByTestId('link-tab-Enumerations')).toBeInTheDocument();
 			expect(screen.getByTestId('link-tab-Audits')).toBeInTheDocument();
@@ -190,7 +190,7 @@ describe('AdministratorLayout Component', () => {
 
 		it('should render BusinessMetadataTab by default when no tabActive', async () => {
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
 			expect(screen.queryByTestId('enumerations-tab')).not.toBeInTheDocument();
 			expect(screen.queryByTestId('audit-table')).not.toBeInTheDocument();
@@ -198,13 +198,13 @@ describe('AdministratorLayout Component', () => {
 
 		it('should render BusinessMetadataTab when tabActive is undefined', async () => {
 			renderComponent(['/administrator'], '');
-			
+
 			await waitForLazyTab('business-metadata-tab');
 		});
 
 		it('should render BusinessMetadataTab when tabActive is businessMetadata', async () => {
 			renderComponent(['/administrator'], '?tabActive=businessMetadata');
-			
+
 			await waitForLazyTab('business-metadata-tab');
 		});
 	});
@@ -213,9 +213,9 @@ describe('AdministratorLayout Component', () => {
 		it('should navigate to enum tab when clicked', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(true);
-			
+
 			renderComponent();
-			
+
 			// Call onChange directly to cover lines 55-59
 			if (capturedOnChange) {
 				const clickEvent = {
@@ -229,10 +229,10 @@ describe('AdministratorLayout Component', () => {
 					altKey: false,
 					shiftKey: false
 				} as React.MouseEvent<HTMLAnchorElement, MouseEvent>;
-				
+
 				mockNavigate.mockClear();
 				capturedOnChange(clickEvent, 1);
-				
+
 				expect(mockNavigate).toHaveBeenCalledWith({
 					pathname: '/administrator',
 					search: 'tabActive=enum'
@@ -243,9 +243,9 @@ describe('AdministratorLayout Component', () => {
 		it('should navigate to audit tab when clicked', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(true);
-			
+
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				const clickEvent = {
 					type: 'click',
@@ -258,10 +258,10 @@ describe('AdministratorLayout Component', () => {
 					altKey: false,
 					shiftKey: false
 				} as React.MouseEvent<HTMLAnchorElement, MouseEvent>;
-				
+
 				mockNavigate.mockClear();
 				capturedOnChange(clickEvent, 2);
-				
+
 				expect(mockNavigate).toHaveBeenCalledWith({
 					pathname: '/administrator',
 					search: 'tabActive=audit'
@@ -272,7 +272,7 @@ describe('AdministratorLayout Component', () => {
 		it('should navigate to typeSystem tab when clicked', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(true);
-			
+
 			mockUseAppSelector.mockImplementation((selector: any) => {
 				const state = {
 					entity: {
@@ -283,9 +283,9 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				const clickEvent = {
 					type: 'click',
@@ -298,10 +298,10 @@ describe('AdministratorLayout Component', () => {
 					altKey: false,
 					shiftKey: false
 				} as React.MouseEvent<HTMLAnchorElement, MouseEvent>;
-				
+
 				mockNavigate.mockClear();
 				capturedOnChange(clickEvent, 3);
-				
+
 				expect(mockNavigate).toHaveBeenCalledWith({
 					pathname: '/administrator',
 					search: 'tabActive=typeSystem'
@@ -312,9 +312,9 @@ describe('AdministratorLayout Component', () => {
 		it('should handle tab change with click event', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(true);
-			
+
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				const clickEvent = {
 					type: 'click',
@@ -327,10 +327,10 @@ describe('AdministratorLayout Component', () => {
 					altKey: false,
 					shiftKey: false
 				} as React.MouseEvent<HTMLAnchorElement, MouseEvent>;
-				
+
 				mockNavigate.mockClear();
 				capturedOnChange(clickEvent, 1);
-				
+
 				expect(mockNavigate).toHaveBeenCalled();
 			}
 		});
@@ -338,9 +338,9 @@ describe('AdministratorLayout Component', () => {
 		it('should navigate when event type is not click', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			// Don't set mockReturnValue - let the default implementation run
-			
+
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				const keyDownEvent = {
 					type: 'keydown',
@@ -353,10 +353,10 @@ describe('AdministratorLayout Component', () => {
 					altKey: false,
 					shiftKey: false
 				} as React.SyntheticEvent;
-				
+
 				mockNavigate.mockClear();
 				capturedOnChange(keyDownEvent, 1);
-				
+
 				// SHOULD navigate for non-click events
 				// The condition is: event.type !== "click" || (event.type === "click" && samePageLinkNavigation(event))
 				// Since type is 'keydown', event.type !== "click" is true, so navigation happens
@@ -370,9 +370,9 @@ describe('AdministratorLayout Component', () => {
 		it('should handle samePageLinkNavigation returning true', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(true);
-			
+
 			renderComponent();
-			
+
 			const enumTab = screen.getByTestId('link-tab-Enumerations');
 			const clickEvent = {
 				type: 'click',
@@ -385,9 +385,9 @@ describe('AdministratorLayout Component', () => {
 				altKey: false,
 				shiftKey: false
 			} as any;
-			
+
 			fireEvent.click(enumTab, clickEvent);
-			
+
 			// Tab should be rendered
 			expect(enumTab).toBeInTheDocument();
 		});
@@ -395,9 +395,9 @@ describe('AdministratorLayout Component', () => {
 		it('should handle samePageLinkNavigation returning false', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(false);
-			
+
 			renderComponent();
-			
+
 			const enumTab = screen.getByTestId('link-tab-Enumerations');
 			const clickEvent = {
 				type: 'click',
@@ -410,9 +410,9 @@ describe('AdministratorLayout Component', () => {
 				altKey: false,
 				shiftKey: false
 			} as any;
-			
+
 			fireEvent.click(enumTab, clickEvent);
-			
+
 			// Tab should be rendered
 			expect(enumTab).toBeInTheDocument();
 		});
@@ -420,9 +420,9 @@ describe('AdministratorLayout Component', () => {
 		it('should handle click event with preventDefault', () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(false); // Returns false when defaultPrevented
-			
+
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				const clickEvent = {
 					type: 'click',
@@ -435,10 +435,10 @@ describe('AdministratorLayout Component', () => {
 					altKey: false,
 					shiftKey: false
 				} as React.MouseEvent<HTMLAnchorElement, MouseEvent>;
-				
+
 				mockNavigate.mockClear();
 				capturedOnChange(clickEvent, 1);
-				
+
 				// Ensure handler execution path completes
 				expect(mockNavigate).not.toHaveBeenCalled();
 			}
@@ -448,14 +448,14 @@ describe('AdministratorLayout Component', () => {
 	describe('Tab Content Rendering', () => {
 		it('should render Enumerations tab when tabActive is enum', async () => {
 			renderComponent(['/administrator'], '?tabActive=enum');
-			
+
 			await waitForLazyTab('enumerations-tab');
 			expect(screen.queryByTestId('business-metadata-tab')).not.toBeInTheDocument();
 		});
 
 		it('should render AdminAuditTable when tabActive is audit', async () => {
 			renderComponent(['/administrator'], '?tabActive=audit');
-			
+
 			await waitForLazyTab('audit-table');
 			expect(screen.queryByTestId('business-metadata-tab')).not.toBeInTheDocument();
 		});
@@ -471,9 +471,9 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent(['/administrator'], '?tabActive=typeSystem');
-			
+
 			await waitForLazyTab('type-system-tree-view');
 			expect(screen.getByText(/1 entities/)).toBeInTheDocument();
 		});
@@ -489,15 +489,15 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			const { isEmpty } = require('@utils/Utils');
 			isEmpty.mockImplementation((val: any) => {
 				if (Array.isArray(val)) return val.length === 0;
 				return val === null || val === undefined || val === '';
 			});
-			
+
 			renderComponent(['/administrator'], '?tabActive=typeSystem');
-			
+
 			expect(screen.queryByTestId('type-system-tree-view')).not.toBeInTheDocument();
 		});
 
@@ -510,16 +510,16 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			const { isEmpty } = require('@utils/Utils');
 			isEmpty.mockImplementation((val: any) => {
 				if (val === undefined) return true;
 				if (Array.isArray(val)) return val.length === 0;
 				return val === null || val === '';
 			});
-			
+
 			renderComponent(['/administrator'], '?tabActive=typeSystem');
-			
+
 			expect(screen.queryByTestId('type-system-tree-view')).not.toBeInTheDocument();
 		});
 	});
@@ -527,22 +527,22 @@ describe('AdministratorLayout Component', () => {
 	describe('Form State Management', () => {
 		it('should render BusinessMetaDataForm when form is true', async () => {
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
 
 			// Click setForm button to trigger form display
 			const setFormBtn = screen.getByTestId('bm-set-form');
 			fireEvent.click(setFormBtn);
-			
+
 			// Note: Since setForm is internal state, we need to test through props
 			// The actual form rendering would require state management
 		});
 
 		it('should pass setForm and setBMAttribute to BusinessMetadataTab', async () => {
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
-			
+
 			// Verify buttons exist (which use the props)
 			expect(screen.getByTestId('bm-set-form')).toBeInTheDocument();
 			expect(screen.getByTestId('bm-set-attribute')).toBeInTheDocument();
@@ -552,14 +552,14 @@ describe('AdministratorLayout Component', () => {
 	describe('Initial Tab Value', () => {
 		it('should set initial tab value to 0 when tabActive is empty', async () => {
 			renderComponent(['/administrator'], '');
-			
+
 			// Should render business metadata tab (index 0)
 			await waitForLazyTab('business-metadata-tab');
 		});
 
 		it('should set initial tab value based on tabActive query param', async () => {
 			renderComponent(['/administrator'], '?tabActive=enum');
-			
+
 			// Should render enumerations tab
 			await waitForLazyTab('enumerations-tab');
 		});
@@ -570,10 +570,10 @@ describe('AdministratorLayout Component', () => {
 			isEmpty.mockImplementation((val: any) => {
 				return val === null || val === undefined || val === '';
 			});
-			
+
 			// Set tabActive to a value NOT in allTabs ('businessMetadata', 'enum', 'audit', 'typeSystem')
 			renderComponent(['/administrator'], '?tabActive=nonExistentTab');
-			
+
 			// When findIndex returns -1, line 44 evaluates to:
 			// !isEmpty('nonExistentTab') = true, so it runs findIndex()
 			// allTabs.findIndex(val => val === 'nonExistentTab') = -1
@@ -582,7 +582,7 @@ describe('AdministratorLayout Component', () => {
 			// none of the tab content conditions match, so only the tabs themselves render
 			expect(screen.getByTestId('item')).toBeInTheDocument();
 			expect(screen.getByTestId('link-tab-Business Metadata')).toBeInTheDocument();
-			
+
 			// No tab content should be rendered
 			expect(screen.queryByTestId('business-metadata-tab')).not.toBeInTheDocument();
 			expect(screen.queryByTestId('enumerations-tab')).not.toBeInTheDocument();
@@ -601,9 +601,9 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
 		});
 
@@ -616,9 +616,9 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
 		});
 
@@ -633,9 +633,9 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
 		});
 
@@ -648,9 +648,9 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
 		});
 
@@ -665,21 +665,21 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
 		});
 
 		it('should handle multiple tab switches', async () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(true);
-			
+
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				mockNavigate.mockClear();
-				
+
 				// Switch to enum
 				const clickEvent1 = {
 					type: 'click',
@@ -693,16 +693,16 @@ describe('AdministratorLayout Component', () => {
 					shiftKey: false,
 					preventDefault: jest.fn()
 				} as unknown as React.SyntheticEvent;
-				
+
 				capturedOnChange(clickEvent1, 1);
-				
+
 				await waitFor(() => {
 					expect(mockNavigate).toHaveBeenCalledWith({
 						pathname: '/administrator',
 						search: 'tabActive=enum'
 					});
 				}, { timeout: 10000 });
-				
+
 				// Switch to audit
 				mockNavigate.mockClear();
 				const clickEvent2 = {
@@ -717,9 +717,9 @@ describe('AdministratorLayout Component', () => {
 					shiftKey: false,
 					preventDefault: jest.fn()
 				} as unknown as React.SyntheticEvent;
-				
+
 				capturedOnChange(clickEvent2, 2);
-				
+
 				await waitFor(() => {
 					expect(mockNavigate).toHaveBeenCalledWith({
 						pathname: '/administrator',
@@ -731,7 +731,7 @@ describe('AdministratorLayout Component', () => {
 
 		it('should handle click event with preventDefault', async () => {
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				// Test that when defaultPrevented is true, samePageLinkNavigation returns false
 				// and navigation doesn't happen
@@ -747,9 +747,9 @@ describe('AdministratorLayout Component', () => {
 					shiftKey: false,
 					preventDefault: jest.fn()
 				} as unknown as React.SyntheticEvent;
-				
+
 				capturedOnChange(clickEvent, 1);
-				
+
 				// Ensure handler execution path completes
 				expect(mockNavigate).toHaveBeenCalled();
 			}
@@ -759,9 +759,9 @@ describe('AdministratorLayout Component', () => {
 	describe('Component Props', () => {
 		it('should pass correct props to BusinessMetadataTab', async () => {
 			renderComponent();
-			
+
 			await waitForLazyTab('business-metadata-tab');
-			
+
 			// Verify the component can use the props
 			const setFormBtn = screen.getByTestId('bm-set-form');
 			expect(setFormBtn).toBeInTheDocument();
@@ -772,7 +772,7 @@ describe('AdministratorLayout Component', () => {
 				{ guid: '1', name: 'Entity1' },
 				{ guid: '2', name: 'Entity2' }
 			];
-			
+
 			mockUseAppSelector.mockImplementation((selector: any) => {
 				const state = {
 					entity: {
@@ -783,9 +783,9 @@ describe('AdministratorLayout Component', () => {
 				};
 				return selector(state);
 			});
-			
+
 			renderComponent(['/administrator'], '?tabActive=typeSystem');
-			
+
 			await waitForLazyTab('type-system-tree-view');
 			expect(screen.getByText(/2 entities/)).toBeInTheDocument();
 		});
@@ -794,19 +794,19 @@ describe('AdministratorLayout Component', () => {
 	describe('URL Search Params', () => {
 		it('should handle search params correctly', async () => {
 			renderComponent(['/administrator'], '?tabActive=enum&other=value');
-			
+
 			await waitForLazyTab('enumerations-tab');
 		});
 
 		it('should update URL when tab changes', async () => {
 			const { samePageLinkNavigation } = require('@utils/Muiutils');
 			samePageLinkNavigation.mockReturnValue(true);
-			
+
 			renderComponent();
-			
+
 			if (capturedOnChange) {
 				mockNavigate.mockClear();
-				
+
 				const clickEvent = {
 					type: 'click',
 					currentTarget: document.createElement('a'),
@@ -819,9 +819,9 @@ describe('AdministratorLayout Component', () => {
 					shiftKey: false,
 					preventDefault: jest.fn()
 				} as unknown as React.SyntheticEvent;
-				
+
 				capturedOnChange(clickEvent, 1);
-				
+
 				await waitFor(() => {
 					expect(mockNavigate).toHaveBeenCalledWith({
 						pathname: '/administrator',
diff --git a/dashboardv2/public/css/scss/drawer.scss b/dashboardv2/public/css/scss/drawer.scss
new file mode 100644
index 0000000000..622cd502b9
--- /dev/null
+++ b/dashboardv2/public/css/scss/drawer.scss
@@ -0,0 +1,417 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/* Purge Summary Wrapper to match React */
+.purge-summary-wrapper {
+    margin-top: 16px;
+    margin-bottom: 16px;
+    padding: 20px;
+    border-radius: 8px;
+    background-color: #f0f4f8;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02);
+}
+
+.purge-run-id-row {
+    margin-bottom: 15px;
+    font-size: 14px;
+    font-weight: 500;
+}
+
+.runid-text {
+    font-family: monospace;
+}
+
+.purge-run-id-copy {
+    cursor: pointer;
+    color: #6b7280;
+    margin-left: 5px;
+}
+
+.purge-warning-alert {
+    padding: 10px;
+    margin-bottom: 15px;
+}
+
+.audit-type-details-title {
+    word-break: break-word;
+}
+
+.purge-summary-container {
+    display: flex;
+    gap: 15px;
+    margin-top: 15px;
+    flex-wrap: wrap;
+}
+
+.purge-summary-card {
+    flex: 1;
+    min-width: 120px;
+    padding: 12px;
+    border-radius: 8px;
+    border: 1px solid rgba(0, 0, 0, 0.08);
+    background-color: #fafafa;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    justify-content: flex-start;
+    text-align: left;
+    cursor: default;
+
+    &.clickable {
+        cursor: pointer;
+        transition: box-shadow 0.2s;
+
+        &:hover {
+            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
+        }
+    }
+
+    &.card-legacy {
+        max-width: 250px;
+    }
+
+    .card-label {
+        font-size: 11px;
+        color: #6b7280;
+        /* textSecondary */
+        margin-bottom: 4px;
+        font-weight: 600;
+        text-transform: uppercase;
+        letter-spacing: 0.5px;
+    }
+
+    .card-value {
+        font-size: 14px;
+        font-weight: 500;
+        color: #111827;
+        /* textPrimary */
+    }
+
+    &.card-blue {
+        background-color: #eff6ff;
+        border-color: #bfdbfe;
+
+        .card-label,
+        .card-value {
+            color: #1d4ed8;
+        }
+    }
+
+    &.card-green {
+        background-color: #f0fdf4;
+        border-color: #bbf7d0;
+
+        .card-label,
+        .card-value {
+            color: #15803d;
+        }
+    }
+
+    &.card-red.has-count {
+        background-color: #fef2f2;
+        border-color: #fecaca;
+
+        .card-label,
+        .card-value {
+            color: #d32f2f;
+        }
+    }
+
+    &.card-amber.has-count {
+        background-color: #fffbeb;
+        border-color: #fef08a;
+
+        .card-label,
+        .card-value {
+            color: #ed6c02;
+        }
+    }
+}
+
+/* Drawer styles */
+.drawer-overlay {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.4);
+    z-index: 1030;
+    display: none;
+    &.open { display: block; }
+}
+
+.drawer-panel {
+    position: fixed;
+    top: 0;
+    right: -400px;
+    width: 400px;
+    height: 100vh;
+    overflow: hidden;
+    background-color: #fff;
+    box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
+    z-index: 1038;
+    transition: right 0.3s ease;
+    display: flex;
+    flex-direction: column;
+
+    &.open { right: 0; }
+
+    .drawer-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 8px 12px;
+        flex-shrink: 0;
+        h4 { margin: 0; font-size: 16px; font-weight: 600; }
+        .close-drawer { cursor: pointer; font-size: 18px; color: #6b7280; &:hover { color: #111827; } }
+    }
+
+    .drawer-body {
+        flex: 1;
+        min-height: 0;
+        overflow: hidden;
+        display: flex;
+        flex-direction: column;
+
+        .drawer-search, .drawer-run-id {
+            margin: 4px 12px;
+            flex-shrink: 0;
+        }
+
+        .drawer-search {
+            padding-bottom: 8px;
+            .search-input-wrapper {
+                position: relative;
+                i { position: absolute; left: 12px; top: 10px; color: #9ca3af; }
+                input {
+                    width: 100%;
+                    padding: 8px 12px 8px 32px;
+                    border: none;
+                    border-bottom: 1px solid #d1d5db;
+                    border-radius: 0;
+                    font-size: 13px;
+                    outline: none;
+                    background-color: transparent;
+                    &:focus { border-bottom-color: #3b82f6; }
+                }
+            }
+        }
+
+        .drawer-run-id {
+            padding: 4px 0;
+            display: flex;
+            justify-content: flex-start;
+            align-items: center;
+            .run-id-text { font-size: 13px; color: #4b5563; font-weight: 500; }
+            .run-id-value { color: #6b7280; font-weight: normal; margin-left: 4px; margin-right: 12px;}
+            i { cursor: pointer; color: #6b7280; font-size: 14px; &:hover { color: #111827; } }
+        }
+
+        .drawer-list {
+            flex: 1;
+            overflow-y: auto;
+            min-height: 0;
+            margin: 0 15px;
+            padding-right: 5px;
+            
+
+            /* Custom scrollbar to match modern React UI */
+            &::-webkit-scrollbar {
+                width: 6px;
+            }
+            &::-webkit-scrollbar-track {
+                background: #f1f1f1;
+                border-radius: 4px;
+            }
+            &::-webkit-scrollbar-thumb {
+                background: #888;
+                border-radius: 4px;
+            }
+            &::-webkit-scrollbar-thumb:hover {
+                background: #555;
+            }
+
+            .drawer-items-list {
+                list-style-type: none;
+                padding-left: 0;
+                margin: 0;
+
+                .drawer-list-item {
+                    border-bottom: 1px solid rgba(0, 0, 0, 0.04);
+                    padding: 8px 0;
+                    display: flex;
+                    align-items: center;
+                    color: #6b7280;
+                    font-size: 13px;
+                    padding-left: 10px; /* Added left padding for spacing */
+                    
+                    .item-index {
+                        color: #6b7280;
+                        min-width: 24px;
+                        text-align: right;
+                        display: inline-block;
+                    }
+
+                    .blue-link {
+                        cursor: pointer;
+                        flex: 1;
+                        text-overflow: ellipsis;
+                        overflow: hidden;
+                        white-space: nowrap;
+                        margin-left: 12px;
+                    }
+                }
+            }
+        }
+
+        .drawer-empty, .drawer-loading {
+            padding: 20px 0;
+            text-align: center;
+            color: #6b7280;
+        }
+
+        .drawer-load-more {
+            text-align: center;
+            color: rgba(0,0,0,0.6);
+            font-style: italic;
+            margin-top: 15px;
+            margin-bottom: 15px;
+            font-size: 12px;
+            cursor: default;
+        }
+
+        .drawer-observer { height: 20px; width: 100%; }
+    }
+
+    .drawer-pagination-footer {
+        padding: 8px 12px;
+        background-color: #fff;
+        border-top: 1px solid #e2e8f0;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        flex-shrink: 0;
+
+        .drawer-showing {
+            font-size: 13px;
+            color: #6b7280;
+            white-space: nowrap;
+        }
+
+        .drawer-pagination-controls {
+            display: flex;
+            align-items: center;
+            gap: 4px;
+            
+            .drawer-btn-page {
+                background: none;
+                border: none;
+                cursor: pointer;
+                min-width: 24px;
+                height: 24px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                border-radius: 12px;
+                font-size: 13px;
+                color: #4b5563;
+                padding: 0;
+                
+                &:hover:not(:disabled) { background-color: rgba(0,0,0,0.04); }
+                &:disabled { color: #d1d5db; cursor: default; }
+                &.active { background-color: rgba(59, 130, 246, 0.08); color: #1976d2; font-weight: 600; }
+            }
+        }
+
+        .drawer-limit-control {
+            display: flex;
+            align-items: center;
+            gap: 4px;
+            
+            span { font-size: 13px; color: #6b7280; }
+            
+            input {
+                width: 48px;
+                height: 24px;
+                box-sizing: border-box;
+                font-size: 13px;
+                border: 1px solid #cbd5e1;
+                border-radius: 4px;
+                padding: 0 4px;
+                text-align: center;
+                outline: none;
+                &:focus { border-color: #90caf9; }
+            }
+        }
+    }
+
+    .drawer-footer {
+        padding: 12px 20px;
+        border-top: 1px solid #e2e8f0;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        background-color: #fff;
+        flex-shrink: 0;
+        .drawer-showing { font-size: 13px; color: #6b7280; }
+        .drawer-limit {
+            display: flex;
+            align-items: center;
+            font-size: 13px;
+            color: #4b5563;
+            input {
+                width: 50px;
+                margin-left: 8px;
+                padding: 4px;
+                border: 1px solid #d1d5db;
+                border-radius: 4px;
+                text-align: center;
+            }
+        }
+    }
+}
+
+body.drawer-open-lock { overflow: hidden \!important; }
+
+.drawer-list-item {
+    height: 24px;
+    box-sizing: border-box;
+    padding: 0;
+}
+.item-index {
+    margin-right: 8px;
+}
+.fontLoader {
+    &.table-loader {
+        display: block;
+        position: relative;
+        min-height: 50px;
+        z-index: 0;
+    }
+}
+.attr-type-container {
+    h4.attr-type-header {
+        word-break: break-word;
+    }
+}
+
+.purge-spinner-container {
+    padding: 20px;
+    text-align: center;
+}
diff --git a/dashboardv2/public/css/scss/style.scss b/dashboardv2/public/css/scss/style.scss
index 3932acf1cf..7cdac0f9d1 100644
--- a/dashboardv2/public/css/scss/style.scss
+++ b/dashboardv2/public/css/scss/style.scss
@@ -39,4 +39,5 @@
 @import "override.scss";
 @import "trumbowyg.scss";
 @import "texteditor.scss";
-@import "downloads.scss";
\ No newline at end of file
+@import "downloads.scss";
+@import "drawer.scss";
diff --git a/dashboardv2/public/js/templates/audit/DrawerView_tmpl.html b/dashboardv2/public/js/templates/audit/DrawerView_tmpl.html
new file mode 100644
index 0000000000..84a82c0cc6
--- /dev/null
+++ b/dashboardv2/public/js/templates/audit/DrawerView_tmpl.html
@@ -0,0 +1,66 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<div class="drawer-overlay"></div>
+<div class="drawer-panel">
+    <div class="drawer-header">
+        <h4 data-id="drawerTitle">{{title}}</h4>
+        <div class="close-drawer" data-id="closeDrawer" aria-label="Close drawer" role="button" tabindex="0">
+            ✕
+        </div>
+    </div>
+    <div class="drawer-body">
+        {{#if runId}}
+        <div class="drawer-run-id">
+            <span class="run-id-text">Run Id: <span class="run-id-value">{{runId}}</span></span>
+            <i class="fa fa-copy" data-id="copyRunId" title="Copy to clipboard" aria-label="Copy Run Id to clipboard" role="button" tabindex="0"></i>
+        </div>
+        {{/if}}
+        <div class="drawer-search">
+            <div class="search-input-wrapper">
+                <i class="fa fa-search" aria-hidden="true"></i>
+                <input type="text" data-id="searchInput" placeholder="Search GUIDs..." aria-label="Search GUIDs" />
+            </div>
+        </div>
+        <div class="drawer-list" data-id="drawerScrollRegion">
+            <ol class="drawer-items-list" data-id="drawerList"></ol>
+            <div class="drawer-empty" data-id="drawerEmpty">
+                No matching GUIDs found
+            </div>
+            <div class="drawer-loading" data-id="drawerLoading">
+                Loading...
+            </div>
+            <div class="drawer-observer" data-id="drawerObserver"></div>
+        </div>
+    </div>
+    <div class="drawer-pagination-footer">
+        <div class="drawer-showing" data-id="showingText">
+            0-0 of 0
+        </div>
+        <div class="drawer-pagination-controls">
+            <button data-id="btnFirstPage" class="drawer-btn-page"><i class="fa fa-angle-double-left"></i></button>
+            <button data-id="btnPrevPage" class="drawer-btn-page"><i class="fa fa-angle-left"></i></button>
+            <button data-id="btnCurrentPage" class="drawer-btn-page active" disabled>1</button>
+            <button data-id="btnNextPage" class="drawer-btn-page"><i class="fa fa-angle-right"></i></button>
+            <button data-id="btnLastPage" class="drawer-btn-page"><i class="fa fa-angle-double-right"></i></button>
+        </div>
+        <div class="drawer-limit-control">
+            <span>Limit</span>
+            <input type="number" data-id="limitInput" min="1" value="25" />
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/dashboardv2/public/js/utils/Utils.js b/dashboardv2/public/js/utils/Utils.js
index fb4d850a3d..7b21a04b24 100644
--- a/dashboardv2/public/js/utils/Utils.js
+++ b/dashboardv2/public/js/utils/Utils.js
@@ -1395,5 +1395,38 @@ define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums',
         return parts.join('&');
     };
 
+    Utils.virtualizeList = function(options) {
+        var items = options.items || [];
+        var scrollTop = options.scrollTop || 0;
+        var itemHeight = options.itemHeight || 37;
+        var overscan = options.overscan || 10;
+        var visibleCount = options.visibleCount || 40;
+
+        var totalItems = items.length;
+        if (totalItems === 0) {
+            return {
+                visibleItems: [],
+                paddingTop: 0,
+                paddingBottom: 0,
+                startIndex: 0
+            };
+        }
+
+        var startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
+        var endIndex = Math.min(totalItems - 1, Math.floor(scrollTop / itemHeight) + visibleCount + overscan);
+        
+        var visibleItems = items.slice(startIndex, endIndex + 1);
+        
+        var paddingTop = startIndex * itemHeight;
+        var paddingBottom = Math.max(0, (totalItems - 1 - endIndex) * itemHeight);
+
+        return {
+            visibleItems: visibleItems,
+            paddingTop: paddingTop,
+            paddingBottom: paddingBottom,
+            startIndex: startIndex
+        };
+    };
+
     return Utils;
 });
\ No newline at end of file
diff --git a/dashboardv2/public/js/views/audit/AdminAuditTableLayoutView.js b/dashboardv2/public/js/views/audit/AdminAuditTableLayoutView.js
index cc56e03cfe..cc6438d14a 100644
--- a/dashboardv2/public/js/views/audit/AdminAuditTableLayoutView.js
+++ b/dashboardv2/public/js/views/audit/AdminAuditTableLayoutView.js
@@ -25,7 +25,7 @@ define(['require',
     'utils/CommonViewFunction',
     'utils/Enums',
     'moment'
-], function(require, Backbone, AdminAuditTableLayoutView_tmpl, VEntityList, Utils, UrlLinks, CommonViewFunction, Enums, moment) {
+], function (require, Backbone, AdminAuditTableLayoutView_tmpl, VEntityList, Utils, UrlLinks, CommonViewFunction, Enums, moment) {
     'use strict';
 
     var AdminAuditTableLayoutView = Backbone.Marionette.LayoutView.extend(
@@ -53,21 +53,23 @@ define(['require',
 
             },
             /** ui events hash */
-            events: function() {
+            events: function () {
                 var events = {},
                     that = this;
                 events["click " + this.ui.adminPurgedEntityClick] = "onClickAdminPurgedEntity";
                 events["click " + this.ui.adminAuditEntityDetails] = "showAdminAuditEntity";
-                events["click " + this.ui.attrFilter] = function(e) {
+                events["click [data-id='drawerSummaryTrigger']"] = "onSummaryCardClick";
+                events["click [data-id='copyRunIdMain']"] = "onCopyRunIdMain";
+                events["click " + this.ui.attrFilter] = function (e) {
                     this.ui.attrFilter.find('.fa-angle-right').toggleClass('fa-angle-down');
                     this.$('.attributeResultContainer').addClass("overlay");
                     this.$('.attribute-filter-container, .attr-filter-overlay').toggleClass('hide');
                     this.onClickAttrFilter();
                 };
-                events["click " + this.ui.attrClose] = function(e) {
+                events["click " + this.ui.attrClose] = function (e) {
                     that.closeAttributeModel();
                 };
-                events["click " + this.ui.attrApply] = function(e) {
+                events["click " + this.ui.attrApply] = function (e) {
                     that.okAttrFilterButton(e);
                 };
                 return events;
@@ -76,7 +78,7 @@ define(['require',
              * intialize a new AdminTableLayoutView Layout
              * @constructs
              */
-            initialize: function(options) {
+            initialize: function (options) {
                 _.extend(this, _.pick(options, 'searchTableFilters', 'entityDefCollection', 'enumDefCollection'));
                 this.entityCollection = new VEntityList();
                 this.limit = 25;
@@ -119,33 +121,33 @@ define(['require',
                 this.isFilters = null;
                 this.adminAuditEntityData = {};
             },
-            onRender: function() {
+            onRender: function () {
                 this.ui.adminRegion.hide();
                 this.getAdminCollection();
-                this.entityCollection.comparator = function(model) {
+                this.entityCollection.comparator = function (model) {
                     return -model.get('timestamp');
                 }
                 this.renderTableLayoutView();
             },
-            onShow: function() {
+            onShow: function () {
                 this.$('.fontLoader').show();
                 this.$('.tableOverlay').show();
             },
-            bindEvents: function() {},
-            closeAttributeModel: function() {
+            bindEvents: function () { },
+            closeAttributeModel: function () {
                 var that = this;
                 that.$('.attributeResultContainer').removeClass("overlay");
                 that.ui.attrFilter.find('.fa-angle-right').toggleClass('fa-angle-down');
                 that.$('.attribute-filter-container, .attr-filter-overlay').toggleClass('hide');
             },
-            onClickAttrFilter: function() {
+            onClickAttrFilter: function () {
                 var that = this;
                 this.ui.adminRegion.show();
-                require(['views/search/QueryBuilderView'], function(QueryBuilderView) {
+                require(['views/search/QueryBuilderView'], function (QueryBuilderView) {
                     that.RQueryBuilderAdmin.show(new QueryBuilderView({ adminAttrFilters: true, searchTableFilters: that.searchTableFilters, entityDefCollection: that.entityDefCollection, enumDefCollection: that.enumDefCollection }));
                 });
             },
-            okAttrFilterButton: function(options) {
+            okAttrFilterButton: function (options) {
                 var that = this,
                     isFilterValidate = true,
                     queryBuilderRef = that.RQueryBuilderAdmin.currentView.ui.builder;
@@ -164,18 +166,51 @@ define(['require',
                     that.getAdminCollection();
                 }
             },
-            getAdminCollection: function(option) {
+            getAdminCollection: function (option) {
                 var that = this,
                     auditFilters = CommonViewFunction.attributeFilter.generateAPIObj(that.ruleUrl);
+
+                if (that.isFilters && auditFilters && (auditFilters.criterion || auditFilters.attributeName)) {
+                    var hasRunId = false;
+                    var checkRunId = function (crit) {
+                        if (!crit) return;
+                        if (crit.attributeName === 'runId') hasRunId = true;
+                        if (crit.criterion && Array.isArray(crit.criterion)) {
+                            crit.criterion.forEach(checkRunId);
+                        }
+                    };
+                    checkRunId(auditFilters);
+
+                    if (hasRunId) {
+                        // Remove any existing auditRowKind to prevent duplicates/conflicts
+                        var removeAuditRowKind = function (node) {
+                            if (node.criterion && Array.isArray(node.criterion)) {
+                                node.criterion = node.criterion.filter(function (c) {
+                                    return c.attributeName !== 'auditRowKind';
+                                });
+                                node.criterion.forEach(removeAuditRowKind);
+                            }
+                        };
+                        removeAuditRowKind(auditFilters);
+                        auditFilters = {
+                            "condition": "AND",
+                            "criterion": [
+                                auditFilters,
+                                { "attributeName": "auditRowKind", "operator": "eq", "attributeValue": "SUMMARY" }
+                            ]
+                        };
+                    }
+                }
+
                 $.extend(that.entityCollection.queryParams, { auditFilters: that.isFilters ? auditFilters : null, limit: that.entityCollection.queryParams.limit || that.limit, offset: that.entityCollection.queryParams.offset || that.offset, sortBy: "startTime", sortOrder: "DESCENDING" });
                 var apiObj = {
                     sort: false,
                     data: _.pick(that.entityCollection.queryParams, 'auditFilters', 'limit', 'offset', 'sortBy', 'sortOrder'),
-                    success: function(dataOrCollection, response) {
+                    success: function (dataOrCollection, response) {
                         that.entityCollection.state.pageSize = that.entityCollection.queryParams.limit || 25;
                         that.entityCollection.fullCollection.reset(dataOrCollection, option);
                     },
-                    complete: function() {
+                    complete: function () {
                         that.$('.fontLoader').hide();
                         that.$('.tableOverlay').hide();
                         that.$('.auditTable').show();
@@ -184,20 +219,20 @@ define(['require',
                 }
                 this.entityCollection.getAdminData(apiObj);
             },
-            renderTableLayoutView: function() {
+            renderTableLayoutView: function () {
                 var that = this;
                 this.ui.showDefault.hide();
-                require(['utils/TableLayout'], function(TableLayout) {
+                require(['utils/TableLayout'], function (TableLayout) {
                     var cols = new Backgrid.Columns(that.getAuditTableColumns());
                     that.RAuditTableLayoutView.show(new TableLayout(_.extend({}, that.commonTableOptions, {
                         columns: cols
                     })));
                 });
             },
-            createTableWithValues: function(tableDetails, isAdminAudit) {
+            createTableWithValues: function (tableDetails, isAdminAudit) {
                 var attrTable = CommonViewFunction.propertyTable({
                     scope: this,
-                    getValue: function(val, key) {
+                    getValue: function (val, key) {
                         if (key && key.toLowerCase().indexOf("time") > 0) {
                             return Utils.formatDate({ date: val });
                         } else {
@@ -209,7 +244,7 @@ define(['require',
                 });
                 return attrTable;
             },
-            getAuditTableColumns: function() {
+            getAuditTableColumns: function () {
                 var that = this;
                 return this.entityCollection.constructor.getTableCols({
                     result: {
@@ -222,14 +257,14 @@ define(['require',
                         accordion: false,
                         alwaysVisible: true,
                         renderable: true,
-                        isExpandVisible: function(el, model) {
+                        isExpandVisible: function (el, model) {
                             if (Enums.serverAudits[model.get('operation')]) {
                                 return false;
                             } else {
                                 return true;
                             }
                         },
-                        expand: function(el, model) {
+                        expand: function (el, model) {
                             var operation = model.get('operation'),
                                 results = model.get('result') || null,
                                 adminText = 'No records found',
@@ -242,17 +277,35 @@ define(['require',
                                     adminTypDetails: adminTypDetails
                                 };
                             el.attr('colspan', '8');
+                            var $container = $('<div>').html(adminText);
+                            $(el).append($container);
+
                             if (results) {
-                                var adminValues = null;
                                 if (operation == "PURGE" || operation == "AUTO_PURGE") {
-                                    adminText = that.displayPurgeAndImportAudits(auditData);
+                                    $container.html('<div class="purge-spinner-container"><i class="fa fa-spinner fa-spin fa-2x"></i></div>');
+                                    var summaryGuid = model.get('guid');
+                                    $.ajax({
+                                        url: UrlLinks.baseUrl + '/admin/audit/' + summaryGuid + '/summary',
+                                        type: 'GET',
+                                        success: function (response) {
+                                            if (response) {
+                                                auditData.originalResults = auditData.results;
+                                                auditData.results = response;
+                                            }
+                                            $container.html(that.displayPurgeAndImportAudits(auditData));
+                                        },
+                                        error: function () {
+                                            $container.html(that.displayPurgeAndImportAudits(auditData));
+                                        }
+                                    });
                                 } else if (operation == "EXPORT" || operation == "IMPORT") {
                                     adminText = that.displayExportAudits(auditData);
+                                    $container.html(adminText);
                                 } else {
                                     adminText = that.displayCreateUpdateAudits(auditData);
+                                    $container.html(adminText);
                                 }
                             }
-                            $(el).append($('<div>').html(adminText));
                         }
                     },
                     userName: {
@@ -279,7 +332,7 @@ define(['require',
                         renderable: true,
                         editable: false,
                         formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
-                            fromRaw: function(rawValue, model) {
+                            fromRaw: function (rawValue, model) {
                                 if (Enums.serverAudits[model.get('operation')]) {
                                     return "N/A"
                                 } else {
@@ -294,7 +347,7 @@ define(['require',
                         renderable: true,
                         editable: false,
                         formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
-                            fromRaw: function(rawValue, model) {
+                            fromRaw: function (rawValue, model) {
                                 return Utils.formatDate({ date: rawValue });
                             }
                         })
@@ -305,7 +358,7 @@ define(['require',
                         renderable: true,
                         editable: false,
                         formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
-                            fromRaw: function(rawValue, model) {
+                            fromRaw: function (rawValue, model) {
                                 return Utils.formatDate({ date: rawValue });
                             }
                         })
@@ -317,7 +370,7 @@ define(['require',
                         editable: false,
                         sortable: false,
                         formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
-                            fromRaw: function(rawValue, model) {
+                            fromRaw: function (rawValue, model) {
                                 var startTime = model.get('startTime') ? parseInt(model.get('startTime')) : null,
                                     endTime = model.get('endTime') ? parseInt(model.get('endTime')) : null;
                                 if (_.isNumber(startTime) && _.isNumber(endTime)) {
@@ -331,11 +384,11 @@ define(['require',
                     }
                 }, this.entityCollection);
             },
-            defaultPagination: function() {
+            defaultPagination: function () {
                 $.extend(this.entityCollection.queryParams, { limit: this.limit, offset: this.offset });
                 this.renderTableLayoutView();
             },
-            showAdminAuditEntity: function(e) {
+            showAdminAuditEntity: function (e) {
                 var typeDefObj = this.adminAuditEntityData[e.target.dataset.auditentityid],
                     typeDetails = this.createTableWithValues(typeDefObj, true),
                     view = '<table class="table admin-audit-details bold-key" ><tbody >' + typeDetails + '</tbody></table>',
@@ -349,25 +402,97 @@ define(['require',
                     };
                 this.showModal(modalData);
             },
-            displayPurgeAndImportAudits: function(obj) {
+            displayPurgeAndImportAudits: function (obj) {
+                var adminTypDetails = Enums.category[obj.operation];
+
+                // If it's a new JSON string (from new API changes), parse it.
+                var isJson = false;
+                var summaryData = {};
+                try {
+                    summaryData = typeof obj.results === 'string' ? JSON.parse(obj.results) : obj.results;
+                    if (summaryData && typeof summaryData === 'object' && !Array.isArray(summaryData)) {
+                        isJson = true;
+                    }
+                } catch (e) {
+                    isJson = false;
+                }
+
+                if (isJson) {
+                    // It's the new Summary format
+                    var runId = summaryData.runId || obj.model.get('runId') || '';
+                    var paramsArr = obj.model.get('params') ? obj.model.get('params').split(',') : [];
+
+                    var reqCount = summaryData.requestedCount !== undefined ? summaryData.requestedCount : paramsArr.length;
+                    var purgedCount = summaryData.purgedCount !== undefined ? summaryData.purgedCount : 0;
+                    var purgedDependenciesCount = summaryData.purgedDependenciesCount || 0;
+                    var totalPurgedCount = purgedCount + purgedDependenciesCount;
+                    var failedCount = summaryData.failedCount || 0;
+                    var failedDependenciesCount = summaryData.failedDependenciesCount || 0;
+                    var totalFailedCount = failedCount + failedDependenciesCount;
+                    var skippedCount = summaryData.skippedCount || 0;
+
+                    var html = '<div class="row"><div class="attr-details">';
+
+                    html += '<div class="purge-summary-wrapper">';
+                    if (runId) {
+                        html += '<div class="purge-run-id-row"><strong>Run Id:</strong> <span class="runid-text" data-id="runIdValue">' + _.escape(runId) + '</span> <i class="fa fa-copy purge-run-id-copy" data-id="copyRunIdMain" title="Copy to clipboard"></i></div>';
+                    }
+
+                    html += '<div class="purge-summary-container">';
+
+                    // Requested
+                    html += '<div class="purge-summary-card card-blue clickable" data-id="drawerSummaryTrigger" data-type="requested" data-runid="' + _.escape(runId) + '" data-guid="' + _.escape(obj.model.get('guid')) + '" data-params="' + _.escape(obj.model.get('params')) + '">';
+                    html += '<div class="card-label">REQUESTED</div><div class="card-value">' + reqCount + '</div></div>';
+
+                    // Total Purged
+                    var rawResults = obj.originalResults ? obj.originalResults : (typeof obj.results === 'string' ? obj.results : JSON.stringify(obj.results));
+                    html += '<div class="purge-summary-card card-green ' + (totalPurgedCount > 0 ? 'clickable' : '') + '" ' + (totalPurgedCount > 0 ? 'data-id="drawerSummaryTrigger" data-type="purged" data-runid="' + _.escape(runId) + '" data-guid="' + _.escape(obj.model.get('guid')) + '" data-results="' + _.escape(rawResults) + '"' : '') + '>';
+                    html += '<div class="card-label">PURGED</div><div class="card-value">' + totalPurgedCount + '</div></div>';
+
+                    // Failed
+                    var failedTitle = (totalFailedCount > 0 || summaryData.executionFailed) ? 'Some entities failed to purge. Please check purgefailure.log for details.' : 'No failed entities during this purge operation.';
+                    html += '<div class="purge-summary-card card-red ' + (totalFailedCount > 0 ? 'has-count' : '') + '" title="' + failedTitle + '">';
+                    html += '<div class="card-label">FAILED</div><div class="card-value">' + totalFailedCount + '</div></div>';
+
+                    // Skipped
+                    var skippedTitle = (skippedCount > 0 || summaryData.executionFailed) ? 'Some entities were skipped during purge. Please check purgefailure.log for details.' : 'No skipped entities during this purge operation.';
+                    html += '<div class="purge-summary-card card-amber ' + (skippedCount > 0 ? 'has-count' : '') + '" title="' + skippedTitle + '">';
+                    html += '<div class="card-label">SKIPPED</div><div class="card-value">' + skippedCount + '</div></div>';
+
+                    html += '</div></div></div></div>';
+                    return html;
+                }
+
+                // Legacy render format
                 var adminValues = '<ul class="col-sm-6">',
-                    guids = null,
-                    adminTypDetails = Enums.category[obj.operation];
+                    guids = [];
                 if (obj.operation == "PURGE" || obj.operation == "AUTO_PURGE") {
                     guids = obj.results ? obj.results.replace('[', '').replace(']', '').split(',') : guids;
+
+                    var legacyPurgedCount = guids.length;
+                    if (legacyPurgedCount === 1 && guids[0] === "") {
+                        legacyPurgedCount = 0;
+                    }
+
+                    var html = '<div class="row"><div class="attr-details">';
+                    html += '<div class="purge-summary-container">';
+                    html += '<div class="purge-summary-card card-legacy card-green ' + (legacyPurgedCount > 0 ? 'clickable' : '') + '" ' + (legacyPurgedCount > 0 ? 'data-id="drawerSummaryTrigger" data-type="legacy-purged" data-runid="" data-guid="' + _.escape(obj.model.get('guid')) + '" data-results="' + _.escape(obj.results) + '"' : '') + '>';
+                    html += '<div class="card-label">PURGED</div><div class="card-value">' + legacyPurgedCount + '</div></div>';
+                    html += '</div></div></div>';
+                    return html;
                 } else {
                     guids = obj.model.get('params') ? obj.model.get('params').split(',') : guids;
                 }
-                _.each(guids, function(adminGuid, index) {
+                _.each(guids, function (adminGuid, index) {
                     if (index % 5 == 0 && index != 0) {
                         adminValues += '</ul><ul class="col-sm-6">';
                     }
                     adminValues += '<li class="blue-link" data-id="adminPurgedEntity" data-operation=' + obj.operation + '>' + adminGuid.trim() + '</li>';
                 })
                 adminValues += '</ul>';
-                return '<div class="row"><div class="attr-details"><h4 style="word-break: break-word;">' + adminTypDetails + '</h4>' + adminValues + '</div></div>';
+                return '<div class="row"><div class="attr-details">' + adminValues + '</div></div>';
             },
-            displayExportAudits: function(obj) {
+            displayExportAudits: function (obj) {
                 var adminValues = "",
                     adminTypDetails = (obj.operation === 'IMPORT') ? Enums.category[obj.operation] : Enums.category[obj.operation] + " And Options",
                     resultData = obj.results ? JSON.parse(obj.results) : null,
@@ -380,15 +505,15 @@ define(['require',
                     adminValues += this.showImportExportTable(_.extend(paramsData, { "paramsCount": obj.model.get('paramsCount') }));
                 }
                 adminValues = adminValues ? adminValues : obj.adminText;
-                return '<div class="row"><div class="attr-details"><h4 style="word-break: break-word;">' + adminTypDetails + '</h4>' + adminValues + '</div></div>';
+                return '<div class="row"><div class="attr-details"><h4 class="audit-type-details-title">' + adminTypDetails + '</h4>' + adminValues + '</div></div>';
             },
-            showImportExportTable: function(obj, operations) {
+            showImportExportTable: function (obj, operations) {
                 var that = this,
                     typeDetails = "",
                     view = '<ul class="col-sm-5 import-export"><table class="table admin-audit-details bold-key" ><tbody >';
                 if (operations && operations === "IMPORT") {
                     var importKeys = Object.keys(obj);
-                    _.each(importKeys, function(key, index) {
+                    _.each(importKeys, function (key, index) {
                         var newObj = {};
                         newObj[key] = obj[key];
                         if (index % 5 === 0 && index != 0) {
@@ -401,17 +526,17 @@ define(['require',
                 }
                 return view += '</tbody></table></ul>';;
             },
-            displayCreateUpdateAudits: function(obj) {
+            displayCreateUpdateAudits: function (obj) {
                 var that = this,
                     resultData = JSON.parse(obj.results),
                     typeName = obj.model ? obj.model.get('params').split(',') : null,
                     typeContainer = '';
-                _.each(typeName, function(name) {
+                _.each(typeName, function (name) {
                     var typeData = resultData[name],
                         adminValues = (typeName.length == 1) ? '<ul class="col-sm-4">' : '<ul>',
                         adminTypDetails = Enums.category[name] + " " + Enums.auditAction[obj.operation];
-                    typeContainer += '<div class="attr-type-container"><h4 style="word-break: break-word;">' + adminTypDetails + '</h4>';
-                    _.each(typeData, function(typeDefObj, index) {
+                    typeContainer += '<div class="attr-type-container"><h4 class="attr-type-header">' + adminTypDetails + '</h4>';
+                    _.each(typeData, function (typeDefObj, index) {
                         if (index % 5 == 0 && index != 0 && typeName.length == 1) {
                             adminValues += '</ul><ul class="col-sm-4">';
                         }
@@ -425,17 +550,17 @@ define(['require',
                 var typeClass = (typeName.length == 1) ? null : "admin-audit-details";
                 return '<div class="row"><div class="attr-details ' + typeClass + '">' + typeContainer + '</div></div>';
             },
-            onClickAdminPurgedEntity: function(e) {
+            onClickAdminPurgedEntity: function (e) {
                 var that = this;
-                require(['views/audit/AuditTableLayoutView'], function(AuditTableLayoutView) {
+                require(['views/audit/AuditTableLayoutView'], function (AuditTableLayoutView) {
                     const titles = {
                         PURGE: "Purged Entity Details",
                         AUTO_PURGE: "Auto Purge Entity Details"
                     };
                     var obj = {
-                            guid: $(e.target).text(),
-                            titleText: (titles[e.target.dataset.operation] || "Import Details") + ": "
-                        },
+                        guid: $(e.target).text(),
+                        titleText: (titles[e.target.dataset.operation] || "Import Details") + ": "
+                    },
                         modalData = {
                             title: obj.titleText + obj.guid,
                             content: new AuditTableLayoutView(obj),
@@ -446,17 +571,138 @@ define(['require',
                     that.showModal(modalData);
                 });
             },
-            showModal: function(modalObj, title) {
+            onCopyRunIdMain: function (e) {
+                var $temp = $("<input>");
+                $("body").append($temp);
+                var runIdText = $(e.currentTarget).siblings('[data-id="runIdValue"]').text();
+                $temp.val(runIdText).select();
+                document.execCommand("copy");
+                $temp.remove();
+                Utils.notifySuccess({
+                    content: "Run Id copied to clipboard"
+                });
+            },
+            onSummaryCardClick: function (e) {
+                var that = this;
+                var $target = $(e.currentTarget);
+                var type = $target.data('type');
+                var runId = $target.data('runid');
+                var guid = $target.data('guid');
+                var params = $target.data('params');
+                var totalCount = parseInt($target.find('.card-value').text(), 10) || 0;
+
+                require(['views/audit/DrawerView'], function (DrawerView) {
+                    if (type === 'requested') {
+                        var items = [];
+                        if (params) {
+                            if (Array.isArray(params)) {
+                                items = params.map(function (item) {
+                                    return typeof item === "string" ? item : (item.guid || String(item));
+                                });
+                            } else {
+                                try {
+                                    var parsedParams = JSON.parse(params);
+                                    if (Array.isArray(parsedParams)) {
+                                        items = parsedParams.map(function (item) {
+                                            return typeof item === "string" ? item : (item.guid || String(item));
+                                        });
+                                    } else if (typeof params === "string") {
+                                        items = params.replace(/^\[|\]$/g, "").split(",").map(function (s) { return s.trim(); }).filter(Boolean);
+                                    }
+                                } catch (e) {
+                                    if (typeof params === "string") {
+                                        items = params.replace(/^\[|\]$/g, "").split(",").map(function (s) { return s.trim(); }).filter(Boolean);
+                                    }
+                                }
+                            }
+                        }
+                        var drawerView = new DrawerView({
+                            title: 'Requested Entities',
+                            items: items,
+                            totalCount: items.length,
+                            runId: runId,
+                            actionType: 'requested',
+                            onItemClickCb: function (clickedGuid) {
+                                require(['views/audit/AuditTableLayoutView'], function (AuditTableLayoutView) {
+                                    var obj = {
+                                        guid: clickedGuid,
+                                        titleText: "Entity Audit Details: "
+                                    };
+                                    var modalData = {
+                                        title: obj.titleText + obj.guid,
+                                        content: new AuditTableLayoutView(obj),
+                                        mainClass: "modal-full-screen",
+                                        okCloses: true,
+                                        showFooter: false,
+                                    };
+                                    that.showModal(modalData);
+                                });
+                            }
+                        });
+                        drawerView.render();
+                    } else if (type === 'legacy-purged' || type === 'purged') {
+                        var resultsData = $target.data('results');
+                        var items = [];
+                        if (resultsData) {
+                            if (Array.isArray(resultsData)) {
+                                items = resultsData.map(function (item) {
+                                    return typeof item === "string" ? item : (item.guid || String(item));
+                                });
+                            } else {
+                                try {
+                                    var parsed = JSON.parse(resultsData);
+                                    if (Array.isArray(parsed)) {
+                                        items = parsed.map(function (item) {
+                                            return typeof item === "string" ? item : (item.guid || String(item));
+                                        });
+                                    } else if (typeof resultsData === "string") {
+                                        items = resultsData.replace(/^\[|\]$/g, "").split(",").map(function (s) { return s.trim(); }).filter(Boolean);
+                                    }
+                                } catch (e) {
+                                    if (typeof resultsData === "string") {
+                                        items = resultsData.replace(/^\[|\]$/g, "").split(",").map(function (s) { return s.trim(); }).filter(Boolean);
+                                    }
+                                }
+                            }
+                        }
+                        var drawerView = new DrawerView({
+                            title: 'Purged Entities',
+                            items: items,
+                            totalCount: items.length,
+                            runId: runId,
+                            actionType: 'purged',
+                            onItemClickCb: function (clickedGuid) {
+                                require(['views/audit/AuditTableLayoutView'], function (AuditTableLayoutView) {
+                                    var obj = {
+                                        guid: clickedGuid,
+                                        titleText: "Purged Entity Details: "
+                                    };
+                                    var modalData = {
+                                        title: obj.titleText + obj.guid,
+                                        content: new AuditTableLayoutView(obj),
+                                        mainClass: "modal-full-screen",
+                                        okCloses: true,
+                                        showFooter: false,
+                                    };
+                                    that.showModal(modalData);
+                                });
+                            }
+                        });
+                        drawerView.render();
+                    }
+                });
+            },
+            showModal: function (modalObj, title) {
                 var that = this;
                 require([
                     'modules/Modal'
-                ], function(Modal) {
+                ], function (Modal) {
                     var modal = new Modal(modalObj).open();
-                    modal.on('closeModal', function() {
+                    modal.on('closeModal', function () {
                         $('.modal').css({ 'padding-right': '0px !important' });
                         modal.trigger('cancel');
                     });
-                    modal.$el.on('click', 'td a', function() {
+                    modal.$el.on('click', 'td a', function () {
                         modal.trigger('cancel');
                     });
                 });
diff --git a/dashboardv2/public/js/views/audit/DrawerView.js b/dashboardv2/public/js/views/audit/DrawerView.js
new file mode 100644
index 0000000000..f0a2cc985d
--- /dev/null
+++ b/dashboardv2/public/js/views/audit/DrawerView.js
@@ -0,0 +1,298 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+define(['require',
+    'backbone',
+    'hbs!tmpl/audit/DrawerView_tmpl',
+    'utils/Utils'
+], function (require, Backbone, DrawerView_tmpl, Utils) {
+    'use strict';
+
+    var DrawerView = Backbone.Marionette.LayoutView.extend({
+        _viewName: 'DrawerView',
+        template: DrawerView_tmpl,
+
+        ui: {
+            overlay: ".drawer-overlay",
+            panel: ".drawer-panel",
+            closeBtn: "[data-id='closeDrawer']",
+            searchInput: "[data-id='searchInput']",
+            drawerList: "[data-id='drawerList']",
+            drawerItemsList: "[data-id='drawerList']",
+            drawerScrollRegion: "[data-id='drawerScrollRegion']",
+            drawerEmpty: "[data-id='drawerEmpty']",
+            limitInput: "[data-id='limitInput']",
+            showingText: "[data-id='showingText']",
+            copyRunId: "[data-id='copyRunId']",
+            drawerLoading: "[data-id='drawerLoading']",
+            drawerObserver: "[data-id='drawerObserver']",
+            btnFirstPage: "[data-id='btnFirstPage']",
+            btnPrevPage: "[data-id='btnPrevPage']",
+            btnCurrentPage: "[data-id='btnCurrentPage']",
+            btnNextPage: "[data-id='btnNextPage']",
+            btnLastPage: "[data-id='btnLastPage']"
+        },
+
+        events: function () {
+            var events = {};
+            events["click " + this.ui.closeBtn] = "closeDrawer";
+            events["click " + this.ui.overlay] = "closeDrawer";
+            events["keyup " + this.ui.searchInput] = "onSearch";
+            events["click .blue-link"] = "onItemClick";
+            events["click " + this.ui.copyRunId] = "onCopyRunId";
+            events["click " + this.ui.btnFirstPage] = "goToFirstPage";
+            events["click " + this.ui.btnPrevPage] = "goToPrevPage";
+            events["click " + this.ui.btnNextPage] = "goToNextPage";
+            events["click " + this.ui.btnLastPage] = "goToLastPage";
+            events["keyup " + this.ui.limitInput] = "onLimitInput";
+            return events;
+        },
+
+        initialize: function (options) {
+            _.extend(this, _.pick(options, 'title', 'items', 'fetchData', 'onItemClickCb', 'actionType', 'runId', 'totalCount'));
+            this.drawerPageSize = 25;
+            this.limit = 25; // fallback
+            this.searchText = "";
+            this.displayItems = [];
+            this.offset = 0;
+            this.page = 1;
+            this.hasMore = true;
+            this.isLoading = false;
+            this.scrollTop = 0;
+            this.totalFilteredCount = 0;
+        },
+
+        onRender: function () {
+            var that = this;
+            $('body').append(this.$el);
+
+            setTimeout(function () {
+                that.ui.overlay.addClass('open');
+                that.ui.panel.addClass('open');
+                $('body').addClass('drawer-open-lock');
+            }, 10);
+
+            // Scroll events don't bubble, so we must bind directly to the UI element
+            this.ui.drawerScrollRegion.on('scroll', function () {
+                var el = this;
+                that.scrollTop = el.scrollTop;
+                that.renderList(); // re-render the visible chunk
+            });
+
+            this.loadData();
+        },
+
+        onBeforeDestroy: function () {
+            if (this.ui && this.ui.drawerScrollRegion) {
+                this.ui.drawerScrollRegion.off('scroll');
+            }
+        },
+
+        loadData: function () {
+            this.page = 1;
+            this.displayItems = [];
+
+            if (this.fetchData) {
+                // If there's an API, load it all or handle server pagination. 
+                // For this UI port, we assume we load what's needed.
+                var that = this;
+                this.isLoading = true;
+                this.ui.drawerLoading.show();
+                this.fetchData(10000, 0, function (data) {
+                    that.isLoading = false;
+                    that.ui.drawerLoading.hide();
+                    if (data && data.length > 0) {
+                        that.items = data;
+                    }
+                    that.renderList();
+                });
+            } else {
+                this.renderList();
+            }
+        },
+
+        goToFirstPage: function () {
+            this.page = 1;
+            this.scrollTop = 0;
+            this.renderList();
+        },
+
+        goToPrevPage: function () {
+            if (this.page > 1) {
+                this.page -= 1;
+                this.scrollTop = 0;
+                this.renderList();
+            }
+        },
+
+        goToNextPage: function () {
+            var maxPage = Math.ceil(this.totalFilteredCount / this.drawerPageSize) || 1;
+            if (this.page < maxPage) {
+                this.page += 1;
+                this.scrollTop = 0;
+                this.renderList();
+            }
+        },
+
+        goToLastPage: function () {
+            var maxPage = Math.ceil(this.totalFilteredCount / this.drawerPageSize) || 1;
+            this.page = maxPage;
+            this.scrollTop = 0;
+            this.renderList();
+        },
+
+        onLimitInput: function (e) {
+            if (e.keyCode === 13) {
+                var parsed = parseInt($(e.currentTarget).val(), 10);
+                if (Number.isFinite(parsed) && parsed > 0) {
+                    this.drawerPageSize = parsed;
+                    this.page = 1;
+                    this.scrollTop = 0;
+                    this.renderList();
+                }
+            }
+        },
+
+        renderList: function () {
+            var fullList = this.items || [];
+            if (this.searchText) {
+                var lowerSearch = this.searchText.toLowerCase();
+                fullList = fullList.filter(function (item) {
+                    var nameStr = (typeof item === 'object' && item.attributes && item.attributes.name) ? item.attributes.name : "";
+                    var guidStr = (typeof item === 'object' && item.guid) ? item.guid : item;
+                    return (nameStr.toLowerCase().indexOf(lowerSearch) !== -1) || 
+                           (guidStr.toLowerCase().indexOf(lowerSearch) !== -1);
+                });
+            }
+
+            this.totalFilteredCount = fullList.length;
+
+            var startIndex = (this.page - 1) * this.drawerPageSize;
+            var endIndex = Math.min(startIndex + this.drawerPageSize, this.totalFilteredCount);
+            this.displayItems = fullList.slice(startIndex, endIndex);
+
+            var listHtml = "";
+
+            var virtualizedData = Utils.virtualizeList({
+                items: this.displayItems,
+                scrollTop: this.scrollTop,
+                itemHeight: 24,
+                overscan: 10,
+                visibleCount: 40
+            });
+            
+            if (virtualizedData.paddingTop > 0) {
+                listHtml += '<div style="height: ' + virtualizedData.paddingTop + 'px"></div>';
+            }
+
+            _.each(virtualizedData.visibleItems, function (item, index) {
+                var globalIndex = startIndex + virtualizedData.startIndex + index + 1;
+                var isObj = typeof item === 'object' && item !== null;
+                var guidStr = isObj ? item.guid : item;
+                var typeName = isObj && item.typeName ? '[' + _.escape(item.typeName) + '] ' : '';
+                var entityName = isObj && item.attributes && item.attributes.name ? _.escape(item.attributes.name) : _.escape(guidStr);
+                var displayName = isObj && item.attributes && item.attributes.name ? typeName + entityName : _.escape(guidStr);
+                
+                listHtml += '<li class="drawer-list-item" ><span class="item-index" >' + globalIndex + '.</span> <a class="blue-link" title="' + _.escape(guidStr) + '" data-guid="' + _.escape(guidStr) + '">' + displayName + '</a></li>';
+            });
+            
+            if (virtualizedData.paddingBottom > 0) {
+                listHtml += '<div style="height: ' + virtualizedData.paddingBottom + 'px"></div>';
+            }
+
+            this.ui.drawerItemsList.html(listHtml);
+            this.ui.drawerLoading.hide();
+
+            if (this.displayItems.length === 0) {
+                this.ui.drawerEmpty.html("No matching GUIDs found");
+                this.ui.drawerEmpty.show();
+            } else {
+                this.ui.drawerEmpty.hide();
+            }
+
+            // Update Pagination UI
+            var showingStart = Math.min(startIndex + 1, this.totalFilteredCount);
+            var showingEnd = endIndex;
+            this.ui.showingText.text(showingStart + "-" + showingEnd + " of " + this.totalFilteredCount);
+            this.ui.btnCurrentPage.text(this.page);
+            
+            var maxPage = Math.ceil(this.totalFilteredCount / this.drawerPageSize) || 1;
+            
+            if (this.page <= 1) {
+                this.ui.btnFirstPage.prop("disabled", true);
+                this.ui.btnPrevPage.prop("disabled", true);
+            } else {
+                this.ui.btnFirstPage.prop("disabled", false);
+                this.ui.btnPrevPage.prop("disabled", false);
+            }
+            
+            if (this.page >= maxPage) {
+                this.ui.btnNextPage.prop("disabled", true);
+                this.ui.btnLastPage.prop("disabled", true);
+            } else {
+                this.ui.btnNextPage.prop("disabled", false);
+                this.ui.btnLastPage.prop("disabled", false);
+            }
+        },
+
+        onSearch: function (e) {
+            this.searchText = $(e.currentTarget).val().trim();
+            this.scrollTop = 0;
+            this.loadData();
+        },
+
+        onItemClick: function (e) {
+            var guid = $(e.currentTarget).data('guid');
+            if (this.onItemClickCb) {
+                this.onItemClickCb(guid, this.actionType);
+            }
+        },
+
+        onCopyRunId: function () {
+            var $temp = $("<input>");
+            $("body").append($temp);
+            $temp.val(this.runId).select();
+            document.execCommand("copy");
+            $temp.remove();
+            Utils.notifySuccess({
+                content: "Run Id copied to clipboard"
+            });
+        },
+
+        closeDrawer: function () {
+            var that = this;
+            this.ui.overlay.removeClass('open');
+            this.ui.panel.removeClass('open');
+            $('body').removeClass('drawer-open-lock');
+
+            setTimeout(function () {
+                that.destroy();
+            }, 300);
+        },
+
+        templateHelpers: function () {
+            return {
+                title: this.title || 'Entities',
+                runId: this.runId,
+                fetchData: this.fetchData
+            };
+        }
+    });
+
+    return DrawerView;
+});
diff --git a/dashboardv2/public/js/views/search/QueryBuilderView.js b/dashboardv2/public/js/views/search/QueryBuilderView.js
index 0446f424ae..a5b0c1e1f9 100644
--- a/dashboardv2/public/js/views/search/QueryBuilderView.js
+++ b/dashboardv2/public/js/views/search/QueryBuilderView.js
@@ -397,6 +397,9 @@ define(['require',
                     }
                     if (auditEntryAttributeDefs) {
                         _.each(auditEntryAttributeDefs, function(attributes) {
+                            if (attributes.name === "auditRowKind") {
+                                return; // Skip backend-only field
+                            }
                             var returnObj = that.getObjDef(attributes, rules_widgets);
                             if (returnObj) {
                                 filters.push(returnObj);