diff --git a/.github/playwright/impact-map.generated.json b/.github/playwright/impact-map.generated.json index 9d8356a86f83..bc83a6b772b4 100644 --- a/.github/playwright/impact-map.generated.json +++ b/.github/playwright/impact-map.generated.json @@ -6148,8 +6148,6 @@ "playwright/e2e/Features/StorageMetadataAgentForm.spec.ts", "playwright/e2e/Features/Table.spec.ts", "playwright/e2e/Features/TierWidget.spec.ts", - "playwright/e2e/Pages/DataContracts.spec.ts", - "playwright/e2e/Pages/DataContractsSemanticRules.spec.ts", "playwright/e2e/Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts", "playwright/e2e/Pages/TableCreationTime.spec.ts" ] @@ -6218,6 +6216,15 @@ "playwright/e2e/Pages/ODCSImportExportPermissions.spec.ts" ] }, + { + "sources": [ + "openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractLatestResultBadge/ContractLatestResultBadge.tsx" + ], + "specs": [ + "playwright/e2e/Pages/DataContracts.spec.ts", + "playwright/e2e/Pages/DataContractsSemanticRules.spec.ts" + ] + }, { "sources": [ "openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractQualityFormTab/ContractQualityFormTab.tsx" @@ -6405,8 +6412,6 @@ ], "specs": [ "playwright/e2e/Features/DataAssetRulesEnabled.spec.ts", - "playwright/e2e/Pages/DataContracts.spec.ts", - "playwright/e2e/Pages/DataContractsSemanticRules.spec.ts", "playwright/e2e/Pages/DataProductAndSubdomains.spec.ts", "playwright/e2e/Pages/DomainAdvanced.spec.ts" ] diff --git a/docs/decisions/2026-10-16-classic-mode-is-deprecated.md b/docs/decisions/2026-10-16-classic-mode-is-deprecated.md new file mode 100644 index 000000000000..34b98605516a --- /dev/null +++ b/docs/decisions/2026-10-16-classic-mode-is-deprecated.md @@ -0,0 +1,35 @@ +# Classic mode is deprecated; the UI says so only where the mode is chosen + +- **Status:** Accepted +- **Revisions:** v1 2026-10-16 (initial) +- **Deciders:** Pere Miquel Brull (product), Chirag Madlani (UI), Reethika (design) +- **Guard:** `AppModeSwitcher.test.tsx`, `InterfaceModeMenuItem.test.tsx`, + `DefaultAppModePage.test.tsx` (Deprecated badge and notice on the Classic option only) +- **Related:** open-metadata/openmetadata-collate#7533, #35609, + ADR:2026-10-12-dark-mode-targets-ai-mode-core-surfaces-only + +## Context +AI mode is the go-forward interface, and Classic mode will be removed. Users and admins need +to hear that before it happens. Product asked that the product itself not be invasive about +it: the main channel is the release notes, and the UI should not nag every user on every page. + +## Decision +- Classic mode is deprecated as of 2026-10. It stays fully supported until the release that + removes it, which the release notes will name. +- The UI marks Classic as *Deprecated* (core warning `Badge`), with a short notice and a link + to the release notes, only where the mode is chosen: + - the AI-shell mode switcher and the classic profile dropdown, on the Classic option once the + menu is open (a `HoverCard` on hover or keyboard focus). It is never on the always-visible + switcher trigger; + - the admin *Default App Mode* setting, as a hint under the Classic radio. +- The shipped copy promises the following, and the removal release must honour it: an + organization `defaultAppMode` or persona app mode still set to Classic is moved to AI, and a + user switching from Classic to AI keeps their settings. + +## Consequences +New surfaces that offer a Classic/AI choice add the same badge and notice; nothing else +mentions the deprecation. The removal release owns the migration of Classic values to AI. If +product drops that migration, change the copy (`message.classic-mode-retiring-*`) in the same +release and amend this record. ADR:2026-10-12-dark-mode-targets-ai-mode-core-surfaces-only +already scopes dark mode to AI mode; once Classic is removed, its Classic carve-out no longer +applies. diff --git a/docs/decisions/2026-10-16-light-featured-icons-stay-tinted-in-dark.md b/docs/decisions/2026-10-16-light-featured-icons-stay-tinted-in-dark.md new file mode 100644 index 000000000000..e53778c683cf --- /dev/null +++ b/docs/decisions/2026-10-16-light-featured-icons-stay-tinted-in-dark.md @@ -0,0 +1,26 @@ +# Light featured icons stay tinted in dark mode + +- **Status:** Accepted +- **Revisions:** v1 2026-10-16 (initial) +- **Deciders:** UI team, design +- **Guard:** `reviewer` for `featured-icon.tsx` +- **Related:** open-metadata/openmetadata-collate#7533, ADR:2026-10-12-dark-mode-targets-ai-mode-core-surfaces-only + +## Context +`FeaturedIcon` with `theme="light"` switched to a solid fill (`bg-*-solid`, white glyph) under +`.dark-mode`, inherited from the UntitledUI port (#29412). The AI-mode Home topic cards draw a +tinted tile instead (`bg-utility--50` + `text-utility--600`), so AI Automations and other +pages read as heavier, louder blocks next to Home, and callers patched it locally +(`CriticalAnnouncementBanner`, `StatCard`, Collate `agentIconMap`). + +## Decision +- `FeaturedIcon` `theme="light"` keeps a tinted tile in dark: `tw:dark:bg-utility--50 + tw:dark:text-utility--600`. Light mode is unchanged. +- Domain and data product avatars (`getEntityAvatarProps`) are out of scope: users pick their + color, so they stay a solid fill in both themes until custom colors are revisited. + +## Consequences +Tinted icon tiles come from the core component, so callers stop overriding colors to match Home. +A page that wants a solid tile in dark must pick a solid theme (`dark`, `modern`) rather than +`light`. Revisit only if design moves Home itself back to solid tiles, or when entity custom +colors are reworked. diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.test.tsx b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.test.tsx index a7e9836bebe8..aef0898c5d5e 100644 --- a/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.test.tsx +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.test.tsx @@ -89,6 +89,24 @@ describe('HoverCard', () => { expect(screen.queryByText('Profile')).toBeNull(); }); + it('stays open while keyboard focus moves from the trigger into the card', () => { + renderCard({ openDelay: 0, closeDelay: 200 }); + + fireEvent.keyDown(document.body, { key: 'Tab' }); + act(() => screen.getByText('Jane').focus()); + advance(0); + + act(() => screen.getByText('Profile').focus()); + advance(500); + + expect(screen.getByText('Profile')).toBeInTheDocument(); + + act(() => screen.getByText('Profile').blur()); + advance(500); + + expect(screen.queryByText('Profile')).toBeNull(); + }); + it('never opens when disabled', () => { renderCard({ isDisabled: true, openDelay: 0 }); diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.tsx b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.tsx index 8683b73495ad..17c9b816d336 100644 --- a/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.tsx +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/components/application/hover-card/hover-card.tsx @@ -92,6 +92,12 @@ export const HoverCard = ({ onHoverStart: () => schedule(true), onHoverEnd: () => schedule(false), }); + // Focus moving from the trigger into the card (e.g. to a link inside it) must + // keep the card open; otherwise the trigger's blur closes it under the user. + const { focusWithinProps: cardFocusWithinProps } = useFocusWithin({ + isDisabled, + onFocusWithinChange: (isFocused) => schedule(isFocused), + }); // Only keyboard focus opens the card; a mouse click that focuses the trigger // is already covered by hover. const { focusWithinProps } = useFocusWithin({ @@ -121,7 +127,7 @@ export const HoverCard = ({ triggerRef={triggerRef} onOpenChange={setIsOpen}>
({ // Row border—using an "after" pseudo-element to avoid the border taking up space. bordered && 'tw:[&>tr>th]:after:pointer-events-none tw:[&>tr>th]:after:absolute tw:[&>tr>th]:after:inset-x-0 tw:[&>tr>th]:after:bottom-0 tw:[&>tr>th]:after:h-px tw:[&>tr>th]:after:bg-border-secondary tw:[&>tr>th]:focus-visible:after:bg-transparent', + // Dark: the 8% white divider resolves to the body-row surface under the darker + // header band, so the edge vanishes; border-primary keeps it visible. + bordered && 'tw:dark:[&>tr>th]:after:bg-border-primary', typeof className === 'function' ? className(state) : className ) diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.test.tsx b/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.test.tsx index 8ef5a7e673ef..1865fb8a0a07 100644 --- a/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.test.tsx +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.test.tsx @@ -108,4 +108,18 @@ describe('BadgeWithButton tooltip', () => { expect(screen.getAllByRole('button')).toHaveLength(1); }); + + it('keeps its own text color when rendered as a link', () => { + render( + + Failed + + ); + + const badge = screen.getByTestId('linked-badge'); + + expect(badge.tagName).toBe('A'); + expect(badge).toHaveClass('tw:text-utility-error-700', 'tw:no-underline'); + expect(badge).not.toHaveClass('tw:text-inherit'); + }); }); diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.tsx b/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.tsx index f56f50b9222c..d61c1f248b4c 100644 --- a/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.tsx +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/components/base/badges/badges.tsx @@ -229,10 +229,10 @@ export const Badge = (props: BadgeProps) => { sizes[type][size], colors.styles[color].root, bordered && 'tw:outline-1 tw:-outline-offset-1', - // A linked badge must look identical to a non-linked one — no - // browser default underline/link color, and it must inherit the - // badge's own text color rather than react-aria-components' link styles. - href && 'tw:text-inherit tw:no-underline', + // A linked badge must look identical to a non-linked one: no underline, + // and the color root's text class sets its color. `tw:text-inherit` here + // would win the tailwind-merge conflict and drop that color. + href && 'tw:no-underline', className )} data-testid={dataTestId} diff --git a/openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/featured-icon/featured-icon.tsx b/openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/featured-icon/featured-icon.tsx index 3e6b65d19fa0..6ad1fb4b4d5a 100644 --- a/openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/featured-icon/featured-icon.tsx +++ b/openmetadata-ui-core-components/src/main/resources/ui/src/components/foundations/featured-icon/featured-icon.tsx @@ -29,8 +29,9 @@ const outlineBorders: Record = { }; const styles = sortCx({ + // Dark: tinted tile (the home topic-card look), not a solid fill. light: { - base: 'tw:rounded-full tw:dark:text-fg-white', + base: 'tw:rounded-full', sizes: { sm: 'tw:size-8', md: 'tw:size-10', @@ -39,14 +40,14 @@ const styles = sortCx({ }, colors: { brand: - 'tw:bg-brand-secondary tw:text-featured-icon-light-fg-brand tw:dark:bg-brand-solid', + 'tw:bg-brand-secondary tw:text-featured-icon-light-fg-brand tw:dark:bg-utility-brand-50 tw:dark:text-utility-brand-600', error: - 'tw:bg-error-secondary tw:text-featured-icon-light-fg-error tw:dark:bg-error-solid', - gray: 'tw:bg-tertiary tw:text-featured-icon-light-fg-gray tw:dark:bg-secondary-solid', + 'tw:bg-error-secondary tw:text-featured-icon-light-fg-error tw:dark:bg-utility-error-50 tw:dark:text-utility-error-600', + gray: 'tw:bg-tertiary tw:text-featured-icon-light-fg-gray tw:dark:bg-utility-gray-50 tw:dark:text-utility-gray-600', success: - 'tw:bg-success-secondary tw:text-featured-icon-light-fg-success tw:dark:bg-success-solid', + 'tw:bg-success-secondary tw:text-featured-icon-light-fg-success tw:dark:bg-utility-success-50 tw:dark:text-utility-success-600', warning: - 'tw:bg-warning-secondary tw:text-featured-icon-light-fg-warning tw:dark:bg-warning-solid', + 'tw:bg-warning-secondary tw:text-featured-icon-light-fg-warning tw:dark:bg-utility-warning-50 tw:dark:text-utility-warning-600', }, }, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.test.tsx index e79e82f73da7..c115d511369a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.test.tsx @@ -59,6 +59,16 @@ jest.mock('react-i18next', () => ({ })); jest.mock('@openmetadata/ui-core-components', () => ({ + Badge: ({ + children, + ...props + }: React.PropsWithChildren>) => + React.createElement('span', props, children), + HoverCard: ({ + children, + content, + }: React.PropsWithChildren<{ content: React.ReactNode }>) => + React.createElement(React.Fragment, null, children, content), Box: ({ children, ...props @@ -100,6 +110,7 @@ jest.mock('../../hooks/currentUserStore/useCurrentUserStore', () => ({ jest.mock('../../constants/appMode.constants', () => ({ AI_APP_MODE: 'ai', DEFAULT_APP_MODE: 'default', + CLASSIC_MODE_RELEASE_NOTES_URL: 'https://docs.example/releases', })); const getTrigger = () => screen.getByTestId('app-mode-switcher-trigger'); @@ -220,4 +231,22 @@ describe('AppModeSwitcher', () => { expect(container).not.toBeEmptyDOMElement(); expect(getTrigger()).toBeInTheDocument(); }); + + it('marks Classic as deprecated inside the switcher only', () => { + render(); + + expect( + screen.queryByTestId('classic-deprecated-badge') + ).not.toBeInTheDocument(); + + fireEvent.click(getTrigger()); + + expect(screen.getByTestId('classic-deprecated-badge')).toHaveTextContent( + 'label.deprecated' + ); + expect(screen.getByTestId('classic-deprecation-card')).toBeInTheDocument(); + expect( + screen.getByTestId('classic-deprecation-release-notes') + ).toHaveAttribute('href', 'https://docs.example/releases'); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.tsx index 69af2f9d9705..689d4ba1d545 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppModeSwitcher/AppModeSwitcher.tsx @@ -11,7 +11,13 @@ * limitations under the License. */ -import { Box, Popover, Typography } from '@openmetadata/ui-core-components'; +import { + Badge, + Box, + HoverCard, + Popover, + Typography, +} from '@openmetadata/ui-core-components'; import { Check, ChevronUp } from '@openmetadata/ui-core-components/icons'; import classNames from 'classnames'; import React, { useRef, useState } from 'react'; @@ -30,6 +36,7 @@ import { useIsAiMode, writeAppMode, } from '../../hooks/useAppMode'; +import { ClassicDeprecationCard } from './ClassicDeprecationNotice'; const OPTION_ICON_BOX = 'tw:w-9 tw:h-9 tw:rounded-[10px] tw:bg-utility-blue-50 tw:border tw:border-utility-blue-100 tw:shrink-0'; @@ -183,35 +190,45 @@ const AppModeSwitcher: React.FC<{ {t('label.switch-interface')}

- + } placement="right"> + + + } placement="left"> + + + status={dataContract.latestResult.status} + /> ); } return null; - }, [dataContract, customizedPage?.tabs, entityType, dataAsset, navigate, t]); + }, [dataContract, customizedPage?.tabs, entityType, dataAsset]); const triggerTheAutoPilotApplication = useCallback(async () => { try { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx index 4f62c31784f4..8b3b0ff13db4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsHeader/DataAssetsHeader.test.tsx @@ -1274,12 +1274,15 @@ describe('DataAssetsHeader component', () => { const button = screen.getByTestId('data-contract-latest-result-btn'); - expect(button).toBeInTheDocument(); - expect(button).toHaveClass('data-contract-latest-result-button'); - expect(button).toHaveClass('failed'); + expect(button).toHaveTextContent('label.entity-failed'); + // Core Badge error palette: meets contrast in both themes. + expect(button).toHaveClass( + 'tw:bg-utility-error-50', + 'tw:text-utility-error-700' + ); }); - it('should tint the aborted contract button via legacy tokens that flip in dark', async () => { + it('should tint the aborted contract badge with the orange badge palette', async () => { mockUseCustomPages.mockReturnValue({ customizedPage: { tabs: [{ id: EntityTabs.CONTRACT }] }, }); @@ -1296,10 +1299,8 @@ describe('DataAssetsHeader component', () => { }); expect(screen.getByTestId('data-contract-latest-result-btn')).toHaveClass( - 'tw:text-(--om-legacy-color-b93815)!', - 'tw:bg-(--om-legacy-color-fef6ee)!', - 'tw:dark:text-utility-orange-700!', - 'tw:dark:bg-utility-orange-50!' + 'tw:bg-utility-orange-50', + 'tw:text-utility-orange-700' ); }); @@ -1323,13 +1324,15 @@ describe('DataAssetsHeader component', () => { ).toBeInTheDocument(); }); - it('should navigate to contract tab when button is clicked', async () => { + it('should link to the contract tab', async () => { mockUseCustomPages.mockReturnValue({ customizedPage: { tabs: [{ id: EntityTabs.CONTRACT }], }, }); + mockGetEntityDetailsPath.mockReturnValue('/contract-tab'); + const mockDataContract = { ...MOCK_DATA_CONTRACT, latestResult: { @@ -1345,15 +1348,14 @@ describe('DataAssetsHeader component', () => { render(); }); - const button = screen.getByTestId('data-contract-latest-result-btn'); - fireEvent.click(button); - expect(mockGetEntityDetailsPath).toHaveBeenCalledWith( EntityType.CONTAINER, 'fullyQualifiedName', EntityTabs.CONTRACT ); - expect(mockNavigate).toHaveBeenCalled(); + expect( + screen.getByTestId('data-contract-latest-result-btn') + ).toHaveAttribute('href', '/contract-tab'); }); it('should not render data contract button when contract tab is not visible', async () => { @@ -1470,7 +1472,9 @@ describe('DataAssetsHeader component', () => { const button = screen.getByTestId('data-contract-latest-result-btn'); - expect(button.querySelector('[data-icon="leading"]')).toBeInTheDocument(); + expect( + within(button).getByTestId('contract-status-icon') + ).toBeInTheDocument(); }); it('should render button without icon when getDataContractStatusIcon returns null', async () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractLatestResultBadge/ContractLatestResultBadge.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractLatestResultBadge/ContractLatestResultBadge.tsx new file mode 100644 index 000000000000..764311f63d62 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractLatestResultBadge/ContractLatestResultBadge.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2026 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 { Badge } from '@openmetadata/ui-core-components'; +import { toLower } from 'lodash'; +import { useTranslation } from 'react-i18next'; +import { CONTRACT_RESULT_BADGE_COLOR } from '../../../constants/DataContract.constants'; +import { ContractExecutionStatus } from '../../../generated/type/contractExecutionStatus'; +import { getDataContractStatusIcon } from '../../../utils/DataContract/DataContractUtils'; + +interface ContractLatestResultBadgeProps { + status: ContractExecutionStatus; + /** Contract tab of the asset; the pill is a link so it navigates like one. */ + href: string; +} + +/** Header pill for a failed, aborted or running contract, linking to its Contract tab. */ +const ContractLatestResultBadge = ({ + status, + href, +}: ContractLatestResultBadgeProps) => { + const { t } = useTranslation(); + const StatusIcon = getDataContractStatusIcon(status); + + return ( + + {StatusIcon && } + {t(`label.entity-${toLower(status)}`, { entity: t('label.contract') })} + + ); +}; + +export default ContractLatestResultBadge; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.test.tsx index 8a43c2bbb989..0f9937e20bb9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.test.tsx @@ -11,8 +11,7 @@ * limitations under the License. */ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import { useNavigate } from 'react-router-dom'; +import { render, screen, waitFor } from '@testing-library/react'; import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface'; import { ResourceEntity } from '../../../enums/permissions.enum'; import { DataProduct } from '../../../generated/entity/domains/dataProduct'; @@ -451,27 +450,38 @@ describe('DataProductsDetailsPage — contract latest-result button', () => { setMockPermissions(ENTITY_PERMISSIONS); }); - it('renders the aborted state with its status tint and opens the contract tab', async () => { + it('renders the aborted state as an orange badge linking to the contract tab', async () => { (getContractByEntityId as jest.Mock).mockResolvedValueOnce({ latestResult: { status: ContractExecutionStatus.Aborted }, }); render(); - const button = await screen.findByTestId('data-contract-latest-result-btn'); + const badge = await screen.findByTestId('data-contract-latest-result-btn'); - expect(button.tagName).toBe('BUTTON'); - expect(button).toHaveClass( - 'tw:bg-(--om-legacy-color-fef6ee)!', - 'tw:after:outline-(--om-legacy-color-f9dbaf)!' + expect(badge.tagName).toBe('A'); + expect(badge).toHaveClass( + 'tw:bg-utility-orange-50', + 'tw:text-utility-orange-700' ); + expect(badge).toHaveAttribute( + 'href', + '/data-product/test.dataproduct/contract' + ); + }); - fireEvent.click(button); + it('keeps the selected version when linking to the contract tab from version history', async () => { + (getContractByEntityId as jest.Mock).mockResolvedValueOnce({ + latestResult: { status: ContractExecutionStatus.Failed }, + }); - expect((useNavigate as jest.Mock)()).toHaveBeenCalledWith( - '/data-product/test.dataproduct/contract', - { replace: true } - ); + render(); + + const badge = await screen.findByTestId('data-contract-latest-result-btn'); + const href = badge.getAttribute('href') ?? ''; + + expect(href).toContain('/versions/0.1'); + expect(href.endsWith('/contract')).toBe(true); }); it('does not render the button for a passing contract', async () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx index 7c6227434010..d2d5bc46812a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx @@ -16,8 +16,7 @@ import { RefreshCcw01, } from '@openmetadata/ui-core-components/icons'; import { AxiosError } from 'axios'; -import classNames from 'classnames'; -import { toLower, toString } from 'lodash'; +import { toString } from 'lodash'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; @@ -27,7 +26,6 @@ import { ReactComponent as ExportIcon } from '../../../assets/svg/ic-export.svg' import { ReactComponent as ImportIcon } from '../../../assets/svg/ic-import.svg'; import { ReactComponent as StyleIcon } from '../../../assets/svg/style.svg'; import { ROUTES } from '../../../constants/constants'; -import { CONTRACT_RESULT_BUTTON_CLASS } from '../../../constants/DataContract.constants'; import { FEED_COUNT_INITIAL_DATA } from '../../../constants/entity.constants'; import { EntityField } from '../../../constants/Feeds.constants'; import { LEARNING_PAGE_IDS } from '../../../constants/Learning.constants'; @@ -75,7 +73,6 @@ import { getRenderedActiveTab, getTabLabelMapFromTabs, } from '../../../utils/CustomizePage/CustomizePageEntityTabUtils'; -import { getDataContractStatusIcon } from '../../../utils/DataContract/DataContractUtils'; import dataProductClassBase from '../../../utils/DataProduct/DataProductClassBase'; import { getQueryFilterToIncludeDomain } from '../../../utils/DomainFilterUtils'; import { getEntityName } from '../../../utils/EntityNameUtils'; @@ -115,6 +112,7 @@ import { ManageButtonItemLabel } from '../../common/ManageButtonContentItem/Mana import { GenericProvider } from '../../Customization/GenericProvider/GenericProvider'; import { AssetSelectionDrawer } from '../../DataAssets/AssetsSelectionModal/AssetSelectionDrawer'; import { StatItem } from '../../DataAssets/DataAssetsHeader/StatItem.component'; +import ContractLatestResultBadge from '../../DataContract/ContractLatestResultBadge/ContractLatestResultBadge'; import { EntityHeader } from '../../Entity/EntityHeader/EntityHeader.component'; import { EntityStatusBadge } from '../../Entity/EntityStatusBadge/EntityStatusBadge.component'; import Voting from '../../Entity/Voting/Voting.component'; @@ -139,20 +137,6 @@ type TranslateFn = (key: string, options?: Record) => string; // 12px padding + the 1px antd border that the core ::after outline no longer // takes; not-last:-mr-px keeps antd's `.ant-btn + .ant-btn` 1px overlap with // the next group button. -const CONTRACT_RESULT_BUTTON_BASE_CLASS = [ - 'tw:h-10 tw:gap-2 tw:rounded-xl tw:px-3.25 tw:py-1.5 tw:text-sm tw:font-semibold tw:not-last:-mr-px', - 'tw:shadow-[0px_2px_2px_-1px_var(--om-legacy-color-10-13-18-0-04),0px_4px_6px_-2px_var(--om-legacy-color-10-13-18-0-04),0px_12px_16px_-4px_var(--om-legacy-color-10-13-18-0-04)]!', -].join(' '); - -const CONTRACT_RESULT_BUTTON_BORDER_CLASS: Partial< - Record -> = { - [ContractExecutionStatus.Failed]: 'tw:after:outline-utility-error-200!', - [ContractExecutionStatus.Aborted]: - 'tw:after:outline-(--om-legacy-color-f9dbaf)! tw:dark:after:outline-utility-orange-200!', - [ContractExecutionStatus.Running]: 'tw:after:outline-utility-brand-200!', -}; - // Extracted from DataProductsDetailsPage's render to keep the component's // complexity down. Builds the "Manage" dropdown menu content from the // permission flags and the modal/action toggles the page owns. @@ -960,37 +944,25 @@ const DataProductsDetailsPage = ({ ContractExecutionStatus.Running, ].includes(dataContract.latestResult.status) ) { - const StatusIcon = getDataContractStatusIcon( - dataContract.latestResult.status - ); - return ( - + status={dataContract.latestResult.status} + /> ); } return null; - }, [dataContract]); + }, [dataContract, dataProductFqn, dataProduct.version, isVersionsView]); const statusBadge = useMemo(() => { const shouldShowStatus = entityUtilClassBase.shouldShowEntityStatus( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/DataAssetsCoveragePieChartWidget/DataAssetsCoveragePieChartWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/DataAssetsCoveragePieChartWidget/DataAssetsCoveragePieChartWidget.component.tsx index 2920c141e1fc..7885452599f4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/DataAssetsCoveragePieChartWidget/DataAssetsCoveragePieChartWidget.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/DataAssetsCoveragePieChartWidget/DataAssetsCoveragePieChartWidget.component.tsx @@ -107,7 +107,9 @@ const DataAssetsCoveragePieChartWidget = ({ - + + + ); } @@ -116,24 +118,26 @@ const DataAssetsCoveragePieChartWidget = ({ -
-
-
- + +
+
+
+ +
+ + {t('label.data-asset-plural-coverage')} +
- - {t('label.data-asset-plural-coverage')} - +
- -
+ ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/EntityHealthStatusPieChartWidget/EntityHealthStatusPieChartWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/EntityHealthStatusPieChartWidget/EntityHealthStatusPieChartWidget.component.tsx index 3211cb11264a..d1a218b3dc2a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/EntityHealthStatusPieChartWidget/EntityHealthStatusPieChartWidget.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/EntityHealthStatusPieChartWidget/EntityHealthStatusPieChartWidget.component.tsx @@ -93,31 +93,35 @@ const EntityHealthStatusPieChartWidget = ({ if (isLoading) { return ( - + + + ); } return ( -
-
-
- + +
+
+
+ +
+ + {t('label.healthy-data-asset-plural')} +
- - {t('label.healthy-data-asset-plural')} - +
- -
+ ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusAreaChartWidget/TestCaseStatusAreaChartWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusAreaChartWidget/TestCaseStatusAreaChartWidget.component.tsx index 48cf56922a60..ee352376f3b2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusAreaChartWidget/TestCaseStatusAreaChartWidget.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusAreaChartWidget/TestCaseStatusAreaChartWidget.component.tsx @@ -119,7 +119,7 @@ const TestCaseStatusAreaChartWidget = ({
{showEmptyFooter ? ( - +
{footerWhenEmpty}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusPieChartWidget/TestCaseStatusPieChartWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusPieChartWidget/TestCaseStatusPieChartWidget.component.tsx index f4d98ce8713c..bc1e9a6edee2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusPieChartWidget/TestCaseStatusPieChartWidget.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/ChartWidgets/TestCaseStatusPieChartWidget/TestCaseStatusPieChartWidget.component.tsx @@ -96,31 +96,35 @@ const TestCaseStatusPieChartWidget = ({ - + + + ); } return ( -
-
-
- + +
+
+
+ +
+ + {t('label.test-case-result')} +
- - {t('label.test-case-result')} - +
- -
+ ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/data-quality-dashboard.style.less b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/data-quality-dashboard.style.less index 7026d4132750..d929ee8857e9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/data-quality-dashboard.style.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQualityDashboard/data-quality-dashboard.style.less @@ -20,7 +20,6 @@ .data-quality-dashboard-pie-chart { background-color: @grey-26; border-color: @grey-9; - padding: var(--om-space-12) var(--om-space-24); } } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/ColumnDetailPanel.less b/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/ColumnDetailPanel.less index fff17380039a..b5e4d9f0d029 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/ColumnDetailPanel.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/ColumnDetailPanel.less @@ -16,6 +16,11 @@ } .navigation-container { background-color: @grey-50 !important; + + // Dark: @grey-50 resolves to bg-secondary, a darker band than the drawer's overlay surface. + .dark-mode & { + background-color: transparent !important; + } } .pagination-header-text { color: @grey-700 !important; @@ -52,6 +57,10 @@ background-color: @grey-9; + .dark-mode & { + background-color: transparent; + } + .entity-title-link { font-size: var(--om-font-size-md); font-weight: var(--om-font-weight-semibold); @@ -86,6 +95,11 @@ .column-detail-panel-container { background-color: @grey-50; + // Dark: let the drawer's overlay surface show instead of a darker bg-secondary strip. + .dark-mode & { + background-color: transparent; + } + height: calc(100vh - 170px); display: flex; flex-direction: column; @@ -99,6 +113,10 @@ height: 100%; background: @grey-50; overflow: hidden; + + .dark-mode & { + background: transparent; + } border-radius: 0; margin-left: var(--om-space-8); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx index d30a840f8fe6..c12d7a0578a3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx @@ -38,7 +38,7 @@ export const KeyProfileMetrics = ({ profile }: KeyProfileMetricsProps) => {
{metrics.map((metric) => (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx index 5e3b1ff059d0..34734b0ae6e5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/CustomPropertiesSection/CustomPropertiesSection.tsx @@ -185,7 +185,7 @@ const CustomPropertiesSection = ({ /> {filteredProperties.length > 0 ? (
    {filteredProperties.map((property: CustomProperty) => ( ( - + + + )) : filteredChartsData.map((chart) => ( - - {getTitleByChartType(chart.chartType)} - - - - {getReadableCountString(chart.currentPercentage)}% + + + {getTitleByChartType(chart.chartType)} - {!isUndefined(chart.percentageChange) && ( - - - {chart.percentageChange !== 0 && ( - - )} - {getReadableCountString(chart.percentageChange)}% - - - {chart.numberOfDays === 1 - ? t('label.in-the-last-day') - : t('label.in-last-number-of-days', { - numberOfDays: chart.numberOfDays, - })} - - - )} - + + + {getReadableCountString(chart.currentPercentage)}% + + {!isUndefined(chart.percentageChange) && ( + + + {chart.percentageChange !== 0 && ( + + )} + {getReadableCountString(chart.percentageChange)}% + + + {chart.numberOfDays === 1 + ? t('label.in-the-last-day') + : t('label.in-last-number-of-days', { + numberOfDays: chart.numberOfDays, + })} + + + )} + + ))}
@@ -168,8 +172,8 @@ function PlatformInsightsWidget({ // instead of disabling an interactive collapse header through CSS. if (variant === 'embedded') { return ( - - {header} + + {header} {content} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/TotalDataAssetsWidget/TotalDataAssetsWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/TotalDataAssetsWidget/TotalDataAssetsWidget.tsx index 2137691a14a8..7ee00b5a724b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/TotalDataAssetsWidget/TotalDataAssetsWidget.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/TotalDataAssetsWidget/TotalDataAssetsWidget.tsx @@ -12,6 +12,7 @@ */ import { Box, + Card, SkeletonParagraph, Typography, } from '@openmetadata/ui-core-components'; @@ -55,19 +56,14 @@ function TotalDataAssetsWidget({ return ( - + direction="col" + gap={4}> + {t('label.total-entity', { entity: t('label.data-asset-plural') })} @@ -83,29 +79,32 @@ function TotalDataAssetsWidget({ )} {!isLoading && showPlaceholder && errorPlaceholder} {!isLoading && !showPlaceholder && ( - - {totalAssetsCount?.map((entity) => ( - - - - {entity.icon} - + + + + {totalAssetsCount?.map((entity) => ( + + + + {entity.icon} + - {entity.name} - + {entity.name} + - - {getReadableCountString(entity.value)} - + + {getReadableCountString(entity.value)} + + + ))} - ))} - + + )} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.constants.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.constants.ts new file mode 100644 index 000000000000..8c1ac95ad125 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.constants.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2026 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 type { BadgeColors } from '@openmetadata/ui-core-components'; + +export const STATUS_BADGE_COLOR: Record = { + success: 'success', + failed: 'error', + warning: 'warning', +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.test.tsx new file mode 100644 index 000000000000..a259edde30a8 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2026 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 { render, screen } from '@testing-library/react'; +import IngestionStatusCount from './IngestionStatusCount'; + +describe('IngestionStatusCount', () => { + it('renders each count as a status-coloured badge', () => { + render( + + ); + + expect(screen.getByTestId('success-count')).toHaveTextContent('33'); + expect(screen.getByTestId('success-count')).toHaveClass( + 'tw:bg-utility-success-50' + ); + expect(screen.getByTestId('failed-count')).toHaveTextContent('2'); + expect(screen.getByTestId('failed-count')).toHaveClass( + 'tw:bg-utility-error-50' + ); + expect(screen.getByTestId('warning-count')).toHaveTextContent('1'); + expect(screen.getByTestId('warning-count')).toHaveClass( + 'tw:bg-utility-warning-50' + ); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.tsx index aa95ad2f6272..fedf7bbf774d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/IngestionStatusCount.tsx @@ -11,11 +11,10 @@ * limitations under the License. */ -import { Box, Typography } from '@openmetadata/ui-core-components'; -import classNames from 'classnames'; +import { Badge, Box } from '@openmetadata/ui-core-components'; import { useMemo } from 'react'; import { getIngestionStatusCountData } from '../../../../../../utils/IngestionConfigUtils'; -import './ingestion-status-count.less'; +import { STATUS_BADGE_COLOR } from './IngestionStatusCount.constants'; import { IngestionStatusCountProps } from './IngestionStatusCount.interface'; function IngestionStatusCount({ @@ -28,22 +27,17 @@ function IngestionStatusCount({ ); return ( - + {records.map((record) => ( - - {record.value} - {record.label} - + + {record.value}  + {record.label} + ))} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/ingestion-status-count.less b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/ingestion-status-count.less deleted file mode 100644 index 1569cfaf13ab..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionStatusCount/ingestion-status-count.less +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2025 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 (reference) '../../../../../../styles/variables.less'; - -.ingestion-status-count { - .status-count { - height: 48px; - width: 62px; - padding: var(--om-space-6) var(--om-space-12); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - border-radius: var(--om-radius-xl); - - .record-count { - font-size: var(--om-font-size-sm); - line-height: 20px; - font-weight: var(--om-font-weight-bold); - } - - .record-label { - font-size: var(--om-font-size-8); - } - } - - .success { - background-color: @green-9; - border-color: @green-9; - .record-count, - .record-label { - color: @green-10; - } - } - - .failed { - background-color: @red-9; - border-color: @red-9; - .record-count, - .record-label { - color: @red-10; - } - } - - .warning { - background-color: @yellow-10; - border-color: @yellow-10; - .record-count, - .record-label { - color: @yellow-11; - } - } -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/TabsLabel.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/TabsLabel.test.tsx index 7bc036ce19fd..3a9c16234f79 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/TabsLabel.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/TabsLabel.test.tsx @@ -92,13 +92,13 @@ describe('TabsLabel component', () => { const badge = (id: string) => screen.getByTestId(id).querySelector('[data-testid="count"] > span'); - expect(badge('first')).toHaveClass('bg-primary', 'text-white'); - expect(badge('second')).toHaveClass('ant-tag'); + expect(badge('first')).toHaveClass('tw:bg-utility-brand-50'); + expect(badge('second')).toHaveClass('tw:bg-utility-gray-50'); fireEvent.click(screen.getByRole('tab', { name: /Second/ })); - expect(badge('second')).toHaveClass('bg-primary', 'text-white'); - expect(badge('first')).toHaveClass('ant-tag'); + expect(badge('second')).toHaveClass('tw:bg-utility-brand-50'); + expect(badge('first')).toHaveClass('tw:bg-utility-gray-50'); }); it('Count badge stays neutral inside a non-card tab list', () => { @@ -116,7 +116,7 @@ describe('TabsLabel component', () => { .getByTestId('first') .querySelector('[data-testid="count"] > span'); - expect(badge).not.toHaveClass('bg-primary'); - expect(badge).not.toHaveClass('ant-tag'); + expect(badge).not.toHaveClass('tw:bg-utility-brand-50'); + expect(badge).not.toHaveClass('tw:bg-utility-gray-50'); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/tabs-label.less b/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/tabs-label.less index a9a1b7162c40..b205c16d6641 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/tabs-label.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/TabsLabel/tabs-label.less @@ -20,19 +20,4 @@ color: @text-grey-muted; line-height: 12px; } - .service-beta-tag { - position: absolute; - top: 3px; - right: -22px; - - sup { - height: 14px; - padding: var(--om-space-2) var(--om-space-4); - font-size: var(--om-font-size-8); - font-weight: var(--om-font-weight-medium); - display: flex; - justify-content: center; - align-items: center; - } - } } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/MyData/components/StatCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/MyData/components/StatCard.tsx index f1f540ceb7a0..4aafdb676410 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/MyData/components/StatCard.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/MyData/components/StatCard.tsx @@ -57,8 +57,8 @@ const StatCard: React.FC = ({ gap={2}> { await renderForm(); await screen.findByTestId('settings-source-env-alert'); - expect(screen.getByRole('radio', { name: 'label.classic' })).toBeDisabled(); + expect( + screen.getByRole('radio', { name: /label\.classic/ }) + ).toBeDisabled(); expect(screen.getByTestId('save-button')).toBeDisabled(); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/Profile/tabs/platform-settings/AppModeSettingsForm.tsx b/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/Profile/tabs/platform-settings/AppModeSettingsForm.tsx index 4fcde25e80d5..a43e9d3f3d9e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/Profile/tabs/platform-settings/AppModeSettingsForm.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/discovery/personal-space/Profile/tabs/platform-settings/AppModeSettingsForm.tsx @@ -30,6 +30,10 @@ import { showErrorToast, showSuccessToast, } from '../../../../../../utils/ToastUtils'; +import { + ClassicDeprecatedLabel, + ClassicDeprecationHint, +} from '../../../../../AppModeSwitcher/ClassicDeprecationNotice'; import SettingsSourceBanner from '../../../../../platform/settings/SettingsSourceBanner/SettingsSourceBanner'; import { APP_MODE_OPTIONS, NO_DEFAULT_VALUE } from './AppModeSettings'; import { APP_MODE_FIELDS } from './PlatformSettings.constants'; @@ -127,8 +131,19 @@ const AppModeSettingsForm = ({ {APP_MODE_OPTIONS.map((option) => ( + ) : undefined + } key={option.value} - label={t(option.labelKey)} + label={ + option.value === DefaultAppMode.Classic ? ( + + ) : ( + t(option.labelKey) + ) + } value={option.value} /> ))} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.test.tsx index 7eb07c9bc36b..ae016ea17c70 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.test.tsx @@ -119,7 +119,7 @@ jest.mock('@openmetadata/ui-core-components', () => ({ ), Content: ({ children }: { children: React.ReactNode }) => ( -
{children}
+
{children}
), } ), @@ -399,6 +399,10 @@ describe('ConnectionsPage', () => { render(); expect(screen.getByTestId('connections-footer-slot')).toBeInTheDocument(); + // Outside the padded Content, so it pins to the canvas edge like Home's. + expect(screen.getByTestId('page-layout-content')).not.toContainElement( + screen.getByTestId('connections-footer-slot') + ); }); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.tsx index 22fbc57af571..3f8798728516 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.tsx @@ -232,17 +232,19 @@ const ConnectionsPage: React.FC = () => { onCategoryChange={handleCategoryChange} />
- - {footerContributions.length > 0 && ( -
- {footerContributions.map((contribution) => ( - - ))} -
- )} + + {/* Pinned to the page canvas, not the padded Content, so the composer meets the canvas + edge the way Home's footer slot does. */} + {footerContributions.length > 0 && ( +
+ {footerContributions.map((contribution) => ( + + ))} +
+ )} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts index d3051e76f880..8f53dae8b07f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts @@ -11,6 +11,7 @@ * limitations under the License. */ +import type { BadgeColors } from '@openmetadata/ui-core-components'; import type { ChartStatus } from '@openmetadata/ui-core-components/charts'; import { EntityReferenceFields } from '../enums/AdvancedSearch.enum'; import { EntityType } from '../enums/entity.enum'; @@ -121,17 +122,12 @@ export const MAX_LATENCY_UNITS = ['minute', 'hour', 'day']; export const REFRESH_FREQUENCY_UNITS = ['hour', 'day', 'week', 'month', 'year']; export const RETENTION_UNITS = ['day', 'week', 'month', 'year']; -// Latest-result button tints. `!` beats the secondary core Button's own -// bg/text/hover utilities. Aborted's light hexes (legacy @orange-7/@orange-50) -// have no upstream palette step, so they come from legacy tokens and flip to -// the utility-orange pair in dark. -export const CONTRACT_RESULT_BUTTON_CLASS: Partial< - Record +// Header pill for the latest contract run, drawn with the core Badge palette +// (utility-*-50 fill, -700 text, -200 outline) so it meets contrast in both themes. +export const CONTRACT_RESULT_BADGE_COLOR: Partial< + Record > = { - [ContractExecutionStatus.Failed]: - 'tw:text-utility-error-600! tw:bg-utility-error-50!', - [ContractExecutionStatus.Aborted]: - 'tw:text-(--om-legacy-color-b93815)! tw:bg-(--om-legacy-color-fef6ee)! tw:dark:text-utility-orange-700! tw:dark:bg-utility-orange-50!', - [ContractExecutionStatus.Running]: - 'tw:text-utility-brand-700! tw:bg-utility-brand-50!', + [ContractExecutionStatus.Failed]: 'error', + [ContractExecutionStatus.Aborted]: 'orange', + [ContractExecutionStatus.Running]: 'brand', }; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/appMode.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/appMode.constants.ts index 9272e62d5611..1ede12154f23 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/appMode.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/appMode.constants.ts @@ -66,3 +66,7 @@ export const DEFAULT_APP_MODE = 'default'; * admin-facing `AppMode` enum value into the runtime mode string. */ export const AI_APP_MODE = 'ai'; + +// Classic mode is deprecated; its removal is announced in the release notes. +export const CLASSIC_MODE_RELEASE_NOTES_URL = + 'https://docs.open-metadata.org/latest/releases/all-releases'; diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json index a39e1ecac4d7..8d25d22a78f5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "ردود الفعل", "read": "قراءة", "read-only": "للقراءة فقط", + "read-the-release-notes": "اقرأ ملاحظات الإصدار", "read-type": "قراءة {{type}}", "reader-avg": "متوسط القراءة", "reader-stat-plural": "إحصاءات القارئ", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "اختر الأصول التي يمكن أن يعمل عليها هذا سير العمل.", "choose-widget-style-first": "اختر نمط عنصر الواجهة أولاً.", "choose-widget-to-add-to-tab": "اختر عنصر واجهة لإضافته إلى علامة التبويب هذه.", + "classic-mode-retiring-admin": "سيتم إيقاف الوضع الكلاسيكي في إصدار قادم. ستنتقل المؤسسات والشخصيات التي لا تزال مضبوطة على الوضع الكلاسيكي إلى وضع الذكاء الاصطناعي تلقائيًا.", + "classic-mode-retiring-description": "ستتم إزالة الوضع الكلاسيكي في إصدار قادم. بعد ذلك سيكون وضع الذكاء الاصطناعي هو الواجهة الوحيدة. يمكنك التبديل الآن مع الاحتفاظ بإعداداتك.", + "classic-mode-retiring-title": "يجري إيقاف الوضع الكلاسيكي", "classification-add-tags-description": "حدّد التسميات التي ستطبّقها فرقك.", "classification-create-description": "سمِّ الفئة، مثل الحساسية أو الاعتماد.", "classification-empty-state-description": "تتيح لك التصنيفات بناء نظام تسمية متسق — حدّد الفئات مرة واحدة، ووسِّم الأصول في كل مكان.", 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 5420943c55bf..4606c8ecbace 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reaktionen", "read": "Lesen", "read-only": "Schreibgeschützt", + "read-the-release-notes": "Versionshinweise lesen", "read-type": "Leseart {{type}}", "reader-avg": "Lesen Ø", "reader-stat-plural": "Leser-Statistiken", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Wählen Sie, auf welche Assets dieser Workflow angewendet werden kann.", "choose-widget-style-first": "Wählen Sie zuerst einen Widget-Stil.", "choose-widget-to-add-to-tab": "Wählen Sie ein Widget, das diesem Tab hinzugefügt werden soll.", + "classic-mode-retiring-admin": "Der klassische Modus wird in einer kommenden Version eingestellt. Organisationen und Personas, die noch auf Klassisch eingestellt sind, wechseln automatisch zu KI.", + "classic-mode-retiring-description": "Der klassische Modus wird in einer kommenden Version entfernt. Danach ist der KI-Modus die einzige Oberfläche. Sie können jetzt wechseln und Ihre Einstellungen behalten.", + "classic-mode-retiring-title": "Der klassische Modus wird eingestellt", "classification-add-tags-description": "Definieren Sie die Beschriftungen, die Ihre Teams anwenden werden.", "classification-create-description": "Benennen Sie die Kategorie, z. B. Sensibilität oder Zertifizierung.", "classification-empty-state-description": "Mit Klassifizierungen erstellen Sie ein einheitliches Beschriftungssystem — definieren Sie Kategorien einmal und kennzeichnen Sie Assets überall.", 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 754f39431ce3..691f06b7f01b 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reactions", "read": "Read", "read-only": "Read-only", + "read-the-release-notes": "Read the release notes", "read-type": "Read {{type}}", "reader-avg": "Reader avg", "reader-stat-plural": "Reader Stats", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Choose which assets this workflow can act on.", "choose-widget-style-first": "Choose a widget style first.", "choose-widget-to-add-to-tab": "Choose a widget to add to this tab.", + "classic-mode-retiring-admin": "Classic mode retires in an upcoming release. Organizations and personas still set to Classic will move to AI automatically.", + "classic-mode-retiring-description": "Classic will be removed in an upcoming release. After that, AI mode will be the only interface. You can switch now and keep your settings.", + "classic-mode-retiring-title": "Classic mode is being retired", "classification-add-tags-description": "Define the labels your teams will apply.", "classification-create-description": "Name the category, like Sensitivity or Certification.", "classification-empty-state-description": "Classifications let you build a consistent labeling system — define categories once, and tag assets everywhere.", 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 643a3fa53011..a494f81135b5 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reacciones", "read": "Leer", "read-only": "Solo lectura", + "read-the-release-notes": "Leer las notas de la versión", "read-type": "Leer {{type}}", "reader-avg": "Lectura prom.", "reader-stat-plural": "Estadísticas de Lectura", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Elija sobre qué activos puede actuar este flujo de trabajo.", "choose-widget-style-first": "Elige primero un estilo de widget.", "choose-widget-to-add-to-tab": "Elige un widget para añadirlo a esta pestaña.", + "classic-mode-retiring-admin": "El modo clásico se retirará en una próxima versión. Las organizaciones y personas que aún usen Clásico pasarán a IA automáticamente.", + "classic-mode-retiring-description": "El modo clásico se eliminará en una próxima versión. Después, el modo IA será la única interfaz. Puedes cambiar ahora y conservar tu configuración.", + "classic-mode-retiring-title": "El modo clásico se está retirando", "classification-add-tags-description": "Define los distintivos que aplicarán tus equipos.", "classification-create-description": "Nombra la categoría, como Sensibilidad o Certificación.", "classification-empty-state-description": "Las clasificaciones te permiten crear un sistema de etiquetado coherente: define las categorías una vez y etiqueta activos en cualquier lugar.", 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 ef3353ee8942..b74cd975894f 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "réactions", "read": "Lire", "read-only": "Lecture seule", + "read-the-release-notes": "Lire les notes de version", "read-type": "Lire {{type}}", "reader-avg": "Moy. lecture", "reader-stat-plural": "Statistiques de Lecture", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Choisissez les actifs sur lesquels ce workflow peut agir.", "choose-widget-style-first": "Choisissez d'abord un style de widget.", "choose-widget-to-add-to-tab": "Choisissez un widget à ajouter à cet onglet.", + "classic-mode-retiring-admin": "Le mode classique sera retiré dans une prochaine version. Les organisations et personas encore configurées en Classique passeront automatiquement en IA.", + "classic-mode-retiring-description": "Le mode classique sera supprimé dans une prochaine version. Le mode IA sera alors la seule interface. Vous pouvez basculer dès maintenant et conserver vos paramètres.", + "classic-mode-retiring-title": "Le mode classique va être retiré", "classification-add-tags-description": "Définissez les libellés que vos équipes appliqueront.", "classification-create-description": "Nommez la catégorie, comme Sensibilité ou Certification.", "classification-empty-state-description": "Les classifications vous permettent de créer un système d’étiquetage cohérent — définissez les catégories une fois, et étiquetez vos actifs partout.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index b9cc94484137..756ea794f344 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reaccións", "read": "Ler", "read-only": "Só lectura", + "read-the-release-notes": "Ler as notas da versión", "read-type": "Ler {{type}}", "reader-avg": "Media de lectura", "reader-stat-plural": "Estatísticas de Lectura", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Escolla os activos sobre os que pode actuar este fluxo de traballo.", "choose-widget-style-first": "Escolle primeiro un estilo de widget.", "choose-widget-to-add-to-tab": "Escolle un widget para engadir a esta lapela.", + "classic-mode-retiring-admin": "O modo clásico retirarase nunha próxima versión. As organizacións e persoas que aínda usen Clásico pasarán a IA automaticamente.", + "classic-mode-retiring-description": "O modo clásico eliminarase nunha próxima versión. Despois, o modo IA será a única interface. Podes cambiar agora e conservar a túa configuración.", + "classic-mode-retiring-title": "O modo clásico vaise retirar", "classification-add-tags-description": "Define os distintivos que aplicarán os teus equipos.", "classification-create-description": "Nomea a categoría, como Sensibilidade ou Certificación.", "classification-empty-state-description": "As clasificacións permítenche crear un sistema de etiquetado coherente: define as categorías unha vez e etiqueta activos en calquera lugar.", 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 0c4d320dbc8e..8108261db3ba 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "תגובות", "read": "קרא", "read-only": "לקריאה בלבד", + "read-the-release-notes": "קרא את הערות הגרסה", "read-type": "קרא {{type}}", "reader-avg": "ממוצע קריאה", "reader-stat-plural": "סטטיסטיקות קריאה", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "בחרו אילו נכסים זרימת העבודה יכולה לפעול עליהם.", "choose-widget-style-first": "בחר תחילה סגנון ווידג'ט.", "choose-widget-to-add-to-tab": "בחר ווידג'ט להוספה ללשונית זו.", + "classic-mode-retiring-admin": "המצב הקלאסי יוצא משימוש בגרסה הקרובה. ארגונים ופרסונות שעדיין מוגדרים לקלאסי יעברו ל-AI באופן אוטומטי.", + "classic-mode-retiring-description": "המצב הקלאסי יוסר בגרסה הקרובה. לאחר מכן מצב AI יהיה הממשק היחיד. אפשר לעבור עכשיו ולשמור על ההגדרות שלך.", + "classic-mode-retiring-title": "המצב הקלאסי יוצא משימוש", "classification-add-tags-description": "הגדירו את התוויות שהצוותים שלכם ייישמו.", "classification-create-description": "תנו שם לקטגוריה, כמו רגישות או הסמכה.", "classification-empty-state-description": "סיווגים מאפשרים לכם לבנות מערכת תיוג עקבית — הגדירו קטגוריות פעם אחת, ותייגו נכסים בכל מקום.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/it-it.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/it-it.json index d3b477f6df53..cb440875f9d0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/it-it.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/it-it.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reazioni", "read": "Letto", "read-only": "Sola lettura", + "read-the-release-notes": "Leggi le note di rilascio", "read-type": "Leggi {{type}}", "reader-avg": "Media lettura", "reader-stat-plural": "Statistiche di Lettura", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Scegli su quali asset può agire questo workflow.", "choose-widget-style-first": "Scegli prima uno stile per il widget.", "choose-widget-to-add-to-tab": "Scegli un widget da aggiungere a questa scheda.", + "classic-mode-retiring-admin": "La modalità Classica verrà ritirata in una prossima release. Le organizzazioni e le persona ancora impostate su Classica passeranno automaticamente ad AI.", + "classic-mode-retiring-description": "La modalità Classica verrà rimossa in una prossima release. Da quel momento, la modalità AI sarà l'unica interfaccia. Puoi passare ora e mantenere le tue impostazioni.", + "classic-mode-retiring-title": "La modalità Classica sta per essere ritirata", "classification-add-tags-description": "Definisci le etichette che i tuoi team applicheranno.", "classification-create-description": "Dai un nome alla categoria, ad esempio Sensibilità o Certificazione.", "classification-empty-state-description": "Le classificazioni ti permettono di creare un sistema di etichettatura coerente — definisci le categorie una volta e tagga gli asset ovunque.", 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 fc0268248e9c..f60b12db5e34 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "リアクション", "read": "読み取り", "read-only": "読み取り専用", + "read-the-release-notes": "リリースノートを読む", "read-type": "{{type}} を読む", "reader-avg": "読み取り平均", "reader-stat-plural": "読み取り統計", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "このワークフローが対象とするアセットを選択してください。", "choose-widget-style-first": "先にウィジェットのスタイルを選択してください。", "choose-widget-to-add-to-tab": "このタブに追加するウィジェットを選択してください。", + "classic-mode-retiring-admin": "クラシックモードは今後のリリースで廃止されます。クラシックに設定されている組織とペルソナは自動的に AI に移行します。", + "classic-mode-retiring-description": "クラシックモードは今後のリリースで削除されます。その後は AI モードが唯一のインターフェースになります。今すぐ切り替えても設定はそのまま引き継がれます。", + "classic-mode-retiring-title": "クラシックモードは廃止予定です", "classification-add-tags-description": "チームが適用するラベルを定義します。", "classification-create-description": "「機密性」や「認定」などのカテゴリ名を付けます。", "classification-empty-state-description": "分類を使えば、一貫したラベル付けの仕組みを構築できます — 一度カテゴリを定義すれば、あらゆる場所でアセットにタグを付けられます。", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json index 8af7bf9d10a4..0a1c2204a3a0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "반응들", "read": "읽기", "read-only": "읽기 전용", + "read-the-release-notes": "릴리스 노트 읽기", "read-type": "{{type}} 읽기", "reader-avg": "읽기 평균", "reader-stat-plural": "읽기 통계", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "이 워크플로가 대상으로 할 자산을 선택하세요.", "choose-widget-style-first": "먼저 위젯 스타일을 선택하세요.", "choose-widget-to-add-to-tab": "이 탭에 추가할 위젯을 선택하세요.", + "classic-mode-retiring-admin": "클래식 모드는 향후 릴리스에서 폐지됩니다. 아직 클래식으로 설정된 조직과 페르소나는 자동으로 AI로 전환됩니다.", + "classic-mode-retiring-description": "클래식 모드는 향후 릴리스에서 제거됩니다. 이후에는 AI 모드가 유일한 인터페이스가 됩니다. 지금 전환해도 설정은 그대로 유지됩니다.", + "classic-mode-retiring-title": "클래식 모드가 폐지될 예정입니다", "classification-add-tags-description": "팀이 적용할 라벨을 정의합니다.", "classification-create-description": "민감도나 인증과 같은 카테고리 이름을 지정합니다.", "classification-empty-state-description": "분류를 사용하면 일관된 라벨링 체계를 구축할 수 있습니다 — 카테고리를 한 번 정의하면 어디서나 자산에 태그를 지정할 수 있습니다.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json index 3a4bdf1fe8f4..c35a6e873be5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "प्रतिक्रिया", "read": "वाचा", "read-only": "केवळ वाचनासाठी", + "read-the-release-notes": "रिलीज नोट्स वाचा", "read-type": "{{type}} वाचा", "reader-avg": "वाचक सरासरी", "reader-stat-plural": "वाचन आकडेवारी", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "हा कार्यप्रवाह कोणत्या अॅसेटवर कार्य करू शकतो ते निवडा.", "choose-widget-style-first": "आधी विजेट शैली निवडा.", "choose-widget-to-add-to-tab": "या टॅबमध्ये जोडण्यासाठी विजेट निवडा.", + "classic-mode-retiring-admin": "आगामी रिलीजमध्ये क्लासिक मोड बंद होईल. अजूनही क्लासिकवर असलेल्या संस्था आणि पर्सोना आपोआप AI वर जातील.", + "classic-mode-retiring-description": "आगामी रिलीजमध्ये क्लासिक मोड काढून टाकला जाईल. त्यानंतर AI मोड हाच एकमेव इंटरफेस असेल. तुम्ही आत्ताच बदलू शकता आणि तुमच्या सेटिंग्ज कायम राहतील.", + "classic-mode-retiring-title": "क्लासिक मोड बंद केला जात आहे", "classification-add-tags-description": "तुमचे संघ लावतील ती लेबल परिभाषित करा.", "classification-create-description": "संवेदनशीलता किंवा प्रमाणन यासारख्या श्रेणीला नाव द्या.", "classification-empty-state-description": "वर्गीकरणे तुम्हाला सुसंगत लेबलिंग प्रणाली तयार करू देतात — श्रेणी एकदाच परिभाषित करा, आणि सर्वत्र मालमत्तांना टॅग करा.", 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 b1561969398f..3c0900045c64 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reacties", "read": "Lezen", "read-only": "Alleen-lezen", + "read-the-release-notes": "Lees de release-opmerkingen", "read-type": "Lees {{type}}", "reader-avg": "Lezer gem.", "reader-stat-plural": "Leesstatistieken", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Kies op welke assets deze workflow mag inwerken.", "choose-widget-style-first": "Kies eerst een widgetstijl.", "choose-widget-to-add-to-tab": "Kies een widget om aan dit tabblad toe te voegen.", + "classic-mode-retiring-admin": "De klassieke modus wordt in een komende release uitgefaseerd. Organisaties en persona's die nog op Klassiek staan, gaan automatisch over naar AI.", + "classic-mode-retiring-description": "De klassieke modus wordt in een komende release verwijderd. Daarna is de AI-modus de enige interface. Je kunt nu overstappen en je instellingen behouden.", + "classic-mode-retiring-title": "De klassieke modus wordt uitgefaseerd", "classification-add-tags-description": "Definieer de labels die uw teams zullen toepassen.", "classification-create-description": "Geef de categorie een naam, zoals Gevoeligheid of Certificering.", "classification-empty-state-description": "Met classificaties bouwt u een consistent labelsysteem — definieer categorieën één keer en label assets overal.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index 5db80c7f7e30..6c83f47f5cd4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "واکنش‌ها", "read": "خواندن", "read-only": "فقط خواندنی", + "read-the-release-notes": "یادداشت‌های انتشار را بخوانید", "read-type": "خواندن {{type}}", "reader-avg": "میانگین خواندن", "reader-stat-plural": "Estadísticas de Lectura", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "دارایی‌هایی را که این گردش کار روی آنها اثر می‌گذارد انتخاب کنید.", "choose-widget-style-first": "ابتدا یک سبک ویجت انتخاب کنید.", "choose-widget-to-add-to-tab": "یک ویجت برای افزودن به این زبانه انتخاب کنید.", + "classic-mode-retiring-admin": "حالت کلاسیک در نسخه‌ای آینده کنار گذاشته می‌شود. سازمان‌ها و پرسوناهایی که هنوز روی کلاسیک تنظیم شده‌اند، به‌طور خودکار به هوش مصنوعی منتقل می‌شوند.", + "classic-mode-retiring-description": "حالت کلاسیک در نسخه‌ای آینده حذف می‌شود. پس از آن، حالت هوش مصنوعی تنها رابط کاربری خواهد بود. می‌توانید همین حالا تغییر دهید و تنظیمات خود را حفظ کنید.", + "classic-mode-retiring-title": "حالت کلاسیک در حال کنار گذاشته شدن است", "classification-add-tags-description": "برچسب‌هایی که تیم‌های شما اعمال خواهند کرد را تعریف کنید.", "classification-create-description": "دسته را نام‌گذاری کنید، مانند حساسیت یا گواهی‌نامه.", "classification-empty-state-description": "طبقه‌بندی‌ها به شما امکان می‌دهند یک سیستم برچسب‌گذاری منسجم بسازید — دسته‌ها را یک بار تعریف کنید و دارایی‌ها را در همه جا برچسب‌گذاری کنید.", 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 ed7243620f1b..2bd027852320 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reações", "read": "Ler", "read-only": "Somente leitura", + "read-the-release-notes": "Ler as notas de versão", "read-type": "Leia {{type}}", "reader-avg": "Média de leitura", "reader-stat-plural": "Estatísticas de Leitura", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Escolha em quais ativos este fluxo de trabalho pode atuar.", "choose-widget-style-first": "Escolha primeiro um estilo de widget.", "choose-widget-to-add-to-tab": "Escolha um widget para adicionar a esta aba.", + "classic-mode-retiring-admin": "O modo clássico será descontinuado em uma próxima versão. Organizações e personas ainda configuradas como Clássico passarão para IA automaticamente.", + "classic-mode-retiring-description": "O modo clássico será removido em uma próxima versão. Depois disso, o modo IA será a única interface. Você pode mudar agora e manter suas configurações.", + "classic-mode-retiring-title": "O modo clássico está sendo descontinuado", "classification-add-tags-description": "Defina os rótulos que suas equipes aplicarão.", "classification-create-description": "Dê um nome à categoria, como Sensibilidade ou Certificação.", "classification-empty-state-description": "As classificações permitem criar um sistema de rotulagem consistente — defina as categorias uma vez e marque ativos em todos os lugares.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json index 842d7c8a0e8a..dddf2851cfa3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reações", "read": "Ler", "read-only": "Apenas leitura", + "read-the-release-notes": "Ler as notas de versão", "read-type": "Leia {{type}}", "reader-avg": "Média de leitura", "reader-stat-plural": "Estatísticas de Leitura", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Escolha em que ativos este fluxo de trabalho pode atuar.", "choose-widget-style-first": "Escolha primeiro um estilo de widget.", "choose-widget-to-add-to-tab": "Escolha um widget para adicionar a este separador.", + "classic-mode-retiring-admin": "O modo clássico será descontinuado numa próxima versão. As organizações e personas ainda definidas como Clássico passarão para IA automaticamente.", + "classic-mode-retiring-description": "O modo clássico será removido numa próxima versão. Depois disso, o modo IA será a única interface. Pode mudar agora e manter as suas definições.", + "classic-mode-retiring-title": "O modo clássico vai ser descontinuado", "classification-add-tags-description": "Defina os rótulos que as suas equipas irão aplicar.", "classification-create-description": "Dê um nome à categoria, como Sensibilidade ou Certificação.", "classification-empty-state-description": "As classificações permitem criar um sistema de rotulagem consistente — defina as categorias uma vez e etiquete ativos em qualquer lugar.", 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 7fbb6f54caed..e5c1955ff8b7 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "реакции", "read": "Читать", "read-only": "Только для чтения", + "read-the-release-notes": "Читать примечания к выпуску", "read-type": "Читать {{type}}", "reader-avg": "Сред. чтение", "reader-stat-plural": "Статистика чтения", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Выберите, к каким активам может применяться этот рабочий процесс.", "choose-widget-style-first": "Сначала выберите стиль виджета.", "choose-widget-to-add-to-tab": "Выберите виджет для добавления на эту вкладку.", + "classic-mode-retiring-admin": "Классический режим будет выведен из эксплуатации в одном из ближайших выпусков. Организации и персоны, у которых всё ещё выбран классический режим, будут автоматически переведены в режим ИИ.", + "classic-mode-retiring-description": "Классический режим будет удалён в одном из ближайших выпусков. После этого режим ИИ станет единственным интерфейсом. Вы можете переключиться уже сейчас и сохранить свои настройки.", + "classic-mode-retiring-title": "Классический режим выводится из эксплуатации", "classification-add-tags-description": "Определите метки, которые будут применять ваши команды.", "classification-create-description": "Назовите категорию, например «Чувствительность» или «Сертификация».", "classification-empty-state-description": "Классификации позволяют выстроить единую систему меток: определите категории один раз и проставляйте теги на ресурсы повсюду.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/sv-se.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/sv-se.json index a18ab6144925..7017e07c351b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/sv-se.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/sv-se.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "reaktioner", "read": "Läs", "read-only": "Skrivskyddad", + "read-the-release-notes": "Läs versionsinformationen", "read-type": "Läs {{type}}", "reader-avg": "Läsar-genomsnitt", "reader-stat-plural": "Läsarstatistik", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Välj vilka tillgångar detta arbetsflöde kan agera på.", "choose-widget-style-first": "Välj en widgetstil först.", "choose-widget-to-add-to-tab": "Välj en widget att lägga till på den här fliken.", + "classic-mode-retiring-admin": "Klassiskt läge fasas ut i en kommande version. Organisationer och personas som fortfarande är inställda på Klassiskt flyttas automatiskt till AI.", + "classic-mode-retiring-description": "Klassiskt läge tas bort i en kommande version. Därefter blir AI-läget det enda gränssnittet. Du kan byta nu och behålla dina inställningar.", + "classic-mode-retiring-title": "Klassiskt läge fasas ut", "classification-add-tags-description": "Definiera de etiketter som dina team kommer att tillämpa.", "classification-create-description": "Namnge kategorin, till exempel Känslighet eller Certifiering.", "classification-empty-state-description": "Med klassificeringar bygger du ett konsekvent etikettsystem — definiera kategorier en gång och tagga tillgångar överallt.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json index 291ee8bb43ee..9ebfdeee2f56 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "ปฏิกิริยาหลายรายการ", "read": "อ่าน", "read-only": "อ่านอย่างเดียว", + "read-the-release-notes": "อ่านบันทึกประจำรุ่น", "read-type": "อ่าน {{type}}", "reader-avg": "การอ่านเฉลี่ย", "reader-stat-plural": "สถิติการอ่าน", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "เลือกสินทรัพย์ที่เวิร์กโฟลว์นี้สามารถดำเนินการได้", "choose-widget-style-first": "เลือกรูปแบบวิดเจ็ตก่อน", "choose-widget-to-add-to-tab": "เลือกวิดเจ็ตที่จะเพิ่มลงในแท็บนี้", + "classic-mode-retiring-admin": "โหมดคลาสสิกจะเลิกใช้ในรุ่นถัดไป องค์กรและเพอร์โซนาที่ยังตั้งค่าเป็นคลาสสิกจะย้ายไปใช้ AI โดยอัตโนมัติ", + "classic-mode-retiring-description": "โหมดคลาสสิกจะถูกนำออกในรุ่นถัดไป หลังจากนั้นโหมด AI จะเป็นอินเทอร์เฟซเดียว คุณสามารถเปลี่ยนตอนนี้และเก็บการตั้งค่าไว้ได้", + "classic-mode-retiring-title": "โหมดคลาสสิกกำลังจะเลิกใช้", "classification-add-tags-description": "กำหนดป้ายกำกับที่ทีมของคุณจะนำไปใช้", "classification-create-description": "ตั้งชื่อหมวดหมู่ เช่น ความอ่อนไหวหรือการรับรอง", "classification-empty-state-description": "การจำแนกประเภทช่วยให้คุณสร้างระบบการติดป้ายกำกับที่สอดคล้องกัน — กำหนดหมวดหมู่เพียงครั้งเดียว แล้วติดแท็กสินทรัพย์ได้ทุกที่", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json index 0d3d80e23cf5..7266b49352d3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "tepkiler", "read": "Oku", "read-only": "Salt okunur", + "read-the-release-notes": "Sürüm notlarını okuyun", "read-type": "{{type}} Oku", "reader-avg": "Okuma Ort.", "reader-stat-plural": "Okuma İstatistikleri", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "Bu iş akışının hangi varlıklara uygulanabileceğini seçin.", "choose-widget-style-first": "Önce bir widget stili seçin.", "choose-widget-to-add-to-tab": "Bu sekmeye eklemek için bir widget seçin.", + "classic-mode-retiring-admin": "Klasik mod yakında çıkacak bir sürümde kullanımdan kalkacak. Hâlâ Klasik olarak ayarlı kuruluşlar ve personalar otomatik olarak yapay zeka moduna geçecek.", + "classic-mode-retiring-description": "Klasik mod yakında çıkacak bir sürümde kaldırılacak. Bundan sonra tek arayüz yapay zeka modu olacak. Şimdi geçiş yapabilir ve ayarlarınızı koruyabilirsiniz.", + "classic-mode-retiring-title": "Klasik mod kullanımdan kaldırılıyor", "classification-add-tags-description": "Ekiplerinizin uygulayacağı işaretleri tanımlayın.", "classification-create-description": "Kategoriye bir ad verin; örneğin Hassasiyet veya Sertifikasyon.", "classification-empty-state-description": "Sınıflandırmalar tutarlı bir etiketleme sistemi oluşturmanızı sağlar — kategorileri bir kez tanımlayın, varlıkları her yerde etiketleyin.", 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 46326a048c18..118db577fc70 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 @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "表情回复", "read": "读取", "read-only": "只读", + "read-the-release-notes": "阅读发行说明", "read-type": "查看{{type}}", "reader-avg": "读取平均值", "reader-stat-plural": "读取统计", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "选择此工作流可以作用的数据资产。", "choose-widget-style-first": "请先选择小组件样式。", "choose-widget-to-add-to-tab": "选择要添加到此标签页的小组件。", + "classic-mode-retiring-admin": "经典模式将在即将发布的版本中停用。仍设置为经典模式的组织和角色将自动切换到 AI 模式。", + "classic-mode-retiring-description": "经典模式将在即将发布的版本中移除。之后,AI 模式将成为唯一的界面。你现在就可以切换,并保留现有设置。", + "classic-mode-retiring-title": "经典模式即将停用", "classification-add-tags-description": "定义团队将要应用的标签。", "classification-create-description": "为类别命名,例如敏感度或认证。", "classification-empty-state-description": "分类可帮助您构建一致的标记体系——只需定义一次类别,即可随处为资产添加标签。", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json index 31f5a18c5ec0..e99df535c1fb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json @@ -2653,6 +2653,7 @@ "reaction-lowercase-plural": "回應", "read": "閱讀", "read-only": "唯讀", + "read-the-release-notes": "閱讀版本說明", "read-type": "讀取 {{type}}", "reader-avg": "讀取平均", "reader-stat-plural": "讀取統計", @@ -4078,6 +4079,9 @@ "choose-which-assets-this-workflow-can-act-on": "選擇此工作流程可作用的資產。", "choose-widget-style-first": "請先選擇小工具樣式。", "choose-widget-to-add-to-tab": "選擇要新增到此分頁的小工具。", + "classic-mode-retiring-admin": "經典模式將在即將推出的版本中停用。仍設定為經典模式的組織和角色將自動切換到 AI 模式。", + "classic-mode-retiring-description": "經典模式將在即將推出的版本中移除。之後,AI 模式將成為唯一的介面。你現在就可以切換,並保留現有設定。", + "classic-mode-retiring-title": "經典模式即將停用", "classification-add-tags-description": "定義團隊將要套用的標籤。", "classification-create-description": "為類別命名,例如敏感度或認證。", "classification-empty-state-description": "分類可協助您建立一致的標記體系——只需定義一次類別,即可隨處為資產加上標籤。", diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.test.tsx index 4e965070eb7b..da802e053215 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.test.tsx @@ -68,6 +68,19 @@ describe('DefaultAppModePage', () => { expect(screen.getByTestId('app-mode-option-ai')).toBeInTheDocument(); }); + it('marks only the Classic option as deprecated, with the release notes link', async () => { + await renderPage(); + + const classic = screen.getByTestId('app-mode-option-classic'); + + expect(classic).toHaveTextContent('label.deprecated'); + expect(classic).toHaveTextContent('message.classic-mode-retiring-admin'); + expect(screen.getAllByTestId('classic-deprecated-badge')).toHaveLength(1); + expect( + screen.getByTestId('classic-deprecation-release-notes') + ).toHaveAttribute('target', '_blank'); + }); + it('keeps Save disabled until the selection changes', async () => { await renderPage(); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.tsx index 62f4adb235ab..3402b1adfc4d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/Settings/DefaultAppModePage/DefaultAppModePage.tsx @@ -20,6 +20,10 @@ import { import { AxiosError } from 'axios'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { + ClassicDeprecatedLabel, + ClassicDeprecationHint, +} from '../../../components/AppModeSwitcher/ClassicDeprecationNotice'; import DocumentTitle from '../../../components/common/DocumentTitle/DocumentTitle'; import SettingsSourceBanner from '../../../components/platform/settings/SettingsSourceBanner/SettingsSourceBanner'; import { DefaultAppMode } from '../../../generated/api/configuration/appConfiguration'; @@ -136,8 +140,19 @@ const DefaultAppModePage: React.FC = () => { {OPTIONS.map((option) => ( + ) : undefined + } key={option.value} - label={t(option.labelKey)} + label={ + option.value === DefaultAppMode.Classic ? ( + + ) : ( + t(option.labelKey) + ) + } value={option.value} /> ))} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TagPage/TagPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TagPage/TagPage.test.tsx index ba738f424f94..c66931c24b54 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TagPage/TagPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TagPage/TagPage.test.tsx @@ -56,6 +56,7 @@ jest.mock('../../hooks/useEntityPermissions/useEntityPermissions', () => ({ })); jest.mock('@openmetadata/ui-core-components', () => ({ + Badge: jest.requireActual('@openmetadata/ui-core-components').Badge, Box: jest.requireActual('@openmetadata/ui-core-components').Box, Tabs: jest.requireActual('@openmetadata/ui-core-components').Tabs, Divider: jest.requireActual('@openmetadata/ui-core-components').Divider, diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less b/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less index 58e725677678..0519d613313d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/components/tabs.less @@ -65,12 +65,6 @@ &.ant-tabs-tab-active { color: @primary-color; font-weight: var(--om-font-weight-semibold); - - [data-testid='count'] > span { - background-color: @primary-color; - border-color: @primary-color; - color: var(--tw-color-text-white); - } } } } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.test.tsx index 8a02203d77b7..b5f23d6c73e7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.test.tsx @@ -16,26 +16,21 @@ import { getCountBadge } from './EntityDisplayPureUtils'; const getBadgeClassName = (badge: ReactElement): string => (badge as ReactElement<{ className: string }>).props.className; -describe('getCountBadge active class branch', () => { - it('renders the active classes when isActive is true', () => { - const className = getBadgeClassName(getCountBadge(1, '', true)); +const getBadgeColor = (badge: ReactElement): string => + (badge as ReactElement<{ color: string }>).props.color; - expect(className).toContain('bg-primary text-white no-border'); - expect(className).not.toContain('ant-tag'); +describe('getCountBadge active class branch', () => { + it('renders a brand badge when isActive is true', () => { + expect(getBadgeColor(getCountBadge(1, '', true))).toBe('brand'); }); - it('renders the ant-tag class when isActive is false', () => { - const className = getBadgeClassName(getCountBadge(1, '', false)); - - expect(className).toContain('ant-tag'); - expect(className).not.toContain('bg-primary'); + it('renders a gray badge when isActive is false', () => { + expect(getBadgeColor(getCountBadge(1, '', false))).toBe('gray'); }); - it('renders neither active nor ant-tag class when isActive is undefined', () => { + it('renders the plain bordered count when isActive is undefined', () => { const className = getBadgeClassName(getCountBadge(1)); - expect(className).not.toContain('ant-tag'); - expect(className).not.toContain('bg-primary'); expect(className).toContain('global-border'); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.tsx index 54e845bb977e..5599feef41b9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityDisplayPureUtils.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ -import { Tooltip } from '@openmetadata/ui-core-components'; +import { Badge, Tooltip } from '@openmetadata/ui-core-components'; import classNames from 'classnames'; import { capitalize, isUndefined } from 'lodash'; import type { CurrentState } from 'Models'; @@ -28,14 +28,22 @@ export const getCountBadge = ( isActive?: boolean, excludeTriggerFromTabOrder?: boolean ) => { - const activeCls = isActive ? 'bg-primary text-white no-border' : 'ant-tag'; - const clsBG = isUndefined(isActive) ? '' : activeCls; + if (!isUndefined(isActive)) { + return ( + + {count} + + ); + } return (