From 850a5f3af87f7bdf705b65e8fe5cc0ffce0357e3 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 26 Feb 2024 15:41:24 +0530 Subject: [PATCH 01/13] add metapilot app oss side config --- .../src/main/resources/ui/package.json | 1 + .../src/main/resources/ui/src/App.tsx | 13 ++-- .../ApplicationsProvider.interface.ts | 18 +++++ .../ApplicationsProvider.tsx | 69 +++++++++++++++++++ .../main/resources/ui/src/hooks/usePubSub.ts | 47 +++++++++++++ .../TableDetailsPageV1/TableDetailsPageV1.tsx | 9 +++ .../resources/ui/src/utils/AppsClassBase.ts | 28 ++++++++ .../src/main/resources/ui/yarn.lock | 5 ++ 8 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/AppsClassBase.ts diff --git a/openmetadata-ui/src/main/resources/ui/package.json b/openmetadata-ui/src/main/resources/ui/package.json index c0be71736dc2..5c6aedc9fbc5 100644 --- a/openmetadata-ui/src/main/resources/ui/package.json +++ b/openmetadata-ui/src/main/resources/ui/package.json @@ -84,6 +84,7 @@ "crypto-random-string-with-promisify-polyfill": "^5.0.0", "dagre": "^0.8.5", "diff": "^5.0.0", + "eventemitter3": "^5.0.1", "fast-json-patch": "^3.1.1", "history": "4.5.1", "html-react-parser": "^1.4.14", diff --git a/openmetadata-ui/src/main/resources/ui/src/App.tsx b/openmetadata-ui/src/main/resources/ui/src/App.tsx index d7abea1deac0..4fb86f836433 100644 --- a/openmetadata-ui/src/main/resources/ui/src/App.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/App.tsx @@ -22,6 +22,7 @@ import { AuthProvider } from './components/Auth/AuthProviders/AuthProvider'; import ErrorBoundary from './components/common/ErrorBoundary/ErrorBoundary'; import DomainProvider from './components/Domain/DomainProvider/DomainProvider'; import { EntityExportModalProvider } from './components/Entity/EntityExportModalProvider/EntityExportModalProvider.component'; +import ApplicationsProvider from './components/Settings/Applications/ApplicationsProvider/ApplicationsProvider'; import WebAnalyticsProvider from './components/WebAnalytics/WebAnalyticsProvider'; import { TOAST_OPTIONS } from './constants/Toasts.constants'; import ApplicationConfigProvider from './context/ApplicationConfigProvider/ApplicationConfigProvider'; @@ -49,11 +50,13 @@ const App: FC = () => { - - - - - + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts new file mode 100644 index 000000000000..2e21220e2b27 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 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 { App } from '../../../../generated/entity/applications/app'; + +export type ApplicationsContextType = { + applications: App[]; + loading: boolean; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx new file mode 100644 index 000000000000..16ca60af4cca --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2024 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 { isEmpty } from 'lodash'; +import React, { + createContext, + ReactNode, + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; +import { usePermissionProvider } from '../../../../context/PermissionProvider/PermissionProvider'; +import { App } from '../../../../generated/entity/applications/app'; +import { getApplicationList } from '../../../../rest/applicationAPI'; +import { ApplicationsContextType } from './ApplicationsProvider.interface'; + +export const ApplicationsContext = createContext({} as ApplicationsContextType); + +export const ApplicationsProvider = ({ children }: { children: ReactNode }) => { + const [applications, setApplications] = useState([]); + const [loading, setLoading] = useState(false); + const { permissions } = usePermissionProvider(); + + const fetchApplicationList = useCallback(async () => { + try { + setLoading(true); + const { data } = await getApplicationList({ + limit: 100, + }); + + setApplications(data); + } catch (err) { + // do not handle error + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + if (!isEmpty(permissions)) { + fetchApplicationList(); + } + }, [permissions]); + + const appContext = useMemo(() => { + return { applications, loading }; + }, [applications, loading]); + + return ( + + {children} + + ); +}; + +export const useApplicationsProvider = () => useContext(ApplicationsContext); + +export default ApplicationsProvider; diff --git a/openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts b/openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts new file mode 100644 index 000000000000..a7a4fff5b9eb --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2024 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 { EventEmitter } from 'eventemitter3'; +import { DependencyList, useEffect } from 'react'; + +type EventCallback = (data: T) => void; +type UnsubscribeFunction = () => void; + +const emitter = new EventEmitter(); + +export const useSub = ( + event: string, + callback: EventCallback, + dependencies?: DependencyList +): UnsubscribeFunction => { + const unsubscribe = () => { + emitter.off(event, callback); + }; + + useEffect(() => { + emitter.on(event, callback); + + // If dependencies are provided, remove the callback when the component unmounts + return () => { + emitter.off(event, callback); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, dependencies ?? []); + + return unsubscribe; +}; + +export const usePub = () => { + return (event: string, data: T) => { + emitter.emit(event, data); + }; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx index 69aaba6c7bf3..a46770546781 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx @@ -65,6 +65,7 @@ import { JoinedWith, Table } from '../../generated/entity/data/table'; import { ThreadType } from '../../generated/entity/feed/thread'; import { TagLabel } from '../../generated/type/tagLabel'; import { useFqn } from '../../hooks/useFqn'; +import { useSub } from '../../hooks/usePubSub'; import { FeedCounts } from '../../interface/feed.interface'; import { postThread } from '../../rest/feedsAPI'; import { getQueriesList } from '../../rest/queryAPI'; @@ -913,6 +914,14 @@ const TableDetailsPageV1 = () => { } }, [tableDetails?.fullyQualifiedName]); + useSub( + 'updateDetails', + (_) => { + // handle table update here + }, + [tableDetails] + ); + const onThreadPanelClose = () => { setThreadLink(''); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AppsClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/AppsClassBase.ts new file mode 100644 index 000000000000..e650a1dcce4b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AppsClassBase.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2024 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 { FC } from 'react'; + +class AppsClassBase { + public getMetaPilotActionButton(): FC<{ + entityId: string; + entityType: string; + }> | null { + return null; + } +} + +const appsClassBase = new AppsClassBase(); + +export default appsClassBase; + +export { AppsClassBase }; diff --git a/openmetadata-ui/src/main/resources/ui/yarn.lock b/openmetadata-ui/src/main/resources/ui/yarn.lock index 339cce488878..f0dbc8ee4c5f 100644 --- a/openmetadata-ui/src/main/resources/ui/yarn.lock +++ b/openmetadata-ui/src/main/resources/ui/yarn.lock @@ -7793,6 +7793,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.1, eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" From c215c3cdd16574e96c6008c579e4a09bdeb7be93 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 26 Feb 2024 15:41:24 +0530 Subject: [PATCH 02/13] add metapilot app oss side config --- .../src/main/resources/ui/package.json | 1 + .../src/main/resources/ui/src/App.tsx | 13 ++-- .../ApplicationsProvider.interface.ts | 18 +++++ .../ApplicationsProvider.tsx | 69 +++++++++++++++++++ .../main/resources/ui/src/hooks/usePubSub.ts | 47 +++++++++++++ .../TableDetailsPageV1/TableDetailsPageV1.tsx | 9 +++ .../src/main/resources/ui/yarn.lock | 5 ++ 7 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts diff --git a/openmetadata-ui/src/main/resources/ui/package.json b/openmetadata-ui/src/main/resources/ui/package.json index c0be71736dc2..5c6aedc9fbc5 100644 --- a/openmetadata-ui/src/main/resources/ui/package.json +++ b/openmetadata-ui/src/main/resources/ui/package.json @@ -84,6 +84,7 @@ "crypto-random-string-with-promisify-polyfill": "^5.0.0", "dagre": "^0.8.5", "diff": "^5.0.0", + "eventemitter3": "^5.0.1", "fast-json-patch": "^3.1.1", "history": "4.5.1", "html-react-parser": "^1.4.14", diff --git a/openmetadata-ui/src/main/resources/ui/src/App.tsx b/openmetadata-ui/src/main/resources/ui/src/App.tsx index d7abea1deac0..4fb86f836433 100644 --- a/openmetadata-ui/src/main/resources/ui/src/App.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/App.tsx @@ -22,6 +22,7 @@ import { AuthProvider } from './components/Auth/AuthProviders/AuthProvider'; import ErrorBoundary from './components/common/ErrorBoundary/ErrorBoundary'; import DomainProvider from './components/Domain/DomainProvider/DomainProvider'; import { EntityExportModalProvider } from './components/Entity/EntityExportModalProvider/EntityExportModalProvider.component'; +import ApplicationsProvider from './components/Settings/Applications/ApplicationsProvider/ApplicationsProvider'; import WebAnalyticsProvider from './components/WebAnalytics/WebAnalyticsProvider'; import { TOAST_OPTIONS } from './constants/Toasts.constants'; import ApplicationConfigProvider from './context/ApplicationConfigProvider/ApplicationConfigProvider'; @@ -49,11 +50,13 @@ const App: FC = () => { - - - - - + + + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts new file mode 100644 index 000000000000..2e21220e2b27 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 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 { App } from '../../../../generated/entity/applications/app'; + +export type ApplicationsContextType = { + applications: App[]; + loading: boolean; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx new file mode 100644 index 000000000000..16ca60af4cca --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2024 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 { isEmpty } from 'lodash'; +import React, { + createContext, + ReactNode, + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; +import { usePermissionProvider } from '../../../../context/PermissionProvider/PermissionProvider'; +import { App } from '../../../../generated/entity/applications/app'; +import { getApplicationList } from '../../../../rest/applicationAPI'; +import { ApplicationsContextType } from './ApplicationsProvider.interface'; + +export const ApplicationsContext = createContext({} as ApplicationsContextType); + +export const ApplicationsProvider = ({ children }: { children: ReactNode }) => { + const [applications, setApplications] = useState([]); + const [loading, setLoading] = useState(false); + const { permissions } = usePermissionProvider(); + + const fetchApplicationList = useCallback(async () => { + try { + setLoading(true); + const { data } = await getApplicationList({ + limit: 100, + }); + + setApplications(data); + } catch (err) { + // do not handle error + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + if (!isEmpty(permissions)) { + fetchApplicationList(); + } + }, [permissions]); + + const appContext = useMemo(() => { + return { applications, loading }; + }, [applications, loading]); + + return ( + + {children} + + ); +}; + +export const useApplicationsProvider = () => useContext(ApplicationsContext); + +export default ApplicationsProvider; diff --git a/openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts b/openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts new file mode 100644 index 000000000000..a7a4fff5b9eb --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/hooks/usePubSub.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2024 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 { EventEmitter } from 'eventemitter3'; +import { DependencyList, useEffect } from 'react'; + +type EventCallback = (data: T) => void; +type UnsubscribeFunction = () => void; + +const emitter = new EventEmitter(); + +export const useSub = ( + event: string, + callback: EventCallback, + dependencies?: DependencyList +): UnsubscribeFunction => { + const unsubscribe = () => { + emitter.off(event, callback); + }; + + useEffect(() => { + emitter.on(event, callback); + + // If dependencies are provided, remove the callback when the component unmounts + return () => { + emitter.off(event, callback); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, dependencies ?? []); + + return unsubscribe; +}; + +export const usePub = () => { + return (event: string, data: T) => { + emitter.emit(event, data); + }; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx index 69aaba6c7bf3..a46770546781 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx @@ -65,6 +65,7 @@ import { JoinedWith, Table } from '../../generated/entity/data/table'; import { ThreadType } from '../../generated/entity/feed/thread'; import { TagLabel } from '../../generated/type/tagLabel'; import { useFqn } from '../../hooks/useFqn'; +import { useSub } from '../../hooks/usePubSub'; import { FeedCounts } from '../../interface/feed.interface'; import { postThread } from '../../rest/feedsAPI'; import { getQueriesList } from '../../rest/queryAPI'; @@ -913,6 +914,14 @@ const TableDetailsPageV1 = () => { } }, [tableDetails?.fullyQualifiedName]); + useSub( + 'updateDetails', + (_) => { + // handle table update here + }, + [tableDetails] + ); + const onThreadPanelClose = () => { setThreadLink(''); }; diff --git a/openmetadata-ui/src/main/resources/ui/yarn.lock b/openmetadata-ui/src/main/resources/ui/yarn.lock index 339cce488878..f0dbc8ee4c5f 100644 --- a/openmetadata-ui/src/main/resources/ui/yarn.lock +++ b/openmetadata-ui/src/main/resources/ui/yarn.lock @@ -7793,6 +7793,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.1, eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" From b19bd7d741e9d3aec594ed4cd94c874cee81180f Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 4 Mar 2024 19:54:59 +0530 Subject: [PATCH 03/13] suggestions changes --- .../src/main/resources/ui/src/App.tsx | 9 +- .../components/AppRouter/withSuggestions.tsx | 24 +++ ...seFallback.js => withSuspenseFallback.tsx} | 10 +- .../TableDescription.component.tsx | 68 ++++++-- .../SuggestionsAlert.interface.ts | 19 +++ .../SuggestionsAlert/SuggestionsAlert.tsx | 88 ++++++++++ .../SuggestionsAlert/suggestions-alert.less | 23 +++ .../SuggestionsProvider.interface.ts | 33 ++++ .../SuggestionsProvider.tsx | 161 ++++++++++++++++++ .../common/AvatarCarousel/AvatarCarousel.tsx | 82 +++++++++ .../AvatarCarousel/avatar-carousel.less | 18 ++ .../EntityDescription/DescriptionV1.tsx | 75 ++++++-- .../ui/src/locale/languages/en-us.json | 1 + .../TableDetailsPageV1/TableDetailsPageV1.tsx | 4 +- .../resources/ui/src/rest/suggestionsAPI.ts | 41 +++++ 15 files changed, 621 insertions(+), 35 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuggestions.tsx rename openmetadata-ui/src/main/resources/ui/src/components/AppRouter/{withSuspenseFallback.js => withSuspenseFallback.tsx} (77%) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/SuggestionsAlert.interface.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/SuggestionsAlert.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/suggestions-alert.less create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.interface.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsProvider/SuggestionsProvider.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/avatar-carousel.less create mode 100644 openmetadata-ui/src/main/resources/ui/src/rest/suggestionsAPI.ts diff --git a/openmetadata-ui/src/main/resources/ui/src/App.tsx b/openmetadata-ui/src/main/resources/ui/src/App.tsx index 4fb86f836433..e53bdc62a23e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/App.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/App.tsx @@ -23,6 +23,7 @@ import ErrorBoundary from './components/common/ErrorBoundary/ErrorBoundary'; import DomainProvider from './components/Domain/DomainProvider/DomainProvider'; import { EntityExportModalProvider } from './components/Entity/EntityExportModalProvider/EntityExportModalProvider.component'; import ApplicationsProvider from './components/Settings/Applications/ApplicationsProvider/ApplicationsProvider'; +import SuggestionsProvider from './components/Suggestions/SuggestionsProvider/SuggestionsProvider'; import WebAnalyticsProvider from './components/WebAnalytics/WebAnalyticsProvider'; import { TOAST_OPTIONS } from './constants/Toasts.constants'; import ApplicationConfigProvider from './context/ApplicationConfigProvider/ApplicationConfigProvider'; @@ -52,9 +53,11 @@ const App: FC = () => { - - - + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuggestions.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuggestions.tsx new file mode 100644 index 000000000000..8083a2d07415 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuggestions.tsx @@ -0,0 +1,24 @@ +/* + * Copyright 2024 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 React, { FC } from 'react'; +import SuggestionsProvider from '../Suggestions/SuggestionsProvider/SuggestionsProvider'; + +export const withSuggestions = + (Component: FC) => + (props: JSX.IntrinsicAttributes & { children?: React.ReactNode } & T) => { + return ( + + + + ); + }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.js b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx similarity index 77% rename from openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.js rename to openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx index 5c69f2be4081..4fdd5d3b1e54 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.js +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx @@ -11,11 +11,15 @@ * limitations under the License. */ -import React, { Suspense } from 'react'; +import React, { FC, Suspense } from 'react'; import Loader from '../common/Loader/Loader'; -export default function withSuspenseFallback(Component) { - return function DefaultFallback(props) { +export default function withSuspenseFallback( + Component: FC +) { + return function DefaultFallback( + props: JSX.IntrinsicAttributes & { children?: React.ReactNode } & T + ) { return ( { const { t } = useTranslation(); + const { selectedUserSuggestions = [] } = useSuggestionsContext(); - return ( - - {columnData.field ? ( - - ) : ( + const entityLink = useMemo( + () => + entityType === EntityType.TABLE + ? EntityLink.getTableEntityLink( + entityFqn, + columnData.record?.name ?? '' + ) + : getEntityFeedLink(entityType, columnData.fqn), + [entityType, entityFqn] + ); + + const suggestionData = useMemo(() => { + const activeSuggestion = selectedUserSuggestions.find( + (suggestion) => suggestion.entityLink === entityLink + ); + + if (activeSuggestion?.entityLink === entityLink) { + return ( + + ); + } + + return null; + }, [hasEditPermission, entityLink, selectedUserSuggestions]); + + const descriptionContent = useMemo(() => { + if (suggestionData) { + return suggestionData; + } else if (columnData.field) { + return ; + } else { + return ( {t('label.no-entity', { entity: t('label.description'), })} - )} - {!isReadOnly ? ( + ); + } + }, [columnData, suggestionData]); + + return ( + + {descriptionContent} + + {!suggestionData && !isReadOnly ? ( {hasEditPermission && ( + + + ); +}; + +export default SuggestionsSlider; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx index f16b36a62612..bca33b9356a2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx @@ -52,7 +52,7 @@ const AvatarCarousel = () => { onProfileClick(to)} dots={false} - slidesToShow={2}> + slidesToShow={avatarList.length < 3 ? avatarList.length : 3}> {avatarList.map((avatar, index) => (
{ }`} key={index} onClick={() => onProfileClick(index)}> - + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/avatar-carousel.less b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/avatar-carousel.less index 84bd3b20b4ad..b929e97a3f3e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/avatar-carousel.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/avatar-carousel.less @@ -16,3 +16,9 @@ opacity: 1; } } + +.avatar-carousel-container { + .slick-slide { + width: 32px !important; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/EntityDescription/DescriptionV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/EntityDescription/DescriptionV1.tsx index 6e6cfc496113..1407bc638d66 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/EntityDescription/DescriptionV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/EntityDescription/DescriptionV1.tsx @@ -33,7 +33,7 @@ import { import { ModalWithMarkdownEditor } from '../../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import SuggestionsAlert from '../../Suggestions/SuggestionsAlert/SuggestionsAlert'; import { useSuggestionsContext } from '../../Suggestions/SuggestionsProvider/SuggestionsProvider'; -import AvatarCarousel from '../AvatarCarousel/AvatarCarousel'; +import SuggestionsSlider from '../../Suggestions/SuggestionsSlider/SuggestionsSlider'; import RichTextEditorPreviewer from '../RichTextEditor/RichTextEditorPreviewer'; const { Text } = Typography; @@ -80,7 +80,8 @@ const DescriptionV1 = ({ reduceDescription, }: Props) => { const history = useHistory(); - const { suggestions, selectedUserSuggestions = [] } = useSuggestionsContext(); + const { suggestions = [], selectedUserSuggestions = [] } = + useSuggestionsContext(); const handleRequestDescription = useCallback(() => { history.push( @@ -230,7 +231,7 @@ const DescriptionV1 = ({ {t('label.description')} {showActions && actionButtons}
- {suggestions.length > 0 && } + {suggestions.length > 0 && }
diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 09ef19b24f36..3002775f5aa0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -2,6 +2,7 @@ "label": { "aborted": "Aborted", "accept": "Accept", + "accept-all": "Accept All", "accept-suggestion": "Accept Suggestion", "access": "Access", "access-block-time": "Access block time", @@ -869,6 +870,7 @@ "region-name": "Region Name", "registry": "Registry", "reject": "Reject", + "reject-all": "Reject All", "rejected": "Rejected", "related-term-plural": "Related Terms", "relevance": "Relevance", diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx index ee3235c7635b..b6a44d69af3f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx @@ -22,6 +22,7 @@ import { useHistory, useParams } from 'react-router-dom'; import { useActivityFeedProvider } from '../../components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; import { ActivityFeedTab } from '../../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component'; import ActivityThreadPanel from '../../components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; +import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import { withSuggestions } from '../../components/AppRouter/withSuggestions'; import { useAuthContext } from '../../components/Auth/AuthProviders/AuthProvider'; import { CustomPropertyTable } from '../../components/common/CustomPropertyTable/CustomPropertyTable'; @@ -65,6 +66,7 @@ import { import { CreateThread } from '../../generated/api/feed/createThread'; import { Tag } from '../../generated/entity/classification/tag'; import { JoinedWith, Table } from '../../generated/entity/data/table'; +import { Suggestion } from '../../generated/entity/feed/suggestion'; import { ThreadType } from '../../generated/entity/feed/thread'; import { TagLabel } from '../../generated/type/tagLabel'; import { useFqn } from '../../hooks/useFqn'; @@ -88,6 +90,7 @@ import { sortTagsCaseInsensitive, } from '../../utils/CommonUtils'; import { defaultFields } from '../../utils/DatasetDetailsUtils'; +import EntityLink from '../../utils/EntityLink'; import { getEntityName } from '../../utils/EntityUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils'; @@ -97,7 +100,7 @@ import { FrequentlyJoinedTables } from './FrequentlyJoinedTables/FrequentlyJoine import './table-details-page-v1.less'; import TableConstraints from './TableConstraints/TableConstraints'; -const TableDetailsPageV1 = () => { +const TableDetailsPageV1: React.FC = () => { const { isTourOpen, activeTabForTourDatasetPage, isTourPage } = useTourProvider(); const { currentUser } = useAuthContext(); @@ -898,6 +901,49 @@ const TableDetailsPageV1 = () => { })); }, []); + const updateDescriptionFromSuggestions = useCallback( + (suggestion: Suggestion) => { + setTableDetails((prev) => { + if (!prev) { + return; + } + + const activeCol = prev?.columns.find((column) => { + return ( + EntityLink.getTableEntityLink( + prev.fullyQualifiedName ?? '', + column.name ?? '' + ) === suggestion.entityLink + ); + }); + + if (!activeCol) { + return { + ...prev, + description: suggestion.description, + }; + } else { + const updatedColumns = prev.columns.map((column) => { + if (column.fullyQualifiedName === activeCol.fullyQualifiedName) { + return { + ...column, + description: suggestion.description, + }; + } else { + return column; + } + }); + + return { + ...prev, + columns: updatedColumns, + }; + } + }); + }, + [] + ); + useEffect(() => { if (isTourOpen || isTourPage) { setTableDetails(mockDatasetData.tableDetails as unknown as Table); @@ -915,8 +961,8 @@ const TableDetailsPageV1 = () => { useSub( 'updateDetails', - (_) => { - // handle table update here + (suggestion: Suggestion) => { + updateDescriptionFromSuggestions(suggestion); }, [tableDetails] ); @@ -1023,4 +1069,4 @@ const TableDetailsPageV1 = () => { ); }; -export default withSuggestions(TableDetailsPageV1); +export default withSuggestions(withActivityFeed(TableDetailsPageV1)); From 7e67b5882df774f7ba7bb50f6fd847177bd72b6a Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Mar 2024 15:44:03 +0530 Subject: [PATCH 06/13] localisation --- .../src/main/resources/ui/src/locale/languages/de-de.json | 2 ++ .../src/main/resources/ui/src/locale/languages/es-es.json | 2 ++ .../src/main/resources/ui/src/locale/languages/fr-fr.json | 2 ++ .../src/main/resources/ui/src/locale/languages/he-he.json | 2 ++ .../src/main/resources/ui/src/locale/languages/ja-jp.json | 2 ++ .../src/main/resources/ui/src/locale/languages/nl-nl.json | 2 ++ .../src/main/resources/ui/src/locale/languages/pt-br.json | 2 ++ .../src/main/resources/ui/src/locale/languages/ru-ru.json | 2 ++ .../src/main/resources/ui/src/locale/languages/zh-cn.json | 2 ++ 9 files changed, 18 insertions(+) diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index d8adb8c7d0d3..a2f6b9f7f664 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -2,6 +2,7 @@ "label": { "aborted": "Abgebrochen", "accept": "Akzeptieren", + "accept-all": "Accept All", "accept-suggestion": "Vorschlag akzeptieren", "access": "Zugriff", "access-block-time": "Access block time", @@ -869,6 +870,7 @@ "region-name": "Region Name", "registry": "Register", "reject": "Ablehnen", + "reject-all": "Reject All", "rejected": "Rejected", "related-term-plural": "Verwandte Begriffe", "relevance": "Relevanz", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index dc8db766625c..86de2b4e0b59 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -2,6 +2,7 @@ "label": { "aborted": "Abortado", "accept": "Aceptar", + "accept-all": "Accept All", "accept-suggestion": "Aceptar sugerencia", "access": "Acceso", "access-block-time": "Tiempo de Bloqueo de Acceso", @@ -869,6 +870,7 @@ "region-name": "Nombre de la región", "registry": "Registro", "reject": "Rechazar", + "reject-all": "Reject All", "rejected": "Rechazado", "related-term-plural": "Términos relacionados", "relevance": "Relevancia", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index b3ba41b6ccea..1558359c8a20 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -2,6 +2,7 @@ "label": { "aborted": "Interrompu", "accept": "Accepter", + "accept-all": "Accept All", "accept-suggestion": "Accepter la Suggestion", "access": "Accès", "access-block-time": "Access block time", @@ -869,6 +870,7 @@ "region-name": "Nom de Région", "registry": "Registre", "reject": "Rejeter", + "reject-all": "Reject All", "rejected": "Rejected", "related-term-plural": "Termes Liés", "relevance": "Pertinence", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 9362e12b1a3c..889587b43bec 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -2,6 +2,7 @@ "label": { "aborted": "בוטל", "accept": "קבל", + "accept-all": "Accept All", "accept-suggestion": "קבל הצעה", "access": "גישה", "access-block-time": "זמן חסימת גישה", @@ -869,6 +870,7 @@ "region-name": "שם האזור", "registry": "רשומון", "reject": "דחה", + "reject-all": "Reject All", "rejected": "נדחה", "related-term-plural": "מונחים קשורים", "relevance": "רלוונטיות", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index fe75669fca52..b304ce8e1bd0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -2,6 +2,7 @@ "label": { "aborted": "中断", "accept": "Accept", + "accept-all": "Accept All", "accept-suggestion": "提案を受け入れる", "access": "アクセス", "access-block-time": "Access block time", @@ -869,6 +870,7 @@ "region-name": "リージョン名", "registry": "レジストリ", "reject": "Reject", + "reject-all": "Reject All", "rejected": "Rejected", "related-term-plural": "関連する用語", "relevance": "Relevance", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index e92752c46ed8..2186e8c52295 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -2,6 +2,7 @@ "label": { "aborted": "Afgebroken", "accept": "Accepteren", + "accept-all": "Accept All", "accept-suggestion": "Suggestie Accepteren", "access": "Toegang", "access-block-time": "Blokkeertijd Toegang", @@ -869,6 +870,7 @@ "region-name": "Regionaam", "registry": "Register", "reject": "Weigeren", + "reject-all": "Reject All", "rejected": "Geweigerd", "related-term-plural": "Gerelateerde termen", "relevance": "Relevantie", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 27a60f349ed1..2af6501581a6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -2,6 +2,7 @@ "label": { "aborted": "Abortado", "accept": "Aceitar", + "accept-all": "Accept All", "accept-suggestion": "Aceitar Sugestão", "access": "Acesso", "access-block-time": "Tempo de bloqueio de acesso", @@ -869,6 +870,7 @@ "region-name": "Nome da Região", "registry": "Registro", "reject": "Rejeitar", + "reject-all": "Reject All", "rejected": "Rejeitado", "related-term-plural": "Termos Relacionados", "relevance": "Relevância", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index e28c091e40fb..ecf4c3f0b9ed 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -2,6 +2,7 @@ "label": { "aborted": "Прервано", "accept": "Принять", + "accept-all": "Accept All", "accept-suggestion": "Согласовать предложение", "access": "Доступ", "access-block-time": "Access block time", @@ -869,6 +870,7 @@ "region-name": "Наименование региона", "registry": "Реестр", "reject": "Reject", + "reject-all": "Reject All", "rejected": "Rejected", "related-term-plural": "Связанные термины", "relevance": "Актуальность", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index c777472b7e3c..f5cc6ae8e360 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -2,6 +2,7 @@ "label": { "aborted": "已中止", "accept": "接受", + "accept-all": "Accept All", "accept-suggestion": "接受建议", "access": "访问", "access-block-time": "Access block time", @@ -869,6 +870,7 @@ "region-name": "区域名称", "registry": "仓库", "reject": "Reject", + "reject-all": "Reject All", "rejected": "Rejected", "related-term-plural": "关联术语", "relevance": "相关性", From 192f15460ac874d3f3945dafc5eba6338307777e Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Mar 2024 20:52:26 +0530 Subject: [PATCH 07/13] add suggestions count button --- .../SchemaTab/SchemaTab.component.tsx | 38 +++++++--- .../SuggestionsAlert.test.tsx | 74 +++++++++++++++++++ .../SuggestionsAlert/SuggestionsAlert.tsx | 2 + .../SuggestionsAlert/suggestions-alert.less | 7 ++ .../SuggestionsProvider.tsx | 1 - .../SuggestionsSlider/SuggestionsSlider.tsx | 27 ++++--- .../ui/src/locale/languages/en-us.json | 1 + .../TableDetailsPageV1.test.tsx | 23 ++++++ 8 files changed, 152 insertions(+), 21 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/SuggestionsAlert.test.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx index a6c7f3884a88..88b3e69ead8c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx @@ -11,10 +11,13 @@ * limitations under the License. */ +import { Button } from 'antd'; import { t } from 'i18next'; import { lowerCase } from 'lodash'; -import React, { Fragment, FunctionComponent, useState } from 'react'; +import React, { Fragment, FunctionComponent, useMemo, useState } from 'react'; +import EntityLink from '../../../utils/EntityLink'; import Searchbar from '../../common/SearchBarComponent/SearchBar.component'; +import { useSuggestionsContext } from '../../Suggestions/SuggestionsProvider/SuggestionsProvider'; import SchemaTable from '../SchemaTable/SchemaTable.component'; import { Props } from './SchemaTab.interfaces'; @@ -31,23 +34,38 @@ const SchemaTab: FunctionComponent = ({ tableConstraints, }: Props) => { const [searchText, setSearchText] = useState(''); + const { selectedUserSuggestions } = useSuggestionsContext(); const handleSearchAction = (searchValue: string) => { setSearchText(searchValue); }; + const columnSuggestions = useMemo( + () => + selectedUserSuggestions?.filter( + (item) => EntityLink.getTableColumnName(item.entityLink) !== undefined + ), + [selectedUserSuggestions] + ); + return ( -
- +
+
+ +
+ {columnSuggestions.length > 0 && ( + + )}
- ({ + useFqn: jest.fn().mockImplementation(() => ({ fqn: 'testFqn' })), +})); + +jest.mock('../../common/ProfilePicture/ProfilePicture', () => { + return jest.fn().mockImplementation(() =>

ProfilePicture

); +}); + +jest.mock('../SuggestionsProvider/SuggestionsProvider', () => ({ + useSuggestionsContext: jest.fn().mockImplementation(() => ({ + suggestions: [ + { + id: '1', + description: 'Test suggestion', + createdBy: { id: '1', name: 'Test User', type: 'user' }, + entityLink: '<#E::table::sample_data.ecommerce_db.shopify.dim_address>', + }, + ], + acceptRejectSuggestion: jest.fn(), + })), + __esModule: true, + default: 'SuggestionsProvider', +})); + +describe('SuggestionsAlert', () => { + const mockSuggestion: Suggestion = { + id: '1', + description: 'Test suggestion', + createdBy: { id: '1', name: 'Test User', type: 'user' }, + entityLink: '<#E::table::sample_data.ecommerce_db.shopify.dim_address>', + }; + + it('renders alert without access', () => { + render( + + + + ); + + expect(screen.getByText(/Test suggestion/i)).toBeInTheDocument(); + expect(screen.getByText(/Test User/i)).toBeInTheDocument(); + }); + + it('renders alert with access', () => { + render( + + + + ); + + expect(screen.getByText(/Test suggestion/i)).toBeInTheDocument(); + expect(screen.getByText(/Test User/i)).toBeInTheDocument(); + expect(screen.getByTestId('reject-suggestion')).toBeInTheDocument(); + expect(screen.getByTestId('accept-suggestion')).toBeInTheDocument(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/SuggestionsAlert.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/SuggestionsAlert.tsx index d0549d9eecef..407573c35da9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/SuggestionsAlert.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Suggestions/SuggestionsAlert/SuggestionsAlert.tsx @@ -63,6 +63,7 @@ const SuggestionsAlert = ({
- + {selectedUserSuggestions.length > 0 && ( + <> + + + + )}
); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 3002775f5aa0..5ff84f732e57 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "Suggestion", "suggestion-lowercase-plural": "suggestions", + "suggestion-pending": "Suggestions Pending", "suite": "Suite", "sum": "Sum", "summary": "Summary", diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx index 9b62b0dbc0f1..80be9e4b90d8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx @@ -43,6 +43,10 @@ jest.mock('../../rest/tableAPI', () => ({ restoreTable: jest.fn(), })); +jest.mock('../../rest/suggestionsAPI', () => ({ + getSuggestionsList: jest.fn().mockImplementation(() => Promise.resolve([])), +})); + jest.mock('../../utils/CommonUtils', () => ({ getFeedCounts: jest.fn(), getPartialNameFromTableFQN: jest.fn().mockImplementation(() => 'fqn'), @@ -157,6 +161,25 @@ jest.mock( }) ); +jest.mock( + '../../components/Suggestions/SuggestionsProvider/SuggestionsProvider', + () => ({ + useSuggestionsContext: jest.fn().mockImplementation(() => ({ + suggestions: [], + suggestionsByUser: new Map(), + selectedUserSuggestions: [], + entityFqn: 'fqn', + loading: false, + allSuggestionsUsers: [], + onUpdateActiveUser: jest.fn(), + fetchSuggestions: jest.fn(), + acceptRejectSuggestion: jest.fn(), + })), + __esModule: true, + default: 'SuggestionsProvider', + }) +); + jest.mock('react-router-dom', () => ({ useParams: jest .fn() From 5f3103b1013de3ce26bac62589ddabbfb0a9d490 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Mar 2024 20:54:16 +0530 Subject: [PATCH 08/13] locales --- .../src/main/resources/ui/src/locale/languages/de-de.json | 1 + .../src/main/resources/ui/src/locale/languages/es-es.json | 1 + .../src/main/resources/ui/src/locale/languages/fr-fr.json | 1 + .../src/main/resources/ui/src/locale/languages/he-he.json | 1 + .../src/main/resources/ui/src/locale/languages/ja-jp.json | 1 + .../src/main/resources/ui/src/locale/languages/nl-nl.json | 1 + .../src/main/resources/ui/src/locale/languages/pt-br.json | 1 + .../src/main/resources/ui/src/locale/languages/ru-ru.json | 1 + .../src/main/resources/ui/src/locale/languages/zh-cn.json | 1 + 9 files changed, 9 insertions(+) diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index a2f6b9f7f664..196796ec670d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "Vorschlag", "suggestion-lowercase-plural": "Vorschläge", + "suggestion-pending": "Suggestions Pending", "suite": "Suite", "sum": "Summe", "summary": "Zusammenfassung", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index 86de2b4e0b59..bc1c0c26881e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Descripciones Sugeridas", "suggestion": "Sugerencia", "suggestion-lowercase-plural": "sugerencias", + "suggestion-pending": "Suggestions Pending", "suite": "Suite", "sum": "Suma", "summary": "Resumen", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 1558359c8a20..00a9ec2c1766 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "Suggestion", "suggestion-lowercase-plural": "suggestions", + "suggestion-pending": "Suggestions Pending", "suite": "Ensemble", "sum": "Somme", "summary": "Résumé", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 889587b43bec..7602db042390 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "הצעה", "suggestion-lowercase-plural": "הצעות", + "suggestion-pending": "Suggestions Pending", "suite": "יחידת בדיקה", "sum": "סכום", "summary": "סיכום", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index b304ce8e1bd0..18ece7701951 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "提案", "suggestion-lowercase-plural": "提案", + "suggestion-pending": "Suggestions Pending", "suite": "スイート", "sum": "合計", "summary": "サマリ", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index 2186e8c52295..f9b64547ce79 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Voorgestelde beschrijvingen", "suggestion": "Suggestie", "suggestion-lowercase-plural": "suggesties", + "suggestion-pending": "Suggestions Pending", "suite": "Suite", "sum": "Som", "summary": "Samenvatting", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 2af6501581a6..dfb5585d5425 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "Sugestão", "suggestion-lowercase-plural": "sugestões", + "suggestion-pending": "Suggestions Pending", "suite": "Conjuto de Testes", "sum": "Soma", "summary": "Resumo", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index ecf4c3f0b9ed..beab4604a2cf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "Предложение", "suggestion-lowercase-plural": "предложения", + "suggestion-pending": "Suggestions Pending", "suite": "Набор", "sum": "Сумма", "summary": "Сводка", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index f5cc6ae8e360..a8e4787174c4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -1057,6 +1057,7 @@ "suggested-description-plural": "Suggested Descriptions", "suggestion": "建议", "suggestion-lowercase-plural": "建议", + "suggestion-pending": "Suggestions Pending", "suite": "套件", "sum": "总和", "summary": "概要", From 1929b2e57d57f60bf91c286eaff7eb854753070b Mon Sep 17 00:00:00 2001 From: karanh37 Date: Wed, 6 Mar 2024 13:51:30 +0530 Subject: [PATCH 09/13] fix tests --- .../src/components/Database/SchemaTab/SchemaTab.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx index 88b3e69ead8c..426883753328 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTab/SchemaTab.component.tsx @@ -44,7 +44,7 @@ const SchemaTab: FunctionComponent = ({ () => selectedUserSuggestions?.filter( (item) => EntityLink.getTableColumnName(item.entityLink) !== undefined - ), + ) ?? [], [selectedUserSuggestions] ); From c879dfeff5a76039745e6dfa188c5c473d512259 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Thu, 7 Mar 2024 20:54:10 +0530 Subject: [PATCH 10/13] add tests --- .../AvatarCarousel/AvatarCarousel.test.tsx | 88 +++++++++++++++++++ .../common/AvatarCarousel/AvatarCarousel.tsx | 17 +++- 2 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.test.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.test.tsx new file mode 100644 index 000000000000..8c74fd23c211 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2024 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 { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; +import AvatarCarousel from './AvatarCarousel'; + +jest.mock('../../Suggestions/SuggestionsProvider/SuggestionsProvider', () => ({ + useSuggestionsContext: jest.fn().mockImplementation(() => ({ + suggestions: [ + { + id: '1', + description: 'Test suggestion', + createdBy: { id: '1', name: 'Avatar 1', type: 'user' }, + entityLink: '<#E::table::sample_data.ecommerce_db.shopify.dim_address>', + }, + { + id: '2', + description: 'Test suggestion', + createdBy: { id: '2', name: 'Avatar 2', type: 'user' }, + entityLink: '<#E::table::sample_data.ecommerce_db.shopify.dim_address>', + }, + ], + allSuggestionsUsers: [ + { id: '1', name: 'Avatar 1', type: 'user' }, + { id: '2', name: 'Avatar 2', type: 'user' }, + ], + acceptRejectSuggestion: jest.fn(), + onUpdateActiveUser: jest.fn(), + })), + __esModule: true, + default: 'SuggestionsProvider', +})); + +jest.mock('../ProfilePicture/ProfilePicture', () => + jest + .fn() + .mockImplementation(({ name }) => ( + {name} + )) +); + +jest.mock('../../../rest/suggestionsAPI', () => ({ + getSuggestionsList: jest.fn().mockImplementation(() => + Promise.resolve([ + { + id: '1', + description: 'Test suggestion', + createdBy: { id: '1', name: 'Avatar 1', type: 'user' }, + entityLink: '<#E::table::sample_data.ecommerce_db.shopify.dim_address>', + }, + { + id: '2', + description: 'Test suggestion', + createdBy: { id: '1', name: 'Avatar 2', type: 'user' }, + entityLink: '<#E::table::sample_data.ecommerce_db.shopify.dim_address>', + }, + ]) + ), +})); + +describe('AvatarCarousel', () => { + it('renders without crashing', () => { + render(); + + expect(screen.getByText(/Avatar 1/i)).toBeInTheDocument(); + expect(screen.getByText(/Avatar 2/i)).toBeInTheDocument(); + expect(screen.getByTestId('prev-slide')).toBeDisabled(); + }); + + it('disables the next button when on the last slide', () => { + render(); + const nextButton = screen.getByTestId('next-slide'); + fireEvent.click(nextButton); + fireEvent.click(nextButton); + + expect(nextButton).toBeDisabled(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx index bca33b9356a2..45880a4b4f2d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/AvatarCarousel/AvatarCarousel.tsx @@ -12,7 +12,7 @@ */ import { LeftOutlined, RightOutlined } from '@ant-design/icons'; import { Button, Carousel } from 'antd'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useSuggestionsContext } from '../../Suggestions/SuggestionsProvider/SuggestionsProvider'; import UserPopOverCard from '../PopOverCard/UserPopOverCard'; import ProfilePicture from '../ProfilePicture/ProfilePicture'; @@ -35,22 +35,27 @@ const AvatarCarousel = () => { (index: number) => { const activeUser = avatarList[index]; onUpdateActiveUser(activeUser); - setCurrentSlide(index); }, [avatarList] ); + useEffect(() => { + onProfileClick(currentSlide); + }, [currentSlide]); + return (
+ ))}