mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-01 11:09:14 +00:00
chore(ui): customize landing page improvements (#13557)
* CustomizePageClassBase class improvements * updated the reset button reordered customize page option on setting page * localization change for other languages * added functionality to choose the widget size and updated widget content according to the sizes * styling fix * fixed double API calls on my data page and flakiness in showing loader for feeds widget * fixed code smell
This commit is contained in:
parent
2ef49d52ca
commit
53629c8cc0
@ -19,5 +19,21 @@ export interface AddWidgetModalProps {
|
||||
placeholderWidgetKey: string;
|
||||
addedWidgetsList: Array<string>;
|
||||
handleCloseAddWidgetModal: () => void;
|
||||
handleAddWidget: (widget: Document, widgetKey: string) => void;
|
||||
handleAddWidget: (
|
||||
widget: Document,
|
||||
widgetKey: string,
|
||||
widgetSize: number
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface AddWidgetTabContentProps {
|
||||
widget: Document;
|
||||
maxGridSizeSupport: number;
|
||||
getAddWidgetHandler: (widget: Document, widgetSize: number) => () => void;
|
||||
widgetSizeOptions: Array<WidgetSizeInfo>;
|
||||
}
|
||||
|
||||
export interface WidgetSizeInfo {
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
@ -11,19 +11,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CheckOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Image,
|
||||
Modal,
|
||||
Row,
|
||||
Space,
|
||||
Tabs,
|
||||
TabsProps,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { CheckOutlined } from '@ant-design/icons';
|
||||
import { Modal, Space, Tabs, TabsProps } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@ -32,11 +21,14 @@ import { ERROR_PLACEHOLDER_TYPE } from '../../../enums/common.enum';
|
||||
import { WidgetWidths } from '../../../enums/CustomizablePage.enum';
|
||||
import { Document } from '../../../generated/entity/docStore/document';
|
||||
import { getAllKnowledgePanels } from '../../../rest/DocStoreAPI';
|
||||
import { CustomizePageClassBase } from '../../../utils/CustomizePageClassBase';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import ErrorPlaceHolder from '../../common/error-with-placeholder/ErrorPlaceHolder';
|
||||
import { AddWidgetModalProps } from './AddWidgetModal.interface';
|
||||
import {
|
||||
AddWidgetModalProps,
|
||||
WidgetSizeInfo,
|
||||
} from './AddWidgetModal.interface';
|
||||
import './AddWidgetModal.less';
|
||||
import AddWidgetTabContent from './AddWidgetTabContent';
|
||||
|
||||
function AddWidgetModal({
|
||||
open,
|
||||
@ -62,29 +54,19 @@ function AddWidgetModal({
|
||||
}, []);
|
||||
|
||||
const getAddWidgetHandler = useCallback(
|
||||
(widget: Document) => () => handleAddWidget(widget, placeholderWidgetKey),
|
||||
(widget: Document, widgetSize: number) => () =>
|
||||
handleAddWidget(widget, placeholderWidgetKey, widgetSize),
|
||||
[handleAddWidget, placeholderWidgetKey]
|
||||
);
|
||||
|
||||
const checkAddWidgetValidity = useCallback(
|
||||
(widget: Document) => {
|
||||
const gridSizes = widget.data.gridSizes;
|
||||
const gridSizesInNumbers: Array<number> = gridSizes.map(
|
||||
(size: WidgetWidths) => WidgetWidths[size]
|
||||
);
|
||||
|
||||
return gridSizesInNumbers.every((size) => size <= maxGridSizeSupport);
|
||||
},
|
||||
[widgetsList]
|
||||
);
|
||||
|
||||
const tabItems: TabsProps['items'] = useMemo(
|
||||
() =>
|
||||
widgetsList?.map((widget) => {
|
||||
const widgetAddable = checkAddWidgetValidity(widget);
|
||||
const widgetImage = CustomizePageClassBase.getWidgetImageFromKey(
|
||||
widget.fullyQualifiedName
|
||||
);
|
||||
const widgetSizeOptions: Array<WidgetSizeInfo> =
|
||||
widget.data.gridSizes.map((size: WidgetWidths) => ({
|
||||
label: size,
|
||||
value: WidgetWidths[size],
|
||||
}));
|
||||
|
||||
return {
|
||||
label: (
|
||||
@ -102,36 +84,16 @@ function AddWidgetModal({
|
||||
),
|
||||
key: widget.fullyQualifiedName,
|
||||
children: (
|
||||
<Row align="middle" className="h-min-480" justify="center">
|
||||
<Col>
|
||||
<Space align="center" direction="vertical">
|
||||
<Image className="p-y-md" preview={false} src={widgetImage} />
|
||||
<Typography.Paragraph className="d-block text-center">
|
||||
{widget.description}
|
||||
</Typography.Paragraph>
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={
|
||||
widgetAddable ? '' : t('message.can-not-add-widget')
|
||||
}>
|
||||
<Button
|
||||
ghost
|
||||
className="p-x-lg m-t-md"
|
||||
data-testid="add-widget-placeholder-button"
|
||||
disabled={!widgetAddable}
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
onClick={getAddWidgetHandler(widget)}>
|
||||
{t('label.add')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
<AddWidgetTabContent
|
||||
getAddWidgetHandler={getAddWidgetHandler}
|
||||
maxGridSizeSupport={maxGridSizeSupport}
|
||||
widget={widget}
|
||||
widgetSizeOptions={widgetSizeOptions}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}),
|
||||
[widgetsList, addedWidgetsList, checkAddWidgetValidity, getAddWidgetHandler]
|
||||
[widgetsList, addedWidgetsList, getAddWidgetHandler, maxGridSizeSupport]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed 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 { PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Image,
|
||||
Radio,
|
||||
RadioChangeEvent,
|
||||
Row,
|
||||
Space,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import customizePageClassBase from '../../../utils/CustomizePageClassBase';
|
||||
import { AddWidgetTabContentProps } from './AddWidgetModal.interface';
|
||||
|
||||
function AddWidgetTabContent({
|
||||
getAddWidgetHandler,
|
||||
maxGridSizeSupport,
|
||||
widget,
|
||||
widgetSizeOptions,
|
||||
}: Readonly<AddWidgetTabContentProps>) {
|
||||
const { t } = useTranslation();
|
||||
const [selectedWidgetSize, setSelectedWidgetSize] = useState<number>(
|
||||
widgetSizeOptions[0].value
|
||||
);
|
||||
|
||||
const widgetAddable = useMemo(
|
||||
() => selectedWidgetSize <= maxGridSizeSupport,
|
||||
[selectedWidgetSize, maxGridSizeSupport]
|
||||
);
|
||||
|
||||
const widgetImage = useMemo(
|
||||
() =>
|
||||
customizePageClassBase.getWidgetImageFromKey(
|
||||
widget.fullyQualifiedName,
|
||||
selectedWidgetSize
|
||||
),
|
||||
[widget, selectedWidgetSize]
|
||||
);
|
||||
|
||||
const handleSizeChange = useCallback((e: RadioChangeEvent) => {
|
||||
setSelectedWidgetSize(e.target.value);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Space>
|
||||
<Typography.Text>{`${t('label.size')}:`}</Typography.Text>
|
||||
<Radio.Group
|
||||
defaultValue={selectedWidgetSize}
|
||||
optionType="button"
|
||||
options={widgetSizeOptions}
|
||||
onChange={handleSizeChange}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Row className="h-min-480" justify="center">
|
||||
<Col>
|
||||
<Space align="center" direction="vertical">
|
||||
<Image className="p-y-md" preview={false} src={widgetImage} />
|
||||
<Typography.Paragraph className="d-block text-center">
|
||||
{widget.description}
|
||||
</Typography.Paragraph>
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={widgetAddable ? '' : t('message.can-not-add-widget')}>
|
||||
<Button
|
||||
ghost
|
||||
className="p-x-lg m-t-md"
|
||||
data-testid="add-widget-placeholder-button"
|
||||
disabled={!widgetAddable}
|
||||
icon={<PlusOutlined />}
|
||||
type="primary"
|
||||
onClick={getAddWidgetHandler(widget, selectedWidgetSize)}>
|
||||
{t('label.add')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddWidgetTabContent;
|
||||
@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Col, Row, Space, Typography } from 'antd';
|
||||
import { Button, Col, Modal, Row, Space, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import { isEmpty, isNil, uniqBy } from 'lodash';
|
||||
@ -21,12 +21,6 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link, useHistory, useLocation, useParams } from 'react-router-dom';
|
||||
import AppState from '../../../AppState';
|
||||
import gridBgImg from '../../../assets/img/grid-bg-img.png';
|
||||
import {
|
||||
LANDING_PAGE_LAYOUT,
|
||||
LANDING_PAGE_MAX_GRID_SIZE,
|
||||
LANDING_PAGE_ROW_HEIGHT,
|
||||
LANDING_PAGE_WIDGET_MARGIN,
|
||||
} from '../../../constants/CustomizePage.constants';
|
||||
import {
|
||||
GlobalSettingOptions,
|
||||
GlobalSettingsMenuCategory,
|
||||
@ -48,7 +42,7 @@ import {
|
||||
getLayoutUpdateHandler,
|
||||
getRemoveWidgetHandler,
|
||||
} from '../../../utils/CustomizableLandingPageUtils';
|
||||
import { CustomizePageClassBase } from '../../../utils/CustomizePageClassBase';
|
||||
import customizePageClassBase from '../../../utils/CustomizePageClassBase';
|
||||
import {
|
||||
getPersonaDetailsPath,
|
||||
getSettingPath,
|
||||
@ -75,7 +69,8 @@ function CustomizeMyData({
|
||||
const [resetRightPanelLayout, setResetRightPanelLayout] =
|
||||
useState<boolean>(false);
|
||||
const [layout, setLayout] = useState<Array<WidgetConfig>>([
|
||||
...(initialPageData.data?.page?.layout ?? LANDING_PAGE_LAYOUT),
|
||||
...(initialPageData.data?.page?.layout ??
|
||||
customizePageClassBase.landingPageDefaultLayout),
|
||||
{
|
||||
h: 2,
|
||||
i: LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER,
|
||||
@ -89,6 +84,7 @@ function CustomizeMyData({
|
||||
LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER
|
||||
);
|
||||
const [isWidgetModalOpen, setIsWidgetModalOpen] = useState<boolean>(false);
|
||||
const [isResetModalOpen, setIsResetModalOpen] = useState<boolean>(false);
|
||||
const { isAuthDisabled } = useAuth(location.pathname);
|
||||
const [followedData, setFollowedData] = useState<Array<EntityReference>>();
|
||||
const [followedDataCount, setFollowedDataCount] = useState(0);
|
||||
@ -124,12 +120,17 @@ function CustomizeMyData({
|
||||
}, []);
|
||||
|
||||
const handleAddWidget = useCallback(
|
||||
(newWidgetData: Document, placeholderWidgetKey: string) => {
|
||||
(
|
||||
newWidgetData: Document,
|
||||
placeholderWidgetKey: string,
|
||||
widgetSize: number
|
||||
) => {
|
||||
setLayout(
|
||||
getAddWidgetHandler(
|
||||
newWidgetData,
|
||||
placeholderWidgetKey,
|
||||
LANDING_PAGE_MAX_GRID_SIZE
|
||||
widgetSize,
|
||||
customizePageClassBase.landingPageMaxGridSize
|
||||
)
|
||||
);
|
||||
setIsWidgetModalOpen(false);
|
||||
@ -146,6 +147,14 @@ function CustomizeMyData({
|
||||
[layout]
|
||||
);
|
||||
|
||||
const handleOpenResetModal = useCallback(() => {
|
||||
setIsResetModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleCloseResetModal = useCallback(() => {
|
||||
setIsResetModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleOpenAddWidgetModal = useCallback(() => {
|
||||
setIsWidgetModalOpen(true);
|
||||
}, []);
|
||||
@ -212,7 +221,7 @@ function CustomizeMyData({
|
||||
);
|
||||
}
|
||||
|
||||
const Widget = CustomizePageClassBase.getWidgetsFromKey(widgetConfig.i);
|
||||
const Widget = customizePageClassBase.getWidgetsFromKey(widgetConfig.i);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
@ -222,6 +231,7 @@ function CustomizeMyData({
|
||||
followedDataCount={followedDataCount}
|
||||
handleRemoveWidget={handleRemoveWidget}
|
||||
isLoadingOwnedData={isLoadingOwnedData}
|
||||
selectedGridSize={widgetConfig.w}
|
||||
widgetKey={widgetConfig.i}
|
||||
/>
|
||||
);
|
||||
@ -315,7 +325,7 @@ function CustomizeMyData({
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
setLayout([
|
||||
...LANDING_PAGE_LAYOUT,
|
||||
...customizePageClassBase.landingPageDefaultLayout,
|
||||
{
|
||||
h: 2,
|
||||
i: LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER,
|
||||
@ -326,6 +336,7 @@ function CustomizeMyData({
|
||||
},
|
||||
]);
|
||||
setResetRightPanelLayout(true);
|
||||
setIsResetModalOpen(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@ -363,8 +374,8 @@ function CustomizeMyData({
|
||||
<Button size="small" onClick={handleCancel}>
|
||||
{t('label.cancel')}
|
||||
</Button>
|
||||
<Button size="small" onClick={handleReset}>
|
||||
{t('label.reset-default-layout')}
|
||||
<Button size="small" onClick={handleOpenResetModal}>
|
||||
{t('label.reset')}
|
||||
</Button>
|
||||
<Button size="small" type="primary" onClick={handleSave}>
|
||||
{t('label.save')}
|
||||
@ -380,8 +391,11 @@ function CustomizeMyData({
|
||||
cols={{ lg: 4, md: 4, sm: 4, xs: 4, xxs: 4 }}
|
||||
draggableHandle=".drag-widget-icon"
|
||||
isResizable={false}
|
||||
margin={[LANDING_PAGE_WIDGET_MARGIN, LANDING_PAGE_WIDGET_MARGIN]}
|
||||
rowHeight={LANDING_PAGE_ROW_HEIGHT}
|
||||
margin={[
|
||||
customizePageClassBase.landingPageWidgetMargin,
|
||||
customizePageClassBase.landingPageWidgetMargin,
|
||||
]}
|
||||
rowHeight={customizePageClassBase.landingPageRowHeight}
|
||||
style={{
|
||||
backgroundImage: `url(${gridBgImg})`,
|
||||
}}
|
||||
@ -393,11 +407,23 @@ function CustomizeMyData({
|
||||
addedWidgetsList={addedWidgetsList}
|
||||
handleAddWidget={handleAddWidget}
|
||||
handleCloseAddWidgetModal={handleCloseAddWidgetModal}
|
||||
maxGridSizeSupport={LANDING_PAGE_MAX_GRID_SIZE}
|
||||
maxGridSizeSupport={customizePageClassBase.landingPageMaxGridSize}
|
||||
open={isWidgetModalOpen}
|
||||
placeholderWidgetKey={placeholderWidgetKey}
|
||||
/>
|
||||
)}
|
||||
{isResetModalOpen && (
|
||||
<Modal
|
||||
centered
|
||||
cancelText={t('label.no')}
|
||||
okText={t('label.yes')}
|
||||
open={isResetModalOpen}
|
||||
title={t('label.reset-default-layout')}
|
||||
onCancel={handleCloseResetModal}
|
||||
onOk={handleReset}>
|
||||
{t('message.reset-layout-confirmation')}
|
||||
</Modal>
|
||||
)}
|
||||
</ActivityFeedProvider>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@ -32,6 +32,7 @@ import {
|
||||
} from '../../constants/constants';
|
||||
import { KPI_WIDGET_GRAPH_COLORS } from '../../constants/DataInsight.constants';
|
||||
import { DATA_INSIGHT_DOCS } from '../../constants/docs.constants';
|
||||
import { WidgetWidths } from '../../enums/CustomizablePage.enum';
|
||||
import { Kpi, KpiResult } from '../../generated/dataInsight/kpi/kpi';
|
||||
import { UIKpiResult } from '../../interface/data-insight.interface';
|
||||
import {
|
||||
@ -88,6 +89,7 @@ const KPIWidget = ({
|
||||
selectedDays = CHART_WIDGET_DAYS_DURATION,
|
||||
handleRemoveWidget,
|
||||
widgetKey,
|
||||
selectedGridSize = WidgetWidths.medium,
|
||||
}: KPIWidgetProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [kpiList, setKpiList] = useState<Array<Kpi>>([]);
|
||||
@ -187,6 +189,11 @@ const KPIWidget = ({
|
||||
!isUndefined(handleRemoveWidget) && handleRemoveWidget(widgetKey);
|
||||
}, [widgetKey]);
|
||||
|
||||
const isWidgetSizeMedium = useMemo(
|
||||
() => selectedGridSize === WidgetWidths.medium,
|
||||
[selectedGridSize]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchKpiList().catch(() => {
|
||||
// catch handled in parent function
|
||||
@ -211,34 +218,38 @@ const KPIWidget = ({
|
||||
data-testid="kpi-card"
|
||||
id="kpi-charts"
|
||||
loading={isKPIListLoading || isLoading}>
|
||||
<Row justify="end">
|
||||
<Col>
|
||||
{isEditView && (
|
||||
<Space align="center">
|
||||
<DragOutlined
|
||||
className="drag-widget-icon cursor-pointer"
|
||||
size={14}
|
||||
/>
|
||||
<CloseOutlined size={14} onClick={handleCloseClick} />
|
||||
</Space>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row align="middle" justify="space-between">
|
||||
<Col>
|
||||
<Typography.Text className="font-medium">
|
||||
{t('label.kpi-title')}
|
||||
</Typography.Text>
|
||||
</Col>
|
||||
{isEditView && (
|
||||
<Space align="center">
|
||||
<DragOutlined
|
||||
className="drag-widget-icon cursor-pointer"
|
||||
size={14}
|
||||
/>
|
||||
<CloseOutlined size={14} onClick={handleCloseClick} />
|
||||
</Space>
|
||||
)}
|
||||
</Row>
|
||||
{kpiList.length > 0 ? (
|
||||
<Row>
|
||||
<Row className="p-t-md">
|
||||
{graphData.length ? (
|
||||
<>
|
||||
<Col span={14}>
|
||||
<Col span={isWidgetSizeMedium ? 14 : 24}>
|
||||
<ResponsiveContainer debounce={1} height={250} width="100%">
|
||||
<LineChart
|
||||
data={graphData}
|
||||
margin={{
|
||||
top: 10,
|
||||
right: 50,
|
||||
left: -20,
|
||||
right: isWidgetSizeMedium ? 50 : 20,
|
||||
left: -30,
|
||||
bottom: 0,
|
||||
}}>
|
||||
<CartesianGrid
|
||||
@ -258,16 +269,18 @@ const KPIWidget = ({
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</Col>
|
||||
{!isUndefined(kpiLatestResults) && !isEmpty(kpiLatestResults) && (
|
||||
<Col span={10}>
|
||||
<KPILatestResultsV1
|
||||
kpiLatestResultsRecord={kpiLatestResults}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
{!isUndefined(kpiLatestResults) &&
|
||||
!isEmpty(kpiLatestResults) &&
|
||||
isWidgetSizeMedium && (
|
||||
<Col span={10}>
|
||||
<KPILatestResultsV1
|
||||
kpiLatestResultsRecord={kpiLatestResults}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Col className="justify-center" span={24}>
|
||||
<Col span={24}>
|
||||
<EmptyPlaceholder />
|
||||
</Col>
|
||||
)}
|
||||
|
||||
@ -37,6 +37,7 @@ const MyDataWidgetInternal = ({
|
||||
const currentUserDetails = AppState.getCurrentUserDetails();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [data, setData] = useState<EntityReference[]>([]);
|
||||
const [totalOwnedAssetsCount, setTotalOwnedAssetsCount] = useState<number>(0);
|
||||
|
||||
const fetchMyDataAssets = async () => {
|
||||
if (!currentUserDetails || !currentUserDetails.id) {
|
||||
@ -54,7 +55,8 @@ const MyDataWidgetInternal = ({
|
||||
includeData.includes(data.type as AssetsType)
|
||||
);
|
||||
|
||||
setData(includedOwnsData.slice(0, 8));
|
||||
setData(includedOwnsData.slice(0, 9));
|
||||
setTotalOwnedAssetsCount(includedOwnsData.length);
|
||||
}
|
||||
} catch (err) {
|
||||
setData([]);
|
||||
@ -87,7 +89,7 @@ const MyDataWidgetInternal = ({
|
||||
<span className="text-grey-muted font-normal text-xs">
|
||||
{t('label.view-all')}{' '}
|
||||
<span data-testid="my-data-total-count">
|
||||
{`(${data.length})`}
|
||||
{`(${totalOwnedAssetsCount})`}
|
||||
</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
@ -38,7 +38,7 @@ function AnnouncementsWidget({
|
||||
}, [widgetKey]);
|
||||
|
||||
return (
|
||||
<div className="bg-white h-full">
|
||||
<div className="bg-white h-max-full overflow-y-scroll">
|
||||
<Row justify="space-between">
|
||||
<Col>
|
||||
<Typography.Paragraph className="right-panel-label m-b-sm">
|
||||
|
||||
@ -13,12 +13,6 @@
|
||||
import { isEmpty, isUndefined, uniqBy } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Layout, Responsive, WidthProvider } from 'react-grid-layout';
|
||||
import {
|
||||
LANDING_PAGE_RIGHT_CONTAINER_MAX_GRID_SIZE,
|
||||
LANDING_PAGE_ROW_HEIGHT,
|
||||
LANDING_PAGE_WIDGET_MARGIN,
|
||||
RIGHT_PANEL_LAYOUT,
|
||||
} from '../../../constants/CustomizePage.constants';
|
||||
import { SIZE } from '../../../enums/common.enum';
|
||||
import { LandingPageWidgetKeys } from '../../../enums/CustomizablePage.enum';
|
||||
import { Document } from '../../../generated/entity/docStore/document';
|
||||
@ -28,7 +22,7 @@ import {
|
||||
getLayoutUpdateHandler,
|
||||
getRemoveWidgetHandler,
|
||||
} from '../../../utils/CustomizableLandingPageUtils';
|
||||
import { CustomizePageClassBase } from '../../../utils/CustomizePageClassBase';
|
||||
import customizePageClassBase from '../../../utils/CustomizePageClassBase';
|
||||
import AddWidgetModal from '../../CustomizableComponents/AddWidgetModal/AddWidgetModal';
|
||||
import EmptyWidgetPlaceholder from '../../CustomizableComponents/EmptyWidgetPlaceholder/EmptyWidgetPlaceholder';
|
||||
import './right-sidebar.less';
|
||||
@ -86,12 +80,17 @@ const RightSidebar = ({
|
||||
}, []);
|
||||
|
||||
const handleAddWidget = useCallback(
|
||||
(newWidgetData: Document, placeholderWidgetKey: string) => {
|
||||
(
|
||||
newWidgetData: Document,
|
||||
placeholderWidgetKey: string,
|
||||
widgetSize: number
|
||||
) => {
|
||||
setLayout(
|
||||
getAddWidgetHandler(
|
||||
newWidgetData,
|
||||
placeholderWidgetKey,
|
||||
LANDING_PAGE_RIGHT_CONTAINER_MAX_GRID_SIZE
|
||||
widgetSize,
|
||||
customizePageClassBase.landingPageRightContainerMaxGridSize
|
||||
)
|
||||
);
|
||||
setIsWidgetModalOpen(false);
|
||||
@ -117,7 +116,7 @@ const RightSidebar = ({
|
||||
);
|
||||
}
|
||||
|
||||
const Widget = CustomizePageClassBase.getWidgetsFromKey(widgetConfig.i);
|
||||
const Widget = customizePageClassBase.getWidgetsFromKey(widgetConfig.i);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
@ -127,6 +126,7 @@ const RightSidebar = ({
|
||||
handleRemoveWidget={handleRemoveWidget}
|
||||
isEditView={isEditView}
|
||||
isLoadingOwnedData={isLoadingOwnedData}
|
||||
selectedGridSize={widgetConfig.w}
|
||||
widgetKey={widgetConfig.i}
|
||||
/>
|
||||
);
|
||||
@ -207,7 +207,7 @@ const RightSidebar = ({
|
||||
useEffect(() => {
|
||||
if (resetLayout && handleResetLayout) {
|
||||
setLayout([
|
||||
...RIGHT_PANEL_LAYOUT,
|
||||
...customizePageClassBase.rightPanelDefaultLayout,
|
||||
...(isEditView
|
||||
? [
|
||||
{
|
||||
@ -230,11 +230,14 @@ const RightSidebar = ({
|
||||
<ResponsiveGridLayout
|
||||
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
||||
cols={{ lg: 1, md: 1, sm: 1, xs: 1, xxs: 1 }}
|
||||
containerPadding={[0, LANDING_PAGE_WIDGET_MARGIN]}
|
||||
containerPadding={[0, customizePageClassBase.landingPageWidgetMargin]}
|
||||
draggableHandle=".drag-widget-icon"
|
||||
isResizable={false}
|
||||
margin={[LANDING_PAGE_WIDGET_MARGIN, LANDING_PAGE_WIDGET_MARGIN]}
|
||||
rowHeight={LANDING_PAGE_ROW_HEIGHT}
|
||||
margin={[
|
||||
customizePageClassBase.landingPageWidgetMargin,
|
||||
customizePageClassBase.landingPageWidgetMargin,
|
||||
]}
|
||||
rowHeight={customizePageClassBase.landingPageRowHeight}
|
||||
onLayoutChange={handleLayoutUpdate}>
|
||||
{widgets}
|
||||
</ResponsiveGridLayout>
|
||||
@ -243,7 +246,9 @@ const RightSidebar = ({
|
||||
addedWidgetsList={addedWidgetsList}
|
||||
handleAddWidget={handleAddWidget}
|
||||
handleCloseAddWidgetModal={handleCloseAddWidgetModal}
|
||||
maxGridSizeSupport={LANDING_PAGE_RIGHT_CONTAINER_MAX_GRID_SIZE}
|
||||
maxGridSizeSupport={
|
||||
customizePageClassBase.landingPageRightContainerMaxGridSize
|
||||
}
|
||||
open={isWidgetModalOpen}
|
||||
placeholderWidgetKey={placeholderWidgetKey}
|
||||
/>
|
||||
|
||||
@ -11,9 +11,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
.announcement-container-list {
|
||||
max-height: 360px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
.feed-card-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import {
|
||||
} from 'recharts';
|
||||
import { CHART_WIDGET_DAYS_DURATION } from '../../constants/constants';
|
||||
import { TOTAL_ENTITY_CHART_COLOR } from '../../constants/DataInsight.constants';
|
||||
import { WidgetWidths } from '../../enums/CustomizablePage.enum';
|
||||
import { DataReportIndex } from '../../generated/dataInsight/dataInsightChart';
|
||||
import {
|
||||
DataInsightChartResult,
|
||||
@ -57,6 +58,7 @@ const TotalDataAssetsWidget = ({
|
||||
selectedDays = CHART_WIDGET_DAYS_DURATION,
|
||||
handleRemoveWidget,
|
||||
widgetKey,
|
||||
selectedGridSize,
|
||||
}: TotalDataAssetsWidgetProps) => {
|
||||
const [totalEntitiesByType, setTotalEntitiesByType] =
|
||||
useState<DataInsightChartResult>();
|
||||
@ -96,6 +98,11 @@ const TotalDataAssetsWidget = ({
|
||||
!isUndefined(handleRemoveWidget) && handleRemoveWidget(widgetKey);
|
||||
}, [widgetKey]);
|
||||
|
||||
const isWidgetSizeLarge = useMemo(
|
||||
() => selectedGridSize === WidgetWidths.large,
|
||||
[selectedGridSize]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTotalEntitiesByType();
|
||||
}, [selectedDays]);
|
||||
@ -121,18 +128,18 @@ const TotalDataAssetsWidget = ({
|
||||
)}
|
||||
{data.length ? (
|
||||
<Row>
|
||||
<Col span={14}>
|
||||
<Col span={isWidgetSizeLarge ? 14 : 24}>
|
||||
<Typography.Text className="font-medium">
|
||||
{t('label.data-insight-total-entity-summary')}
|
||||
</Typography.Text>
|
||||
<div className="p-t-lg">
|
||||
<div className="p-t-md">
|
||||
<ResponsiveContainer height={250} width="100%">
|
||||
<AreaChart
|
||||
data={data}
|
||||
margin={{
|
||||
top: 10,
|
||||
right: 50,
|
||||
left: -20,
|
||||
right: isWidgetSizeLarge ? 50 : 20,
|
||||
left: -30,
|
||||
bottom: 0,
|
||||
}}
|
||||
syncId="anyId">
|
||||
@ -152,15 +159,17 @@ const TotalDataAssetsWidget = ({
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<TotalEntityInsightSummary
|
||||
entities={entities}
|
||||
latestData={latestData}
|
||||
relativePercentage={relativePercentage}
|
||||
selectedDays={selectedDays}
|
||||
total={total}
|
||||
/>
|
||||
</Col>
|
||||
{isWidgetSizeLarge && (
|
||||
<Col span={10}>
|
||||
<TotalEntityInsightSummary
|
||||
entities={entities}
|
||||
latestData={latestData}
|
||||
relativePercentage={relativePercentage}
|
||||
selectedDays={selectedDays}
|
||||
total={total}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
) : (
|
||||
<Row>
|
||||
|
||||
@ -16,7 +16,6 @@ import { isUndefined } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import AppState from '../../../AppState';
|
||||
import ActivityFeedListV1 from '../../../components/ActivityFeed/ActivityFeedList/ActivityFeedListV1.component';
|
||||
import { useActivityFeedProvider } from '../../../components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
|
||||
import { ActivityFeedTabs } from '../../../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface';
|
||||
@ -32,6 +31,7 @@ import { WidgetCommonProps } from '../../../pages/CustomizablePage/CustomizableP
|
||||
import { getFeedsWithFilter } from '../../../rest/feedsAPI';
|
||||
import { getCountBadge, getEntityDetailLink } from '../../../utils/CommonUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import FeedsFilterPopover from '../../common/FeedsFilterPopover/FeedsFilterPopover.component';
|
||||
import './feeds-widget.less';
|
||||
|
||||
@ -43,18 +43,16 @@ const FeedsWidget = ({
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const { isTourOpen } = useTourProvider();
|
||||
const { currentUser } = useAuthContext();
|
||||
const [activeTab, setActiveTab] = useState<ActivityFeedTabs>(
|
||||
ActivityFeedTabs.ALL
|
||||
);
|
||||
const { loading, entityThread, entityPaging, getFeedData } =
|
||||
useActivityFeedProvider();
|
||||
const [taskCount, setTaskCount] = useState(0);
|
||||
const currentUser = useMemo(
|
||||
() => AppState.getCurrentUserDetails(),
|
||||
[AppState.userDetails, AppState.nonSecureUserDetails]
|
||||
);
|
||||
|
||||
const [defaultFilter, setDefaultFilter] = useState<FeedFilter>(
|
||||
FeedFilter.OWNER_OR_FOLLOWS
|
||||
currentUser?.isAdmin ? FeedFilter.ALL : FeedFilter.OWNER_OR_FOLLOWS
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Collate.
|
||||
* Licensed 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 { LandingPageWidgetKeys } from '../enums/CustomizablePage.enum';
|
||||
|
||||
export const DEFAULT_WIDGET_HEIGHT = 3;
|
||||
export const LANDING_PAGE_WIDGET_MARGIN = 16;
|
||||
export const LANDING_PAGE_ROW_HEIGHT = 100;
|
||||
export const LANDING_PAGE_RIGHT_CONTAINER_EDIT_HEIGHT = 16;
|
||||
export const LANDING_PAGE_MAX_GRID_SIZE = 3;
|
||||
export const LANDING_PAGE_RIGHT_CONTAINER_MAX_GRID_SIZE = 1;
|
||||
|
||||
export const LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS = {
|
||||
activityFeed: 5,
|
||||
rightSidebar: 11.5,
|
||||
announcements: 3.9,
|
||||
following: 2.4,
|
||||
recentlyViewed: 2.1,
|
||||
myData: 2.8,
|
||||
kpi: 2.8,
|
||||
totalDataAssets: 3.42,
|
||||
};
|
||||
|
||||
export const RIGHT_PANEL_LAYOUT = [
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.announcements,
|
||||
i: LandingPageWidgetKeys.ANNOUNCEMENTS,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.following,
|
||||
i: LandingPageWidgetKeys.FOLLOWING,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 1.5,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.recentlyViewed,
|
||||
i: LandingPageWidgetKeys.RECENTLY_VIEWED,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 3,
|
||||
static: false,
|
||||
},
|
||||
];
|
||||
|
||||
export const LANDING_PAGE_LAYOUT = [
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.activityFeed,
|
||||
i: LandingPageWidgetKeys.ACTIVITY_FEED,
|
||||
w: 3,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.rightSidebar,
|
||||
i: LandingPageWidgetKeys.RIGHT_PANEL,
|
||||
w: 1,
|
||||
x: 3,
|
||||
y: 0,
|
||||
data: {
|
||||
page: {
|
||||
layout: RIGHT_PANEL_LAYOUT,
|
||||
},
|
||||
},
|
||||
static: true,
|
||||
},
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.myData,
|
||||
i: LandingPageWidgetKeys.MY_DATA,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 6,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.kpi,
|
||||
i: LandingPageWidgetKeys.KPI,
|
||||
w: 2,
|
||||
x: 1,
|
||||
y: 6,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.totalDataAssets,
|
||||
i: LandingPageWidgetKeys.TOTAL_DATA_ASSETS,
|
||||
w: 3,
|
||||
x: 0,
|
||||
y: 9.1,
|
||||
static: false,
|
||||
},
|
||||
];
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "Benennen Sie den Namen und die Anzeigebezeichnung für das {{entity}} um.",
|
||||
"request-description": "Beschreibung der Anfrage",
|
||||
"request-update-description": "Aktualisierung der Anfragebeschreibung",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "Der Zurücksetzungslink wurde an Ihre E-Mail gesendet",
|
||||
"restore-action-description": "Durch das Wiederherstellen dieses {{entityType}} werden seine Metadaten in OpenMetadata wiederhergestellt.",
|
||||
"restore-deleted-team": "Das Wiederherstellen des Teams fügt alle Metadaten wieder zu OpenMetadata hinzu.",
|
||||
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "Rename the Name and Display Name for the {{entity}}.",
|
||||
"request-description": "Request description",
|
||||
"request-update-description": "Request update description",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "Reset link has been sent to your email",
|
||||
"restore-action-description": "Restoring this {{entityType}} will restore its metadata in OpenMetadata.",
|
||||
"restore-deleted-team": " Restoring the Team will add all the metadata back to OpenMetadata",
|
||||
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "Renombrar el nombre y nombre visualizado para el {{entity}}.",
|
||||
"request-description": "Descripción de la solicitud",
|
||||
"request-update-description": "Descripción de la actualización de la solicitud",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "Se ha enviado un enlace de restablecimiento a tu correo electrónico",
|
||||
"restore-action-description": "Restaurar este {{entityType}} restaurará sus metadatos en OpenMetadata.",
|
||||
"restore-deleted-team": "Restaurar el equipo agregará todos los metadatos de nuevo a OpenMetadata",
|
||||
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "Renommer le nom et le nom d'affichage pour {{entity}}.",
|
||||
"request-description": "Demander une description",
|
||||
"request-update-description": "Mettre à jour la demande de description",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "Lien de réinitialisation a été envoyé à votre adresse e-mail.",
|
||||
"restore-action-description": "Restaurer cette {{entityType}} restaurera les métadonnées dans OpenMetadata.",
|
||||
"restore-deleted-team": " Restaurer cette Equipe ajoutera toutes les métadonnées dans OpenMetadata",
|
||||
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "Rename the Name and Display Name for the {{entity}}.",
|
||||
"request-description": "Request description",
|
||||
"request-update-description": "Request update description",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "パスワードリセット用のリンクがあなたのメールアドレスに送信されました",
|
||||
"restore-action-description": "この{{entityType}}をリストアすると、OpenMetadataのメタデータがリストアされます。",
|
||||
"restore-deleted-team": "チームをリストアすると全てのメタデータがOpenMetadataに戻されます。",
|
||||
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "Rename the Name and Display Name for the {{entity}}.",
|
||||
"request-description": "Descrição da solicitação",
|
||||
"request-update-description": "Atualizar descrição da solicitação",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "O link de redefinição foi enviado para o seu e-mail",
|
||||
"restore-action-description": "Restaurar esta {{entityType}} irá restaurar seus metadados no OpenMetadata.",
|
||||
"restore-deleted-team": "Restaurar a equipe irá adicionar todos os metadados de volta ao OpenMetadata",
|
||||
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "Измените имя и отображаемое имя для {{entity}}.",
|
||||
"request-description": "Запросить описание",
|
||||
"request-update-description": "Запросить обновление описания",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "Ссылка для сброса отправлена на вашу электронную почту",
|
||||
"restore-action-description": "Восстановление этого {{entityType}} восстановит его метаданные в OpenMetadata.",
|
||||
"restore-deleted-team": "Восстановление команды добавит все метаданные обратно в OpenMetadata.",
|
||||
|
||||
@ -1467,6 +1467,7 @@
|
||||
"rename-entity": "修改{{entity}}的名称和显示名",
|
||||
"request-description": "请求详细描述",
|
||||
"request-update-description": "请求更新描述",
|
||||
"reset-layout-confirmation": "Are you sure you want to apply the \"Default Layout\"?",
|
||||
"reset-link-has-been-sent": "重置链接已发送到您的电子邮箱",
|
||||
"restore-action-description": "还原此{{entityType}}将在 OpenMetadata 中还原其元数据",
|
||||
"restore-deleted-team": "还原团队将会把所有元数据添加回 OpenMetadata",
|
||||
|
||||
@ -23,6 +23,7 @@ export interface WidgetConfig
|
||||
}
|
||||
|
||||
export interface WidgetCommonProps {
|
||||
selectedGridSize?: number;
|
||||
isEditView?: boolean;
|
||||
widgetKey: string;
|
||||
handleRemoveWidget?: (widgetKey: string) => void;
|
||||
|
||||
@ -16,7 +16,6 @@ import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import CustomizeMyData from '../../components/CustomizableComponents/CustomizeMyData/CustomizeMyData';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import { LANDING_PAGE_LAYOUT } from '../../constants/CustomizePage.constants';
|
||||
import { ClientErrors } from '../../enums/axios.enum';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { Document } from '../../generated/entity/docStore/document';
|
||||
@ -27,6 +26,7 @@ import {
|
||||
updateDocument,
|
||||
} from '../../rest/DocStoreAPI';
|
||||
import { getFinalLandingPage } from '../../utils/CustomizableLandingPageUtils';
|
||||
import customizePageClassBase from '../../utils/CustomizePageClassBase';
|
||||
|
||||
export const CustomizablePage = () => {
|
||||
const { fqn, pageFqn } = useParams<{ fqn: string; pageFqn: PageType }>();
|
||||
@ -57,7 +57,7 @@ export const CustomizablePage = () => {
|
||||
entityType: EntityType.PAGE,
|
||||
data: {
|
||||
page: {
|
||||
layout: LANDING_PAGE_LAYOUT,
|
||||
layout: customizePageClassBase.landingPageDefaultLayout,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -31,10 +31,6 @@ import Loader from '../../components/Loader/Loader';
|
||||
import RightSidebar from '../../components/MyData/RightSidebar/RightSidebar.component';
|
||||
import WelcomeScreen from '../../components/WelcomeScreen/WelcomeScreen.component';
|
||||
import { LOGGED_IN_USER_STORAGE_KEY } from '../../constants/constants';
|
||||
import {
|
||||
LANDING_PAGE_LAYOUT,
|
||||
LANDING_PAGE_WIDGET_MARGIN,
|
||||
} from '../../constants/CustomizePage.constants';
|
||||
import { LandingPageWidgetKeys } from '../../enums/CustomizablePage.enum';
|
||||
import { AssetsType, EntityType } from '../../enums/entity.enum';
|
||||
import { Thread } from '../../generated/entity/feed/thread';
|
||||
@ -44,7 +40,7 @@ import { useAuth } from '../../hooks/authHooks';
|
||||
import { getDocumentByFQN } from '../../rest/DocStoreAPI';
|
||||
import { getActiveAnnouncement } from '../../rest/feedsAPI';
|
||||
import { getUserById } from '../../rest/userAPI';
|
||||
import { CustomizePageClassBase } from '../../utils/CustomizePageClassBase';
|
||||
import customizePageClassBase from '../../utils/CustomizePageClassBase';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { WidgetConfig } from '../CustomizablePage/CustomizablePage.interface';
|
||||
import './my-data.less';
|
||||
@ -86,10 +82,10 @@ const MyDataPageV1 = () => {
|
||||
const pageData = await getDocumentByFQN(pageFQN);
|
||||
setLayout(pageData.data.page.layout);
|
||||
} else {
|
||||
setLayout(LANDING_PAGE_LAYOUT);
|
||||
setLayout(customizePageClassBase.landingPageDefaultLayout);
|
||||
}
|
||||
} catch {
|
||||
setLayout(LANDING_PAGE_LAYOUT);
|
||||
setLayout(customizePageClassBase.landingPageDefaultLayout);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@ -173,7 +169,7 @@ const MyDataPageV1 = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const Widget = CustomizePageClassBase.getWidgetsFromKey(widgetConfig.i);
|
||||
const Widget = customizePageClassBase.getWidgetsFromKey(widgetConfig.i);
|
||||
|
||||
return (
|
||||
<Widget
|
||||
@ -181,6 +177,7 @@ const MyDataPageV1 = () => {
|
||||
followedData={followedData ?? []}
|
||||
followedDataCount={followedDataCount}
|
||||
isLoadingOwnedData={isLoadingOwnedData}
|
||||
selectedGridSize={widgetConfig.w}
|
||||
widgetKey={widgetConfig.i}
|
||||
/>
|
||||
);
|
||||
@ -255,7 +252,10 @@ const MyDataPageV1 = () => {
|
||||
cols={{ lg: 4, md: 4, sm: 4, xs: 4, xxs: 4 }}
|
||||
draggableHandle=".drag-widget-icon"
|
||||
isResizable={false}
|
||||
margin={[LANDING_PAGE_WIDGET_MARGIN, LANDING_PAGE_WIDGET_MARGIN]}
|
||||
margin={[
|
||||
customizePageClassBase.landingPageWidgetMargin,
|
||||
customizePageClassBase.landingPageWidgetMargin,
|
||||
]}
|
||||
rowHeight={100}>
|
||||
{widgets}
|
||||
</ResponsiveGridLayout>
|
||||
|
||||
@ -273,6 +273,9 @@
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
.h-max-full {
|
||||
max-height: 100%;
|
||||
}
|
||||
.h-auto {
|
||||
height: auto;
|
||||
}
|
||||
@ -284,3 +287,11 @@
|
||||
.max-width-md {
|
||||
max-width: 768px;
|
||||
}
|
||||
|
||||
.overflow-y-scroll {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.overflow-x-scroll {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
@ -21,30 +21,26 @@ import {
|
||||
uniqueId,
|
||||
} from 'lodash';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
import {
|
||||
DEFAULT_WIDGET_HEIGHT,
|
||||
LANDING_PAGE_RIGHT_CONTAINER_EDIT_HEIGHT,
|
||||
LANDING_PAGE_ROW_HEIGHT,
|
||||
LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS,
|
||||
LANDING_PAGE_WIDGET_MARGIN,
|
||||
} from '../constants/CustomizePage.constants';
|
||||
import {
|
||||
LandingPageWidgetKeys,
|
||||
WidgetWidths,
|
||||
} from '../enums/CustomizablePage.enum';
|
||||
import { Document } from '../generated/entity/docStore/document';
|
||||
import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface';
|
||||
import customizePageClassBase from './CustomizePageClassBase';
|
||||
|
||||
export const getAddWidgetHandler =
|
||||
(
|
||||
newWidgetData: Document,
|
||||
placeholderWidgetKey: string,
|
||||
widgetWidth: number,
|
||||
maxGridSize: number
|
||||
) =>
|
||||
(currentLayout: Array<WidgetConfig>) => {
|
||||
const widgetFQN = uniqueId(`${newWidgetData.fullyQualifiedName}-`);
|
||||
const widgetWidth = getWidgetWidth(newWidgetData);
|
||||
const widgetHeight = getWidgetHeight(newWidgetData.name);
|
||||
const widgetHeight = customizePageClassBase.getWidgetHeight(
|
||||
newWidgetData.name
|
||||
);
|
||||
|
||||
// The widget with key "ExtraWidget.EmptyWidgetPlaceholder" will always remain in the bottom
|
||||
// and is not meant to be replaced hence
|
||||
@ -142,29 +138,6 @@ export const getWidgetWidth = (widget: Document) => {
|
||||
return widgetSize as number;
|
||||
};
|
||||
|
||||
export const getWidgetHeight = (widgetName: string) => {
|
||||
switch (widgetName) {
|
||||
case 'ActivityFeed':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.activityFeed;
|
||||
case 'RightSidebar':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.rightSidebar;
|
||||
case 'Announcements':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.announcements;
|
||||
case 'Following':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.following;
|
||||
case 'RecentlyViewed':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.recentlyViewed;
|
||||
case 'MyData':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.myData;
|
||||
case 'KPI':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.kpi;
|
||||
case 'TotalDataAssets':
|
||||
return LANDING_PAGE_WIDGET_DEFAULT_HEIGHTS.totalDataAssets;
|
||||
default:
|
||||
return DEFAULT_WIDGET_HEIGHT;
|
||||
}
|
||||
};
|
||||
|
||||
const getAllWidgetsArray = (layout: WidgetConfig[]) => {
|
||||
const widgetsArray: WidgetConfig[] = [];
|
||||
|
||||
@ -191,13 +164,15 @@ const getLayoutWithCalculatedRightPanelHeight = (
|
||||
const floorHeightAndPosValue = floor(widgetHeightAndPos);
|
||||
|
||||
const heightOfWidget =
|
||||
widgetHeightAndPos * LANDING_PAGE_ROW_HEIGHT +
|
||||
(floorHeightAndPosValue + 1) * LANDING_PAGE_WIDGET_MARGIN;
|
||||
widgetHeightAndPos * customizePageClassBase.landingPageRowHeight +
|
||||
(floorHeightAndPosValue + 1) *
|
||||
customizePageClassBase.landingPageWidgetMargin;
|
||||
|
||||
return {
|
||||
h: round(
|
||||
(heightOfWidget + LANDING_PAGE_WIDGET_MARGIN) /
|
||||
(LANDING_PAGE_ROW_HEIGHT + LANDING_PAGE_WIDGET_MARGIN),
|
||||
(heightOfWidget + customizePageClassBase.landingPageWidgetMargin) /
|
||||
(customizePageClassBase.landingPageRowHeight +
|
||||
customizePageClassBase.landingPageWidgetMargin),
|
||||
2
|
||||
),
|
||||
height: heightOfWidget,
|
||||
@ -211,7 +186,7 @@ const getLayoutWithCalculatedRightPanelHeight = (
|
||||
? {
|
||||
...widget,
|
||||
h: increaseHeight
|
||||
? LANDING_PAGE_RIGHT_CONTAINER_EDIT_HEIGHT
|
||||
? customizePageClassBase.landingPageRightContainerEditHeight
|
||||
: maxHeight?.h ?? widget.h,
|
||||
}
|
||||
: widget
|
||||
|
||||
@ -30,10 +30,121 @@ import FollowingWidget, {
|
||||
import RecentlyViewed from '../components/recently-viewed/RecentlyViewed';
|
||||
import TotalDataAssetsWidget from '../components/TotalDataAssetsWidget/TotalDataAssetsWidget.component';
|
||||
import FeedsWidget from '../components/Widgets/FeedsWidget/FeedsWidget.component';
|
||||
import { LandingPageWidgetKeys } from '../enums/CustomizablePage.enum';
|
||||
import { WidgetCommonProps } from '../pages/CustomizablePage/CustomizablePage.interface';
|
||||
import {
|
||||
LandingPageWidgetKeys,
|
||||
WidgetWidths,
|
||||
} from '../enums/CustomizablePage.enum';
|
||||
import {
|
||||
WidgetCommonProps,
|
||||
WidgetConfig,
|
||||
} from '../pages/CustomizablePage/CustomizablePage.interface';
|
||||
|
||||
class CustomizePageClassBase {
|
||||
defaultWidgetHeight = 3;
|
||||
landingPageWidgetMargin = 16;
|
||||
landingPageRowHeight = 100;
|
||||
landingPageRightContainerEditHeight = 16;
|
||||
landingPageMaxGridSize = 3;
|
||||
landingPageRightContainerMaxGridSize = 1;
|
||||
|
||||
landingPageWidgetDefaultHeights: Record<string, number> = {
|
||||
activityFeed: 5,
|
||||
rightSidebar: 11.5,
|
||||
announcements: 3.1,
|
||||
following: 2.4,
|
||||
recentlyViewed: 2.1,
|
||||
myData: 3.1,
|
||||
kpi: 3.1,
|
||||
totalAssets: 3.1,
|
||||
};
|
||||
|
||||
rightPanelDefaultLayout: Array<WidgetConfig> = [
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.announcements,
|
||||
i: LandingPageWidgetKeys.ANNOUNCEMENTS,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.following,
|
||||
i: LandingPageWidgetKeys.FOLLOWING,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 1.5,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.recentlyViewed,
|
||||
i: LandingPageWidgetKeys.RECENTLY_VIEWED,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 3,
|
||||
static: false,
|
||||
},
|
||||
];
|
||||
|
||||
landingPageDefaultLayout: Array<WidgetConfig> = [
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.activityFeed,
|
||||
i: LandingPageWidgetKeys.ACTIVITY_FEED,
|
||||
w: 3,
|
||||
x: 0,
|
||||
y: 0,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.rightSidebar,
|
||||
i: LandingPageWidgetKeys.RIGHT_PANEL,
|
||||
w: 1,
|
||||
x: 3,
|
||||
y: 0,
|
||||
data: {
|
||||
page: {
|
||||
layout: this.rightPanelDefaultLayout,
|
||||
},
|
||||
},
|
||||
static: true,
|
||||
},
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.myData,
|
||||
i: LandingPageWidgetKeys.MY_DATA,
|
||||
w: 1,
|
||||
x: 0,
|
||||
y: 6,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.kpi,
|
||||
i: LandingPageWidgetKeys.KPI,
|
||||
w: 2,
|
||||
x: 1,
|
||||
y: 6,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
h: this.landingPageWidgetDefaultHeights.totalAssets,
|
||||
i: LandingPageWidgetKeys.TOTAL_DATA_ASSETS,
|
||||
w: 3,
|
||||
x: 0,
|
||||
y: 9.1,
|
||||
static: false,
|
||||
},
|
||||
];
|
||||
|
||||
protected updateRightPanelDefaultLayout(layout: Array<WidgetConfig>) {
|
||||
this.rightPanelDefaultLayout = layout;
|
||||
}
|
||||
|
||||
protected updateLandingPageDefaultLayout(layout: Array<WidgetConfig>) {
|
||||
this.landingPageDefaultLayout = layout;
|
||||
}
|
||||
|
||||
protected updateLandingPageWidgetDefaultHeights(obj: Record<string, number>) {
|
||||
this.landingPageWidgetDefaultHeights = obj;
|
||||
}
|
||||
|
||||
export class CustomizePageClassBase {
|
||||
/**
|
||||
*
|
||||
* @param string widgetKey
|
||||
@ -49,11 +160,9 @@ export class CustomizePageClassBase {
|
||||
}
|
||||
>
|
||||
*/
|
||||
static getWidgetsFromKey = (
|
||||
public getWidgetsFromKey(
|
||||
widgetKey: string
|
||||
): FC<
|
||||
WidgetCommonProps & AnnouncementsWidgetProps & FollowingWidgetProps
|
||||
> => {
|
||||
): FC<WidgetCommonProps & AnnouncementsWidgetProps & FollowingWidgetProps> {
|
||||
if (widgetKey.startsWith(LandingPageWidgetKeys.ACTIVITY_FEED)) {
|
||||
return FeedsWidget;
|
||||
}
|
||||
@ -77,9 +186,9 @@ export class CustomizePageClassBase {
|
||||
}
|
||||
|
||||
return (() => null) as React.FC;
|
||||
};
|
||||
}
|
||||
|
||||
static getWidgetImageFromKey = (widgetKey: string): string => {
|
||||
public getWidgetImageFromKey(widgetKey: string, size?: number): string {
|
||||
switch (widgetKey) {
|
||||
case LandingPageWidgetKeys.ACTIVITY_FEED: {
|
||||
return ActivityFeedImg;
|
||||
@ -88,9 +197,17 @@ export class CustomizePageClassBase {
|
||||
return MyDataImg;
|
||||
}
|
||||
case LandingPageWidgetKeys.KPI: {
|
||||
if (size === WidgetWidths.small) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return KPIImg;
|
||||
}
|
||||
case LandingPageWidgetKeys.TOTAL_DATA_ASSETS: {
|
||||
if (size === WidgetWidths.medium) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return TotalAssetsImg;
|
||||
}
|
||||
case LandingPageWidgetKeys.ANNOUNCEMENTS: {
|
||||
@ -106,5 +223,33 @@ export class CustomizePageClassBase {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public getWidgetHeight(widgetName: string) {
|
||||
switch (widgetName) {
|
||||
case 'ActivityFeed':
|
||||
return this.landingPageWidgetDefaultHeights.activityFeed;
|
||||
case 'RightSidebar':
|
||||
return this.landingPageWidgetDefaultHeights.rightSidebar;
|
||||
case 'Announcements':
|
||||
return this.landingPageWidgetDefaultHeights.announcements;
|
||||
case 'Following':
|
||||
return this.landingPageWidgetDefaultHeights.following;
|
||||
case 'RecentlyViewed':
|
||||
return this.landingPageWidgetDefaultHeights.recentlyViewed;
|
||||
case 'MyData':
|
||||
return this.landingPageWidgetDefaultHeights.myData;
|
||||
case 'KPI':
|
||||
return this.landingPageWidgetDefaultHeights.kpi;
|
||||
case 'TotalAssets':
|
||||
return this.landingPageWidgetDefaultHeights.totalAssets;
|
||||
default:
|
||||
return this.defaultWidgetHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const customizePageClassBase = new CustomizePageClassBase();
|
||||
|
||||
export default customizePageClassBase;
|
||||
export { CustomizePageClassBase };
|
||||
|
||||
@ -328,18 +328,18 @@ export const getGlobalSettingsMenuWithPermission = (
|
||||
category: i18next.t('label.open-metadata'),
|
||||
key: 'openMetadata',
|
||||
items: [
|
||||
{
|
||||
label: i18next.t('label.email'),
|
||||
isProtected: Boolean(isAdminUser),
|
||||
key: 'openMetadata.email',
|
||||
icon: <EmailSettingsIcon className="w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: i18next.t('label.customize-landing-page'),
|
||||
isProtected: Boolean(isAdminUser),
|
||||
key: 'openMetadata.customizeLandingPage',
|
||||
icon: <CustomDashboardLogoIcon className="w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: i18next.t('label.email'),
|
||||
isProtected: Boolean(isAdminUser),
|
||||
key: 'openMetadata.email',
|
||||
icon: <EmailSettingsIcon className="w-4 side-panel-icons" />,
|
||||
},
|
||||
{
|
||||
label: i18next.t('label.custom-logo'),
|
||||
isProtected: Boolean(isAdminUser),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user