From c2a27bc617cf20ef976cae0a15c4450ceb4f7e3c Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Mon, 21 Oct 2024 10:14:41 -0500 Subject: [PATCH 01/24] first pass at system/project tables --- .../src/features/common/ResultStatusBadge.tsx | 22 ++ .../src/features/common/nav/v2/nav-config.ts | 5 + .../src/features/common/nav/v2/routes.ts | 3 + .../features/common/table/v2/FidesCell.tsx | 1 + .../features/common/table/v2/FidesTable.tsx | 3 + .../hooks/useDetectionResultColumns.tsx | 2 +- .../hooks/useDiscoveryResultColumns.tsx | 2 +- .../tables/ActivityTable.tsx | 2 +- ....tsx => StagedResourceStatusBadgeCell.tsx} | 15 +- .../dataset-lifecycle/StatusBadgeCell.tsx | 29 ++ .../projects/ProjectActionsCell.tsx | 0 .../projects/ProjectsTable.tsx | 246 +++++++++++++++ .../projects/useSpoofGetProjectsQuery.tsx | 61 ++++ .../systems/SystemActionCell.tsx | 41 +++ .../systems/SystemNameCell.tsx | 13 + .../systems/SystemsTable.tsx | 285 ++++++++++++++++++ .../systems/systemHasHeliosIntegration.ts | 11 + .../systems/useSpoofGetSystemsQuery.tsx | 89 ++++++ .../src/features/dataset-lifecycle/types.ts | 10 + .../src/pages/e2e-datasets/[system-id].tsx | 16 + .../admin-ui/src/pages/e2e-datasets/index.tsx | 14 + 21 files changed, 853 insertions(+), 17 deletions(-) create mode 100644 clients/admin-ui/src/features/common/ResultStatusBadge.tsx rename clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/{ResultStatusBadgeCell.tsx => StagedResourceStatusBadgeCell.tsx} (78%) create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/StatusBadgeCell.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectActionsCell.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/projects/useSpoofGetProjectsQuery.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/systems/SystemActionCell.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/systems/systemHasHeliosIntegration.ts create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/systems/useSpoofGetSystemsQuery.tsx create mode 100644 clients/admin-ui/src/features/dataset-lifecycle/types.ts create mode 100644 clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx create mode 100644 clients/admin-ui/src/pages/e2e-datasets/index.tsx diff --git a/clients/admin-ui/src/features/common/ResultStatusBadge.tsx b/clients/admin-ui/src/features/common/ResultStatusBadge.tsx new file mode 100644 index 0000000000..ffe36634e8 --- /dev/null +++ b/clients/admin-ui/src/features/common/ResultStatusBadge.tsx @@ -0,0 +1,22 @@ +import { Badge, BadgeProps } from "fidesui"; +import React from "react"; + +const ResultStatusBadge = React.forwardRef( + (props, ref) => { + return ( + + {props.children} + + ); + }, +); + +ResultStatusBadge.displayName = "ResultStatusBadge"; + +export default ResultStatusBadge; diff --git a/clients/admin-ui/src/features/common/nav/v2/nav-config.ts b/clients/admin-ui/src/features/common/nav/v2/nav-config.ts index 4b5fba1402..23918e8e08 100644 --- a/clients/admin-ui/src/features/common/nav/v2/nav-config.ts +++ b/clients/admin-ui/src/features/common/nav/v2/nav-config.ts @@ -59,6 +59,11 @@ export const NAV_CONFIG: NavConfigGroup[] = [ requiresFlag: "dataDiscoveryAndDetection", requiresPlus: true, }, + { + title: "E2E datasets (WIP)", + path: routes.E2E_DATASETS_ROUTE, + scopes: [], + }, ], }, { diff --git a/clients/admin-ui/src/features/common/nav/v2/routes.ts b/clients/admin-ui/src/features/common/nav/v2/routes.ts index fbf573f233..574735aa1d 100644 --- a/clients/admin-ui/src/features/common/nav/v2/routes.ts +++ b/clients/admin-ui/src/features/common/nav/v2/routes.ts @@ -29,6 +29,9 @@ export const DATA_DISCOVERY_ROUTE = "/data-discovery/discovery"; export const DATA_DISCOVERY_ROUTE_DETAIL = "/data-discovery/discovery/[resourceUrn]"; +// End-to-end datasets +export const E2E_DATASETS_ROUTE = "/e2e-datasets"; + // Privacy requests group export const DATASTORE_CONNECTION_ROUTE = "/datastore-connection"; export const PRIVACY_REQUESTS_ROUTE = "/privacy-requests"; diff --git a/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx b/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx index 494f5e8dc8..eeb5dd4091 100644 --- a/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx +++ b/clients/admin-ui/src/features/common/table/v2/FidesCell.tsx @@ -106,6 +106,7 @@ export const FidesCell = ({ height="inherit" onClick={handleCellClick} data-testid={`row-${cell.row.id}-col-${cell.column.id}`} + {...cell.column.columnDef.meta?.cellProps} > {!cell.getIsPlaceholder() || isFirstRowOfGroupedRows ? flexRender(cell.column.columnDef.cell, { diff --git a/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx b/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx index e3a0cba64e..a3a7664d53 100644 --- a/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx +++ b/clients/admin-ui/src/features/common/table/v2/FidesTable.tsx @@ -22,6 +22,7 @@ import { Portal, SmallCloseIcon, Table, + TableCellProps, TableContainer, Tbody, Td, @@ -58,6 +59,7 @@ declare module "@tanstack/table-core" { showHeaderMenuWrapOption?: boolean; overflow?: "auto" | "visible" | "hidden"; disableRowClick?: boolean; + cellProps?: TableCellProps; onCellClick?: (row: TData) => void; } } @@ -439,6 +441,7 @@ export const FidesTableV2 = ({ opacity: 1, }, }} + {...header.column.columnDef.meta?.cellProps} > { - return ( - - {children} - - ); -}; - const ResultStatusBadgeCell = ({ result, changeTypeOverride, diff --git a/clients/admin-ui/src/features/dataset-lifecycle/StatusBadgeCell.tsx b/clients/admin-ui/src/features/dataset-lifecycle/StatusBadgeCell.tsx new file mode 100644 index 0000000000..6e639bb809 --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/StatusBadgeCell.tsx @@ -0,0 +1,29 @@ +import { Tooltip } from "fidesui"; + +import ResultStatusBadge from "~/features/common/ResultStatusBadge"; +import { + DatasetLifecycleStatusEnum, + DatasetLifecycleStatusResult, +} from "~/features/dataset-lifecycle/types"; + +const STATUS_COLOR_MAP: Record = { + [DatasetLifecycleStatusEnum.ADDED]: "green", + [DatasetLifecycleStatusEnum.IN_PROGRESS]: "orange", + [DatasetLifecycleStatusEnum.ATTENTION]: "red", +}; + +const StatusBadgeCell = ({ + statusResult: { status, detail }, +}: { + statusResult: DatasetLifecycleStatusResult; +}) => { + return ( + + + {status} + + + ); +}; + +export default StatusBadgeCell; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectActionsCell.tsx b/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectActionsCell.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx b/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx new file mode 100644 index 0000000000..9997ec1ef5 --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx @@ -0,0 +1,246 @@ +/* eslint-disable react/no-unstable-nested-components */ +import { + ColumnDef, + createColumnHelper, + getCoreRowModel, + getExpandedRowModel, + getGroupedRowModel, + RowSelectionState, + useReactTable, +} from "@tanstack/react-table"; +import { + AntButton, + Box, + Flex, + Menu, + MenuButton, + MenuItem, + MenuList, + Text, + VStack, +} from "fidesui"; +import { useEffect, useMemo, useState } from "react"; + +import { + DefaultCell, + FidesTableV2, + IndeterminateCheckboxCell, + PaginationBar, + TableActionBar, + TableSkeletonLoader, + useServerSidePagination, +} from "~/features/common/table/v2"; +import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; +import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; +import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; +import useSpoofGetProjectsQuery, { + DatasetLifecycleProject, +} from "~/features/dataset-lifecycle/projects/useSpoofGetProjectsQuery"; +import StatusBadgeCell from "~/features/dataset-lifecycle/StatusBadgeCell"; +import SystemActionsCell from "~/features/dataset-lifecycle/systems/SystemActionCell"; + +const EMPTY_RESPONSE = { + items: [], + total: 0, + page: 1, + size: 50, + pages: 1, +}; + +const EmptyTableNotice = () => ( + + + + No systems found + + You're up to date! + + +); + +const columnHelper = createColumnHelper(); + +const ProjectsTable = () => { + const [searchQuery, setSearchQuery] = useState(""); + const [rowSelectionState, setRowSelectionState] = useState( + {}, + ); + + const { + PAGE_SIZES, + pageSize, + setPageSize, + onPreviousPageClick, + isPreviousPageDisabled, + onNextPageClick, + isNextPageDisabled, + startRange, + endRange, + pageIndex, + setTotalPages, + } = useServerSidePagination(); + + const { + isFetching, + isLoading, + data: queryResult, + } = useSpoofGetProjectsQuery({ + pageIndex, + pageSize, + searchQuery, + }); + + const { + items: data, + total: totalRows, + pages: totalPages, + } = useMemo(() => queryResult ?? EMPTY_RESPONSE, [queryResult]); + + useEffect(() => { + setTotalPages(totalPages); + }, [totalPages, setTotalPages]); + + const columns: ColumnDef[] = useMemo( + () => [ + columnHelper.display({ + id: "select", + cell: ({ row }) => ( + + ), + header: ({ table }) => ( + + ), + maxSize: 25, + enableResizing: false, + meta: { + cellProps: { + borderRight: "none", + }, + }, + }), + columnHelper.accessor((row) => row.name, { + id: "name", + cell: (props) => , + header: "Name", + }), + columnHelper.accessor((row) => row.status, { + id: "status", + cell: (props) => , + header: "Status", + }), + columnHelper.accessor((row) => row.lastUpdated, { + id: "lastUpdated", + cell: (props) => , + header: "Last Updated", + meta: { + cellProps: { + borderRight: "none", + }, + }, + }), + columnHelper.display({ + id: "actions", + cell: (props) => ( + + console.log(`hiding project ${props.row.original.urn}...`) + } + /> + ), + maxSize: 20, + enableResizing: false, + meta: { + cellProps: { + borderLeft: "none", + }, + }, + }), + ], + [], + ); + + const tableInstance = useReactTable({ + getCoreRowModel: getCoreRowModel(), + getGroupedRowModel: getGroupedRowModel(), + getExpandedRowModel: getExpandedRowModel(), + manualPagination: true, + columnResizeMode: "onChange", + columns, + data, + onRowSelectionChange: setRowSelectionState, + state: { + rowSelection: rowSelectionState, + }, + }); + + const selectedRowIds = Object.keys(rowSelectionState); + + const handleBulkHide = () => { + console.log(`hiding projects ${selectedRowIds.join(", ")}...`); + setRowSelectionState({}); + }; + + if (isLoading || isFetching) { + return ; + } + + return ( + <> + + + + + + + + + + Actions + + + Hide + + + + } + onRowClick={() => console.log("row clicked")} + /> + + + ); +}; + +export default ProjectsTable; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/projects/useSpoofGetProjectsQuery.tsx b/clients/admin-ui/src/features/dataset-lifecycle/projects/useSpoofGetProjectsQuery.tsx new file mode 100644 index 0000000000..d0c9385a15 --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/projects/useSpoofGetProjectsQuery.tsx @@ -0,0 +1,61 @@ +import { + DatasetLifecycleStatusEnum, + DatasetLifecycleStatusResult, +} from "~/features/dataset-lifecycle/types"; + +export interface DatasetLifecycleProject { + name?: string; + urn: string; + status: DatasetLifecycleStatusResult; + lastUpdated: string; +} + +interface DatasetLifecycleProjectQueryProps { + pageIndex: number; + pageSize: number; + searchQuery: string; +} + +const PROJECTS: DatasetLifecycleProject[] = [ + { + name: "Project 1", + urn: "project-1", + status: { + status: DatasetLifecycleStatusEnum.ADDED, + detail: "New project", + }, + lastUpdated: "2021-10-01T00:00:00Z", + }, + { + name: "Project 2", + urn: "project-2", + status: { + status: DatasetLifecycleStatusEnum.IN_PROGRESS, + detail: "In progress", + }, + lastUpdated: "2021-10-02T00:00:00Z", + }, + { + name: "Project 3", + urn: "project-3", + status: { + status: DatasetLifecycleStatusEnum.ATTENTION, + detail: "Attention required", + }, + lastUpdated: "2021-10-03T00:00:00Z", + }, +]; + +const useSpoofGetProjectsQuery = ({ + pageIndex, + pageSize, + searchQuery, +}: DatasetLifecycleProjectQueryProps) => { + return { + data: { items: PROJECTS, total: 0, page: 1, size: 50, pages: 1 }, + isLoading: false, + isFetching: false, + }; +}; + +export default useSpoofGetProjectsQuery; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemActionCell.tsx b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemActionCell.tsx new file mode 100644 index 0000000000..72e81e074f --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemActionCell.tsx @@ -0,0 +1,41 @@ +import { + AntButton, + Menu, + MenuButton, + MenuItem, + MenuList, + MoreIcon, +} from "fidesui"; + +interface SystemActionsCellProps { + onDetailClick?: () => void; + onHideClick?: () => void; +} + +const SystemActionsCell = ({ + onDetailClick, + onHideClick, +}: SystemActionsCellProps) => { + return ( + + } + /> + + {onDetailClick && ( + View details + )} + {onHideClick && Hide} + + + ); +}; + +export default SystemActionsCell; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx new file mode 100644 index 0000000000..06c79a40d2 --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx @@ -0,0 +1,13 @@ +import { DefaultCell } from "~/features/common/table/v2"; + +const SystemNameCell = ({ + value, + clickable, +}: { + value: string; + clickable?: boolean; +}) => ( + +); + +export default SystemNameCell; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx new file mode 100644 index 0000000000..95b8b3dca6 --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx @@ -0,0 +1,285 @@ +/* eslint-disable react/no-unstable-nested-components */ + +import { + ColumnDef, + createColumnHelper, + getCoreRowModel, + getExpandedRowModel, + getGroupedRowModel, + RowSelectionState, + useReactTable, +} from "@tanstack/react-table"; +import { + AntButton, + Box, + Flex, + Menu, + MenuButton, + MenuItem, + MenuList, + Text, + VStack, +} from "fidesui"; +import { useRouter } from "next/router"; +import { useEffect, useMemo, useState } from "react"; + +import { E2E_DATASETS_ROUTE } from "~/features/common/nav/v2/routes"; +import { + DefaultCell, + DefaultHeaderCell, + FidesTableV2, + PaginationBar, + TableActionBar, + TableSkeletonLoader, + useServerSidePagination, +} from "~/features/common/table/v2"; +import { + IndeterminateCheckboxCell, + RelativeTimestampCell, +} from "~/features/common/table/v2/cells"; +import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; +import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; +import StatusBadgeCell from "~/features/dataset-lifecycle/StatusBadgeCell"; +import SystemActionsCell from "~/features/dataset-lifecycle/systems/SystemActionCell"; +import systemHasHeliosIntegration from "~/features/dataset-lifecycle/systems/systemHasHeliosIntegration"; +import SystemNameCell from "~/features/dataset-lifecycle/systems/SystemNameCell"; +import useSpoofGetSystemsQuery, { + DatasetLifecycleSystem, +} from "~/features/dataset-lifecycle/systems/useSpoofGetSystemsQuery"; + +const EMPTY_RESPONSE = { + items: [], + total: 0, + page: 1, + size: 50, + pages: 1, +}; + +const columnHelper = createColumnHelper(); + +const EmptyTableNotice = () => ( + + + + No systems found + + You're up to date! + + +); + +const SystemsTable = () => { + const [searchQuery, setSearchQuery] = useState(""); + const [rowSelectionState, setRowSelectionState] = useState( + {}, + ); + + const router = useRouter(); + + const { + PAGE_SIZES, + pageSize, + setPageSize, + onPreviousPageClick, + isPreviousPageDisabled, + onNextPageClick, + isNextPageDisabled, + startRange, + endRange, + pageIndex, + setTotalPages, + } = useServerSidePagination(); + + const { + isFetching, + isLoading, + data: queryResult, + } = useSpoofGetSystemsQuery({ + pageIndex, + pageSize, + searchQuery, + }); + + const { + items: data, + total: totalRows, + pages: totalPages, + } = useMemo(() => queryResult ?? EMPTY_RESPONSE, [queryResult]); + + useEffect(() => { + setTotalPages(totalPages); + }, [totalPages, setTotalPages]); + + const columns: ColumnDef[] = useMemo( + () => [ + columnHelper.display({ + id: "select", + cell: ({ row }) => ( + + ), + header: ({ table }) => ( + + ), + maxSize: 25, + enableResizing: false, + meta: { + cellProps: { + borderRight: "none", + }, + }, + }), + columnHelper.accessor((row) => row.name, { + id: "name", + cell: ({ row, getValue }) => ( + + ), + header: (props) => , + }), + columnHelper.accessor((row) => row.status, { + id: "status", + cell: ({ getValue }) => , + header: (props) => , + }), + columnHelper.accessor((row) => row.changes, { + id: "changes", + cell: ({ getValue }) => , + header: (props) => , + }), + columnHelper.accessor((row) => row.lastUpdated, { + id: "updated-at", + cell: ({ getValue }) => , + header: (props) => , + }), + columnHelper.accessor((row) => row.dataUses, { + id: "data-uses", + cell: ({ getValue }) => , + header: (props) => , + meta: { + cellProps: { + borderRight: "none", + }, + }, + }), + columnHelper.display({ + id: "actions", + cell: (props) => ( + + router.push(`/systems/configure/${props.row.original.id}`) + } + onHideClick={() => + console.log(`hiding system ${props.row.original.id}...`) + } + /> + ), + maxSize: 20, + enableResizing: false, + meta: { + cellProps: { + borderLeft: "none", + }, + }, + }), + ], + [], + ); + + const tableInstance = useReactTable({ + getCoreRowModel: getCoreRowModel(), + getGroupedRowModel: getGroupedRowModel(), + getExpandedRowModel: getExpandedRowModel(), + getRowId: (row) => row.id, + manualPagination: true, + columnResizeMode: "onChange", + columns, + data, + onRowSelectionChange: setRowSelectionState, + state: { + rowSelection: rowSelectionState, + }, + }); + + const selectedRowIds = Object.keys(rowSelectionState).filter( + (k) => rowSelectionState[k], + ); + + const handleBulkAddDataUse = () => { + console.log(`adding a data use to systems ${selectedRowIds.join(", ")}...`); + setRowSelectionState({}); + }; + + const handleBulkHide = () => { + console.log(`hiding systems ${selectedRowIds.join(", ")}...`); + setRowSelectionState({}); + }; + + if (isLoading || isFetching) { + return ; + } + + return ( + <> + + + + + + + + + + Actions + + + Add data use + Hide + + + + } + getRowIsClickable={systemHasHeliosIntegration} + onRowClick={(row) => router.push(`${E2E_DATASETS_ROUTE}/${row.id}`)} + /> + + + ); +}; + +export default SystemsTable; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/systemHasHeliosIntegration.ts b/clients/admin-ui/src/features/dataset-lifecycle/systems/systemHasHeliosIntegration.ts new file mode 100644 index 0000000000..7267a1d8d7 --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/systems/systemHasHeliosIntegration.ts @@ -0,0 +1,11 @@ +import { DatasetLifecycleSystem } from "~/features/dataset-lifecycle/systems/useSpoofGetSystemsQuery"; +import { SUPPORTED_INTEGRATIONS } from "~/features/integrations/add-integration/allIntegrationTypes"; + +const systemHasHeliosIntegration = (system: DatasetLifecycleSystem) => { + return ( + !!system.integration?.connection_type && + SUPPORTED_INTEGRATIONS.includes(system.integration.connection_type) + ); +}; + +export default systemHasHeliosIntegration; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/useSpoofGetSystemsQuery.tsx b/clients/admin-ui/src/features/dataset-lifecycle/systems/useSpoofGetSystemsQuery.tsx new file mode 100644 index 0000000000..51ee99fbc7 --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/systems/useSpoofGetSystemsQuery.tsx @@ -0,0 +1,89 @@ +import { + DatasetLifecycleStatusEnum, + DatasetLifecycleStatusResult, +} from "~/features/dataset-lifecycle/types"; +import { + AccessLevel, + ConnectionConfigurationResponse, + ConnectionType, +} from "~/types/api"; + +export interface DatasetLifecycleSystem { + name?: string; + id: string; + status: DatasetLifecycleStatusResult; + changes: number; + lastUpdated: string; + dataUses: string[]; + integration?: ConnectionConfigurationResponse; +} + +interface DatasetLifecycleSystemQueryProps { + pageIndex: number; + pageSize: number; + searchQuery: string; +} + +const SYSTEMS: DatasetLifecycleSystem[] = [ + { + name: "System 1", + id: "system-1", + status: { + status: DatasetLifecycleStatusEnum.ADDED, + detail: "New system", + }, + changes: 0, + lastUpdated: "2021-10-01T00:00:00Z", + dataUses: ["Data use 1", "Data use 2"], + integration: { + name: "Test", + key: "test", + connection_type: ConnectionType.BIGQUERY, + access: AccessLevel.READ, + created_at: "2021-10-01T00:00:00Z", + }, + }, + { + name: "System 2", + id: "system-2", + status: { + status: DatasetLifecycleStatusEnum.IN_PROGRESS, + detail: "In progress", + }, + changes: 1, + lastUpdated: "2021-10-02T00:00:00Z", + dataUses: ["Data use 1"], + }, + { + name: "System 3", + id: "system-3", + status: { + status: DatasetLifecycleStatusEnum.ATTENTION, + detail: "Requires attention", + }, + changes: 2, + lastUpdated: "2021-10-03T00:00:00Z", + dataUses: ["Data use 2"], + integration: { + name: "Test", + key: "test", + connection_type: ConnectionType.BIGQUERY, + access: AccessLevel.READ, + created_at: "2021-10-01T00:00:00Z", + }, + }, +]; + +const useSpoofGetSystemsQuery = ({ + pageIndex, + pageSize, + searchQuery, +}: DatasetLifecycleSystemQueryProps) => { + return { + data: { items: SYSTEMS, total: 0, page: 1, size: 50, pages: 1 }, + isLoading: false, + isFetching: false, + }; +}; + +export default useSpoofGetSystemsQuery; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/types.ts b/clients/admin-ui/src/features/dataset-lifecycle/types.ts new file mode 100644 index 0000000000..0141d2844d --- /dev/null +++ b/clients/admin-ui/src/features/dataset-lifecycle/types.ts @@ -0,0 +1,10 @@ +export enum DatasetLifecycleStatusEnum { + ADDED = "Added", + IN_PROGRESS = "In progress", + ATTENTION = "Attention required", +} + +export interface DatasetLifecycleStatusResult { + status: DatasetLifecycleStatusEnum; + detail: string; +} diff --git a/clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx b/clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx new file mode 100644 index 0000000000..db549b3cb4 --- /dev/null +++ b/clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx @@ -0,0 +1,16 @@ +import Layout from "~/features/common/Layout"; +import PageHeader from "~/features/common/PageHeader"; +import ProjectsTable from "~/features/dataset-lifecycle/projects/ProjectsTable"; + +const E2EDatasetProjectView = () => { + return ( + + + + + ); +}; + +export default E2EDatasetProjectView; diff --git a/clients/admin-ui/src/pages/e2e-datasets/index.tsx b/clients/admin-ui/src/pages/e2e-datasets/index.tsx new file mode 100644 index 0000000000..a5df276e70 --- /dev/null +++ b/clients/admin-ui/src/pages/e2e-datasets/index.tsx @@ -0,0 +1,14 @@ +import Layout from "~/features/common/Layout"; +import PageHeader from "~/features/common/PageHeader"; +import SystemsTable from "~/features/dataset-lifecycle/systems/SystemsTable"; + +const E2EDatasetSystemView = () => { + return ( + + + + + ); +}; + +export default E2EDatasetSystemView; From 6489e64e46ff6c310ea9c9cd6b1868d6e426a07b Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 31 Oct 2024 02:31:24 -0500 Subject: [PATCH 02/24] fix subfield naming --- .../tables/cells/ResultStatusCell.tsx | 3 ++- .../utils/getResourceName.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/ResultStatusCell.tsx b/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/ResultStatusCell.tsx index 554eb7b3ab..fa41077f30 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/ResultStatusCell.tsx +++ b/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/ResultStatusCell.tsx @@ -3,6 +3,7 @@ import { Flex, Text, Tooltip } from "fidesui"; import { STATUS_INDICATOR_MAP } from "~/features/data-discovery-and-detection/statusIndicators"; import { ResourceChangeType } from "~/features/data-discovery-and-detection/types/ResourceChangeType"; import findResourceChangeType from "~/features/data-discovery-and-detection/utils/findResourceChangeType"; +import getResourceName from "~/features/data-discovery-and-detection/utils/getResourceName"; import resourceHasChildren from "~/features/data-discovery-and-detection/utils/resourceHasChildren"; import { StagedResource } from "~/types/api"; @@ -27,7 +28,7 @@ const ResultStatusCell = ({ overflow="hidden" textOverflow="ellipsis" > - {result.name} + {getResourceName(result)} ); diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts b/clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts new file mode 100644 index 0000000000..93fc8afdd4 --- /dev/null +++ b/clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts @@ -0,0 +1,19 @@ +import { DiscoveryMonitorItem } from "~/features/data-discovery-and-detection/types/DiscoveryMonitorItem"; + +const TOP_LEVEL_FIELD_URN_PARTS = 5; + +const getResourceName = (resource: DiscoveryMonitorItem) => { + const splitUrn = resource.urn.split("."); + if ( + !resource.parent_table_urn || + splitUrn.length === TOP_LEVEL_FIELD_URN_PARTS + ) { + // use name as-is if it's not a subfield + return resource.name; + } + // URN format is "monitor.project.dataset.field.[any number of subfields]" + // for a subfield, we want to show all subfield names separated by "." + return splitUrn.slice(5).join("."); +}; + +export default getResourceName; From 13f9cca54475f246e44a623eca818e8942f221a6 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 31 Oct 2024 02:41:00 -0500 Subject: [PATCH 03/24] apply const --- .../data-discovery-and-detection/utils/getResourceName.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts b/clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts index 93fc8afdd4..2322db667d 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts +++ b/clients/admin-ui/src/features/data-discovery-and-detection/utils/getResourceName.ts @@ -13,7 +13,7 @@ const getResourceName = (resource: DiscoveryMonitorItem) => { } // URN format is "monitor.project.dataset.field.[any number of subfields]" // for a subfield, we want to show all subfield names separated by "." - return splitUrn.slice(5).join("."); + return splitUrn.slice(TOP_LEVEL_FIELD_URN_PARTS).join("."); }; export default getResourceName; From f256f5b1fc981deeabf44ab57c054a1ca57ff493 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 31 Oct 2024 02:44:17 -0500 Subject: [PATCH 04/24] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d98aa8e5cf..a6ae2f3609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ The types of changes are: ### Fixed - API router sanitizer being too aggressive with NextJS Catch-all Segments [#5438](https://github.com/ethyca/fides/pull/5438) + - Fix rendering of subfield names in D&D tables [#5439](https://github.com/ethyca/fides/pull/5439) ## [2.48.0](https://github.com/ethyca/fidesplus/compare/2.47.1...2.48.0) From 8d6a25b6062ce3fa250e5b56bdda8dc3042cf0d2 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Fri, 1 Nov 2024 02:29:04 -0500 Subject: [PATCH 05/24] add data use dropdown UI to system table --- .../src/features/common/TaxonomiesPicker.tsx | 4 +- ...ectDropdown.tsx => DataCategorySelect.tsx} | 10 +- .../ClassificationCategoryBadge.tsx | 11 +- .../new-dataset-lifecycle/DataUseSelect.tsx | 107 ++++++++++++++++++ .../new-dataset-lifecycle/EditDataUseCell.tsx | 74 ++++++++++++ .../tables/cells/EditCategoryCell.tsx | 38 +++---- .../tables/cells/TaxonomyAddButton.tsx | 18 +++ .../tables/cells/TaxonomyCellContainer.tsx | 25 ++++ clients/admin-ui/src/pages/systems/index.tsx | 9 ++ 9 files changed, 257 insertions(+), 39 deletions(-) rename clients/admin-ui/src/features/common/dropdown/{TaxonomySelectDropdown.tsx => DataCategorySelect.tsx} (94%) create mode 100644 clients/admin-ui/src/features/data-discovery-and-detection/new-dataset-lifecycle/DataUseSelect.tsx create mode 100644 clients/admin-ui/src/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell.tsx create mode 100644 clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/TaxonomyAddButton.tsx create mode 100644 clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/TaxonomyCellContainer.tsx diff --git a/clients/admin-ui/src/features/common/TaxonomiesPicker.tsx b/clients/admin-ui/src/features/common/TaxonomiesPicker.tsx index 9f5ab05085..4f5d7c8b03 100644 --- a/clients/admin-ui/src/features/common/TaxonomiesPicker.tsx +++ b/clients/admin-ui/src/features/common/TaxonomiesPicker.tsx @@ -8,7 +8,7 @@ import { } from "fidesui"; import { useCallback, useState } from "react"; -import TaxonomySelectDropdown from "~/features/common/dropdown/TaxonomySelectDropdown"; +import DataCategorySelect from "~/features/common/dropdown/DataCategorySelect"; import { useOutsideClick } from "~/features/common/hooks"; import useTaxonomies from "./hooks/useTaxonomies"; @@ -84,7 +84,7 @@ const TaxonomiesPicker = ({ height="max" bgColor="#fff" > - { setIsAdding(false); onAddTaxonomy(o.value); diff --git a/clients/admin-ui/src/features/common/dropdown/TaxonomySelectDropdown.tsx b/clients/admin-ui/src/features/common/dropdown/DataCategorySelect.tsx similarity index 94% rename from clients/admin-ui/src/features/common/dropdown/TaxonomySelectDropdown.tsx rename to clients/admin-ui/src/features/common/dropdown/DataCategorySelect.tsx index 2c4553696b..ff8e3ebc65 100644 --- a/clients/admin-ui/src/features/common/dropdown/TaxonomySelectDropdown.tsx +++ b/clients/admin-ui/src/features/common/dropdown/DataCategorySelect.tsx @@ -12,7 +12,7 @@ export interface TaxonomySelectOption { description: string; } -const TaxonomyOption = ({ +export const DataCategoryOption = ({ data, setValue, }: OptionProps) => { @@ -59,12 +59,12 @@ const TaxonomyOption = ({ ); }; -interface TaxonomySelectDropdownProps { +export interface TaxonomySelectDropdownProps { onChange: (selectedOption: TaxonomySelectOption) => void; menuIsOpen?: boolean; showDisabled?: boolean; } -const TaxonomySelectDropdown = ({ +const DataCategorySelect = ({ onChange, menuIsOpen, showDisabled = false, @@ -97,7 +97,7 @@ const TaxonomySelectDropdown = ({ isSearchable menuPlacement="auto" components={{ - Option: TaxonomyOption, + Option: DataCategoryOption, }} menuIsOpen={menuIsOpen} chakraStyles={{ @@ -116,4 +116,4 @@ const TaxonomySelectDropdown = ({ /> ); }; -export default TaxonomySelectDropdown; +export default DataCategorySelect; diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/ClassificationCategoryBadge.tsx b/clients/admin-ui/src/features/data-discovery-and-detection/ClassificationCategoryBadge.tsx index b485bceab2..8de135dd0a 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/ClassificationCategoryBadge.tsx +++ b/clients/admin-ui/src/features/data-discovery-and-detection/ClassificationCategoryBadge.tsx @@ -1,15 +1,10 @@ import { Flex, FlexProps } from "fidesui"; -interface ClassificationCategoryBadgeProps extends FlexProps { - classification?: string | JSX.Element; +interface TaxonomyBadgeProps extends FlexProps { children: React.ReactNode; } -const ClassificationCategoryBadge = ({ - children, - onClick, - ...props -}: ClassificationCategoryBadgeProps) => { +const TaxonomyBadge = ({ children, onClick, ...props }: TaxonomyBadgeProps) => { return ( ) => { + const { getPrimaryKey, getDataUseByKey } = useTaxonomies(); + const primaryKey = getPrimaryKey(data.value, 2); + const primaryDataUse = getDataUseByKey(primaryKey); + return ( + setValue(data, "select-option")} + cursor="pointer" + borderBottomColor="gray.100" + borderBottomWidth={1} + paddingX={3} + paddingY={1.5} + _hover={{ + backgroundColor: "gray.100", + }} + data-testid={`option-${data.value}`} + > + + + {sentenceCase(primaryDataUse?.name || "")} + + + : {data.value} + + + + {data.description} + + + ); +}; + +const DataUseSelect = ({ + onChange, + menuIsOpen, + showDisabled = false, +}: TaxonomySelectDropdownProps) => { + const { getDataUseDisplayName, getDataUses } = useTaxonomies(); + + const getActiveDataUses = () => getDataUses().filter((du) => du.active); + + const dataUses = showDisabled ? getDataUses() : getActiveDataUses(); + + const options: Options = dataUses.map((dataUse) => ({ + value: dataUse.fides_key, + label: getDataUseDisplayName(dataUse.fides_key), + description: dataUse.description || "", + })); + + return ( + ), - control: (baseStyles) => ({ ...baseStyles, border: "none" }), - menuList: (baseStyles) => ({ - ...baseStyles, - paddingTop: 0, - paddingBottom: 0, - mt: 10, - position: "fixed", - overflowX: "hidden", - maxWidth: "lg", - }), - }} - /> - ); -}; - -export default DataUseSelect; diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell.tsx b/clients/admin-ui/src/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell.tsx index f658c54183..a817b9532b 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell.tsx +++ b/clients/admin-ui/src/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell.tsx @@ -5,13 +5,11 @@ import { EditIcon, useDisclosure, } from "fidesui"; -import { useCallback, useState } from "react"; +import { useState } from "react"; -import { TaxonomySelectOption } from "~/features/common/dropdown/TaxonomyDropdownOption"; -import { useOutsideClick } from "~/features/common/hooks"; +import DataUseSelect from "~/features/common/dropdown/DataUseSelect"; import useTaxonomies from "~/features/common/hooks/useTaxonomies"; import TaxonomyBadge from "~/features/data-discovery-and-detection/ClassificationCategoryBadge"; -import DataUseSelect from "~/features/data-discovery-and-detection/new-dataset-lifecycle/DataUseSelect"; import EditMinimalDataUseModal from "~/features/data-discovery-and-detection/new-dataset-lifecycle/EditMinimalDataUseModal"; import TaxonomyAddButton from "~/features/data-discovery-and-detection/tables/cells/TaxonomyAddButton"; import TaxonomyCellContainer from "~/features/data-discovery-and-detection/tables/cells/TaxonomyCellContainer"; @@ -63,18 +61,14 @@ const EditDataUseCell = ({ system }: EditDataUseCellProps) => { const dataUses = system.privacy_declarations.map((pd) => pd.data_use); - const handleClickOutside = useCallback(() => setIsAdding(false), []); - - const addDataUse = (use: TaxonomySelectOption) => { - const declaration = createMinimalDataUse(use.value); + const addDataUse = (use: string) => { + const declaration = createMinimalDataUse(use); createDataUse(declaration); setIsAdding(false); }; - const { ref } = useOutsideClick(handleClickOutside); - return ( - + {!isAdding && ( <> {dataUses.map((d, idx) => ( @@ -115,7 +109,12 @@ const EditDataUseCell = ({ system }: EditDataUseCellProps) => { height="max" bgColor="#fff" > - + setIsAdding(false)} + open + /> )} diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/EditCategoryCell.tsx b/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/EditCategoryCell.tsx index 7adbfd0928..3acceb16bc 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/EditCategoryCell.tsx +++ b/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/EditCategoryCell.tsx @@ -1,19 +1,12 @@ -import { - AntButton as Button, - AntButtonProps as ButtonProps, - Box, - CloseIcon, - EditIcon, - SmallAddIcon, - Wrap, -} from "fidesui"; +import { AntButton as Button, Box, CloseIcon, EditIcon } from "fidesui"; import { useState } from "react"; -import { TaxonomySelect } from "~/features/common/dropdown/TaxonomySelect"; +import DataCategorySelect from "~/features/common/dropdown/DataCategorySelect"; import useTaxonomies from "~/features/common/hooks/useTaxonomies"; import { SparkleIcon } from "~/features/common/Icon/SparkleIcon"; import TaxonomyBadge from "~/features/data-discovery-and-detection/ClassificationCategoryBadge"; import TaxonomyAddButton from "~/features/data-discovery-and-detection/tables/cells/TaxonomyAddButton"; +import TaxonomyCellContainer from "~/features/data-discovery-and-detection/tables/cells/TaxonomyCellContainer"; import { DiscoveryMonitorItem } from "~/features/data-discovery-and-detection/types/DiscoveryMonitorItem"; import { useUpdateResourceCategoryMutation } from "../../discovery-detection.slice"; @@ -71,14 +64,7 @@ const EditCategoriesCell = ({ resource }: EditCategoryCellProps) => { !isAdding && !!bestClassifiedCategory && !userCategories.length; return ( - + {noCategories && ( <> None @@ -132,7 +118,7 @@ const EditCategoriesCell = ({ resource }: EditCategoryCellProps) => { height="max" bgColor="#fff" > - { setIsAdding(false); @@ -143,7 +129,7 @@ const EditCategoriesCell = ({ resource }: EditCategoryCellProps) => { /> )} - + ); }; export default EditCategoriesCell; diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/TaxonomyCellContainer.tsx b/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/TaxonomyCellContainer.tsx index 51719abe52..d42f397411 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/TaxonomyCellContainer.tsx +++ b/clients/admin-ui/src/features/data-discovery-and-detection/tables/cells/TaxonomyCellContainer.tsx @@ -1,25 +1,20 @@ import { Wrap, WrapProps } from "fidesui"; import React from "react"; -const TaxonomyCellContainer = React.forwardRef( - ({ children, ...props }, ref) => { - return ( - - {children} - - ); - }, -); - -TaxonomyCellContainer.displayName = "TaxonomyCellContainer"; +const TaxonomyCellContainer = ({ children, ...props }: WrapProps) => { + return ( + + {children} + + ); +}; export default TaxonomyCellContainer; diff --git a/clients/admin-ui/src/features/data-use/useSystemDataUseCrud.ts b/clients/admin-ui/src/features/data-use/useSystemDataUseCrud.ts index c5b91b711c..e8b37e1655 100644 --- a/clients/admin-ui/src/features/data-use/useSystemDataUseCrud.ts +++ b/clients/admin-ui/src/features/data-use/useSystemDataUseCrud.ts @@ -18,7 +18,7 @@ const useSystemDataUseCrud = (system: SystemResponse) => { const declarationAlreadyExists = (values: PrivacyDeclaration) => { if ( - !!system.privacy_declarations.find( + system.privacy_declarations.find( (d) => d.data_use === values.data_use && d.name === values.name, ) ) { From c0900c7240c2da450527bb37f1cb1f08934f8144 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Mon, 18 Nov 2024 12:25:38 -0600 Subject: [PATCH 13/24] WIP: new system endpoint hookup --- .../systems/SystemNameCell.tsx | 13 ---- .../systems/SystemsTable.tsx | 67 ++++++------------- .../systems/systemHasHeliosIntegration.ts | 11 --- .../src/features/system/system.slice.ts | 11 +-- 4 files changed, 29 insertions(+), 73 deletions(-) delete mode 100644 clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx delete mode 100644 clients/admin-ui/src/features/dataset-lifecycle/systems/systemHasHeliosIntegration.ts diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx deleted file mode 100644 index 06c79a40d2..0000000000 --- a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemNameCell.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { DefaultCell } from "~/features/common/table/v2"; - -const SystemNameCell = ({ - value, - clickable, -}: { - value: string; - clickable?: boolean; -}) => ( - -); - -export default SystemNameCell; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx index 95b8b3dca6..c8cc25849c 100644 --- a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx +++ b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx @@ -33,19 +33,12 @@ import { TableSkeletonLoader, useServerSidePagination, } from "~/features/common/table/v2"; -import { - IndeterminateCheckboxCell, - RelativeTimestampCell, -} from "~/features/common/table/v2/cells"; +import { IndeterminateCheckboxCell } from "~/features/common/table/v2/cells"; import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; -import StatusBadgeCell from "~/features/dataset-lifecycle/StatusBadgeCell"; import SystemActionsCell from "~/features/dataset-lifecycle/systems/SystemActionCell"; -import systemHasHeliosIntegration from "~/features/dataset-lifecycle/systems/systemHasHeliosIntegration"; -import SystemNameCell from "~/features/dataset-lifecycle/systems/SystemNameCell"; -import useSpoofGetSystemsQuery, { - DatasetLifecycleSystem, -} from "~/features/dataset-lifecycle/systems/useSpoofGetSystemsQuery"; +import { useGetSystemsQuery } from "~/features/system"; +import { BasicSystemResponse } from "~/types/api"; const EMPTY_RESPONSE = { items: [], @@ -55,7 +48,7 @@ const EMPTY_RESPONSE = { pages: 1, }; -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); const EmptyTableNotice = () => ( { } = useServerSidePagination(); const { - isFetching, - isLoading, data: queryResult, - } = useSpoofGetSystemsQuery({ - pageIndex, - pageSize, - searchQuery, + isLoading, + isFetching, + } = useGetSystemsQuery({ + page: pageIndex, + size: pageSize, + search: searchQuery, + dnd_relevant: true, }); const { @@ -119,7 +113,7 @@ const SystemsTable = () => { setTotalPages(totalPages); }, [totalPages, setTotalPages]); - const columns: ColumnDef[] = useMemo( + const columns: ColumnDef[] = useMemo( () => [ columnHelper.display({ id: "select", @@ -148,30 +142,12 @@ const SystemsTable = () => { }), columnHelper.accessor((row) => row.name, { id: "name", - cell: ({ row, getValue }) => ( - + cell: ({ getValue }) => ( + ), header: (props) => , }), - columnHelper.accessor((row) => row.status, { - id: "status", - cell: ({ getValue }) => , - header: (props) => , - }), - columnHelper.accessor((row) => row.changes, { - id: "changes", - cell: ({ getValue }) => , - header: (props) => , - }), - columnHelper.accessor((row) => row.lastUpdated, { - id: "updated-at", - cell: ({ getValue }) => , - header: (props) => , - }), - columnHelper.accessor((row) => row.dataUses, { + columnHelper.accessor((row) => row.privacy_declarations, { id: "data-uses", cell: ({ getValue }) => , header: (props) => , @@ -186,10 +162,10 @@ const SystemsTable = () => { cell: (props) => ( - router.push(`/systems/configure/${props.row.original.id}`) + router.push(`/systems/configure/${props.row.original.fides_key}`) } onHideClick={() => - console.log(`hiding system ${props.row.original.id}...`) + console.log(`hiding system ${props.row.original.fides_key}...`) } /> ), @@ -205,11 +181,11 @@ const SystemsTable = () => { [], ); - const tableInstance = useReactTable({ + const tableInstance = useReactTable({ getCoreRowModel: getCoreRowModel(), getGroupedRowModel: getGroupedRowModel(), getExpandedRowModel: getExpandedRowModel(), - getRowId: (row) => row.id, + getRowId: (row) => row.fides_key, manualPagination: true, columnResizeMode: "onChange", columns, @@ -264,8 +240,9 @@ const SystemsTable = () => { } - getRowIsClickable={systemHasHeliosIntegration} - onRowClick={(row) => router.push(`${E2E_DATASETS_ROUTE}/${row.id}`)} + onRowClick={(row) => + router.push(`${E2E_DATASETS_ROUTE}/${row.fides_key}`) + } /> { - return ( - !!system.integration?.connection_type && - SUPPORTED_INTEGRATIONS.includes(system.integration.connection_type) - ); -}; - -export default systemHasHeliosIntegration; diff --git a/clients/admin-ui/src/features/system/system.slice.ts b/clients/admin-ui/src/features/system/system.slice.ts index ac0e373d7b..aa161b2661 100644 --- a/clients/admin-ui/src/features/system/system.slice.ts +++ b/clients/admin-ui/src/features/system/system.slice.ts @@ -25,6 +25,12 @@ interface UpsertResponse { updated: number; } +type SystemListQueryParams = PaginationQueryParams & + SearchQueryParams & { + dnd_relevant?: boolean; + show_hidden?: boolean; + }; + export type ConnectionConfigSecretsRequest = { systemFidesKey: string; secrets: { @@ -34,10 +40,7 @@ export type ConnectionConfigSecretsRequest = { const systemApi = baseApi.injectEndpoints({ endpoints: (build) => ({ - getSystems: build.query< - Page_BasicSystemResponse_, - PaginationQueryParams & SearchQueryParams - >({ + getSystems: build.query({ query: (params) => ({ method: "GET", url: `system`, From 0297e4115b891da6a6aec8a604b4ccc5b14d4d6d Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Wed, 27 Nov 2024 14:56:07 -0600 Subject: [PATCH 14/24] WIP: tables --- .../discovery-detection.slice.ts | 19 ++++- .../hooks/useDetectionResultColumns.tsx | 2 +- .../projects/ProjectsTable.tsx | 15 ++-- .../systems/SystemsTable.tsx | 16 +++- .../ConfigureMonitorModal.tsx | 4 +- .../useCumulativeGetDatabases.tsx | 8 +- .../system/hooks/useShowHideSystems.tsx | 77 +++++++++++++++++++ clients/admin-ui/src/types/api/index.ts | 6 ++ .../types/api/models/BigQueryDocsSchema.ts | 2 +- .../types/api/models/DataCategoryCreate.ts | 14 ++++ .../src/types/api/models/DataSubjectCreate.ts | 17 ++++ .../src/types/api/models/DataUseCreate.ts | 14 ++++ .../src/types/api/models/FidesMeta.ts | 5 ++ .../models/FieldMaskingStrategyOverride.ts | 11 +++ .../api/models/MaskingStrategyOverride.ts | 2 +- .../models/Page_StagedResourceResponse_.ts | 13 ++++ .../types/api/models/SnowflakeDocsSchema.ts | 8 +- .../api/models/StagedResourceResponse.ts | 17 ++++ 18 files changed, 225 insertions(+), 25 deletions(-) create mode 100644 clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx create mode 100644 clients/admin-ui/src/types/api/models/DataCategoryCreate.ts create mode 100644 clients/admin-ui/src/types/api/models/DataSubjectCreate.ts create mode 100644 clients/admin-ui/src/types/api/models/DataUseCreate.ts create mode 100644 clients/admin-ui/src/types/api/models/FieldMaskingStrategyOverride.ts create mode 100644 clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts create mode 100644 clients/admin-ui/src/types/api/models/StagedResourceResponse.ts diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts b/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts index 1271b6ca2a..b112052d80 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts +++ b/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts @@ -10,6 +10,7 @@ import { Page_StagedResourceAPIResponse_, Page_str_, } from "~/types/api"; +import { Page_StagedResourceResponse_ } from "~/types/api/models/Page_StagedResourceResponse_"; interface State { page?: number; @@ -34,6 +35,7 @@ interface DatabaseByMonitorQueryParams { page: number; size: number; monitor_config_id: string; + show_hidden?: boolean; } interface DatabaseByConnectionQueryParams { @@ -89,7 +91,7 @@ const discoveryDetectionApi = baseApi.injectEndpoints({ }), }, ), - getDatabasesByConnection: build.query< + getAvailableDatabasesByConnection: build.query< Page_str_, DatabaseByConnectionQueryParams >({ @@ -103,6 +105,16 @@ const discoveryDetectionApi = baseApi.injectEndpoints({ }, }), }), + getProjects: build.query< + Page_StagedResourceResponse_, + DatabaseByMonitorQueryParams + >({ + query: (params) => ({ + method: "GET", + url: `/plus/lifecycle/projects`, + params, + }), + }), executeDiscoveryMonitor: build.mutation({ query: ({ monitor_config_id }) => ({ method: "POST", @@ -227,8 +239,9 @@ export const { useGetMonitorsByIntegrationQuery, usePutDiscoveryMonitorMutation, useGetDatabasesByMonitorQuery, - useGetDatabasesByConnectionQuery, - useLazyGetDatabasesByConnectionQuery, + useGetAvailableDatabasesByConnectionQuery, + useLazyGetAvailableDatabasesByConnectionQuery, + useGetProjectsQuery, useExecuteDiscoveryMonitorMutation, useDeleteDiscoveryMonitorMutation, useGetMonitorResultsQuery, diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/hooks/useDetectionResultColumns.tsx b/clients/admin-ui/src/features/data-discovery-and-detection/hooks/useDetectionResultColumns.tsx index 4e996a634f..32a451bba9 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/hooks/useDetectionResultColumns.tsx +++ b/clients/admin-ui/src/features/data-discovery-and-detection/hooks/useDetectionResultColumns.tsx @@ -4,8 +4,8 @@ import { DefaultCell, DefaultHeaderCell } from "~/features/common/table/v2"; import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; import DetectionItemActionsCell from "~/features/data-discovery-and-detection/tables/cells/DetectionItemActionsCell"; import FieldDataTypeCell from "~/features/data-discovery-and-detection/tables/cells/FieldDataTypeCell"; -import ResultStatusBadgeCell from "~/features/data-discovery-and-detection/tables/cells/StagedResourceStatusBadgeCell"; import ResultStatusCell from "~/features/data-discovery-and-detection/tables/cells/ResultStatusCell"; +import ResultStatusBadgeCell from "~/features/data-discovery-and-detection/tables/cells/StagedResourceStatusBadgeCell"; import { DiscoveryMonitorItem } from "~/features/data-discovery-and-detection/types/DiscoveryMonitorItem"; import { ResourceChangeType } from "~/features/data-discovery-and-detection/types/ResourceChangeType"; import { StagedResourceType } from "~/features/data-discovery-and-detection/types/StagedResourceType"; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx b/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx index 9997ec1ef5..9b4c8d8800 100644 --- a/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx +++ b/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx @@ -31,6 +31,7 @@ import { useServerSidePagination, } from "~/features/common/table/v2"; import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; +import { useGetProjectsQuery } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; import useSpoofGetProjectsQuery, { @@ -69,7 +70,11 @@ const EmptyTableNotice = () => ( const columnHelper = createColumnHelper(); -const ProjectsTable = () => { +const ProjectsTable = ({ + monitor_config_id, +}: { + monitor_config_id?: string; +}) => { const [searchQuery, setSearchQuery] = useState(""); const [rowSelectionState, setRowSelectionState] = useState( {}, @@ -93,10 +98,10 @@ const ProjectsTable = () => { isFetching, isLoading, data: queryResult, - } = useSpoofGetProjectsQuery({ - pageIndex, - pageSize, - searchQuery, + } = useGetProjectsQuery({ + page: pageIndex, + size: pageSize, + monitor_config_id: "bq-monitor", }); const { diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx index c8cc25849c..fe8ecdf3c1 100644 --- a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx +++ b/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx @@ -35,9 +35,11 @@ import { } from "~/features/common/table/v2"; import { IndeterminateCheckboxCell } from "~/features/common/table/v2/cells"; import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; +import EditDataUseCell from "~/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; import SystemActionsCell from "~/features/dataset-lifecycle/systems/SystemActionCell"; import { useGetSystemsQuery } from "~/features/system"; +import useShowHideSystems from "~/features/system/hooks/useShowHideSystems"; import { BasicSystemResponse } from "~/types/api"; const EMPTY_RESPONSE = { @@ -76,6 +78,10 @@ const SystemsTable = () => { {}, ); + // const [showHidden, setShowHidden] = useState(false); + + const { showSystem, hideSystem } = useShowHideSystems(); + const router = useRouter(); const { @@ -101,6 +107,7 @@ const SystemsTable = () => { size: pageSize, search: searchQuery, dnd_relevant: true, + show_hidden: false, }); const { @@ -138,6 +145,7 @@ const SystemsTable = () => { cellProps: { borderRight: "none", }, + disableRowClick: true, }, }), columnHelper.accessor((row) => row.name, { @@ -149,12 +157,13 @@ const SystemsTable = () => { }), columnHelper.accessor((row) => row.privacy_declarations, { id: "data-uses", - cell: ({ getValue }) => , + cell: ({ row }) => , header: (props) => , meta: { cellProps: { borderRight: "none", }, + disableRowClick: true, }, }), columnHelper.display({ @@ -164,9 +173,7 @@ const SystemsTable = () => { onDetailClick={() => router.push(`/systems/configure/${props.row.original.fides_key}`) } - onHideClick={() => - console.log(`hiding system ${props.row.original.fides_key}...`) - } + onHideClick={() => hideSystem(props.row.original)} /> ), maxSize: 20, @@ -175,6 +182,7 @@ const SystemsTable = () => { cellProps: { borderLeft: "none", }, + disableRowClick: true, }, }), ], diff --git a/clients/admin-ui/src/features/integrations/configure-monitor/ConfigureMonitorModal.tsx b/clients/admin-ui/src/features/integrations/configure-monitor/ConfigureMonitorModal.tsx index 191ddb3a53..5fdddba1e4 100644 --- a/clients/admin-ui/src/features/integrations/configure-monitor/ConfigureMonitorModal.tsx +++ b/clients/admin-ui/src/features/integrations/configure-monitor/ConfigureMonitorModal.tsx @@ -5,7 +5,7 @@ import useQueryResultToast from "~/features/common/form/useQueryResultToast"; import FormModal from "~/features/common/modals/FormModal"; import { DEFAULT_TOAST_PARAMS } from "~/features/common/toast"; import { - useGetDatabasesByConnectionQuery, + useGetAvailableDatabasesByConnectionQuery, usePutDiscoveryMonitorMutation, } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import ConfigureMonitorDatabasesForm from "~/features/integrations/configure-monitor/ConfigureMonitorDatabasesForm"; @@ -41,7 +41,7 @@ const ConfigureMonitorModal = ({ const [putMonitorMutationTrigger, { isLoading: isSubmitting }] = usePutDiscoveryMonitorMutation(); - const { data: databases } = useGetDatabasesByConnectionQuery({ + const { data: databases } = useGetAvailableDatabasesByConnectionQuery({ page: 1, size: 25, connection_config_key: integration.key, diff --git a/clients/admin-ui/src/features/integrations/configure-monitor/useCumulativeGetDatabases.tsx b/clients/admin-ui/src/features/integrations/configure-monitor/useCumulativeGetDatabases.tsx index 642f1d84d2..812e33db00 100644 --- a/clients/admin-ui/src/features/integrations/configure-monitor/useCumulativeGetDatabases.tsx +++ b/clients/admin-ui/src/features/integrations/configure-monitor/useCumulativeGetDatabases.tsx @@ -1,8 +1,8 @@ import { useEffect, useRef, useState } from "react"; import { - useGetDatabasesByConnectionQuery, - useLazyGetDatabasesByConnectionQuery, + useGetAvailableDatabasesByConnectionQuery, + useLazyGetAvailableDatabasesByConnectionQuery, } from "~/features/data-discovery-and-detection/discovery-detection.slice"; const TIMEOUT_DELAY = 5000; @@ -22,7 +22,7 @@ const useCumulativeGetDatabases = ( const [nextPage, setNextPage] = useState(2); const { data: initialResult, isLoading: initialIsLoading } = - useGetDatabasesByConnectionQuery({ + useGetAvailableDatabasesByConnectionQuery({ page: 1, size: 25, connection_config_key: integrationKey, @@ -57,7 +57,7 @@ const useCumulativeGetDatabases = ( const [ refetchTrigger, { isLoading: refetchIsLoading, isFetching: refetchIsFetching }, - ] = useLazyGetDatabasesByConnectionQuery(); + ] = useLazyGetAvailableDatabasesByConnectionQuery(); const isLoading = refetchIsLoading || refetchIsFetching || initialIsLoading; diff --git a/clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx b/clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx new file mode 100644 index 0000000000..10f33aaf4f --- /dev/null +++ b/clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx @@ -0,0 +1,77 @@ +import { useToast } from "fidesui"; + +import { getErrorMessage } from "~/features/common/helpers"; +import { useUpsertSystemsMutation } from "~/features/system/system.slice"; +import { System } from "~/types/api"; +import { isErrorResult, RTKResult } from "~/types/errors"; + +const useShowHideSystems = () => { + const [upsertSystems] = useUpsertSystemsMutation(); + + const toast = useToast(); + + const handleResult = (result: RTKResult, isMultiple?: boolean) => { + if (isErrorResult(result)) { + const errorMsg = getErrorMessage( + result.error, + `An unexpected error occurred while updating the ${isMultiple ? "systems" : "system"}. Please try again.`, + ); + toast({ status: "error", description: errorMsg }); + } else { + toast({ + status: "success", + description: `${isMultiple ? "Systems" : "System"} updated successfully`, + }); + } + }; + + const hideSystem = async (system: System) => { + const result = await upsertSystems([ + { + ...system, + description: "I've been hidden!", + hidden: true, + }, + ]); + handleResult(result); + }; + + const showSystem = async (system: System) => { + const result = await upsertSystems([ + { + ...system, + hidden: false, + }, + ]); + handleResult(result); + }; + + const hideMultipleSystems = async (systems: System[]) => { + const newSystems = systems.map((system) => ({ + ...system, + hidden: true, + })); + + const result = await upsertSystems(newSystems); + handleResult(result, true); + }; + + const showMultipleSystems = async (systems: System[]) => { + const newSystems = systems.map((system) => ({ + ...system, + hidden: false, + })); + + const result = await upsertSystems(newSystems); + handleResult(result, true); + }; + + return { + hideSystem, + showSystem, + hideMultipleSystems, + showMultipleSystems, + }; +}; + +export default useShowHideSystems; diff --git a/clients/admin-ui/src/types/api/index.ts b/clients/admin-ui/src/types/api/index.ts index c773e984da..ff3e272420 100644 --- a/clients/admin-ui/src/types/api/index.ts +++ b/clients/admin-ui/src/types/api/index.ts @@ -107,6 +107,7 @@ export type { Database } from "./models/Database"; export type { DatabaseConfig } from "./models/DatabaseConfig"; export type { DatabaseHealthCheck } from "./models/DatabaseHealthCheck"; export type { DataCategory } from "./models/DataCategory"; +export type { DataCategoryCreate } from "./models/DataCategoryCreate"; export type { DataFlow } from "./models/DataFlow"; export type { DatahubDocsSchema } from "./models/DatahubDocsSchema"; export { DATAMAP_GROUPING } from "./models/DATAMAP_GROUPING"; @@ -121,10 +122,12 @@ export type { DatasetMetadata } from "./models/DatasetMetadata"; export type { DatasetSchema } from "./models/DatasetSchema"; export type { DatasetTraversalDetails } from "./models/DatasetTraversalDetails"; export type { DataSubject } from "./models/DataSubject"; +export type { DataSubjectCreate } from "./models/DataSubjectCreate"; export type { DataSubjectRights } from "./models/DataSubjectRights"; export { DataSubjectRightsEnum } from "./models/DataSubjectRightsEnum"; export type { DataUpload } from "./models/DataUpload"; export type { DataUse } from "./models/DataUse"; +export type { DataUseCreate } from "./models/DataUseCreate"; export { DBActions } from "./models/DBActions"; export type { DenyPrivacyRequests } from "./models/DenyPrivacyRequests"; export type { DictionaryStatus } from "./models/DictionaryStatus"; @@ -182,6 +185,7 @@ export type { FidesDocsSchema } from "./models/FidesDocsSchema"; export type { fideslang__models__Policy } from "./models/fideslang__models__Policy"; export type { FidesMeta } from "./models/FidesMeta"; export type { Field } from "./models/Field"; +export type { FieldMaskingStrategyOverride } from "./models/FieldMaskingStrategyOverride"; export type { FieldsAffectedResponse } from "./models/FieldsAffectedResponse"; export type { Generate } from "./models/Generate"; export type { GenerateRequestPayload } from "./models/GenerateRequestPayload"; @@ -300,6 +304,7 @@ export type { Page_Property_ } from "./models/Page_Property_"; export type { Page_RuleResponseWithTargets_ } from "./models/Page_RuleResponseWithTargets_"; export type { Page_RuleTarget_ } from "./models/Page_RuleTarget_"; export type { Page_StagedResourceAPIResponse_ } from "./models/Page_StagedResourceAPIResponse_"; +export type { Page_StagedResourceResponse_ } from "./models/Page_StagedResourceResponse_"; export type { Page_StorageDestinationResponse_ } from "./models/Page_StorageDestinationResponse_"; export type { Page_str_ } from "./models/Page_str_"; export type { Page_SystemHistoryResponse_ } from "./models/Page_SystemHistoryResponse_"; @@ -410,6 +415,7 @@ export type { SovrnDocsSchema } from "./models/SovrnDocsSchema"; export { SpecialCategoryLegalBasisEnum } from "./models/SpecialCategoryLegalBasisEnum"; export type { StagedResource } from "./models/StagedResource"; export type { StagedResourceAPIResponse } from "./models/StagedResourceAPIResponse"; +export type { StagedResourceResponse } from "./models/StagedResourceResponse"; export type { StagedResourceUpdateRequest } from "./models/StagedResourceUpdateRequest"; export { StatusEnum } from "./models/StatusEnum"; export type { StorageApplicationConfig } from "./models/StorageApplicationConfig"; diff --git a/clients/admin-ui/src/types/api/models/BigQueryDocsSchema.ts b/clients/admin-ui/src/types/api/models/BigQueryDocsSchema.ts index 0581376f57..7146aced8b 100644 --- a/clients/admin-ui/src/types/api/models/BigQueryDocsSchema.ts +++ b/clients/admin-ui/src/types/api/models/BigQueryDocsSchema.ts @@ -13,7 +13,7 @@ export type BigQueryDocsSchema = { */ keyfile_creds: fides__api__schemas__connection_configuration__connection_secrets_bigquery__KeyfileCreds; /** - * The default BigQuery dataset that will be used if one isn't provided in the associated Fides datasets. + * Only provide a dataset to scope discovery monitors and privacy request automation to a specific BigQuery dataset. In most cases, this can be left blank. */ dataset?: string | null; }; diff --git a/clients/admin-ui/src/types/api/models/DataCategoryCreate.ts b/clients/admin-ui/src/types/api/models/DataCategoryCreate.ts new file mode 100644 index 0000000000..ab9074a8c0 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/DataCategoryCreate.ts @@ -0,0 +1,14 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type DataCategoryCreate = { + name?: string | null; + description: string; + active?: boolean; + fides_key?: string | null; + is_default?: boolean; + tags?: Array | null; + organization_fides_key?: string | null; + parent_key?: string | null; +}; diff --git a/clients/admin-ui/src/types/api/models/DataSubjectCreate.ts b/clients/admin-ui/src/types/api/models/DataSubjectCreate.ts new file mode 100644 index 0000000000..59ffec8987 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/DataSubjectCreate.ts @@ -0,0 +1,17 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { DataSubjectRights } from "./DataSubjectRights"; + +export type DataSubjectCreate = { + name?: string | null; + description: string; + active?: boolean; + fides_key?: string | null; + is_default?: boolean; + tags?: Array | null; + organization_fides_key?: string | null; + rights?: DataSubjectRights | null; + automated_decisions_or_profiling?: boolean | null; +}; diff --git a/clients/admin-ui/src/types/api/models/DataUseCreate.ts b/clients/admin-ui/src/types/api/models/DataUseCreate.ts new file mode 100644 index 0000000000..b5742b7227 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/DataUseCreate.ts @@ -0,0 +1,14 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type DataUseCreate = { + name?: string | null; + description: string; + active?: boolean; + fides_key?: string | null; + is_default?: boolean; + tags?: Array | null; + organization_fides_key?: string | null; + parent_key?: string | null; +}; diff --git a/clients/admin-ui/src/types/api/models/FidesMeta.ts b/clients/admin-ui/src/types/api/models/FidesMeta.ts index a82e7eb4e7..0973c1de13 100644 --- a/clients/admin-ui/src/types/api/models/FidesMeta.ts +++ b/clients/admin-ui/src/types/api/models/FidesMeta.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { FidesDatasetReference } from "./FidesDatasetReference"; +import type { FieldMaskingStrategyOverride } from "./FieldMaskingStrategyOverride"; /** * Supplementary metadata used by the Fides application for additional features. @@ -40,4 +41,8 @@ export type FidesMeta = { * Optionally specify that a field may be used as a custom request field in DSRs. The value is the name of the field in the DSR. */ custom_request_field?: string | null; + /** + * Optionally specify a masking strategy override for this field. + */ + masking_strategy_override?: FieldMaskingStrategyOverride | null; }; diff --git a/clients/admin-ui/src/types/api/models/FieldMaskingStrategyOverride.ts b/clients/admin-ui/src/types/api/models/FieldMaskingStrategyOverride.ts new file mode 100644 index 0000000000..d47b2b2a10 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/FieldMaskingStrategyOverride.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * Overrides field-level masking strategies. + */ +export type FieldMaskingStrategyOverride = { + strategy: string; + configuration?: null; +}; diff --git a/clients/admin-ui/src/types/api/models/MaskingStrategyOverride.ts b/clients/admin-ui/src/types/api/models/MaskingStrategyOverride.ts index ed8946c5f7..13187f1a42 100644 --- a/clients/admin-ui/src/types/api/models/MaskingStrategyOverride.ts +++ b/clients/admin-ui/src/types/api/models/MaskingStrategyOverride.ts @@ -5,7 +5,7 @@ import type { MaskingStrategies } from "./MaskingStrategies"; /** - * Overrides policy-level masking strategies. + * Overrides collection-level masking strategies. */ export type MaskingStrategyOverride = { strategy: MaskingStrategies; diff --git a/clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts b/clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts new file mode 100644 index 0000000000..5b3f58dafe --- /dev/null +++ b/clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts @@ -0,0 +1,13 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { StagedResourceResponse } from "./StagedResourceResponse"; + +export type Page_StagedResourceResponse_ = { + items: Array; + total: number | null; + page: number | null; + size: number | null; + pages?: number | null; +}; diff --git a/clients/admin-ui/src/types/api/models/SnowflakeDocsSchema.ts b/clients/admin-ui/src/types/api/models/SnowflakeDocsSchema.ts index b9fa1433c7..1993773b8c 100644 --- a/clients/admin-ui/src/types/api/models/SnowflakeDocsSchema.ts +++ b/clients/admin-ui/src/types/api/models/SnowflakeDocsSchema.ts @@ -31,13 +31,13 @@ export type SnowflakeDocsSchema = { */ warehouse_name: string; /** - * The name of the Snowflake database you want to connect to. + * Only provide a database name to scope discovery monitors and privacy request automation to a specific database. In most cases, this can be left blank. */ - database_name: string; + database_name?: string | null; /** - * The name of the Snowflake schema within the selected database. + * Only provide a schema to scope discovery monitors and privacy request automation to a specific schema. In most cases, this can be left blank. */ - schema_name: string; + schema_name?: string | null; /** * The Snowflake role to assume for the session, if different than Username. */ diff --git a/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts b/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts new file mode 100644 index 0000000000..c47014b5b3 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts @@ -0,0 +1,17 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * Response schema for a list of staged resources + */ +export type StagedResourceResponse = { + urn: string; + name: string; + description: string; + created_at: string; + updated_at: string; + diff_status: string; + child_diff_statuses: any; + hidden: boolean; +}; From 8453d8b35db43ecdcc0df136a9c098c05d1bc529 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 12 Dec 2024 17:00:33 -0600 Subject: [PATCH 15/24] regenerate types --- clients/admin-ui/src/types/api/index.ts | 6 ++++++ .../types/api/models/CatalogSystemResponse.ts | 17 +++++++++++++++++ .../src/types/api/models/ConsentMethod.ts | 1 + .../src/types/api/models/DataCategoryCreate.ts | 17 ++++++++++++++++- .../src/types/api/models/DataSubjectCreate.ts | 17 ++++++++++++++++- .../src/types/api/models/DataUseCreate.ts | 17 ++++++++++++++++- .../src/types/api/models/DatasetReachability.ts | 11 +++++++++++ .../api/models/FilteredPrivacyRequestResults.ts | 14 ++++++++++++++ .../api/models/Page_CatalogSystemResponse_.ts | 13 +++++++++++++ .../types/api/models/PrivacyRequestSource.ts | 2 ++ .../src/types/api/models/ScopeRegistryEnum.ts | 1 + .../types/api/models/StagedResourceResponse.ts | 8 +++++--- .../src/types/api/models/TestPrivacyRequest.ts | 10 ++++++++++ .../src/types/api/models/UnlabeledIdentities.ts | 11 +++++++++++ 14 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts create mode 100644 clients/admin-ui/src/types/api/models/DatasetReachability.ts create mode 100644 clients/admin-ui/src/types/api/models/FilteredPrivacyRequestResults.ts create mode 100644 clients/admin-ui/src/types/api/models/Page_CatalogSystemResponse_.ts create mode 100644 clients/admin-ui/src/types/api/models/TestPrivacyRequest.ts create mode 100644 clients/admin-ui/src/types/api/models/UnlabeledIdentities.ts diff --git a/clients/admin-ui/src/types/api/index.ts b/clients/admin-ui/src/types/api/index.ts index ff3e272420..f3085d11e2 100644 --- a/clients/admin-ui/src/types/api/index.ts +++ b/clients/admin-ui/src/types/api/index.ts @@ -41,6 +41,7 @@ export type { BulkPutStorageConfigResponse } from "./models/BulkPutStorageConfig export type { BulkReviewResponse } from "./models/BulkReviewResponse"; export type { BulkSoftDeletePrivacyRequests } from "./models/BulkSoftDeletePrivacyRequests"; export type { BulkUpdateFailed } from "./models/BulkUpdateFailed"; +export type { CatalogSystemResponse } from "./models/CatalogSystemResponse"; export type { CheckpointActionRequiredDetails } from "./models/CheckpointActionRequiredDetails"; export type { Classification } from "./models/Classification"; export type { ClassificationResponse } from "./models/ClassificationResponse"; @@ -119,6 +120,7 @@ export type { DatasetConfigCtlDataset } from "./models/DatasetConfigCtlDataset"; export type { DatasetConfigSchema } from "./models/DatasetConfigSchema"; export type { DatasetField } from "./models/DatasetField"; export type { DatasetMetadata } from "./models/DatasetMetadata"; +export type { DatasetReachability } from "./models/DatasetReachability"; export type { DatasetSchema } from "./models/DatasetSchema"; export type { DatasetTraversalDetails } from "./models/DatasetTraversalDetails"; export type { DataSubject } from "./models/DataSubject"; @@ -187,6 +189,7 @@ export type { FidesMeta } from "./models/FidesMeta"; export type { Field } from "./models/Field"; export type { FieldMaskingStrategyOverride } from "./models/FieldMaskingStrategyOverride"; export type { FieldsAffectedResponse } from "./models/FieldsAffectedResponse"; +export type { FilteredPrivacyRequestResults } from "./models/FilteredPrivacyRequestResults"; export type { Generate } from "./models/Generate"; export type { GenerateRequestPayload } from "./models/GenerateRequestPayload"; export type { GenerateResponse } from "./models/GenerateResponse"; @@ -278,6 +281,7 @@ export type { OpenIDProviderSimple } from "./models/OpenIDProviderSimple"; export type { Organization } from "./models/Organization"; export type { OrganizationMetadata } from "./models/OrganizationMetadata"; export type { Page_BasicSystemResponse_ } from "./models/Page_BasicSystemResponse_"; +export type { Page_CatalogSystemResponse_ } from "./models/Page_CatalogSystemResponse_"; export type { Page_ClassifyInstanceResponseValues_ } from "./models/Page_ClassifyInstanceResponseValues_"; export type { Page_ConnectionConfigurationResponse_ } from "./models/Page_ConnectionConfigurationResponse_"; export type { Page_ConnectionSystemTypeMap_ } from "./models/Page_ConnectionSystemTypeMap_"; @@ -464,9 +468,11 @@ export type { TCFVendorRelationships } from "./models/TCFVendorRelationships"; export type { TCFVendorSave } from "./models/TCFVendorSave"; export type { TCMobileData } from "./models/TCMobileData"; export type { TestMessagingStatusMessage } from "./models/TestMessagingStatusMessage"; +export type { TestPrivacyRequest } from "./models/TestPrivacyRequest"; export { TestStatus } from "./models/TestStatus"; export type { TestStatusMessage } from "./models/TestStatusMessage"; export type { TimescaleDocsSchema } from "./models/TimescaleDocsSchema"; +export type { UnlabeledIdentities } from "./models/UnlabeledIdentities"; export { UserConsentPreference } from "./models/UserConsentPreference"; export type { UserCreate } from "./models/UserCreate"; export type { UserCreateResponse } from "./models/UserCreateResponse"; diff --git a/clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts b/clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts new file mode 100644 index 0000000000..3731ebfd60 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts @@ -0,0 +1,17 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * Extension of SystemResponse response model to include other relationships only needed for Data Catalog view + */ +export type CatalogSystemResponse = { + id: string; + fides_key: string; + name: string | null; + legal_name: string | null; + description: string | null; + system_type: string | null; + hidden: boolean; + monitor_config_keys?: Array; +}; diff --git a/clients/admin-ui/src/types/api/models/ConsentMethod.ts b/clients/admin-ui/src/types/api/models/ConsentMethod.ts index ed8c78cbba..6ae5a06a35 100644 --- a/clients/admin-ui/src/types/api/models/ConsentMethod.ts +++ b/clients/admin-ui/src/types/api/models/ConsentMethod.ts @@ -6,6 +6,7 @@ export enum ConsentMethod { BUTTON = "button", REJECT = "reject", ACCEPT = "accept", + SCRIPT = "script", SAVE = "save", DISMISS = "dismiss", GPC = "gpc", diff --git a/clients/admin-ui/src/types/api/models/DataCategoryCreate.ts b/clients/admin-ui/src/types/api/models/DataCategoryCreate.ts index ab9074a8c0..aee7e5f4ec 100644 --- a/clients/admin-ui/src/types/api/models/DataCategoryCreate.ts +++ b/clients/admin-ui/src/types/api/models/DataCategoryCreate.ts @@ -3,11 +3,26 @@ /* eslint-disable */ export type DataCategoryCreate = { + /** + * The version of Fideslang in which this label was added. + */ + version_added?: string | null; + /** + * The version of Fideslang in which this label was deprecated. + */ + version_deprecated?: string | null; + /** + * The new name, if applicable, for this label after deprecation. + */ + replaced_by?: string | null; + /** + * Denotes whether the resource is part of the default taxonomy or not. + */ + is_default?: boolean; name?: string | null; description: string; active?: boolean; fides_key?: string | null; - is_default?: boolean; tags?: Array | null; organization_fides_key?: string | null; parent_key?: string | null; diff --git a/clients/admin-ui/src/types/api/models/DataSubjectCreate.ts b/clients/admin-ui/src/types/api/models/DataSubjectCreate.ts index 59ffec8987..12bc387f1d 100644 --- a/clients/admin-ui/src/types/api/models/DataSubjectCreate.ts +++ b/clients/admin-ui/src/types/api/models/DataSubjectCreate.ts @@ -5,11 +5,26 @@ import type { DataSubjectRights } from "./DataSubjectRights"; export type DataSubjectCreate = { + /** + * The version of Fideslang in which this label was added. + */ + version_added?: string | null; + /** + * The version of Fideslang in which this label was deprecated. + */ + version_deprecated?: string | null; + /** + * The new name, if applicable, for this label after deprecation. + */ + replaced_by?: string | null; + /** + * Denotes whether the resource is part of the default taxonomy or not. + */ + is_default?: boolean; name?: string | null; description: string; active?: boolean; fides_key?: string | null; - is_default?: boolean; tags?: Array | null; organization_fides_key?: string | null; rights?: DataSubjectRights | null; diff --git a/clients/admin-ui/src/types/api/models/DataUseCreate.ts b/clients/admin-ui/src/types/api/models/DataUseCreate.ts index b5742b7227..8aa095dfc5 100644 --- a/clients/admin-ui/src/types/api/models/DataUseCreate.ts +++ b/clients/admin-ui/src/types/api/models/DataUseCreate.ts @@ -3,11 +3,26 @@ /* eslint-disable */ export type DataUseCreate = { + /** + * The version of Fideslang in which this label was added. + */ + version_added?: string | null; + /** + * The version of Fideslang in which this label was deprecated. + */ + version_deprecated?: string | null; + /** + * The new name, if applicable, for this label after deprecation. + */ + replaced_by?: string | null; + /** + * Denotes whether the resource is part of the default taxonomy or not. + */ + is_default?: boolean; name?: string | null; description: string; active?: boolean; fides_key?: string | null; - is_default?: boolean; tags?: Array | null; organization_fides_key?: string | null; parent_key?: string | null; diff --git a/clients/admin-ui/src/types/api/models/DatasetReachability.ts b/clients/admin-ui/src/types/api/models/DatasetReachability.ts new file mode 100644 index 0000000000..b53ab3307a --- /dev/null +++ b/clients/admin-ui/src/types/api/models/DatasetReachability.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * Response containing reachability details for a single dataset + */ +export type DatasetReachability = { + reachable: boolean; + details: string | null; +}; diff --git a/clients/admin-ui/src/types/api/models/FilteredPrivacyRequestResults.ts b/clients/admin-ui/src/types/api/models/FilteredPrivacyRequestResults.ts new file mode 100644 index 0000000000..8205302d90 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/FilteredPrivacyRequestResults.ts @@ -0,0 +1,14 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { PrivacyRequestStatus } from "./PrivacyRequestStatus"; + +/** + * Schema representing the status and results of a test privacy request + */ +export type FilteredPrivacyRequestResults = { + privacy_request_id: string; + status: PrivacyRequestStatus; + results: string; +}; diff --git a/clients/admin-ui/src/types/api/models/Page_CatalogSystemResponse_.ts b/clients/admin-ui/src/types/api/models/Page_CatalogSystemResponse_.ts new file mode 100644 index 0000000000..1cac90ea6e --- /dev/null +++ b/clients/admin-ui/src/types/api/models/Page_CatalogSystemResponse_.ts @@ -0,0 +1,13 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { CatalogSystemResponse } from "./CatalogSystemResponse"; + +export type Page_CatalogSystemResponse_ = { + items: Array; + total: number | null; + page: number | null; + size: number | null; + pages?: number | null; +}; diff --git a/clients/admin-ui/src/types/api/models/PrivacyRequestSource.ts b/clients/admin-ui/src/types/api/models/PrivacyRequestSource.ts index c948edaf6e..aec25f13b6 100644 --- a/clients/admin-ui/src/types/api/models/PrivacyRequestSource.ts +++ b/clients/admin-ui/src/types/api/models/PrivacyRequestSource.ts @@ -9,10 +9,12 @@ * - Request Manager: Request submitted from the Admin UI's Request manager page * - Consent Webhook: Request created as a side-effect of a consent webhook request (bidirectional consent) * - Fides.js: Request created as a side-effect of a privacy preference update from Fides.js + * - Dataset Test: Standalone dataset test */ export enum PrivacyRequestSource { PRIVACY_CENTER = "Privacy Center", REQUEST_MANAGER = "Request Manager", CONSENT_WEBHOOK = "Consent Webhook", FIDES_JS = "Fides.js", + DATASET_TEST = "Dataset Test", } diff --git a/clients/admin-ui/src/types/api/models/ScopeRegistryEnum.ts b/clients/admin-ui/src/types/api/models/ScopeRegistryEnum.ts index 02b1b47e18..a323692ffe 100644 --- a/clients/admin-ui/src/types/api/models/ScopeRegistryEnum.ts +++ b/clients/admin-ui/src/types/api/models/ScopeRegistryEnum.ts @@ -70,6 +70,7 @@ export enum ScopeRegistryEnum { DATASET_CREATE_OR_UPDATE = "dataset:create_or_update", DATASET_DELETE = "dataset:delete", DATASET_READ = "dataset:read", + DATASET_TEST = "dataset:test", DISCOVERY_MONITOR_READ = "discovery_monitor:read", DISCOVERY_MONITOR_UPDATE = "discovery_monitor:update", ENCRYPTION_EXEC = "encryption:exec", diff --git a/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts b/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts index c47014b5b3..15f9ac32d5 100644 --- a/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts +++ b/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts @@ -7,11 +7,13 @@ */ export type StagedResourceResponse = { urn: string; - name: string; - description: string; + name: string | null; + description: string | null; created_at: string; updated_at: string; - diff_status: string; + diff_status: string | null; child_diff_statuses: any; hidden: boolean; + monitor_config_id: string | null; + resource_type: string | null; }; diff --git a/clients/admin-ui/src/types/api/models/TestPrivacyRequest.ts b/clients/admin-ui/src/types/api/models/TestPrivacyRequest.ts new file mode 100644 index 0000000000..93af45ca8d --- /dev/null +++ b/clients/admin-ui/src/types/api/models/TestPrivacyRequest.ts @@ -0,0 +1,10 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * Schema containing the data for a test privacy request + */ +export type TestPrivacyRequest = { + privacy_request_id: string; +}; diff --git a/clients/admin-ui/src/types/api/models/UnlabeledIdentities.ts b/clients/admin-ui/src/types/api/models/UnlabeledIdentities.ts new file mode 100644 index 0000000000..124c3a81a4 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/UnlabeledIdentities.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * A model for validating identity dictionaries where standard fields use Identity's validation + * but custom fields just need to be valued. + */ +export type UnlabeledIdentities = { + data: any; +}; From 670e206ffcb629018c5586afdd5b6538afaf96b0 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Tue, 17 Dec 2024 23:25:30 -0600 Subject: [PATCH 16/24] WIP: changes for updated backend --- .../admin-ui/src/features/common/api.slice.ts | 2 + .../src/features/common/nav/v2/nav-config.ts | 4 +- .../src/features/common/nav/v2/routes.ts | 2 +- .../StatusBadgeCell.tsx | 2 +- .../data-catalog/data-catalog.slice.ts | 60 ++++++ .../projects/CatalogProjectsTable.tsx} | 72 ++++--- .../projects/ProjectActionsCell.tsx | 0 .../staged-resource/CatalogResourcesTable.tsx | 179 ++++++++++++++++++ .../systems/CatalogSystemsTable.tsx} | 99 ++++++---- .../systems/SystemActionCell.tsx | 0 .../systems/useSpoofGetSystemsQuery.tsx | 2 +- .../types.ts | 0 .../projects/useSpoofGetProjectsQuery.tsx | 61 ------ .../system/hooks/useShowHideSystems.tsx | 77 -------- .../[system-id]/[resource-urn].tsx | 21 ++ .../pages/data-catalog/[system-id]/index.tsx | 30 +++ .../admin-ui/src/pages/data-catalog/index.tsx | 14 ++ .../src/pages/e2e-datasets/[system-id].tsx | 16 -- .../admin-ui/src/pages/e2e-datasets/index.tsx | 14 -- 19 files changed, 424 insertions(+), 231 deletions(-) rename clients/admin-ui/src/features/{dataset-lifecycle => data-catalog}/StatusBadgeCell.tsx (94%) create mode 100644 clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts rename clients/admin-ui/src/features/{dataset-lifecycle/projects/ProjectsTable.tsx => data-catalog/projects/CatalogProjectsTable.tsx} (72%) rename clients/admin-ui/src/features/{dataset-lifecycle => data-catalog}/projects/ProjectActionsCell.tsx (100%) create mode 100644 clients/admin-ui/src/features/data-catalog/staged-resource/CatalogResourcesTable.tsx rename clients/admin-ui/src/features/{dataset-lifecycle/systems/SystemsTable.tsx => data-catalog/systems/CatalogSystemsTable.tsx} (69%) rename clients/admin-ui/src/features/{dataset-lifecycle => data-catalog}/systems/SystemActionCell.tsx (100%) rename clients/admin-ui/src/features/{dataset-lifecycle => data-catalog}/systems/useSpoofGetSystemsQuery.tsx (97%) rename clients/admin-ui/src/features/{dataset-lifecycle => data-catalog}/types.ts (100%) delete mode 100644 clients/admin-ui/src/features/dataset-lifecycle/projects/useSpoofGetProjectsQuery.tsx delete mode 100644 clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx create mode 100644 clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx create mode 100644 clients/admin-ui/src/pages/data-catalog/[system-id]/index.tsx create mode 100644 clients/admin-ui/src/pages/data-catalog/index.tsx delete mode 100644 clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx delete mode 100644 clients/admin-ui/src/pages/e2e-datasets/index.tsx diff --git a/clients/admin-ui/src/features/common/api.slice.ts b/clients/admin-ui/src/features/common/api.slice.ts index 4a6c5a9f1e..4a96076796 100644 --- a/clients/admin-ui/src/features/common/api.slice.ts +++ b/clients/admin-ui/src/features/common/api.slice.ts @@ -19,6 +19,8 @@ export const baseApi = createApi({ tagTypes: [ "Allow List", "Auth", + "Catalog Systems", + "Catalog Projects", "Classify Instances Datasets", "Classify Instances Systems", "Connection Type", diff --git a/clients/admin-ui/src/features/common/nav/v2/nav-config.ts b/clients/admin-ui/src/features/common/nav/v2/nav-config.ts index 23918e8e08..c7e9ee634e 100644 --- a/clients/admin-ui/src/features/common/nav/v2/nav-config.ts +++ b/clients/admin-ui/src/features/common/nav/v2/nav-config.ts @@ -60,8 +60,8 @@ export const NAV_CONFIG: NavConfigGroup[] = [ requiresPlus: true, }, { - title: "E2E datasets (WIP)", - path: routes.E2E_DATASETS_ROUTE, + title: "Data catalog", + path: routes.DATA_CATALOG_ROUTE, scopes: [], }, ], diff --git a/clients/admin-ui/src/features/common/nav/v2/routes.ts b/clients/admin-ui/src/features/common/nav/v2/routes.ts index 786036b558..300dab522c 100644 --- a/clients/admin-ui/src/features/common/nav/v2/routes.ts +++ b/clients/admin-ui/src/features/common/nav/v2/routes.ts @@ -30,7 +30,7 @@ export const DATA_DISCOVERY_ROUTE_DETAIL = "/data-discovery/discovery/[resourceUrn]"; // End-to-end datasets -export const E2E_DATASETS_ROUTE = "/e2e-datasets"; +export const DATA_CATALOG_ROUTE = "/data-catalog"; // Privacy requests group export const DATASTORE_CONNECTION_ROUTE = "/datastore-connection"; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/StatusBadgeCell.tsx b/clients/admin-ui/src/features/data-catalog/StatusBadgeCell.tsx similarity index 94% rename from clients/admin-ui/src/features/dataset-lifecycle/StatusBadgeCell.tsx rename to clients/admin-ui/src/features/data-catalog/StatusBadgeCell.tsx index 6e639bb809..c04a57a160 100644 --- a/clients/admin-ui/src/features/dataset-lifecycle/StatusBadgeCell.tsx +++ b/clients/admin-ui/src/features/data-catalog/StatusBadgeCell.tsx @@ -4,7 +4,7 @@ import ResultStatusBadge from "~/features/common/ResultStatusBadge"; import { DatasetLifecycleStatusEnum, DatasetLifecycleStatusResult, -} from "~/features/dataset-lifecycle/types"; +} from "~/features/data-catalog/types"; const STATUS_COLOR_MAP: Record = { [DatasetLifecycleStatusEnum.ADDED]: "green", diff --git a/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts new file mode 100644 index 0000000000..7374705bf1 --- /dev/null +++ b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts @@ -0,0 +1,60 @@ +import { createSlice } from "@reduxjs/toolkit"; + +import { baseApi } from "~/features/common/api.slice"; +import { Page_StagedResourceAPIResponse_ } from "~/types/api"; +import { Page_CatalogSystemResponse_ } from "~/types/api/models/Page_CatalogSystemResponse_"; +import { PaginationQueryParams } from "~/types/common/PaginationQueryParams"; + +const initialState = { + page: 1, + pageSize: 50, +}; + +interface CatalogSystemQueryParams extends PaginationQueryParams { + show_hidden?: boolean; +} + +interface CatalogProjectQueryParams extends PaginationQueryParams { + monitor_config_ids?: string[]; + show_hidden?: boolean; +} + +const dataCatalogApi = baseApi.injectEndpoints({ + endpoints: (build) => ({ + getCatalogSystems: build.query< + Page_CatalogSystemResponse_, + CatalogSystemQueryParams + >({ + query: (params) => ({ + method: "GET", + url: `/plus/data-catalog/system`, + params, + }), + providesTags: ["Catalog Systems"], + }), + getCatalogProjects: build.query< + Page_StagedResourceAPIResponse_, + CatalogProjectQueryParams + >({ + query: ({ monitor_config_ids, ...params }) => ({ + method: "POST", + url: `/plus/data-catalog/project`, + body: monitor_config_ids, + params, + }), + providesTags: ["Discovery Monitor Results"], + }), + }), +}); + +export const { + useGetCatalogSystemsQuery, + useGetCatalogProjectsQuery, + useLazyGetCatalogProjectsQuery, +} = dataCatalogApi; + +export const dataCatalogApiSlice = createSlice({ + name: "dataCatalog", + initialState, + reducers: {}, +}); diff --git a/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx similarity index 72% rename from clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx rename to clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx index 9b4c8d8800..f9c182eeee 100644 --- a/clients/admin-ui/src/features/dataset-lifecycle/projects/ProjectsTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx @@ -19,8 +19,10 @@ import { Text, VStack, } from "fidesui"; +import { useRouter } from "next/router"; import { useEffect, useMemo, useState } from "react"; +import { DATA_CATALOG_ROUTE } from "~/features/common/nav/v2/routes"; import { DefaultCell, FidesTableV2, @@ -31,14 +33,12 @@ import { useServerSidePagination, } from "~/features/common/table/v2"; import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; -import { useGetProjectsQuery } from "~/features/data-discovery-and-detection/discovery-detection.slice"; +import { useGetCatalogProjectsQuery } from "~/features/data-catalog/data-catalog.slice"; +import SystemActionsCell from "~/features/data-catalog/systems/SystemActionCell"; +import { useMuteResourcesMutation } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; -import useSpoofGetProjectsQuery, { - DatasetLifecycleProject, -} from "~/features/dataset-lifecycle/projects/useSpoofGetProjectsQuery"; -import StatusBadgeCell from "~/features/dataset-lifecycle/StatusBadgeCell"; -import SystemActionsCell from "~/features/dataset-lifecycle/systems/SystemActionCell"; +import { StagedResourceAPIResponse } from "~/types/api"; const EMPTY_RESPONSE = { items: [], @@ -68,12 +68,14 @@ const EmptyTableNotice = () => ( ); -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); -const ProjectsTable = ({ - monitor_config_id, +const CatalogProjectsTable = ({ + systemKey, + monitorConfigIds, }: { - monitor_config_id?: string; + systemKey: string; + monitorConfigIds: string[]; }) => { const [searchQuery, setSearchQuery] = useState(""); const [rowSelectionState, setRowSelectionState] = useState( @@ -98,12 +100,16 @@ const ProjectsTable = ({ isFetching, isLoading, data: queryResult, - } = useGetProjectsQuery({ + } = useGetCatalogProjectsQuery({ page: pageIndex, size: pageSize, - monitor_config_id: "bq-monitor", + monitor_config_ids: monitorConfigIds, }); + const router = useRouter(); + + const [hideProjects] = useMuteResourcesMutation(); + const { items: data, total: totalRows, @@ -114,7 +120,7 @@ const ProjectsTable = ({ setTotalPages(totalPages); }, [totalPages, setTotalPages]); - const columns: ColumnDef[] = useMemo( + const columns: ColumnDef[] = useMemo( () => [ columnHelper.display({ id: "select", @@ -138,6 +144,7 @@ const ProjectsTable = ({ cellProps: { borderRight: "none", }, + disableRowClick: true, }, }), columnHelper.accessor((row) => row.name, { @@ -145,15 +152,25 @@ const ProjectsTable = ({ cell: (props) => , header: "Name", }), - columnHelper.accessor((row) => row.status, { + columnHelper.display({ id: "status", - cell: (props) => , + cell: () => , header: "Status", }), - columnHelper.accessor((row) => row.lastUpdated, { + columnHelper.accessor((row) => row.monitor_config_id, { + id: "monitorConfigId", + cell: (props) => , + header: "Detected by", + }), + columnHelper.accessor((row) => row.description, { + id: "description", + cell: (props) => , + header: "Description", + }), + columnHelper.accessor((row) => row.updated_at, { id: "lastUpdated", cell: (props) => , - header: "Last Updated", + header: "Updated", meta: { cellProps: { borderRight: "none", @@ -164,9 +181,9 @@ const ProjectsTable = ({ id: "actions", cell: (props) => ( - console.log(`hiding project ${props.row.original.urn}...`) - } + onHideClick={async () => { + await hideProjects({ staged_resource_urns: [props.row.id] }); + }} /> ), maxSize: 20, @@ -175,13 +192,14 @@ const ProjectsTable = ({ cellProps: { borderLeft: "none", }, + disableRowClick: true, }, }), ], - [], + [hideProjects], ); - const tableInstance = useReactTable({ + const tableInstance = useReactTable({ getCoreRowModel: getCoreRowModel(), getGroupedRowModel: getGroupedRowModel(), getExpandedRowModel: getExpandedRowModel(), @@ -197,8 +215,8 @@ const ProjectsTable = ({ const selectedRowIds = Object.keys(rowSelectionState); - const handleBulkHide = () => { - console.log(`hiding projects ${selectedRowIds.join(", ")}...`); + const handleBulkHide = async () => { + await hideProjects({ staged_resource_urns: selectedRowIds }); setRowSelectionState({}); }; @@ -231,7 +249,9 @@ const ProjectsTable = ({ } - onRowClick={() => console.log("row clicked")} + onRowClick={(row) => + router.push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) + } /> ( + + + + No resources found + + You're up to date! + + +); + +const columnHelper = createColumnHelper(); + +const CatalogResourcesTable = ({ resourceUrn }: { resourceUrn: string }) => { + const [searchQuery, setSearchQuery] = useState(""); + + const { + PAGE_SIZES, + pageSize, + setPageSize, + onPreviousPageClick, + isPreviousPageDisabled, + onNextPageClick, + isNextPageDisabled, + startRange, + endRange, + pageIndex, + setTotalPages, + resetPageIndexToDefault, + } = useServerSidePagination(); + + // do we need this in this context? + useEffect(() => { + resetPageIndexToDefault(); + }, [resourceUrn, resetPageIndexToDefault]); + + const { + isFetching, + isLoading, + data: resources, + } = useGetMonitorResultsQuery({ + staged_resource_urn: resourceUrn, + page: pageIndex, + size: pageSize, + }); + + const { + items: data, + total: totalRows, + pages: totalPages, + } = useMemo(() => resources ?? EMPTY_RESPONSE, [resources]); + + useEffect(() => { + setTotalPages(totalPages); + }, [totalPages, setTotalPages]); + + const columns: ColumnDef[] = useMemo( + () => [ + columnHelper.display({ + id: "select", + cell: ({ row }) => ( + + ), + header: ({ table }) => ( + + ), + maxSize: 25, + enableResizing: false, + meta: { + cellProps: { + borderRight: "none", + }, + disableRowClick: true, + }, + }), + columnHelper.accessor((row) => row.name, { + id: "name", + cell: (props) => , + header: "Name", + }), + ], + [], + ); + + const tableInstance = useReactTable({ + getCoreRowModel: getCoreRowModel(), + getGroupedRowModel: getGroupedRowModel(), + getExpandedRowModel: getExpandedRowModel(), + columns, + manualPagination: true, + data, + columnResizeMode: "onChange", + }); + + if (isLoading || isFetching) { + return ; + } + + return ( + <> + + + + + + + Actions + + } + onRowClick={(row) => console.log(`row ${row.urn} clicked!`)} + /> + + + ); +}; + +export default CatalogResourcesTable; diff --git a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx b/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx similarity index 69% rename from clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx rename to clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx index fe8ecdf3c1..91f63074df 100644 --- a/clients/admin-ui/src/features/dataset-lifecycle/systems/SystemsTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx @@ -23,7 +23,7 @@ import { import { useRouter } from "next/router"; import { useEffect, useMemo, useState } from "react"; -import { E2E_DATASETS_ROUTE } from "~/features/common/nav/v2/routes"; +import { DATA_CATALOG_ROUTE } from "~/features/common/nav/v2/routes"; import { DefaultCell, DefaultHeaderCell, @@ -34,13 +34,15 @@ import { useServerSidePagination, } from "~/features/common/table/v2"; import { IndeterminateCheckboxCell } from "~/features/common/table/v2/cells"; +import { getQueryParamsFromArray } from "~/features/common/utils"; +import { + useGetCatalogSystemsQuery, + useLazyGetCatalogProjectsQuery, +} from "~/features/data-catalog/data-catalog.slice"; +import SystemActionsCell from "~/features/data-catalog/systems/SystemActionCell"; import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; -import EditDataUseCell from "~/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; -import SystemActionsCell from "~/features/dataset-lifecycle/systems/SystemActionCell"; -import { useGetSystemsQuery } from "~/features/system"; -import useShowHideSystems from "~/features/system/hooks/useShowHideSystems"; -import { BasicSystemResponse } from "~/types/api"; +import { CatalogSystemResponse } from "~/types/api/models/CatalogSystemResponse"; const EMPTY_RESPONSE = { items: [], @@ -50,7 +52,7 @@ const EMPTY_RESPONSE = { pages: 1, }; -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); const EmptyTableNotice = () => ( { {}, ); - // const [showHidden, setShowHidden] = useState(false); - - const { showSystem, hideSystem } = useShowHideSystems(); - const router = useRouter(); const { @@ -102,14 +100,14 @@ const SystemsTable = () => { data: queryResult, isLoading, isFetching, - } = useGetSystemsQuery({ + } = useGetCatalogSystemsQuery({ page: pageIndex, size: pageSize, - search: searchQuery, - dnd_relevant: true, show_hidden: false, }); + const [getProjects] = useLazyGetCatalogProjectsQuery(); + const { items: data, total: totalRows, @@ -120,7 +118,26 @@ const SystemsTable = () => { setTotalPages(totalPages); }, [totalPages, setTotalPages]); - const columns: ColumnDef[] = useMemo( + const handleRowClicked = async (row: CatalogSystemResponse) => { + // if there are projects, go to project view; otherwise go to datasets view + const projectsResponse = await getProjects({ + monitor_config_ids: row.monitor_config_keys, + page: 1, + size: 1, + }); + if (!projectsResponse?.data?.total) { + router.push(`${DATA_CATALOG_ROUTE}/${row.fides_key}/all`); + return; + } + const monitorIdQueryString = getQueryParamsFromArray( + row.monitor_config_keys ?? [], + "monitor_config_ids", + ); + const url = `${DATA_CATALOG_ROUTE}/${row.fides_key}?${monitorIdQueryString}`; + router.push(url); + }; + + const columns: ColumnDef[] = useMemo( () => [ columnHelper.display({ id: "select", @@ -155,17 +172,44 @@ const SystemsTable = () => { ), header: (props) => , }), - columnHelper.accessor((row) => row.privacy_declarations, { - id: "data-uses", - cell: ({ row }) => , - header: (props) => , + // TODO + columnHelper.display({ + id: "status", + cell: () => , + header: (props) => , + }), + // TODO + // columnHelper.display({ + // id: "changes", + // cell: () => , + // header: (props) => , + // maxSize: 100, + // }), + // TODO + columnHelper.display({ + id: "last-updated", + cell: () => , + header: (props) => ( + + ), meta: { cellProps: { borderRight: "none", }, - disableRowClick: true, }, }), + // TODO + // columnHelper.accessor((row) => row.privacy_declarations, { + // id: "data-uses", + // cell: ({ row }) => , + // header: (props) => , + // meta: { + // cellProps: { + // borderRight: "none", + // }, + // disableRowClick: true, + // }, + // }), columnHelper.display({ id: "actions", cell: (props) => ( @@ -173,7 +217,6 @@ const SystemsTable = () => { onDetailClick={() => router.push(`/systems/configure/${props.row.original.fides_key}`) } - onHideClick={() => hideSystem(props.row.original)} /> ), maxSize: 20, @@ -186,10 +229,10 @@ const SystemsTable = () => { }, }), ], - [], + [router], ); - const tableInstance = useReactTable({ + const tableInstance = useReactTable({ getCoreRowModel: getCoreRowModel(), getGroupedRowModel: getGroupedRowModel(), getExpandedRowModel: getExpandedRowModel(), @@ -213,11 +256,6 @@ const SystemsTable = () => { setRowSelectionState({}); }; - const handleBulkHide = () => { - console.log(`hiding systems ${selectedRowIds.join(", ")}...`); - setRowSelectionState({}); - }; - if (isLoading || isFetching) { return ; } @@ -241,16 +279,13 @@ const SystemsTable = () => { Add data use - Hide } - onRowClick={(row) => - router.push(`${E2E_DATASETS_ROUTE}/${row.fides_key}`) - } + onRowClick={handleRowClicked} /> { - return { - data: { items: PROJECTS, total: 0, page: 1, size: 50, pages: 1 }, - isLoading: false, - isFetching: false, - }; -}; - -export default useSpoofGetProjectsQuery; diff --git a/clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx b/clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx deleted file mode 100644 index 10f33aaf4f..0000000000 --- a/clients/admin-ui/src/features/system/hooks/useShowHideSystems.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useToast } from "fidesui"; - -import { getErrorMessage } from "~/features/common/helpers"; -import { useUpsertSystemsMutation } from "~/features/system/system.slice"; -import { System } from "~/types/api"; -import { isErrorResult, RTKResult } from "~/types/errors"; - -const useShowHideSystems = () => { - const [upsertSystems] = useUpsertSystemsMutation(); - - const toast = useToast(); - - const handleResult = (result: RTKResult, isMultiple?: boolean) => { - if (isErrorResult(result)) { - const errorMsg = getErrorMessage( - result.error, - `An unexpected error occurred while updating the ${isMultiple ? "systems" : "system"}. Please try again.`, - ); - toast({ status: "error", description: errorMsg }); - } else { - toast({ - status: "success", - description: `${isMultiple ? "Systems" : "System"} updated successfully`, - }); - } - }; - - const hideSystem = async (system: System) => { - const result = await upsertSystems([ - { - ...system, - description: "I've been hidden!", - hidden: true, - }, - ]); - handleResult(result); - }; - - const showSystem = async (system: System) => { - const result = await upsertSystems([ - { - ...system, - hidden: false, - }, - ]); - handleResult(result); - }; - - const hideMultipleSystems = async (systems: System[]) => { - const newSystems = systems.map((system) => ({ - ...system, - hidden: true, - })); - - const result = await upsertSystems(newSystems); - handleResult(result, true); - }; - - const showMultipleSystems = async (systems: System[]) => { - const newSystems = systems.map((system) => ({ - ...system, - hidden: false, - })); - - const result = await upsertSystems(newSystems); - handleResult(result, true); - }; - - return { - hideSystem, - showSystem, - hideMultipleSystems, - showMultipleSystems, - }; -}; - -export default useShowHideSystems; diff --git a/clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx b/clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx new file mode 100644 index 0000000000..537e2709b0 --- /dev/null +++ b/clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx @@ -0,0 +1,21 @@ +import { NextPage } from "next"; +import { useRouter } from "next/router"; + +import Layout from "~/features/common/Layout"; +import PageHeader from "~/features/common/PageHeader"; +import CatalogResourcesTable from "~/features/data-catalog/staged-resource/CatalogResourcesTable"; + +const CatalogResourceView: NextPage = () => { + const { query } = useRouter(); + // const systemId = query["system-id"] as string; + const resourceUrn = query["resource-urn"] as string; + + return ( + + + + + ); +}; + +export default CatalogResourceView; diff --git a/clients/admin-ui/src/pages/data-catalog/[system-id]/index.tsx b/clients/admin-ui/src/pages/data-catalog/[system-id]/index.tsx new file mode 100644 index 0000000000..02dd77e152 --- /dev/null +++ b/clients/admin-ui/src/pages/data-catalog/[system-id]/index.tsx @@ -0,0 +1,30 @@ +import { useRouter } from "next/router"; + +import FidesSpinner from "~/features/common/FidesSpinner"; +import Layout from "~/features/common/Layout"; +import PageHeader from "~/features/common/PageHeader"; +import CatalogProjectsTable from "~/features/data-catalog/projects/CatalogProjectsTable"; +import { useGetSystemByFidesKeyQuery } from "~/features/system"; + +const CatalogProjectView = () => { + const { query } = useRouter(); + const systemKey = query["system-id"] as string; + const monitorConfigIds = query.monitor_config_ids as string[]; + const { data: system, isLoading } = useGetSystemByFidesKeyQuery(systemKey); + + if (isLoading) { + return ; + } + + return ( + + + + + ); +}; + +export default CatalogProjectView; diff --git a/clients/admin-ui/src/pages/data-catalog/index.tsx b/clients/admin-ui/src/pages/data-catalog/index.tsx new file mode 100644 index 0000000000..a977e7d47c --- /dev/null +++ b/clients/admin-ui/src/pages/data-catalog/index.tsx @@ -0,0 +1,14 @@ +import Layout from "~/features/common/Layout"; +import PageHeader from "~/features/common/PageHeader"; +import SystemsTable from "~/features/data-catalog/systems/CatalogSystemsTable"; + +const DataCatalogMainPage = () => { + return ( + + + + + ); +}; + +export default DataCatalogMainPage; diff --git a/clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx b/clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx deleted file mode 100644 index db549b3cb4..0000000000 --- a/clients/admin-ui/src/pages/e2e-datasets/[system-id].tsx +++ /dev/null @@ -1,16 +0,0 @@ -import Layout from "~/features/common/Layout"; -import PageHeader from "~/features/common/PageHeader"; -import ProjectsTable from "~/features/dataset-lifecycle/projects/ProjectsTable"; - -const E2EDatasetProjectView = () => { - return ( - - - - - ); -}; - -export default E2EDatasetProjectView; diff --git a/clients/admin-ui/src/pages/e2e-datasets/index.tsx b/clients/admin-ui/src/pages/e2e-datasets/index.tsx deleted file mode 100644 index a5df276e70..0000000000 --- a/clients/admin-ui/src/pages/e2e-datasets/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import Layout from "~/features/common/Layout"; -import PageHeader from "~/features/common/PageHeader"; -import SystemsTable from "~/features/dataset-lifecycle/systems/SystemsTable"; - -const E2EDatasetSystemView = () => { - return ( - - - - - ); -}; - -export default E2EDatasetSystemView; From 482d662cd2a679dcd3587b5b432dd15495240c0f Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Tue, 17 Dec 2024 23:26:41 -0600 Subject: [PATCH 17/24] type update --- clients/admin-ui/src/types/api/index.ts | 6 ++---- .../types/api/models/CatalogSystemResponse.ts | 17 ----------------- .../models/MonitorExecutionRequestResponse.ts | 10 ++++++++++ .../models/Page_StagedResourceResponse_.ts | 13 ------------- ...emResponse_.ts => Page_SystemResponse_.ts} | 6 +++--- .../api/models/StagedResourceResponse.ts | 19 ------------------- 6 files changed, 15 insertions(+), 56 deletions(-) delete mode 100644 clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts create mode 100644 clients/admin-ui/src/types/api/models/MonitorExecutionRequestResponse.ts delete mode 100644 clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts rename clients/admin-ui/src/types/api/models/{Page_CatalogSystemResponse_.ts => Page_SystemResponse_.ts} (52%) delete mode 100644 clients/admin-ui/src/types/api/models/StagedResourceResponse.ts diff --git a/clients/admin-ui/src/types/api/index.ts b/clients/admin-ui/src/types/api/index.ts index f3085d11e2..1efb433aad 100644 --- a/clients/admin-ui/src/types/api/index.ts +++ b/clients/admin-ui/src/types/api/index.ts @@ -41,7 +41,6 @@ export type { BulkPutStorageConfigResponse } from "./models/BulkPutStorageConfig export type { BulkReviewResponse } from "./models/BulkReviewResponse"; export type { BulkSoftDeletePrivacyRequests } from "./models/BulkSoftDeletePrivacyRequests"; export type { BulkUpdateFailed } from "./models/BulkUpdateFailed"; -export type { CatalogSystemResponse } from "./models/CatalogSystemResponse"; export type { CheckpointActionRequiredDetails } from "./models/CheckpointActionRequiredDetails"; export type { Classification } from "./models/Classification"; export type { ClassificationResponse } from "./models/ClassificationResponse"; @@ -266,6 +265,7 @@ export type { MongoDBDocsSchema } from "./models/MongoDBDocsSchema"; export type { MonitorClassifyParams } from "./models/MonitorClassifyParams"; export type { MonitorConfig } from "./models/MonitorConfig"; export type { MonitorExecution } from "./models/MonitorExecution"; +export type { MonitorExecutionRequestResponse } from "./models/MonitorExecutionRequestResponse"; export { MonitorExecutionStatus } from "./models/MonitorExecutionStatus"; export { MonitorFrequency } from "./models/MonitorFrequency"; export type { MSSQLDocsSchema } from "./models/MSSQLDocsSchema"; @@ -281,7 +281,6 @@ export type { OpenIDProviderSimple } from "./models/OpenIDProviderSimple"; export type { Organization } from "./models/Organization"; export type { OrganizationMetadata } from "./models/OrganizationMetadata"; export type { Page_BasicSystemResponse_ } from "./models/Page_BasicSystemResponse_"; -export type { Page_CatalogSystemResponse_ } from "./models/Page_CatalogSystemResponse_"; export type { Page_ClassifyInstanceResponseValues_ } from "./models/Page_ClassifyInstanceResponseValues_"; export type { Page_ConnectionConfigurationResponse_ } from "./models/Page_ConnectionConfigurationResponse_"; export type { Page_ConnectionSystemTypeMap_ } from "./models/Page_ConnectionSystemTypeMap_"; @@ -308,10 +307,10 @@ export type { Page_Property_ } from "./models/Page_Property_"; export type { Page_RuleResponseWithTargets_ } from "./models/Page_RuleResponseWithTargets_"; export type { Page_RuleTarget_ } from "./models/Page_RuleTarget_"; export type { Page_StagedResourceAPIResponse_ } from "./models/Page_StagedResourceAPIResponse_"; -export type { Page_StagedResourceResponse_ } from "./models/Page_StagedResourceResponse_"; export type { Page_StorageDestinationResponse_ } from "./models/Page_StorageDestinationResponse_"; export type { Page_str_ } from "./models/Page_str_"; export type { Page_SystemHistoryResponse_ } from "./models/Page_SystemHistoryResponse_"; +export type { Page_SystemResponse_ } from "./models/Page_SystemResponse_"; export type { Page_SystemSummary_ } from "./models/Page_SystemSummary_"; export type { Page_Union_PrivacyExperienceResponse__TCFBannerExperienceMinimalResponse__ } from "./models/Page_Union_PrivacyExperienceResponse__TCFBannerExperienceMinimalResponse__"; export type { Page_Union_PrivacyRequestVerboseResponse__PrivacyRequestResponse__ } from "./models/Page_Union_PrivacyRequestVerboseResponse__PrivacyRequestResponse__"; @@ -419,7 +418,6 @@ export type { SovrnDocsSchema } from "./models/SovrnDocsSchema"; export { SpecialCategoryLegalBasisEnum } from "./models/SpecialCategoryLegalBasisEnum"; export type { StagedResource } from "./models/StagedResource"; export type { StagedResourceAPIResponse } from "./models/StagedResourceAPIResponse"; -export type { StagedResourceResponse } from "./models/StagedResourceResponse"; export type { StagedResourceUpdateRequest } from "./models/StagedResourceUpdateRequest"; export { StatusEnum } from "./models/StatusEnum"; export type { StorageApplicationConfig } from "./models/StorageApplicationConfig"; diff --git a/clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts b/clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts deleted file mode 100644 index 3731ebfd60..0000000000 --- a/clients/admin-ui/src/types/api/models/CatalogSystemResponse.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -/** - * Extension of SystemResponse response model to include other relationships only needed for Data Catalog view - */ -export type CatalogSystemResponse = { - id: string; - fides_key: string; - name: string | null; - legal_name: string | null; - description: string | null; - system_type: string | null; - hidden: boolean; - monitor_config_keys?: Array; -}; diff --git a/clients/admin-ui/src/types/api/models/MonitorExecutionRequestResponse.ts b/clients/admin-ui/src/types/api/models/MonitorExecutionRequestResponse.ts new file mode 100644 index 0000000000..b922d2d109 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/MonitorExecutionRequestResponse.ts @@ -0,0 +1,10 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +/** + * Schema for creating a MonitorExecution. + */ +export type MonitorExecutionRequestResponse = { + monitor_execution_id: string; +}; diff --git a/clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts b/clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts deleted file mode 100644 index 5b3f58dafe..0000000000 --- a/clients/admin-ui/src/types/api/models/Page_StagedResourceResponse_.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { StagedResourceResponse } from "./StagedResourceResponse"; - -export type Page_StagedResourceResponse_ = { - items: Array; - total: number | null; - page: number | null; - size: number | null; - pages?: number | null; -}; diff --git a/clients/admin-ui/src/types/api/models/Page_CatalogSystemResponse_.ts b/clients/admin-ui/src/types/api/models/Page_SystemResponse_.ts similarity index 52% rename from clients/admin-ui/src/types/api/models/Page_CatalogSystemResponse_.ts rename to clients/admin-ui/src/types/api/models/Page_SystemResponse_.ts index 1cac90ea6e..586b132614 100644 --- a/clients/admin-ui/src/types/api/models/Page_CatalogSystemResponse_.ts +++ b/clients/admin-ui/src/types/api/models/Page_SystemResponse_.ts @@ -2,10 +2,10 @@ /* tslint:disable */ /* eslint-disable */ -import type { CatalogSystemResponse } from "./CatalogSystemResponse"; +import type { SystemResponse } from "./SystemResponse"; -export type Page_CatalogSystemResponse_ = { - items: Array; +export type Page_SystemResponse_ = { + items: Array; total: number | null; page: number | null; size: number | null; diff --git a/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts b/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts deleted file mode 100644 index 15f9ac32d5..0000000000 --- a/clients/admin-ui/src/types/api/models/StagedResourceResponse.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -/** - * Response schema for a list of staged resources - */ -export type StagedResourceResponse = { - urn: string; - name: string | null; - description: string | null; - created_at: string; - updated_at: string; - diff_status: string | null; - child_diff_statuses: any; - hidden: boolean; - monitor_config_id: string | null; - resource_type: string | null; -}; From 4e53afa93334bb56b17229b060f6c64ee95cb3d4 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Wed, 18 Dec 2024 03:46:27 -0600 Subject: [PATCH 18/24] tables; rework nav --- .../data-catalog/data-catalog.slice.ts | 26 +++- .../datasets/EmptyCatalogTableNotice.tsx | 23 ++++ .../datasets/useCatalogDatasetColumns.tsx | 82 ++++++++++++ .../projects/CatalogProjectsTable.tsx | 10 +- .../CatalogResourcesTable.tsx | 42 +++++- .../systems/CatalogSystemsTable.tsx | 36 +++-- .../systems/useSpoofGetSystemsQuery.tsx | 89 ------------- .../discovery-detection.slice.ts | 12 -- .../[system-id]/[resource-urn].tsx | 21 --- .../data-catalog/[systemId]/[resourceUrn].tsx | 31 +++++ .../pages/data-catalog/[systemId]/index.tsx | 123 +++++++++++++++++ .../[systemId]/projects/[projectId].tsx | 124 ++++++++++++++++++ .../projects}/index.tsx | 2 +- 13 files changed, 464 insertions(+), 157 deletions(-) create mode 100644 clients/admin-ui/src/features/data-catalog/datasets/EmptyCatalogTableNotice.tsx create mode 100644 clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx rename clients/admin-ui/src/features/data-catalog/{staged-resource => staged-resources}/CatalogResourcesTable.tsx (77%) delete mode 100644 clients/admin-ui/src/features/data-catalog/systems/useSpoofGetSystemsQuery.tsx delete mode 100644 clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx create mode 100644 clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx create mode 100644 clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx create mode 100644 clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx rename clients/admin-ui/src/pages/data-catalog/{[system-id] => [systemId]/projects}/index.tsx (94%) diff --git a/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts index 7374705bf1..a79c820448 100644 --- a/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts +++ b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts @@ -1,8 +1,10 @@ import { createSlice } from "@reduxjs/toolkit"; import { baseApi } from "~/features/common/api.slice"; -import { Page_StagedResourceAPIResponse_ } from "~/types/api"; -import { Page_CatalogSystemResponse_ } from "~/types/api/models/Page_CatalogSystemResponse_"; +import { + Page_StagedResourceAPIResponse_, + Page_SystemResponse_, +} from "~/types/api"; import { PaginationQueryParams } from "~/types/common/PaginationQueryParams"; const initialState = { @@ -14,7 +16,7 @@ interface CatalogSystemQueryParams extends PaginationQueryParams { show_hidden?: boolean; } -interface CatalogProjectQueryParams extends PaginationQueryParams { +interface CatalogResourceQueryParams extends PaginationQueryParams { monitor_config_ids?: string[]; show_hidden?: boolean; } @@ -22,7 +24,7 @@ interface CatalogProjectQueryParams extends PaginationQueryParams { const dataCatalogApi = baseApi.injectEndpoints({ endpoints: (build) => ({ getCatalogSystems: build.query< - Page_CatalogSystemResponse_, + Page_SystemResponse_, CatalogSystemQueryParams >({ query: (params) => ({ @@ -34,7 +36,7 @@ const dataCatalogApi = baseApi.injectEndpoints({ }), getCatalogProjects: build.query< Page_StagedResourceAPIResponse_, - CatalogProjectQueryParams + CatalogResourceQueryParams >({ query: ({ monitor_config_ids, ...params }) => ({ method: "POST", @@ -44,13 +46,25 @@ const dataCatalogApi = baseApi.injectEndpoints({ }), providesTags: ["Discovery Monitor Results"], }), + getCatalogDatasets: build.query< + Page_StagedResourceAPIResponse_, + CatalogResourceQueryParams + >({ + query: ({ monitor_config_ids, ...params }) => ({ + method: "POST", + url: `/plus/data-catalog/dataset`, + body: monitor_config_ids, + params, + }), + providesTags: ["Discovery Monitor Results"], + }), }), }); export const { useGetCatalogSystemsQuery, useGetCatalogProjectsQuery, - useLazyGetCatalogProjectsQuery, + useGetCatalogDatasetsQuery, } = dataCatalogApi; export const dataCatalogApiSlice = createSlice({ diff --git a/clients/admin-ui/src/features/data-catalog/datasets/EmptyCatalogTableNotice.tsx b/clients/admin-ui/src/features/data-catalog/datasets/EmptyCatalogTableNotice.tsx new file mode 100644 index 0000000000..a841fd015b --- /dev/null +++ b/clients/admin-ui/src/features/data-catalog/datasets/EmptyCatalogTableNotice.tsx @@ -0,0 +1,23 @@ +import { Text, VStack } from "fidesui"; + +const EmptyCatalogTableNotice = () => ( + + + + No resources found + + You're up to date! + + +); + +export default EmptyCatalogTableNotice; diff --git a/clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx b/clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx new file mode 100644 index 0000000000..22ce89cbb2 --- /dev/null +++ b/clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx @@ -0,0 +1,82 @@ +/* eslint-disable react/no-unstable-nested-components */ + +import { ColumnDef, createColumnHelper } from "@tanstack/react-table"; +import { AntButton } from "fidesui"; +import { useMemo } from "react"; + +import { + DefaultCell, + IndeterminateCheckboxCell, +} from "~/features/common/table/v2"; +import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; +import { StagedResourceAPIResponse } from "~/types/api"; + +const columnHelper = createColumnHelper(); + +const useCatalogDatasetColumns = () => { + const columns: ColumnDef[] = useMemo( + () => [ + columnHelper.display({ + id: "select", + cell: ({ row }) => ( + + ), + header: ({ table }) => ( + + ), + maxSize: 25, + enableResizing: false, + meta: { + cellProps: { + borderRight: "none", + }, + disableRowClick: true, + }, + }), + columnHelper.accessor((row) => row.name, { + id: "name", + cell: (props) => ( + + ), + header: "Name", + }), + columnHelper.display({ + id: "status", + cell: () => , + header: "Status", + }), + columnHelper.accessor((row) => row.description, { + id: "description", + cell: (props) => , + header: "Description", + }), + columnHelper.accessor((row) => row.updated_at, { + id: "lastUpdated", + cell: (props) => , + header: "Updated", + }), + columnHelper.display({ + id: "actions", + cell: () => ( + + Actions + + ), + header: "Actions", + }), + ], + [], + ); + + return columns; +}; + +export default useCatalogDatasetColumns; diff --git a/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx index f9c182eeee..9e738d0677 100644 --- a/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx @@ -103,7 +103,7 @@ const CatalogProjectsTable = ({ } = useGetCatalogProjectsQuery({ page: pageIndex, size: pageSize, - monitor_config_ids: monitorConfigIds, + monitor_config_ids: ["bq-monitor", "bq-monitor-2"], }); const router = useRouter(); @@ -149,8 +149,10 @@ const CatalogProjectsTable = ({ }), columnHelper.accessor((row) => row.name, { id: "name", - cell: (props) => , - header: "Name", + cell: (props) => ( + + ), + header: "Project", }), columnHelper.display({ id: "status", @@ -250,7 +252,7 @@ const CatalogProjectsTable = ({ tableInstance={tableInstance} emptyTableNotice={} onRowClick={(row) => - router.push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) + router.push(`${DATA_CATALOG_ROUTE}/${systemKey}/projects/${row.urn}`) } /> ( const columnHelper = createColumnHelper(); -const CatalogResourcesTable = ({ resourceUrn }: { resourceUrn: string }) => { +const CatalogResourcesTable = ({ + resourceUrn, + system, +}: { + resourceUrn: string; + system: SystemResponse; +}) => { const [searchQuery, setSearchQuery] = useState(""); + const router = useRouter(); const { PAGE_SIZES, @@ -128,6 +138,30 @@ const CatalogResourcesTable = ({ resourceUrn }: { resourceUrn: string }) => { cell: (props) => , header: "Name", }), + columnHelper.display({ + id: "status", + cell: () => , + header: "Status", + }), + columnHelper.accessor((row) => row.description, { + id: "description", + cell: (props) => , + header: "Description", + }), + columnHelper.accessor((row) => row.updated_at, { + id: "lastUpdated", + cell: (props) => , + header: "Updated", + }), + columnHelper.display({ + id: "actions", + cell: () => ( + + Actions + + ), + header: "Actions", + }), ], [], ); @@ -159,7 +193,9 @@ const CatalogResourcesTable = ({ resourceUrn }: { resourceUrn: string }) => { } - onRowClick={(row) => console.log(`row ${row.urn} clicked!`)} + onRowClick={(row) => + router.push(`${DATA_CATALOG_ROUTE}/${system.fides_key}/${row.urn}`) + } /> (); +const columnHelper = createColumnHelper(); const EmptyTableNotice = () => ( { show_hidden: false, }); - const [getProjects] = useLazyGetCatalogProjectsQuery(); + // const [getProjects] = useLazyGetCatalogProjectsQuery(); + const [getProjects] = useLazyGetAvailableDatabasesByConnectionQuery(); const { items: data, @@ -118,26 +116,21 @@ const SystemsTable = () => { setTotalPages(totalPages); }, [totalPages, setTotalPages]); - const handleRowClicked = async (row: CatalogSystemResponse) => { + const handleRowClicked = async (row: SystemResponse) => { // if there are projects, go to project view; otherwise go to datasets view const projectsResponse = await getProjects({ - monitor_config_ids: row.monitor_config_keys, + connection_config_key: row.connection_configs!.key, page: 1, size: 1, }); - if (!projectsResponse?.data?.total) { - router.push(`${DATA_CATALOG_ROUTE}/${row.fides_key}/all`); - return; - } - const monitorIdQueryString = getQueryParamsFromArray( - row.monitor_config_keys ?? [], - "monitor_config_ids", - ); - const url = `${DATA_CATALOG_ROUTE}/${row.fides_key}?${monitorIdQueryString}`; + + const hasProjects = !!projectsResponse?.data?.total; + + const url = `${DATA_CATALOG_ROUTE}/${row.fides_key}${hasProjects ? "/projects" : ""}`; router.push(url); }; - const columns: ColumnDef[] = useMemo( + const columns: ColumnDef[] = useMemo( () => [ columnHelper.display({ id: "select", @@ -232,7 +225,7 @@ const SystemsTable = () => { [router], ); - const tableInstance = useReactTable({ + const tableInstance = useReactTable({ getCoreRowModel: getCoreRowModel(), getGroupedRowModel: getGroupedRowModel(), getExpandedRowModel: getExpandedRowModel(), @@ -286,6 +279,7 @@ const SystemsTable = () => { tableInstance={tableInstance} emptyTableNotice={} onRowClick={handleRowClicked} + getRowIsClickable={(row) => !!row.connection_configs?.key} /> { - return { - data: { items: SYSTEMS, total: 0, page: 1, size: 50, pages: 1 }, - isLoading: false, - isFetching: false, - }; -}; - -export default useSpoofGetSystemsQuery; diff --git a/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts b/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts index b112052d80..3f6dfab9d6 100644 --- a/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts +++ b/clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts @@ -10,7 +10,6 @@ import { Page_StagedResourceAPIResponse_, Page_str_, } from "~/types/api"; -import { Page_StagedResourceResponse_ } from "~/types/api/models/Page_StagedResourceResponse_"; interface State { page?: number; @@ -105,16 +104,6 @@ const discoveryDetectionApi = baseApi.injectEndpoints({ }, }), }), - getProjects: build.query< - Page_StagedResourceResponse_, - DatabaseByMonitorQueryParams - >({ - query: (params) => ({ - method: "GET", - url: `/plus/lifecycle/projects`, - params, - }), - }), executeDiscoveryMonitor: build.mutation({ query: ({ monitor_config_id }) => ({ method: "POST", @@ -241,7 +230,6 @@ export const { useGetDatabasesByMonitorQuery, useGetAvailableDatabasesByConnectionQuery, useLazyGetAvailableDatabasesByConnectionQuery, - useGetProjectsQuery, useExecuteDiscoveryMonitorMutation, useDeleteDiscoveryMonitorMutation, useGetMonitorResultsQuery, diff --git a/clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx b/clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx deleted file mode 100644 index 537e2709b0..0000000000 --- a/clients/admin-ui/src/pages/data-catalog/[system-id]/[resource-urn].tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { NextPage } from "next"; -import { useRouter } from "next/router"; - -import Layout from "~/features/common/Layout"; -import PageHeader from "~/features/common/PageHeader"; -import CatalogResourcesTable from "~/features/data-catalog/staged-resource/CatalogResourcesTable"; - -const CatalogResourceView: NextPage = () => { - const { query } = useRouter(); - // const systemId = query["system-id"] as string; - const resourceUrn = query["resource-urn"] as string; - - return ( - - - - - ); -}; - -export default CatalogResourceView; diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx new file mode 100644 index 0000000000..c827f79302 --- /dev/null +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx @@ -0,0 +1,31 @@ +import { NextPage } from "next"; +import { useRouter } from "next/router"; + +import FidesSpinner from "~/features/common/FidesSpinner"; +import Layout from "~/features/common/Layout"; +import PageHeader from "~/features/common/PageHeader"; +import CatalogResourcesTable from "~/features/data-catalog/staged-resources/CatalogResourcesTable"; +import { useGetSystemByFidesKeyQuery } from "~/features/system"; + +const CatalogResourceView: NextPage = () => { + const { query } = useRouter(); + const systemId = query.systemId as string; + const resourceUrn = query.resourceUrn as string; + const { data: system, isLoading } = useGetSystemByFidesKeyQuery(systemId); + + if (isLoading) { + return ; + } + + return ( + + + + + ); +}; + +export default CatalogResourceView; diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx new file mode 100644 index 0000000000..1b91ab8ac9 --- /dev/null +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx @@ -0,0 +1,123 @@ +import { + getCoreRowModel, + getExpandedRowModel, + getGroupedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { AntButton, Flex } from "fidesui"; +import { useRouter } from "next/router"; +import { useEffect, useMemo } from "react"; + +import Layout from "~/features/common/Layout"; +import { DATA_CATALOG_ROUTE } from "~/features/common/nav/v2/routes"; +import PageHeader from "~/features/common/PageHeader"; +import { + FidesTableV2, + PaginationBar, + TableActionBar, + TableSkeletonLoader, + useServerSidePagination, +} from "~/features/common/table/v2"; +import { useGetCatalogDatasetsQuery } from "~/features/data-catalog/data-catalog.slice"; +import EmptyCatalogTableNotice from "~/features/data-catalog/datasets/EmptyCatalogTableNotice"; +import useCatalogDatasetColumns from "~/features/data-catalog/datasets/useCatalogDatasetColumns"; +import { StagedResourceAPIResponse } from "~/types/api"; + +// REPLACE: dataset view, system has no projects + +const EMPTY_RESPONSE = { + items: [], + total: 0, + page: 1, + size: 50, + pages: 1, +}; + +const CatalogDatasetViewNoProjects = () => { + const { query, push } = useRouter(); + const systemKey = query["system-id"] as string; + + const { + PAGE_SIZES, + pageSize, + setPageSize, + onPreviousPageClick, + isPreviousPageDisabled, + onNextPageClick, + isNextPageDisabled, + startRange, + endRange, + pageIndex, + setTotalPages, + } = useServerSidePagination(); + + const { + isFetching, + isLoading, + data: resources, + } = useGetCatalogDatasetsQuery({ + page: pageIndex, + size: pageSize, + monitor_config_ids: ["dynamo-monitor"], + }); + + const { + items: data, + total: totalRows, + pages: totalPages, + } = useMemo(() => resources ?? EMPTY_RESPONSE, [resources]); + + useEffect(() => { + setTotalPages(totalPages); + }, [totalPages, setTotalPages]); + + const columns = useCatalogDatasetColumns(); + + const tableInstance = useReactTable({ + getCoreRowModel: getCoreRowModel(), + getGroupedRowModel: getGroupedRowModel(), + getExpandedRowModel: getExpandedRowModel(), + columns, + manualPagination: true, + data, + columnResizeMode: "onChange", + }); + + if (isLoading || isFetching) { + return ; + } + + return ( + + + + + {/* + + */} + + Actions + + } + onRowClick={(row) => + push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) + } + /> + + + ); +}; + +export default CatalogDatasetViewNoProjects; diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx new file mode 100644 index 0000000000..0c578f1a0e --- /dev/null +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx @@ -0,0 +1,124 @@ +import { + getCoreRowModel, + getExpandedRowModel, + getGroupedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { AntButton, Flex } from "fidesui"; +import { useRouter } from "next/router"; +import { useEffect, useMemo } from "react"; + +import Layout from "~/features/common/Layout"; +import { DATA_CATALOG_ROUTE } from "~/features/common/nav/v2/routes"; +import PageHeader from "~/features/common/PageHeader"; +import { + FidesTableV2, + PaginationBar, + TableActionBar, + TableSkeletonLoader, + useServerSidePagination, +} from "~/features/common/table/v2"; +import EmptyCatalogTableNotice from "~/features/data-catalog/datasets/EmptyCatalogTableNotice"; +import useCatalogDatasetColumns from "~/features/data-catalog/datasets/useCatalogDatasetColumns"; +import { useGetMonitorResultsQuery } from "~/features/data-discovery-and-detection/discovery-detection.slice"; +import { StagedResourceAPIResponse } from "~/types/api"; + +const EMPTY_RESPONSE = { + items: [], + total: 0, + page: 1, + size: 50, + pages: 1, +}; + +// dataset view when projects are present + +const CatalogDatasetView = () => { + const { query, push } = useRouter(); + const systemKey = query.systemId as string; + const projectId = query.projectId as string; + + const { + PAGE_SIZES, + pageSize, + setPageSize, + onPreviousPageClick, + isPreviousPageDisabled, + onNextPageClick, + isNextPageDisabled, + startRange, + endRange, + pageIndex, + setTotalPages, + } = useServerSidePagination(); + + const { + isFetching, + isLoading, + data: resources, + } = useGetMonitorResultsQuery({ + staged_resource_urn: projectId, + page: pageIndex, + size: pageSize, + }); + + const { + items: data, + total: totalRows, + pages: totalPages, + } = useMemo(() => resources ?? EMPTY_RESPONSE, [resources]); + + useEffect(() => { + setTotalPages(totalPages); + }, [totalPages, setTotalPages]); + + const columns = useCatalogDatasetColumns(); + + const tableInstance = useReactTable({ + getCoreRowModel: getCoreRowModel(), + getGroupedRowModel: getGroupedRowModel(), + getExpandedRowModel: getExpandedRowModel(), + columns, + manualPagination: true, + data, + columnResizeMode: "onChange", + }); + + if (isLoading || isFetching) { + return ; + } + + return ( + + + + + {/* + + */} + + Actions + + } + onRowClick={(row) => + push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) + } + /> + + + ); +}; + +export default CatalogDatasetView; diff --git a/clients/admin-ui/src/pages/data-catalog/[system-id]/index.tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/index.tsx similarity index 94% rename from clients/admin-ui/src/pages/data-catalog/[system-id]/index.tsx rename to clients/admin-ui/src/pages/data-catalog/[systemId]/projects/index.tsx index 02dd77e152..52b635f83b 100644 --- a/clients/admin-ui/src/pages/data-catalog/[system-id]/index.tsx +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/index.tsx @@ -8,7 +8,7 @@ import { useGetSystemByFidesKeyQuery } from "~/features/system"; const CatalogProjectView = () => { const { query } = useRouter(); - const systemKey = query["system-id"] as string; + const systemKey = query.systemId as string; const monitorConfigIds = query.monitor_config_ids as string[]; const { data: system, isLoading } = useGetSystemByFidesKeyQuery(systemKey); From fcf6b2161831cabe95d16d55f3f294e9d3f937cd Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 19 Dec 2024 01:06:45 -0600 Subject: [PATCH 19/24] type update --- clients/admin-ui/src/types/api/index.ts | 3 +- ...nse_.ts => Page_SystemWithMonitorKeys_.ts} | 6 +- .../types/api/models/SystemWithMonitorKeys.ts | 193 ++++++++++++++++++ 3 files changed, 198 insertions(+), 4 deletions(-) rename clients/admin-ui/src/types/api/models/{Page_SystemResponse_.ts => Page_SystemWithMonitorKeys_.ts} (52%) create mode 100644 clients/admin-ui/src/types/api/models/SystemWithMonitorKeys.ts diff --git a/clients/admin-ui/src/types/api/index.ts b/clients/admin-ui/src/types/api/index.ts index 1efb433aad..1905eb47b8 100644 --- a/clients/admin-ui/src/types/api/index.ts +++ b/clients/admin-ui/src/types/api/index.ts @@ -310,8 +310,8 @@ export type { Page_StagedResourceAPIResponse_ } from "./models/Page_StagedResour export type { Page_StorageDestinationResponse_ } from "./models/Page_StorageDestinationResponse_"; export type { Page_str_ } from "./models/Page_str_"; export type { Page_SystemHistoryResponse_ } from "./models/Page_SystemHistoryResponse_"; -export type { Page_SystemResponse_ } from "./models/Page_SystemResponse_"; export type { Page_SystemSummary_ } from "./models/Page_SystemSummary_"; +export type { Page_SystemWithMonitorKeys_ } from "./models/Page_SystemWithMonitorKeys_"; export type { Page_Union_PrivacyExperienceResponse__TCFBannerExperienceMinimalResponse__ } from "./models/Page_Union_PrivacyExperienceResponse__TCFBannerExperienceMinimalResponse__"; export type { Page_Union_PrivacyRequestVerboseResponse__PrivacyRequestResponse__ } from "./models/Page_Union_PrivacyRequestVerboseResponse__PrivacyRequestResponse__"; export type { Page_UserResponse_ } from "./models/Page_UserResponse_"; @@ -445,6 +445,7 @@ export type { SystemScanResponse } from "./models/SystemScanResponse"; export type { SystemsDiff } from "./models/SystemsDiff"; export type { SystemSummary } from "./models/SystemSummary"; export { SystemType } from "./models/SystemType"; +export type { SystemWithMonitorKeys } from "./models/SystemWithMonitorKeys"; export type { Table } from "./models/Table"; export type { TCDecode } from "./models/TCDecode"; export type { TCFBannerExperienceMinimalResponse } from "./models/TCFBannerExperienceMinimalResponse"; diff --git a/clients/admin-ui/src/types/api/models/Page_SystemResponse_.ts b/clients/admin-ui/src/types/api/models/Page_SystemWithMonitorKeys_.ts similarity index 52% rename from clients/admin-ui/src/types/api/models/Page_SystemResponse_.ts rename to clients/admin-ui/src/types/api/models/Page_SystemWithMonitorKeys_.ts index 586b132614..e922a65fa6 100644 --- a/clients/admin-ui/src/types/api/models/Page_SystemResponse_.ts +++ b/clients/admin-ui/src/types/api/models/Page_SystemWithMonitorKeys_.ts @@ -2,10 +2,10 @@ /* tslint:disable */ /* eslint-disable */ -import type { SystemResponse } from "./SystemResponse"; +import type { SystemWithMonitorKeys } from "./SystemWithMonitorKeys"; -export type Page_SystemResponse_ = { - items: Array; +export type Page_SystemWithMonitorKeys_ = { + items: Array; total: number | null; page: number | null; size: number | null; diff --git a/clients/admin-ui/src/types/api/models/SystemWithMonitorKeys.ts b/clients/admin-ui/src/types/api/models/SystemWithMonitorKeys.ts new file mode 100644 index 0000000000..d94e9b1f00 --- /dev/null +++ b/clients/admin-ui/src/types/api/models/SystemWithMonitorKeys.ts @@ -0,0 +1,193 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { ConnectionConfigurationResponse } from "./ConnectionConfigurationResponse"; +import type { Cookies } from "./Cookies"; +import type { DataFlow } from "./DataFlow"; +import type { DataResponsibilityTitle } from "./DataResponsibilityTitle"; +import type { LegalBasisForProfilingEnum } from "./LegalBasisForProfilingEnum"; +import type { PrivacyDeclarationResponse } from "./PrivacyDeclarationResponse"; +import type { SystemMetadata } from "./SystemMetadata"; +import type { UserResponse } from "./UserResponse"; + +/** + * Extended System response with the monitor config keys attached to the system via ConnectionConfig + */ +export type SystemWithMonitorKeys = { + /** + * A unique key used to identify this resource. + */ + fides_key: string; + /** + * Defines the Organization that this resource belongs to. + */ + organization_fides_key?: string; + tags?: Array | null; + /** + * Human-Readable name for this resource. + */ + name?: string | null; + /** + * A detailed description of what this resource is. + */ + description?: string | null; + /** + * An optional property to store any extra information for a resource. Data can be structured in any way: simple set of `key: value` pairs or deeply nested objects. + */ + meta?: null; + /** + * + * The SystemMetadata resource model. + * + * Object used to hold application specific metadata for a system + * + */ + fidesctl_meta?: SystemMetadata | null; + /** + * A required value to describe the type of system being modeled, examples include: Service, Application, Third Party, etc. + */ + system_type: string; + /** + * The resources to which the system sends data. + */ + egress?: Array | null; + /** + * The resources from which the system receives data. + */ + ingress?: Array | null; + /** + * Extension of base pydantic model to include DB `id` field in the response + */ + privacy_declarations: Array; + /** + * An optional value to identify the owning department or group of the system within your organization + */ + administrating_department?: string | null; + /** + * The unique identifier for the vendor that's associated with this system. + */ + vendor_id?: string | null; + /** + * If specified, the unique identifier for the vendor that was previously associated with this system. + */ + previous_vendor_id?: string | null; + /** + * The deleted date of the vendor that's associated with this system. + */ + vendor_deleted_date?: string | null; + /** + * Referenced Dataset fides keys used by the system. + */ + dataset_references?: Array; + /** + * This toggle indicates whether the system stores or processes personal data. + */ + processes_personal_data?: boolean; + /** + * This toggle indicates whether the system is exempt from privacy regulation if they do process personal data. + */ + exempt_from_privacy_regulations?: boolean; + /** + * The reason that the system is exempt from privacy regulation. + */ + reason_for_exemption?: string | null; + /** + * Whether the vendor uses data to profile a consumer in a way that has a legal effect. + */ + uses_profiling?: boolean; + /** + * The legal basis (or bases) for performing profiling that has a legal effect. + */ + legal_basis_for_profiling?: Array; + /** + * Whether this system transfers data to other countries or international organizations. + */ + does_international_transfers?: boolean; + /** + * The legal basis (or bases) under which the data is transferred. + */ + legal_basis_for_transfers?: Array; + /** + * Whether this system requires data protection impact assessments. + */ + requires_data_protection_assessments?: boolean; + /** + * Location where the DPAs or DIPAs can be found. + */ + dpa_location?: string | null; + /** + * The optional status of a Data Protection Impact Assessment + */ + dpa_progress?: string | null; + /** + * A URL that points to the system's publicly accessible privacy policy. + */ + privacy_policy?: string | null; + /** + * The legal name for the business represented by the system. + */ + legal_name?: string | null; + /** + * The legal address for the business represented by the system. + */ + legal_address?: string | null; + /** + * + * The model defining the responsibility or role over + * the system that processes personal data. + * + * Used to identify whether the organization is a + * Controller, Processor, or Sub-Processor of the data + * + */ + responsibility?: Array; + /** + * The official privacy contact address or DPO. + */ + dpo?: string | null; + /** + * The party or parties that share the responsibility for processing personal data. + */ + joint_controller_info?: string | null; + /** + * The data security practices employed by this system. + */ + data_security_practices?: string | null; + /** + * The maximum storage duration, in seconds, for cookies used by this system. + */ + cookie_max_age_seconds?: number | null; + /** + * Whether this system uses cookie storage. + */ + uses_cookies?: boolean; + /** + * Whether the system's cookies are refreshed after being initially set. + */ + cookie_refresh?: boolean; + /** + * Whether the system uses non-cookie methods of storage or accessing information stored on a user's device. + */ + uses_non_cookie_access?: boolean; + /** + * A URL that points to the system's publicly accessible legitimate interest disclosure. + */ + legitimate_interest_disclosure_url?: string | null; + /** + * System-level cookies unassociated with a data use to deliver services and functionality + */ + cookies?: Array | null; + created_at: string; + /** + * + * Describes the returned schema for a ConnectionConfiguration. + * + */ + connection_configs: ConnectionConfigurationResponse | null; + /** + * System managers of the current system + */ + data_stewards: Array | null; + monitor_config_keys?: Array; +}; From 81789af87a8d99d99e0bdfd03f27707d3aa834b9 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 19 Dec 2024 03:17:39 -0600 Subject: [PATCH 20/24] result & status cells --- .../CatalogResourceActionsCell.tsx | 73 +++++++++++++++++++ .../data-catalog/CatalogStatusCell.tsx | 19 +++++ .../data-catalog/data-catalog.slice.ts | 14 ++-- .../datasets/useCatalogDatasetColumns.tsx | 17 +++-- .../projects/CatalogProjectsTable.tsx | 2 +- .../CatalogResourcesTable.tsx | 16 ++-- .../systems/CatalogSystemsTable.tsx | 17 +++-- .../src/features/data-catalog/utils.ts | 50 +++++++++++++ .../pages/data-catalog/[systemId]/index.tsx | 6 +- 9 files changed, 186 insertions(+), 28 deletions(-) create mode 100644 clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx create mode 100644 clients/admin-ui/src/features/data-catalog/CatalogStatusCell.tsx create mode 100644 clients/admin-ui/src/features/data-catalog/utils.ts diff --git a/clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx b/clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx new file mode 100644 index 0000000000..1da72e1c71 --- /dev/null +++ b/clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx @@ -0,0 +1,73 @@ +import { AntButton as Button } from "fidesui"; + +import { useAlert } from "~/features/common/hooks"; +import { + CatalogResourceStatus, + getCatalogResourceStatus, +} from "~/features/data-catalog/utils"; +import { + useConfirmResourceMutation, + usePromoteResourceMutation, +} from "~/features/data-discovery-and-detection/discovery-detection.slice"; +import { StagedResourceAPIResponse } from "~/types/api"; + +const CatalogResourceActionsCell = ({ + resource, +}: { + resource: StagedResourceAPIResponse; +}) => { + const { successAlert } = useAlert(); + const status = getCatalogResourceStatus(resource); + const [confirmResource, { isLoading: classifyIsLoading }] = + useConfirmResourceMutation(); + const [promoteResource, { isLoading: approveIsLoading }] = + usePromoteResourceMutation(); + + const classifyResource = async () => { + await confirmResource({ + staged_resource_urn: resource.urn, + monitor_config_id: resource.monitor_config_id!, + unmute_children: true, + classify_monitored_resources: true, + }); + successAlert( + `Started classification on ${resource.name ?? "this resource"}`, + ); + }; + + const approveResource = async () => { + await promoteResource({ + staged_resource_urn: resource.urn, + }); + successAlert(`Approved ${resource.name ?? "this resource"}`); + }; + + const anyActionIsLoading = classifyIsLoading || approveIsLoading; + + return ( + <> + {status === CatalogResourceStatus.ATTENTION_REQUIRED && ( + + )} + {status === CatalogResourceStatus.IN_REVIEW && ( + + )} + + ); +}; + +export default CatalogResourceActionsCell; diff --git a/clients/admin-ui/src/features/data-catalog/CatalogStatusCell.tsx b/clients/admin-ui/src/features/data-catalog/CatalogStatusCell.tsx new file mode 100644 index 0000000000..624e4197a8 --- /dev/null +++ b/clients/admin-ui/src/features/data-catalog/CatalogStatusCell.tsx @@ -0,0 +1,19 @@ +import ResultStatusBadge from "~/features/common/ResultStatusBadge"; +import { CatalogResourceStatus } from "~/features/data-catalog/utils"; + +const STATUS_COLOR_MAP: Record = { + [CatalogResourceStatus.ATTENTION_REQUIRED]: "red", + [CatalogResourceStatus.APPROVED]: "green", + [CatalogResourceStatus.IN_REVIEW]: "yellow", + [CatalogResourceStatus.CLASSIFYING]: "blue", +}; + +const CatalogStatusCell = ({ status }: { status: CatalogResourceStatus }) => { + return ( + + {status} + + ); +}; + +export default CatalogStatusCell; diff --git a/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts index a79c820448..191bef3ede 100644 --- a/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts +++ b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts @@ -3,7 +3,7 @@ import { createSlice } from "@reduxjs/toolkit"; import { baseApi } from "~/features/common/api.slice"; import { Page_StagedResourceAPIResponse_, - Page_SystemResponse_, + Page_SystemWithMonitorKeys_, } from "~/types/api"; import { PaginationQueryParams } from "~/types/common/PaginationQueryParams"; @@ -24,7 +24,7 @@ interface CatalogResourceQueryParams extends PaginationQueryParams { const dataCatalogApi = baseApi.injectEndpoints({ endpoints: (build) => ({ getCatalogSystems: build.query< - Page_SystemResponse_, + Page_SystemWithMonitorKeys_, CatalogSystemQueryParams >({ query: (params) => ({ @@ -38,10 +38,9 @@ const dataCatalogApi = baseApi.injectEndpoints({ Page_StagedResourceAPIResponse_, CatalogResourceQueryParams >({ - query: ({ monitor_config_ids, ...params }) => ({ - method: "POST", + query: ({ ...params }) => ({ + method: "GET", url: `/plus/data-catalog/project`, - body: monitor_config_ids, params, }), providesTags: ["Discovery Monitor Results"], @@ -50,10 +49,9 @@ const dataCatalogApi = baseApi.injectEndpoints({ Page_StagedResourceAPIResponse_, CatalogResourceQueryParams >({ - query: ({ monitor_config_ids, ...params }) => ({ - method: "POST", + query: ({ ...params }) => ({ + method: "GET", url: `/plus/data-catalog/dataset`, - body: monitor_config_ids, params, }), providesTags: ["Discovery Monitor Results"], diff --git a/clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx b/clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx index 22ce89cbb2..5e0cd13d95 100644 --- a/clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx +++ b/clients/admin-ui/src/features/data-catalog/datasets/useCatalogDatasetColumns.tsx @@ -1,7 +1,6 @@ /* eslint-disable react/no-unstable-nested-components */ import { ColumnDef, createColumnHelper } from "@tanstack/react-table"; -import { AntButton } from "fidesui"; import { useMemo } from "react"; import { @@ -9,6 +8,9 @@ import { IndeterminateCheckboxCell, } from "~/features/common/table/v2"; import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; +import CatalogResourceActionsCell from "~/features/data-catalog/CatalogResourceActionsCell"; +import CatalogStatusCell from "~/features/data-catalog/CatalogStatusCell"; +import { getCatalogResourceStatus } from "~/features/data-catalog/utils"; import { StagedResourceAPIResponse } from "~/types/api"; const columnHelper = createColumnHelper(); @@ -50,7 +52,9 @@ const useCatalogDatasetColumns = () => { }), columnHelper.display({ id: "status", - cell: () => , + cell: ({ row }) => ( + + ), header: "Status", }), columnHelper.accessor((row) => row.description, { @@ -65,12 +69,13 @@ const useCatalogDatasetColumns = () => { }), columnHelper.display({ id: "actions", - cell: () => ( - - Actions - + cell: ({ row }) => ( + ), header: "Actions", + meta: { + disableRowClick: true, + }, }), ], [], diff --git a/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx index 9e738d0677..a6acd2462b 100644 --- a/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx @@ -103,7 +103,7 @@ const CatalogProjectsTable = ({ } = useGetCatalogProjectsQuery({ page: pageIndex, size: pageSize, - monitor_config_ids: ["bq-monitor", "bq-monitor-2"], + monitor_config_ids: monitorConfigIds ?? ["bq-monitor"], }); const router = useRouter(); diff --git a/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx b/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx index 4421a8d3b0..7a1462a10a 100644 --- a/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx @@ -22,6 +22,9 @@ import { useServerSidePagination, } from "~/features/common/table/v2"; import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; +import CatalogResourceActionsCell from "~/features/data-catalog/CatalogResourceActionsCell"; +import CatalogStatusCell from "~/features/data-catalog/CatalogStatusCell"; +import { getCatalogResourceStatus } from "~/features/data-catalog/utils"; import { useGetMonitorResultsQuery } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; import { StagedResourceAPIResponse, SystemResponse } from "~/types/api"; @@ -140,7 +143,9 @@ const CatalogResourcesTable = ({ }), columnHelper.display({ id: "status", - cell: () => , + cell: ({ row }) => ( + + ), header: "Status", }), columnHelper.accessor((row) => row.description, { @@ -155,12 +160,13 @@ const CatalogResourcesTable = ({ }), columnHelper.display({ id: "actions", - cell: () => ( - - Actions - + cell: ({ row }) => ( + ), header: "Actions", + meta: { + disableRowClick: true, + }, }), ], [], diff --git a/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx b/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx index 95e555c51d..046e80b8c2 100644 --- a/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx @@ -34,12 +34,13 @@ import { useServerSidePagination, } from "~/features/common/table/v2"; import { IndeterminateCheckboxCell } from "~/features/common/table/v2/cells"; +import { getQueryParamsFromArray } from "~/features/common/utils"; import { useGetCatalogSystemsQuery } from "~/features/data-catalog/data-catalog.slice"; import SystemActionsCell from "~/features/data-catalog/systems/SystemActionCell"; import { useLazyGetAvailableDatabasesByConnectionQuery } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; -import { SystemResponse } from "~/types/api"; +import { SystemWithMonitorKeys } from "~/types/api"; const EMPTY_RESPONSE = { items: [], @@ -49,7 +50,7 @@ const EMPTY_RESPONSE = { pages: 1, }; -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); const EmptyTableNotice = () => ( { setTotalPages(totalPages); }, [totalPages, setTotalPages]); - const handleRowClicked = async (row: SystemResponse) => { + const handleRowClicked = async (row: SystemWithMonitorKeys) => { // if there are projects, go to project view; otherwise go to datasets view const projectsResponse = await getProjects({ connection_config_key: row.connection_configs!.key, @@ -125,12 +126,16 @@ const SystemsTable = () => { }); const hasProjects = !!projectsResponse?.data?.total; + const queryString = getQueryParamsFromArray( + row.monitor_config_keys ?? [], + "monitor_config_ids", + ); - const url = `${DATA_CATALOG_ROUTE}/${row.fides_key}${hasProjects ? "/projects" : ""}`; + const url = `${DATA_CATALOG_ROUTE}/${row.fides_key}${hasProjects ? "/projects" : ""}?${queryString}`; router.push(url); }; - const columns: ColumnDef[] = useMemo( + const columns: ColumnDef[] = useMemo( () => [ columnHelper.display({ id: "select", @@ -225,7 +230,7 @@ const SystemsTable = () => { [router], ); - const tableInstance = useReactTable({ + const tableInstance = useReactTable({ getCoreRowModel: getCoreRowModel(), getGroupedRowModel: getGroupedRowModel(), getExpandedRowModel: getExpandedRowModel(), diff --git a/clients/admin-ui/src/features/data-catalog/utils.ts b/clients/admin-ui/src/features/data-catalog/utils.ts new file mode 100644 index 0000000000..f1b2b3fcf5 --- /dev/null +++ b/clients/admin-ui/src/features/data-catalog/utils.ts @@ -0,0 +1,50 @@ +import { DiffStatus, StagedResourceAPIResponse } from "~/types/api"; + +export enum CatalogResourceStatus { + ATTENTION_REQUIRED = "Attention required", + IN_REVIEW = "In review", + APPROVED = "Approved", + CLASSIFYING = "Classifying", +} + +export const getCatalogResourceStatus = ( + resource: StagedResourceAPIResponse, +) => { + const resourceSchemaChanged = + resource.diff_status === DiffStatus.ADDITION || + resource.diff_status === DiffStatus.REMOVAL; + const resourceChildrenSchemaChanged = + resource.child_diff_statuses && + (resource.child_diff_statuses[DiffStatus.ADDITION] || + resource.child_diff_statuses[DiffStatus.REMOVAL]); + + if (resourceSchemaChanged || resourceChildrenSchemaChanged) { + return CatalogResourceStatus.ATTENTION_REQUIRED; + } + + const classificationInProgress = + resource.diff_status === DiffStatus.CLASSIFICATION_QUEUED || + resource.diff_status === DiffStatus.CLASSIFYING; + const childClassificationInProgress = + resource.child_diff_statuses && + (resource.child_diff_statuses[DiffStatus.CLASSIFICATION_QUEUED] || + resource.child_diff_statuses[DiffStatus.CLASSIFYING]); + + if (classificationInProgress || childClassificationInProgress) { + return CatalogResourceStatus.CLASSIFYING; + } + + const classificationChanged = + resource.diff_status === DiffStatus.CLASSIFICATION_ADDITION || + resource.diff_status === DiffStatus.CLASSIFICATION_UPDATE; + const childClassificationChanged = + resource.child_diff_statuses && + (resource.child_diff_statuses[DiffStatus.CLASSIFICATION_ADDITION] || + resource.child_diff_statuses[DiffStatus.CLASSIFICATION_UPDATE]); + + if (classificationChanged || childClassificationChanged) { + return CatalogResourceStatus.IN_REVIEW; + } + + return CatalogResourceStatus.APPROVED; +}; diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx index 1b91ab8ac9..8089970ecf 100644 --- a/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx @@ -35,7 +35,8 @@ const EMPTY_RESPONSE = { const CatalogDatasetViewNoProjects = () => { const { query, push } = useRouter(); - const systemKey = query["system-id"] as string; + const systemKey = query.systemId as string; + const monitorConfigKeys = query.monitor_config_ids as string[]; const { PAGE_SIZES, @@ -58,7 +59,8 @@ const CatalogDatasetViewNoProjects = () => { } = useGetCatalogDatasetsQuery({ page: pageIndex, size: pageSize, - monitor_config_ids: ["dynamo-monitor"], + // TODO: get monitor ids from system + monitor_config_ids: monitorConfigKeys ?? ["dynamo-monitor"], }); const { From da3c337b18c0b5f60b8a8f5a7b7bab2113a72ee3 Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 19 Dec 2024 18:35:46 -0600 Subject: [PATCH 21/24] breadcrumb update --- .../data-catalog/data-catalog.slice.ts | 2 +- .../projects/CatalogProjectsTable.tsx | 6 +- .../CatalogResourcesTable.tsx | 12 ++- .../staged-resources/parseUrnToBreadcrumbs.ts | 20 +++++ .../systems/CatalogSystemsTable.tsx | 45 +++-------- .../new-dataset-lifecycle/EditDataUseCell.tsx | 6 +- .../data-catalog/[systemId]/[resourceUrn].tsx | 17 ++++- .../pages/data-catalog/[systemId]/index.tsx | 75 ++++++++++--------- .../[systemId]/projects/[projectId].tsx | 72 +++++++++++------- .../[systemId]/projects/index.tsx | 9 ++- .../admin-ui/src/pages/data-catalog/index.tsx | 5 +- clients/admin-ui/src/pages/systems/index.tsx | 10 --- 12 files changed, 162 insertions(+), 117 deletions(-) create mode 100644 clients/admin-ui/src/features/data-catalog/staged-resources/parseUrnToBreadcrumbs.ts diff --git a/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts index 191bef3ede..27d8bd9bcd 100644 --- a/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts +++ b/clients/admin-ui/src/features/data-catalog/data-catalog.slice.ts @@ -32,7 +32,7 @@ const dataCatalogApi = baseApi.injectEndpoints({ url: `/plus/data-catalog/system`, params, }), - providesTags: ["Catalog Systems"], + providesTags: ["Catalog Systems", "System"], }), getCatalogProjects: build.query< Page_StagedResourceAPIResponse_, diff --git a/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx index a6acd2462b..4d04e19f52 100644 --- a/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/projects/CatalogProjectsTable.tsx @@ -33,8 +33,10 @@ import { useServerSidePagination, } from "~/features/common/table/v2"; import { RelativeTimestampCell } from "~/features/common/table/v2/cells"; +import CatalogStatusCell from "~/features/data-catalog/CatalogStatusCell"; import { useGetCatalogProjectsQuery } from "~/features/data-catalog/data-catalog.slice"; import SystemActionsCell from "~/features/data-catalog/systems/SystemActionCell"; +import { getCatalogResourceStatus } from "~/features/data-catalog/utils"; import { useMuteResourcesMutation } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import IconLegendTooltip from "~/features/data-discovery-and-detection/IndicatorLegend"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; @@ -156,7 +158,9 @@ const CatalogProjectsTable = ({ }), columnHelper.display({ id: "status", - cell: () => , + cell: ({ row }) => ( + + ), header: "Status", }), columnHelper.accessor((row) => row.monitor_config_id, { diff --git a/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx b/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx index 7a1462a10a..de1ad31db4 100644 --- a/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/staged-resources/CatalogResourcesTable.tsx @@ -27,6 +27,7 @@ import CatalogStatusCell from "~/features/data-catalog/CatalogStatusCell"; import { getCatalogResourceStatus } from "~/features/data-catalog/utils"; import { useGetMonitorResultsQuery } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import { SearchInput } from "~/features/data-discovery-and-detection/SearchInput"; +import resourceHasChildren from "~/features/data-discovery-and-detection/utils/resourceHasChildren"; import { StagedResourceAPIResponse, SystemResponse } from "~/types/api"; const EMPTY_RESPONSE = { @@ -84,7 +85,6 @@ const CatalogResourcesTable = ({ resetPageIndexToDefault, } = useServerSidePagination(); - // do we need this in this context? useEffect(() => { resetPageIndexToDefault(); }, [resourceUrn, resetPageIndexToDefault]); @@ -138,7 +138,14 @@ const CatalogResourcesTable = ({ }), columnHelper.accessor((row) => row.name, { id: "name", - cell: (props) => , + cell: (props) => ( + + ), header: "Name", }), columnHelper.display({ @@ -199,6 +206,7 @@ const CatalogResourcesTable = ({ } + getRowIsClickable={(row) => resourceHasChildren(row)} onRowClick={(row) => router.push(`${DATA_CATALOG_ROUTE}/${system.fides_key}/${row.urn}`) } diff --git a/clients/admin-ui/src/features/data-catalog/staged-resources/parseUrnToBreadcrumbs.ts b/clients/admin-ui/src/features/data-catalog/staged-resources/parseUrnToBreadcrumbs.ts new file mode 100644 index 0000000000..538a65d84d --- /dev/null +++ b/clients/admin-ui/src/features/data-catalog/staged-resources/parseUrnToBreadcrumbs.ts @@ -0,0 +1,20 @@ +import { NextBreadcrumbProps } from "~/features/common/nav/v2/NextBreadcrumb"; + +const parseUrnToBreadcrumbs = ( + urn: string, + urlPrefix: string, +): NextBreadcrumbProps["items"] => { + const urnParts = urn.split("."); + const breadcrumbItems: NextBreadcrumbProps["items"] = []; + urnParts.reduce((prev, current) => { + const next = `${prev}.${current}`; + breadcrumbItems.push({ + title: current, + href: `${urlPrefix}/${next}`, + }); + return next; + }); + return breadcrumbItems; +}; + +export default parseUrnToBreadcrumbs; diff --git a/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx b/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx index 046e80b8c2..b78eca9d0a 100644 --- a/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx +++ b/clients/admin-ui/src/features/data-catalog/systems/CatalogSystemsTable.tsx @@ -104,7 +104,6 @@ const SystemsTable = () => { show_hidden: false, }); - // const [getProjects] = useLazyGetCatalogProjectsQuery(); const [getProjects] = useLazyGetAvailableDatabasesByConnectionQuery(); const { @@ -165,48 +164,24 @@ const SystemsTable = () => { }), columnHelper.accessor((row) => row.name, { id: "name", - cell: ({ getValue }) => ( - + cell: ({ getValue, row }) => ( + ), header: (props) => , }), - // TODO - columnHelper.display({ - id: "status", - cell: () => , - header: (props) => , - }), - // TODO // columnHelper.display({ - // id: "changes", - // cell: () => , - // header: (props) => , - // maxSize: 100, - // }), - // TODO - columnHelper.display({ - id: "last-updated", - cell: () => , - header: (props) => ( - - ), - meta: { - cellProps: { - borderRight: "none", - }, - }, - }), - // TODO - // columnHelper.accessor((row) => row.privacy_declarations, { // id: "data-uses", // cell: ({ row }) => , // header: (props) => , // meta: { - // cellProps: { - // borderRight: "none", - // }, // disableRowClick: true, // }, + // minSize: 280, // }), columnHelper.display({ id: "actions", @@ -267,7 +242,7 @@ const SystemsTable = () => { - + {/* { Add data use - + */} { const { getDataUseDisplayName } = useTaxonomies(); const { isOpen, onOpen, onClose } = useDisclosure(); - const handleOpenEditForm = async ( - declaration: PrivacyDeclarationResponse, - ) => { - await setDeclarationToEdit(declaration); + const handleOpenEditForm = (declaration: PrivacyDeclarationResponse) => { + setDeclarationToEdit(declaration); onOpen(); }; diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx index c827f79302..a82d38aa17 100644 --- a/clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/[resourceUrn].tsx @@ -3,8 +3,10 @@ import { useRouter } from "next/router"; import FidesSpinner from "~/features/common/FidesSpinner"; import Layout from "~/features/common/Layout"; +import { DATA_CATALOG_ROUTE } from "~/features/common/nav/v2/routes"; import PageHeader from "~/features/common/PageHeader"; import CatalogResourcesTable from "~/features/data-catalog/staged-resources/CatalogResourcesTable"; +import parseUrnToBreadcrumbs from "~/features/data-catalog/staged-resources/parseUrnToBreadcrumbs"; import { useGetSystemByFidesKeyQuery } from "~/features/system"; const CatalogResourceView: NextPage = () => { @@ -13,6 +15,10 @@ const CatalogResourceView: NextPage = () => { const resourceUrn = query.resourceUrn as string; const { data: system, isLoading } = useGetSystemByFidesKeyQuery(systemId); + const resourceBreadcrumbs = + parseUrnToBreadcrumbs(resourceUrn, `${DATA_CATALOG_ROUTE}/${systemId}`) ?? + []; + if (isLoading) { return ; } @@ -20,8 +26,15 @@ const CatalogResourceView: NextPage = () => { return ( diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx index 8089970ecf..099e11d1cc 100644 --- a/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/index.tsx @@ -4,7 +4,7 @@ import { getGroupedRowModel, useReactTable, } from "@tanstack/react-table"; -import { AntButton, Flex } from "fidesui"; +import { AntButton } from "fidesui"; import { useRouter } from "next/router"; import { useEffect, useMemo } from "react"; @@ -21,10 +21,9 @@ import { import { useGetCatalogDatasetsQuery } from "~/features/data-catalog/data-catalog.slice"; import EmptyCatalogTableNotice from "~/features/data-catalog/datasets/EmptyCatalogTableNotice"; import useCatalogDatasetColumns from "~/features/data-catalog/datasets/useCatalogDatasetColumns"; +import { useGetSystemByFidesKeyQuery } from "~/features/system"; import { StagedResourceAPIResponse } from "~/types/api"; -// REPLACE: dataset view, system has no projects - const EMPTY_RESPONSE = { items: [], total: 0, @@ -38,6 +37,9 @@ const CatalogDatasetViewNoProjects = () => { const systemKey = query.systemId as string; const monitorConfigKeys = query.monitor_config_ids as string[]; + const { data: system, isLoading: systemIsLoading } = + useGetSystemByFidesKeyQuery(systemKey); + const { PAGE_SIZES, pageSize, @@ -59,8 +61,7 @@ const CatalogDatasetViewNoProjects = () => { } = useGetCatalogDatasetsQuery({ page: pageIndex, size: pageSize, - // TODO: get monitor ids from system - monitor_config_ids: monitorConfigKeys ?? ["dynamo-monitor"], + monitor_config_ids: monitorConfigKeys, }); const { @@ -85,39 +86,45 @@ const CatalogDatasetViewNoProjects = () => { columnResizeMode: "onChange", }); - if (isLoading || isFetching) { - return ; - } + const showContent = !isLoading && !systemIsLoading && !isFetching; return ( - - - - {/* - - */} - - Actions - - } - onRowClick={(row) => - push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) - } - /> - + {!showContent && } + {showContent && ( + <> + + Actions + + } + onRowClick={(row) => + push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) + } + /> + + + )} ); }; diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx index 0c578f1a0e..185f0493bd 100644 --- a/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/[projectId].tsx @@ -21,6 +21,7 @@ import { import EmptyCatalogTableNotice from "~/features/data-catalog/datasets/EmptyCatalogTableNotice"; import useCatalogDatasetColumns from "~/features/data-catalog/datasets/useCatalogDatasetColumns"; import { useGetMonitorResultsQuery } from "~/features/data-discovery-and-detection/discovery-detection.slice"; +import { useGetSystemByFidesKeyQuery } from "~/features/system"; import { StagedResourceAPIResponse } from "~/types/api"; const EMPTY_RESPONSE = { @@ -38,6 +39,9 @@ const CatalogDatasetView = () => { const systemKey = query.systemId as string; const projectId = query.projectId as string; + const { data: system, isLoading: systemIsLoading } = + useGetSystemByFidesKeyQuery(systemKey); + const { PAGE_SIZES, pageSize, @@ -83,40 +87,56 @@ const CatalogDatasetView = () => { data, columnResizeMode: "onChange", }); + const showContent = !isLoading && !systemIsLoading && !isFetching; - if (isLoading || isFetching) { + if (isLoading || isFetching || systemIsLoading) { return ; } return ( - - - - {/* + + {!showContent && } + {showContent && ( + <> + + + {/* */} - - Actions - - } - onRowClick={(row) => - push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) - } - /> - + + Actions + + } + onRowClick={(row) => + push(`${DATA_CATALOG_ROUTE}/${systemKey}/${row.urn}`) + } + /> + + + )} ); }; diff --git a/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/index.tsx b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/index.tsx index 52b635f83b..87fc29e80b 100644 --- a/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/index.tsx +++ b/clients/admin-ui/src/pages/data-catalog/[systemId]/projects/index.tsx @@ -2,6 +2,7 @@ import { useRouter } from "next/router"; import FidesSpinner from "~/features/common/FidesSpinner"; import Layout from "~/features/common/Layout"; +import { DATA_CATALOG_ROUTE } from "~/features/common/nav/v2/routes"; import PageHeader from "~/features/common/PageHeader"; import CatalogProjectsTable from "~/features/data-catalog/projects/CatalogProjectsTable"; import { useGetSystemByFidesKeyQuery } from "~/features/system"; @@ -18,7 +19,13 @@ const CatalogProjectView = () => { return ( - + { return ( - + ); diff --git a/clients/admin-ui/src/pages/systems/index.tsx b/clients/admin-ui/src/pages/systems/index.tsx index b60924ce25..aad2fcb07a 100644 --- a/clients/admin-ui/src/pages/systems/index.tsx +++ b/clients/admin-ui/src/pages/systems/index.tsx @@ -43,7 +43,6 @@ import { useServerSidePagination, } from "~/features/common/table/v2"; import { errorToastParams, successToastParams } from "~/features/common/toast"; -import EditDataUseCell from "~/features/data-discovery-and-detection/new-dataset-lifecycle/EditDataUseCell"; import { setActiveSystem, useDeleteSystemMutation, @@ -167,15 +166,6 @@ const Systems: NextPage = () => { showHeaderMenu: true, }, }), - columnHelper.display({ - id: "data-uses", - cell: ({ row }) => , - header: (props) => , - meta: { - disableRowClick: true, - }, - minSize: 280, - }), columnHelper.accessor((row) => row.administrating_department, { id: "department", cell: (props) => , From 92f1a40196887208c4a3a34f942068a78585271f Mon Sep 17 00:00:00 2001 From: Jeremy Pople Date: Thu, 19 Dec 2024 22:32:58 -0600 Subject: [PATCH 22/24] resource actions & columns --- .../CatalogResourceActionsCell.tsx | 47 ++++++- .../data-catalog/CatalogResourceNameCell.tsx | 19 +++ ...tusCell.tsx => CatalogStatusBadgeCell.tsx} | 8 +- .../data-catalog/data-catalog.slice.ts | 3 +- .../datasets/useCatalogDatasetColumns.tsx | 11 +- .../projects/CatalogProjectsTable.tsx | 11 +- .../projects/ProjectActionsCell.tsx | 0 .../CatalogResourcesTable.tsx | 105 ++++----------- .../systems/CatalogSystemsTable.tsx | 2 +- .../useCatalogResourceColumns.tsx | 122 ++++++++++++++++++ .../hooks/useDiscoveryResultColumns.tsx | 4 +- .../tables/cells/EditCategoryCell.tsx | 4 +- 12 files changed, 235 insertions(+), 101 deletions(-) create mode 100644 clients/admin-ui/src/features/data-catalog/CatalogResourceNameCell.tsx rename clients/admin-ui/src/features/data-catalog/{CatalogStatusCell.tsx => CatalogStatusBadgeCell.tsx} (80%) delete mode 100644 clients/admin-ui/src/features/data-catalog/projects/ProjectActionsCell.tsx create mode 100644 clients/admin-ui/src/features/data-catalog/useCatalogResourceColumns.tsx diff --git a/clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx b/clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx index 1da72e1c71..c37b7ab26b 100644 --- a/clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx +++ b/clients/admin-ui/src/features/data-catalog/CatalogResourceActionsCell.tsx @@ -1,4 +1,14 @@ -import { AntButton as Button } from "fidesui"; +import { + AntButton, + AntButton as Button, + Flex, + Menu, + MenuButton, + MenuItem, + MenuList, + MoreIcon, + Spacer, +} from "fidesui"; import { useAlert } from "~/features/common/hooks"; import { @@ -7,6 +17,7 @@ import { } from "~/features/data-catalog/utils"; import { useConfirmResourceMutation, + useMuteResourceMutation, usePromoteResourceMutation, } from "~/features/data-discovery-and-detection/discovery-detection.slice"; import { StagedResourceAPIResponse } from "~/types/api"; @@ -22,6 +33,8 @@ const CatalogResourceActionsCell = ({ useConfirmResourceMutation(); const [promoteResource, { isLoading: approveIsLoading }] = usePromoteResourceMutation(); + const [muteResource, { isLoading: muteIsLoading }] = + useMuteResourceMutation(); const classifyResource = async () => { await confirmResource({ @@ -39,13 +52,21 @@ const CatalogResourceActionsCell = ({ await promoteResource({ staged_resource_urn: resource.urn, }); - successAlert(`Approved ${resource.name ?? "this resource"}`); + successAlert(`Approved ${resource.name ?? " resource"}`); + }; + + const hideResource = async () => { + await muteResource({ + staged_resource_urn: resource.urn, + }); + successAlert(`Hid ${resource.name ?? " resource"}`); }; - const anyActionIsLoading = classifyIsLoading || approveIsLoading; + const anyActionIsLoading = + classifyIsLoading || approveIsLoading || muteIsLoading; return ( - <> + {status === CatalogResourceStatus.ATTENTION_REQUIRED && (