diff --git a/frontend/amundsen_application/static/js/components/OwnerEditor/index.tsx b/frontend/amundsen_application/static/js/components/OwnerEditor/index.tsx index 25773b19d0..5a0240accf 100644 --- a/frontend/amundsen_application/static/js/components/OwnerEditor/index.tsx +++ b/frontend/amundsen_application/static/js/components/OwnerEditor/index.tsx @@ -9,9 +9,11 @@ import AvatarLabel, { AvatarLabelProps } from 'components/AvatarLabel'; import LoadingSpinner from 'components/LoadingSpinner'; import { ResourceType, UpdateMethod, UpdateOwnerPayload } from 'interfaces'; import { logClick, logAction } from 'utils/analytics'; -import { getUserIdLabel } from 'config/config-utils'; +import { getUserIdLabel, getOwnersSectionConfig } from 'config/config-utils'; import { EditableSectionChildProps } from 'components/EditableSection'; +import InfoButton from 'components/InfoButton'; +import { OwnerCategory } from 'interfaces/OwnerCategory'; import * as Constants from './constants'; @@ -33,6 +35,7 @@ export interface ComponentProps { interface OwnerAvatarLabelProps extends AvatarLabelProps { link?: string; isExternal?: boolean; + additionalOwnerInfo?: any; } export interface StateFromProps { @@ -238,21 +241,31 @@ export class OwnerEditor extends React.Component< ); }; - render() { - const { isEditing, readOnly, resourceType } = this.props; - const { errorText, itemProps } = this.state; - const hasItems = Object.keys(itemProps).length > 0; + renderOwnersSection = (section: OwnerCategory | null) => { + const { resourceType } = this.props; + const { itemProps } = this.state; - if (errorText) { - return ( -
- {errorText} -
+ // check if rendering an owner category that lacks any entries + let isEmptySection = false; + + if (section) { + isEmptySection = Object.keys(itemProps).every( + (key) => + itemProps[key].additionalOwnerInfo.owner_category.toLowerCase() !== + section.label.toLowerCase() ); } - const ownerList = hasItems ? ( + return ( - ) : null; + ); + }; + + renderOwnersList = () => { + const sections = getOwnersSectionConfig().categories; + + if (sections.length > 0) { + return ( +
+ {sections.map((section) => this.renderOwnersSection(section))} +
+ ); + } + + return this.renderOwnersSection(null); + }; + + render() { + const { isEditing, readOnly } = this.props; + const { errorText, itemProps } = this.state; + const hasItems = Object.keys(itemProps).length > 0; + + if (errorText) { + return ( +
+ {errorText} +
+ ); + } + + const ownerList = hasItems ? this.renderOwnersList() : null; return (
diff --git a/frontend/amundsen_application/static/js/components/OwnerEditor/styles.scss b/frontend/amundsen_application/static/js/components/OwnerEditor/styles.scss index c3b15cefcc..2068b0bba9 100644 --- a/frontend/amundsen_application/static/js/components/OwnerEditor/styles.scss +++ b/frontend/amundsen_application/static/js/components/OwnerEditor/styles.scss @@ -112,3 +112,12 @@ } } } + +ul.component-list { + margin-bottom: $spacer-1; + + &:last-child { + margin-bottom: 0; + } + +} \ No newline at end of file diff --git a/frontend/amundsen_application/static/js/config/config-default.ts b/frontend/amundsen_application/static/js/config/config-default.ts index 7240530d40..5bcba4ec5f 100644 --- a/frontend/amundsen_application/static/js/config/config-default.ts +++ b/frontend/amundsen_application/static/js/config/config-default.ts @@ -125,6 +125,9 @@ const configDefault: AppConfig = { maxNestedColumns: 500, }, numberFormat: null, + ownersSection: { + categories: [], + }, productTour: {}, resourceConfig: { [ResourceType.dashboard]: { diff --git a/frontend/amundsen_application/static/js/config/config-types.ts b/frontend/amundsen_application/static/js/config/config-types.ts index c39cbf237d..cc3ce2443b 100644 --- a/frontend/amundsen_application/static/js/config/config-types.ts +++ b/frontend/amundsen_application/static/js/config/config-types.ts @@ -5,6 +5,7 @@ import { SortCriteria, } from '../interfaces'; +import { OwnerCategory } from '../interfaces/OwnerCategory'; import { Widget } from '../interfaces/Widgets'; /** @@ -36,6 +37,7 @@ export interface AppConfig { navTheme: 'dark' | 'light'; nestedColumns: NestedColumnConfig; numberFormat: NumberFormatConfig | null; + ownersSection: OwnersSectionConfig; productTour: ToursConfig; resourceConfig: ResourceConfig; searchPagination: SearchPagination; @@ -84,6 +86,7 @@ export interface AppConfigCustom { productTour?: ToursConfig; searchPagination?: SearchPagination; homePageWidgets?: HomePageWidgetsConfig; + ownersSection?: OwnersSectionConfig; } /** @@ -604,3 +607,7 @@ export interface HomePageWidgetsConfig { */ widgets: Widget[]; } + +export interface OwnersSectionConfig { + categories: OwnerCategory[]; +} diff --git a/frontend/amundsen_application/static/js/config/config-utils.ts b/frontend/amundsen_application/static/js/config/config-utils.ts index 1cc3e65d5e..a8036f73a9 100644 --- a/frontend/amundsen_application/static/js/config/config-utils.ts +++ b/frontend/amundsen_application/static/js/config/config-utils.ts @@ -14,6 +14,7 @@ import { HomePageWidgetsConfig, TableLineageConfig, DateFormatConfig, + OwnersSectionConfig, } from './config-types'; const DEFAULT_DYNAMIC_NOTICES_ENABLED_FLAG = false; @@ -646,3 +647,10 @@ export function getUserIdLabel(): string { export function getDateConfiguration(): DateFormatConfig { return AppConfig.date; } + +/** + * Returns the resource owners section configuration + */ +export function getOwnersSectionConfig(): OwnersSectionConfig { + return AppConfig.ownersSection; +} diff --git a/frontend/amundsen_application/static/js/interfaces/OwnerCategory.ts b/frontend/amundsen_application/static/js/interfaces/OwnerCategory.ts new file mode 100644 index 0000000000..ba83710729 --- /dev/null +++ b/frontend/amundsen_application/static/js/interfaces/OwnerCategory.ts @@ -0,0 +1,7 @@ +// Copyright Contributors to the Amundsen project. +// SPDX-License-Identifier: Apache-2.0 + +export interface OwnerCategory { + label: string; + definition?: string; +} diff --git a/frontend/amundsen_application/static/js/interfaces/User.ts b/frontend/amundsen_application/static/js/interfaces/User.ts index d3fd78734e..588bbf91bb 100644 --- a/frontend/amundsen_application/static/js/interfaces/User.ts +++ b/frontend/amundsen_application/static/js/interfaces/User.ts @@ -6,6 +6,7 @@ export interface User { email: string; profile_url: string; user_id: string; + other_key_values?: Record; } // Not a good name, not sure if we can consolidate yet diff --git a/frontend/amundsen_application/static/js/pages/TableDetailPage/TableOwnerEditor/index.tsx b/frontend/amundsen_application/static/js/pages/TableDetailPage/TableOwnerEditor/index.tsx index cc8eeeb0c1..a98a8e6023 100644 --- a/frontend/amundsen_application/static/js/pages/TableDetailPage/TableOwnerEditor/index.tsx +++ b/frontend/amundsen_application/static/js/pages/TableDetailPage/TableOwnerEditor/index.tsx @@ -19,9 +19,11 @@ export const mapStateToProps = (state: GlobalState) => { const ownerObj = state.tableMetadata.tableOwners.owners; const items = Object.keys(ownerObj).reduce((obj, ownerId) => { // eslint-disable-next-line @typescript-eslint/naming-convention - const { profile_url, user_id, display_name } = ownerObj[ownerId]; + const { profile_url, user_id, display_name, other_key_values } = + ownerObj[ownerId]; let profileLink = profile_url; let isExternalLink = true; + const additionalOwnerInfo = other_key_values; if (indexUsersEnabled()) { isExternalLink = false; @@ -32,6 +34,7 @@ export const mapStateToProps = (state: GlobalState) => { label: display_name, link: profileLink, isExternal: isExternalLink, + additionalOwnerInfo, }; return obj;