diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/theme/createMuiTheme.ts b/openmetadata-ui-core-components/src/main/resources/ui/src/theme/createMuiTheme.ts index a5b967a5ea8..341fccac3bb 100644 --- a/openmetadata-ui-core-components/src/main/resources/ui/src/theme/createMuiTheme.ts +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/theme/createMuiTheme.ts @@ -11,17 +11,17 @@ * limitations under the License. */ -import type { Shadows } from '@mui/material/styles'; -import { createTheme } from '@mui/material/styles'; -import { buttonTheme } from './button-theme'; -import { dataDisplayTheme } from './data-display-theme'; -import { defaultColors } from '../colors/defaultColors'; -import { formTheme } from './form-theme'; -import { generateAllMuiPalettes } from '../colors/generateMuiPalettes'; -import { navigationTheme } from './navigation-theme'; -import { shadows } from './shadows'; -import type { CustomColors, ThemeColors } from '../types'; -import './mui-theme-types'; +import type { Shadows } from "@mui/material/styles"; +import { createTheme } from "@mui/material/styles"; +import { defaultColors } from "../colors/defaultColors"; +import { generateAllMuiPalettes } from "../colors/generateMuiPalettes"; +import type { CustomColors, ThemeColors } from "../types"; +import { buttonTheme } from "./button-theme"; +import { dataDisplayTheme } from "./data-display-theme"; +import { formTheme } from "./form-theme"; +import "./mui-theme-types"; +import { navigationTheme } from "./navigation-theme"; +import { shadows } from "./shadows"; /** * Creates dynamic MUI theme with user customizations or default colors @@ -127,88 +127,90 @@ export const createMuiTheme = ( allShades: themeColors, }, typography: { + fontSize: 14, + htmlFontSize: 14, fontFamily: 'var(--font-inter, "Inter"), -apple-system, "Segoe UI", Roboto, Arial, sans-serif', h1: { - fontSize: '3.75rem', + fontSize: "3.75rem", fontWeight: 600, - lineHeight: '4.5rem', - letterSpacing: '-1.2px', - color: 'var(--color-text-primary)', + lineHeight: "4.5rem", + letterSpacing: "-1.2px", + color: "var(--color-text-primary)", }, h2: { - fontSize: '3rem', + fontSize: "3rem", fontWeight: 600, - lineHeight: '3.75rem', - letterSpacing: '-0.96px', - color: 'var(--color-text-primary)', + lineHeight: "3.75rem", + letterSpacing: "-0.96px", + color: "var(--color-text-primary)", }, h3: { - fontSize: '2.25rem', + fontSize: "2.25rem", fontWeight: 600, - lineHeight: '2.75rem', - letterSpacing: '-0.72px', - color: 'var(--color-text-primary)', + lineHeight: "2.75rem", + letterSpacing: "-0.72px", + color: "var(--color-text-primary)", }, h4: { - fontSize: '1.875rem', + fontSize: "1.875rem", fontWeight: 600, - lineHeight: '2.375rem', - color: 'var(--color-text-primary)', + lineHeight: "2.375rem", + color: "var(--color-text-primary)", }, h5: { - fontSize: '1.5rem', + fontSize: "1.5rem", fontWeight: 600, - lineHeight: '2rem', - color: 'var(--color-text-primary)', + lineHeight: "2rem", + color: "var(--color-text-primary)", }, h6: { - fontSize: '1.25rem', + fontSize: "1.25rem", fontWeight: 600, - lineHeight: '1.875rem', - color: 'var(--color-text-primary)', + lineHeight: "1.875rem", + color: "var(--color-text-primary)", }, subtitle1: { - fontSize: '1.125rem', - lineHeight: '1.75rem', + fontSize: "1.125rem", + lineHeight: "1.75rem", fontWeight: 400, - color: 'var(--color-text-secondary)', + color: "var(--color-text-secondary)", }, subtitle2: { - fontSize: '1rem', - lineHeight: '1.5rem', + fontSize: "1rem", + lineHeight: "1.5rem", fontWeight: 500, - color: 'var(--color-text-secondary)', + color: "var(--color-text-secondary)", }, body1: { - fontSize: '1rem', - lineHeight: '1.5rem', + fontSize: "1rem", + lineHeight: "1.5rem", fontWeight: 400, - color: 'var(--color-text-tertiary)', + color: "var(--color-text-tertiary)", }, body2: { - fontSize: '0.875rem', - lineHeight: '1.25rem', + fontSize: "0.875rem", + lineHeight: "1.25rem", fontWeight: 400, - color: 'var(--color-text-tertiary)', + color: "var(--color-text-tertiary)", }, caption: { - fontSize: '0.75rem', - lineHeight: '1.125rem', + fontSize: "0.75rem", + lineHeight: "1.125rem", fontWeight: 400, - color: 'var(--color-text-quaternary)', + color: "var(--color-text-quaternary)", }, overline: { - fontSize: '0.75rem', - lineHeight: '1.125rem', + fontSize: "0.75rem", + lineHeight: "1.125rem", fontWeight: 600, - textTransform: 'uppercase' as const, - letterSpacing: '0.5px', - color: 'var(--color-text-quaternary)', + textTransform: "uppercase" as const, + letterSpacing: "0.5px", + color: "var(--color-text-quaternary)", }, button: { - fontSize: '0.875rem', - textTransform: 'none' as const, + fontSize: "0.875rem", + textTransform: "none" as const, fontWeight: 600, }, }, @@ -217,32 +219,32 @@ export const createMuiTheme = ( borderRadius: 8, }, shadows: [ - 'none', + "none", shadows.xs, shadows.sm, shadows.md, shadows.lg, shadows.xl, - shadows['2xl'], - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', + shadows["2xl"], + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", // Additional shadows for MUI's 25-shadow requirement - '0px 1px 3px rgba(10, 13, 18, 0.1), 0px 1px 2px -1px rgba(10, 13, 18, 0.1)', - '0px 4px 6px -1px rgba(10, 13, 18, 0.1), 0px 2px 4px -2px rgba(10, 13, 18, 0.06)', - '0px 12px 16px -4px rgba(10, 13, 18, 0.08), 0px 4px 6px -2px rgba(10, 13, 18, 0.03), 0px 2px 2px -1px rgba(10, 13, 18, 0.04)', - '0px 12px 16px -4px rgba(10, 13, 18, 0.08), 0px 4px 6px -2px rgba(10, 13, 18, 0.03), 0px 2px 2px -1px rgba(10, 13, 18, 0.04)', - '0px 20px 24px -4px rgba(10, 13, 18, 0.08), 0px 8px 8px -4px rgba(10, 13, 18, 0.03), 0px 3px 3px -1.5px rgba(10, 13, 18, 0.04)', - '0px 20px 24px -4px rgba(10, 13, 18, 0.08), 0px 8px 8px -4px rgba(10, 13, 18, 0.03), 0px 3px 3px -1.5px rgba(10, 13, 18, 0.04)', - '0px 24px 48px -12px rgba(10, 13, 18, 0.18), 0px 4px 4px -2px rgba(10, 13, 18, 0.04)', - '0px 24px 48px -12px rgba(10, 13, 18, 0.18), 0px 4px 4px -2px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', - '0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)', + "0px 1px 3px rgba(10, 13, 18, 0.1), 0px 1px 2px -1px rgba(10, 13, 18, 0.1)", + "0px 4px 6px -1px rgba(10, 13, 18, 0.1), 0px 2px 4px -2px rgba(10, 13, 18, 0.06)", + "0px 12px 16px -4px rgba(10, 13, 18, 0.08), 0px 4px 6px -2px rgba(10, 13, 18, 0.03), 0px 2px 2px -1px rgba(10, 13, 18, 0.04)", + "0px 12px 16px -4px rgba(10, 13, 18, 0.08), 0px 4px 6px -2px rgba(10, 13, 18, 0.03), 0px 2px 2px -1px rgba(10, 13, 18, 0.04)", + "0px 20px 24px -4px rgba(10, 13, 18, 0.08), 0px 8px 8px -4px rgba(10, 13, 18, 0.03), 0px 3px 3px -1.5px rgba(10, 13, 18, 0.04)", + "0px 20px 24px -4px rgba(10, 13, 18, 0.08), 0px 8px 8px -4px rgba(10, 13, 18, 0.03), 0px 3px 3px -1.5px rgba(10, 13, 18, 0.04)", + "0px 24px 48px -12px rgba(10, 13, 18, 0.18), 0px 4px 4px -2px rgba(10, 13, 18, 0.04)", + "0px 24px 48px -12px rgba(10, 13, 18, 0.18), 0px 4px 4px -2px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", + "0px 32px 64px -12px rgba(10, 13, 18, 0.14), 0px 5px 5px -2.5px rgba(10, 13, 18, 0.04)", ] as Shadows, components: componentThemes, }); diff --git a/openmetadata-ui/src/main/resources/ui/src/App.tsx b/openmetadata-ui/src/main/resources/ui/src/App.tsx index e51c98acb3c..aa8b3d50f8e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/App.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/App.tsx @@ -35,6 +35,7 @@ import { } from './rest/settingConfigAPI'; import { getBasePath } from './utils/HistoryUtils'; +import GlobalStyles from '@mui/material/GlobalStyles'; import { ThemeProvider } from '@mui/material/styles'; import { createMuiTheme, @@ -106,6 +107,7 @@ const App: FC = () => { + = { + New: { bg: '#E1D3FF', color: '#7147E8', border: '#7147E8' }, + Ack: { bg: '#EBF6FE', color: '#3DA2F3', border: '#3DA2F3' }, + Assigned: { bg: '#FFF6E1', color: '#D99601', border: '#D99601' }, + Resolved: { bg: '#E8F5E9', color: '#4CAF50', border: '#81C784' }, +}; + +const InlineTestCaseIncidentStatus = ({ + data, + hasEditPermission, + onSubmit, +}: InlineTestCaseIncidentStatusProps) => { + const { t } = useTranslation(); + const { currentUser } = useApplicationStore(); + const chipRef = React.useRef(null); + const [anchorEl, setAnchorEl] = useState(null); + const [showStatusMenu, setShowStatusMenu] = useState(false); + const [showAssigneePopover, setShowAssigneePopover] = useState(false); + const [showResolvedPopover, setShowResolvedPopover] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [userOptions, setUserOptions] = useState([]); + const [selectedAssignee, setSelectedAssignee] = + useState( + data?.testCaseResolutionStatusDetails?.assignee ?? null + ); + const [selectedReason, setSelectedReason] = + useState(null); + const [comment, setComment] = useState(''); + + const statusType = data.testCaseResolutionStatusType; + + const initialOptions = useMemo(() => { + const assignee = data?.testCaseResolutionStatusDetails?.assignee; + if (assignee) { + return [ + { + label: getEntityName(assignee), + value: assignee.id || '', + type: assignee.type, + name: assignee.name, + displayName: assignee.displayName, + }, + ]; + } + + return []; + }, [data?.testCaseResolutionStatusDetails?.assignee]); + + const searchUsers = useCallback( + async (query: string) => { + try { + const res = await getUserAndTeamSearch(query, true); + const hits = res.data.hits.hits; + const suggestOptions: Option[] = hits.map((hit) => ({ + label: getEntityName(hit._source), + value: hit._id ?? '', + type: hit._source.entityType, + name: hit._source.name, + displayName: hit._source.displayName, + })); + + // If there's an assigned user and it's not in the results, add it at the top + if (initialOptions.length > 0) { + const assigneeId = initialOptions[0].value; + const isAssigneeInResults = suggestOptions.some( + (opt) => opt.value === assigneeId + ); + if (!isAssigneeInResults) { + setUserOptions([initialOptions[0], ...suggestOptions]); + } else { + // Move assignee to top + const filteredOptions = suggestOptions.filter( + (opt) => opt.value !== assigneeId + ); + setUserOptions([initialOptions[0], ...filteredOptions]); + } + } else { + setUserOptions(suggestOptions); + } + } catch (err) { + showErrorToast(err as AxiosError); + } + }, + [initialOptions] + ); + + const debouncedSearch = useMemo( + () => debounce(searchUsers, 300), + [searchUsers] + ); + + const handleSearchUsers = useCallback( + (query: string) => { + if (isEmpty(query)) { + // When search is cleared, trigger search with empty query to get default results + searchUsers(''); + } else { + debouncedSearch(query); + } + }, + [debouncedSearch, searchUsers] + ); + + const submitStatusChange = useCallback( + async ( + status: TestCaseResolutionStatusTypes, + additionalData?: { + assignee?: EntityReference; + reason?: TestCaseFailureReasonType; + comment?: string; + } + ) => { + setIsLoading(true); + const updatedData: CreateTestCaseResolutionStatus = { + testCaseResolutionStatusType: status, + testCaseReference: data.testCaseReference?.fullyQualifiedName ?? '', + }; + + if ( + status === TestCaseResolutionStatusTypes.Assigned && + additionalData?.assignee + ) { + updatedData.testCaseResolutionStatusDetails = { + assignee: { + name: additionalData.assignee.name, + displayName: additionalData.assignee.displayName, + id: additionalData.assignee.id, + type: EntityType.USER, + }, + }; + } else if (status === TestCaseResolutionStatusTypes.Resolved) { + updatedData.testCaseResolutionStatusDetails = { + testCaseFailureReason: additionalData?.reason, + testCaseFailureComment: additionalData?.comment ?? '', + resolvedBy: currentUser + ? getEntityReferenceFromEntity(currentUser, EntityType.USER) + : undefined, + }; + } + + try { + const responseData = await postTestCaseIncidentStatus(updatedData); + onSubmit(responseData); + setShowAssigneePopover(false); + setShowResolvedPopover(false); + setSelectedAssignee(null); + setSelectedReason(null); + setComment(''); + } catch (error) { + showErrorToast(error as AxiosError); + } finally { + setIsLoading(false); + } + }, + [currentUser, data.testCaseReference?.fullyQualifiedName, onSubmit] + ); + + const handleStatusClick = (event: React.MouseEvent) => { + if (!hasEditPermission) { + return; + } + event.stopPropagation(); + event.preventDefault(); + + if (chipRef.current) { + setAnchorEl(chipRef.current); + + // Open directly to the current status detail screen + if (statusType === TestCaseResolutionStatusTypes.Assigned) { + // Load initial user list with empty search + searchUsers(''); + setShowAssigneePopover(true); + } else if (statusType === TestCaseResolutionStatusTypes.Resolved) { + // Pre-populate with existing values + setSelectedReason( + data?.testCaseResolutionStatusDetails?.testCaseFailureReason ?? null + ); + setComment( + data?.testCaseResolutionStatusDetails?.testCaseFailureComment ?? '' + ); + setShowResolvedPopover(true); + } else { + // For New/Ack, show the status menu + setShowStatusMenu(true); + } + } + }; + + const handleCloseStatusMenu = useCallback(() => { + setShowStatusMenu(false); + setAnchorEl(null); + }, []); + + const handleStatusChange = useCallback( + async (newStatus: TestCaseResolutionStatusTypes) => { + setShowStatusMenu(false); + + if (newStatus === TestCaseResolutionStatusTypes.Assigned) { + // Load initial user list with empty search + searchUsers(''); + setShowAssigneePopover(true); + } else if (newStatus === TestCaseResolutionStatusTypes.Resolved) { + setShowResolvedPopover(true); + } else { + setAnchorEl(null); + await submitStatusChange(newStatus); + } + }, + [searchUsers, submitStatusChange] + ); + + const handleBackToStatusMenu = useCallback(() => { + setShowAssigneePopover(false); + setShowResolvedPopover(false); + setSelectedAssignee( + data?.testCaseResolutionStatusDetails?.assignee ?? null + ); + setUserOptions([]); + setSelectedReason(null); + setComment(''); + setShowStatusMenu(true); + }, [data?.testCaseResolutionStatusDetails?.assignee]); + + const handleCloseAllPopovers = useCallback(() => { + setShowAssigneePopover(false); + setShowResolvedPopover(false); + setShowStatusMenu(false); + setAnchorEl(null); + setSelectedAssignee( + data?.testCaseResolutionStatusDetails?.assignee ?? null + ); + setUserOptions([]); + setSelectedReason(null); + setComment(''); + }, [data?.testCaseResolutionStatusDetails?.assignee]); + + const handleAssigneeSelect = (user: EntityReference) => { + setSelectedAssignee(user); + }; + + const handleAssigneeSubmit = () => { + if (selectedAssignee) { + submitStatusChange(TestCaseResolutionStatusTypes.Assigned, { + assignee: selectedAssignee, + }); + } + }; + + const handleResolvedSubmit = () => { + if (selectedReason && comment) { + submitStatusChange(TestCaseResolutionStatusTypes.Resolved, { + reason: selectedReason, + comment, + }); + } + }; + + const getInitials = (name: string) => { + return name + .split(' ') + .map((n) => n[0]) + .join('') + .toUpperCase() + .slice(0, 2); + }; + + const statusColor = STATUS_COLORS[statusType] || STATUS_COLORS.New; + + return ( + + + ) : ( + + ) + ) : undefined + } + disabled={!hasEditPermission} + label={statusType} + sx={{ + px: 1, + backgroundColor: statusColor.bg, + color: statusColor.color, + border: `1px solid ${statusColor.border}`, + borderRadius: '16px', + fontWeight: 500, + fontSize: '12px', + cursor: hasEditPermission ? 'pointer' : 'default', + '& .MuiChip-label': { + px: 1, + }, + '& .MuiChip-deleteIcon': { + color: statusColor.color, + fontSize: '16px', + margin: '0 4px 0 -4px', + }, + '&:hover': hasEditPermission + ? { + backgroundColor: statusColor.bg, + opacity: 0.8, + } + : {}, + }} + onClick={handleStatusClick} + onDelete={handleStatusClick} + /> + + + {Object.values(TestCaseResolutionStatusTypes).map((status) => ( + handleStatusChange(status)}> + {status} + + ))} + + + + + + + + + + {t('label.assigned')} + + + + + + + + + + + handleSearchUsers(e.target.value)} + /> + + + {userOptions.map((option) => { + const user: EntityReference = { + id: option.value, + name: option.name, + displayName: option.displayName, + type: option.type || EntityType.USER, + }; + + return ( + + handleAssigneeSelect(user)}> + + + {getInitials(option.displayName || option.name || 'U')} + + + + + + ); + })} + + + + + + + + + + + + {t('label.resolved')} + + + + + + + + + + + + {t('label.reason')} + + + {Object.values(TestCaseFailureReasonType).map((reason) => ( + setSelectedReason(reason)} + /> + ))} + + + + {t('label.comment')} + + setComment(e.target.value)} + /> + + + + ); +}; + +export default InlineTestCaseIncidentStatus; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseStatus/TestCaseIncidentManagerStatus.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseStatus/TestCaseIncidentManagerStatus.component.tsx index bcbcab2e43b..c7fb02de034 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseStatus/TestCaseIncidentManagerStatus.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/TestCaseStatus/TestCaseIncidentManagerStatus.component.tsx @@ -27,13 +27,16 @@ import AppBadge from '../../../common/Badge/Badge.component'; import { EditIconButton } from '../../../common/IconButtons/EditIconButton'; import { TestCaseStatusModal } from '../../TestCaseStatusModal/TestCaseStatusModal.component'; import '../incident-manager.style.less'; +import InlineTestCaseIncidentStatus from './InlineTestCaseIncidentStatus.component'; import { TestCaseStatusIncidentManagerProps } from './TestCaseIncidentManagerStatus.interface'; + const TestCaseIncidentManagerStatus = ({ data, onSubmit, hasPermission, newLook = false, headerName, + isInline = false, }: TestCaseStatusIncidentManagerProps) => { const [isEditStatus, setIsEditStatus] = useState(false); const { t } = useTranslation(); @@ -107,6 +110,16 @@ const TestCaseIncidentManagerStatus = ({ ); } + if (isInline) { + return ( + + ); + } + return ( <> = ({ ); }, }, + { + title: 'Failed/aborted Reason', + dataIndex: 'testCaseResult', + key: 'Reason', + width: 200, + render: (result: TestCaseResult) => { + return result?.result ? ( + + {result.result} + + ) : ( + '--' + ); + }, + }, + { + title: t('label.last-run'), + dataIndex: 'testCaseResult', + key: 'lastRun', + width: 150, + sorter: true, + render: (result: TestCaseResult) => { + return ; + }, + }, { title: t('label.name'), dataIndex: 'name', @@ -244,7 +278,7 @@ const DataQualityTab: React.FC = ({ title: t('label.column'), dataIndex: 'entityLink', key: 'column', - width: 150, + width: 120, render: (entityLink) => { const isColumn = entityLink.includes('::columns::'); if (isColumn) { @@ -256,7 +290,7 @@ const DataQualityTab: React.FC = ({ + style={{ maxWidth: 120 }}> {name} ); @@ -279,13 +313,41 @@ const DataQualityTab: React.FC = ({ sortDirections: ['ascend', 'descend'], }, { - title: t('label.last-run'), + title: t('label.incident'), dataIndex: 'testCaseResult', - key: 'lastRun', - width: 150, - sorter: true, - render: (result: TestCaseResult) => { - return ; + key: 'incident', + width: 120, + render: (_, record) => { + const testCaseResult = testCaseStatus.find( + (status) => + status.testCaseReference?.fullyQualifiedName === + record.fullyQualifiedName + ); + + if (isStatusLoading) { + return ; + } + + if (!testCaseResult) { + return '--'; + } + + // Check if user has permission to edit incident status + const testCasePermission = testCasePermissions.find( + (permission) => + permission.fullyQualifiedName === record.fullyQualifiedName + ); + const hasEditPermission = + isEditAllowed || testCasePermission?.EditAll; + + return ( + + ); }, }, { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/QualityTab/QualityTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/QualityTab/QualityTab.component.tsx index e0b11d63dde..47a945e946a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/QualityTab/QualityTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/QualityTab/QualityTab.component.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ import { Box, Grid, Stack, Tab, Tabs, useTheme } from '@mui/material'; -import { Col, Form, Row, Select, Space } from 'antd'; +import { Form, Select, Space } from 'antd'; import { isEmpty } from 'lodash'; import QueryString from 'qs'; import { useEffect, useMemo, useState } from 'react'; @@ -45,7 +45,6 @@ import { import { getPrioritizedEditPermission } from '../../../../../utils/PermissionsUtils'; import { getEntityDetailsPath } from '../../../../../utils/RouterUtils'; import ErrorPlaceHolder from '../../../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import NextPrevious from '../../../../common/NextPrevious/NextPrevious'; import { NextPreviousProps } from '../../../../common/NextPrevious/NextPrevious.interface'; import Searchbar from '../../../../common/SearchBarComponent/SearchBar.component'; import SummaryCardV1 from '../../../../common/SummaryCard/SummaryCardV1'; @@ -75,7 +74,6 @@ export const QualityTab = () => { paging, handlePageChange, handlePageSizeChange, - showPagination, } = testCasePaging; const { editTest } = useMemo(() => { @@ -282,6 +280,25 @@ export const QualityTab = () => { ] ); + const pagingData = useMemo(() => { + return { + isNumberBased: true, + currentPage, + isLoading: isTestsLoading, + pageSize, + paging, + pagingHandler: handleTestCasePageChange, + onShowSizeChange: handlePageSizeChange, + }; + }, [ + currentPage, + isTestsLoading, + pageSize, + paging, + handleTestCasePageChange, + handlePageSizeChange, + ]); + const handleTabChange = (_: React.SyntheticEvent, tab: string) => { navigate( { @@ -405,39 +422,23 @@ export const QualityTab = () => { {isTestCaseTab && ( - - - { - await fetchAllTests(...params); // Update current count when Create / Delete operation performed - params?.length && - (await getResourceLimit('dataQuality', true, true)); - }} - breadcrumbData={tableBreadcrumb} - fetchTestCases={handleSortTestCase} - isEditAllowed={editTest} - isLoading={isTestsLoading} - showTableColumn={false} - testCases={allTestCases} - onTestCaseResultUpdate={onTestCaseUpdate} - onTestUpdate={onTestCaseUpdate} - /> - - - {showPagination && ( - - )} - - + { + await fetchAllTests(...params); // Update current count when Create / Delete operation performed + params?.length && + (await getResourceLimit('dataQuality', true, true)); + }} + breadcrumbData={tableBreadcrumb} + fetchTestCases={handleSortTestCase} + isEditAllowed={editTest} + isLoading={isTestsLoading} + pagingData={pagingData} + showTableColumn={false} + testCases={allTestCases} + onTestCaseResultUpdate={onTestCaseUpdate} + onTestUpdate={onTestCaseUpdate} + /> )} {qualityTab === EntityTabs.PIPELINE && (