Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dashboard/src/api/apiUrlLinks/classificationUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* 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 { getBaseApiUrl } from "./commonApiUrl";

Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/DatePicker/CustomDatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const CustomDatepicker = (props: {
selected={selected}
onChange={onChange}
timeInputLabel=""
renderCustomHeader={(headerProps) => <CustomHeader {...headerProps} />}
renderCustomHeader={(headerProps: any) => <CustomHeader {...headerProps} />}
dateFormat="MM/dd/yyyy h:mm:ss aa"
{...rest}
/>
Expand Down
85 changes: 85 additions & 0 deletions dashboard/src/components/Masonry/MasonryCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 React, { useEffect, useRef, useState } from "react";
import "./masonry.css";

export type MasonryCardProps = {
title: string;
maxBodyHeight?: number; // max visible height for body scroll area
footer?: React.ReactNode;
className?: string;
style?: React.CSSProperties;
children: React.ReactNode; // card body content
};

/**
* A card that measures its height and sets grid-row-end to fill the CSS Grid tracks.
* The body section gets a max-height with internal scroll to keep card height bounded.
*/
const MasonryCard: React.FC<MasonryCardProps> = ({
title,
maxBodyHeight = 260,
footer,
className,
style,
children
}) => {
const cardRef = useRef<HTMLDivElement | null>(null);
const [rowSpan, setRowSpan] = useState<number>(1);

useEffect(() => {
const el = cardRef.current;
if (!el) return;

const grid = el.parentElement as HTMLElement | null;
const rowHeight = Number(grid?.dataset.rowHeight || 8);
const rowGap = Number(grid?.dataset.rowGap || 16);

const measure = () => {
const height = el.getBoundingClientRect().height;
const span = Math.max(1, Math.ceil((height + rowGap) / (rowHeight + rowGap)));
setRowSpan(span);
};

measure();
const ro = new ResizeObserver(measure);
ro.observe(el);
return () => ro.disconnect();
}, []);

return (
<div
ref={cardRef}
className={`masonry-card${className ? ` ${className}` : ""}`}
style={{ gridRowEnd: `span ${rowSpan}`, ...style }}
>
<div className="masonry-card__header">{title}</div>
<div className="masonry-card__body" style={{ maxHeight: maxBodyHeight }}>
{children}
</div>
{footer ? <div className="masonry-card__footer">{footer}</div> : null}
</div>
);
};

export default MasonryCard;





72 changes: 72 additions & 0 deletions dashboard/src/components/Masonry/MasonryGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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 React from "react";
import "./masonry.css";

export type MasonryGridProps = {
minColumnWidth?: number;
columnGap?: number;
rowGap?: number;
rowHeight?: number;
className?: string;
style?: React.CSSProperties;
children: React.ReactNode;
};

/**
* Responsive CSS Grid container that supports a Masonry-like layout.
* Children should set their own grid-row-end span based on measured height.
*/
const MasonryGrid: React.FC<MasonryGridProps> = ({
minColumnWidth = 280,
columnGap = 16,
rowGap = 16,
rowHeight = 8,
className,
style,
children
}) => {
const mergedStyle: React.CSSProperties = {
// grid settings
display: "grid",
gridTemplateColumns: `repeat(auto-fill, minmax(${minColumnWidth}px, 1fr))`,
gridAutoFlow: "dense",
gridAutoRows: `${rowHeight}px`,
columnGap,
rowGap,
...style
};

return (
<div
className={`masonry-grid${className ? ` ${className}` : ""}`}
style={mergedStyle}
data-row-height={rowHeight}
data-row-gap={rowGap}
>
{children}
</div>
);
};

export default MasonryGrid;





53 changes: 53 additions & 0 deletions dashboard/src/components/Masonry/masonry.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.
*/

.masonry-grid {
width: 100%;
}

.masonry-card {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 6px;
display: flex;
flex-direction: column;
min-height: 0; /* allow children to shrink */
overflow: hidden;
}

.masonry-card__header {
background: #0a3d62;
color: #ffffff;
padding: 10px 12px;
font-weight: 600;
font-size: 14px;
}

.masonry-card__body {
padding: 12px;
overflow: auto; /* scroll within card when content exceeds maxBodyHeight */
}

.masonry-card__footer {
padding: 10px 12px;
border-top: 1px solid #e5e7eb;
}





2 changes: 1 addition & 1 deletion dashboard/src/components/QueryBuilder/Filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ const Filters = ({
const { classificationDefs } = classificationData || {};
const { entityDefs = {} } = entityData || {};
const { enumDefs = {} } = enumObj?.data || {};
const { businessMetadataDefs = {} } = businessMetaData || {};
const { businessMetadataDefs = [] } = businessMetaData || {};

let allDataObj = {
entitys: entityDefs,
Expand Down
18 changes: 13 additions & 5 deletions dashboard/src/components/ShowMore/ShowMoreView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,18 @@ const ShowMoreView = ({
relationshipGuid: selectedTerm.relationshipGuid
});
} else if (!isEmpty(gType)) {
let values = cloneDeep(currentEntity);
let values = cloneDeep(currentEntity) || {};
let glossaryData;
if (title == "Terms") {
glossaryData = values?.["terms"].filter(
glossaryData = (values["terms"] || []).filter(
(obj: { displayText: string }) => {
return obj.displayText != currentValue.selectedValue;
}
);

values["terms"] = glossaryData;
} else {
glossaryData = values?.["categories"].filter(
glossaryData = (values["categories"] || []).filter(
(obj: { displayText: string }) => {
return obj.displayText != currentValue.selectedValue;
}
Expand Down Expand Up @@ -223,7 +223,13 @@ const ShowMoreView = ({
} else if (title == "Propagated Classifications") {
return getTagParentList(label);
} else {
return label || optionalLabel;
// Ensure we return a string, not an object
if (label) return label;
if (typeof optionalLabel === 'string') return optionalLabel;
if (optionalLabel && typeof optionalLabel === 'object') {
return optionalLabel.displayText || optionalLabel.text || optionalLabel.name || '';
}
return '';
}
};

Expand Down Expand Up @@ -306,7 +312,9 @@ const ShowMoreView = ({
onDelete={
!isEmpty(removeApiMethod) && !isDeleteIcon
? () => {
handleDelete(obj[displayKey] || obj);
// Handle undefined displayKey by extracting a string value
const deleteValue = obj[displayKey] || obj.displayText || obj.text || obj.name || '';
handleDelete(deleteValue);
}
: isDeleteIcon && obj.count > 1
? () => {
Expand Down
4 changes: 3 additions & 1 deletion dashboard/src/components/Table/TableLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ const TableLayout: FC<TableProps> = ({
showGoToPage,
customLeftButton,
defaultPageSize,
onClientPageSizeChange
onClientPageSizeChange,
paginationSummaryVariant
}) => {
let defaultHideColumns = { ...defaultColumnVisibility };
const location = useLocation();
Expand Down Expand Up @@ -733,6 +734,7 @@ const TableLayout: FC<TableProps> = ({
showGoToPage={showGoToPage}
totalCount={totalCount}
onClientPageSizeChange={onClientPageSizeChange}
paginationSummaryVariant={paginationSummaryVariant}
/>
)}
</Paper>
Expand Down
32 changes: 26 additions & 6 deletions dashboard/src/components/Table/TablePagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ interface PaginationProps {
totalCount?: number;
/** Client mode: notify parent when page size changes (user action). */
onClientPageSizeChange?: (pageSize: number) => void;
/** See TableProps.paginationSummaryVariant */
paginationSummaryVariant?: 'default' | 'audit';
}

const TablePagination: React.FC<PaginationProps> = ({
Expand All @@ -90,7 +92,8 @@ const TablePagination: React.FC<PaginationProps> = ({
setIsEmptyData,
showGoToPage = false,
totalCount,
onClientPageSizeChange
onClientPageSizeChange,
paginationSummaryVariant = 'default'
}) => {
const theme: any = useTheme();
const location = useLocation();
Expand Down Expand Up @@ -381,8 +384,18 @@ const TablePagination: React.FC<PaginationProps> = ({
totalDatasetRows === 0 ? 0 : Math.min(displayFrom, displayToCapped);
const footerRangeEnd = totalDatasetRows === 0 ? 0 : displayToCapped;

const showAuditPaginationSummary =
paginationSummaryVariant === 'audit' &&
isServerSide &&
memoizedData.length > 0;

const auditRangeStart = offset + 1;
const auditRangeEnd = offset + memoizedData.length;

return (
<Stack
role="navigation"
aria-label="Table pagination"
spacing={{ xs: 1, sm: 2 }}
direction="row"
useFlexGap
Expand All @@ -393,14 +406,21 @@ const TablePagination: React.FC<PaginationProps> = ({
>
<div>
<span className="text-grey">
{totalDatasetRows === 0 ? (
"No records to display"
{memoizedData.length === 0 ? (
'No records to display'
) : showAuditPaginationSummary ? (
<>
Showing {memoizedData.length.toLocaleString()}{' '}
{memoizedData.length === 1 ? 'record' : 'records'} From{' '}
{auditRangeStart.toLocaleString()} -{' '}
{auditRangeEnd.toLocaleString()}
</>
) : (
<>
Showing {footerRangeStart.toLocaleString()}-
{footerRangeEnd.toLocaleString()} of{" "}
{totalDatasetRows.toLocaleString()}{" "}
{totalDatasetRows === 1 ? "record" : "records"}
{footerRangeEnd.toLocaleString()} of{' '}
{totalDatasetRows.toLocaleString()}{' '}
{totalDatasetRows === 1 ? 'record' : 'records'}
</>
)}
</span>
Expand Down
4 changes: 3 additions & 1 deletion dashboard/src/components/muiComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ const CustomButton = ({
size,
endIcon,
startIcon,
disabled
disabled,
...rest
}: ButtonProps | any) => {
let defaultStyles = {
fontWeight: "600 !important",
Expand All @@ -114,6 +115,7 @@ const CustomButton = ({
endIcon={endIcon}
startIcon={startIcon}
disabled={disabled}
{...rest}
>
{children}
</Button>
Expand Down
5 changes: 5 additions & 0 deletions dashboard/src/models/tableLayoutType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ export interface TableProps {
defaultPageSize?: number;
/** Client pagination: invoked when the user changes page size (e.g. sync schema relationship chunk limit). */
onClientPageSizeChange?: (pageSize: number) => void;
/**
* Admin audit table: API does not return a total count. Footer shows
* "Showing {n} records From {start} - {end}" instead of "… of {total}".
*/
paginationSummaryVariant?: 'default' | 'audit';
}
Loading