diff --git a/js_modules/dagster-ui/packages/ui-core/client.json b/js_modules/dagster-ui/packages/ui-core/client.json index 3fd3a46d63b50..53ebcdce29156 100644 --- a/js_modules/dagster-ui/packages/ui-core/client.json +++ b/js_modules/dagster-ui/packages/ui-core/client.json @@ -169,15 +169,11 @@ "TypeListContainerQuery": "c92c874af7b1e7be221281fa265743a9c426f909ffc7500f302540ef9a6cf8f2", "GraphExplorerRootQuery": "04d1a414b72b466ded40652c5e4110ef46fa905557cee0d737fc20cede490b85", "SingleNonSDAAssetQuery": "a7ef9ca5f114adee47bc5361112b16a2f00b70ae06eb6ab65a9f312bbae7625c", - "SingleGraphQuery": "f1d47e63982c085807a7013c1581574e1284fe12344ea9752349c57aff0e0b2b", "SingleJobQuery": "5ff8f070e59507f5369f1a19abb9a72cfa12439ab04a08dc340866885f6e4702", "SingleScheduleQuery": "508a47e32ce04ba5be52c66cd592b74147bf98ec85b9f5d0e4db45172bd9a897", "SingleSensorQuery": "dbda5ba47d4ba10f8c527c9a7cd45fba0811276441a17a8ac6f173ed588f025b", "WorkspaceAssetsQuery": "53e4bb05e5c1194cc83e0910cfe7c482d75344c10eabf184be26da29d771c236", "LocationWorkspaceQuery": "72685489c28204d48c39ed5d85231756f5637febc8b19e0a0b4967a2d982191e", "CodeLocationStatusQuery": "f92885e073b8b4b9bd588bf248df7b06025e2a1f6e74c082233ac7863f5eef8e", - "WorkspaceGraphsQuery": "ccbef870f327b56beb0d781a476c8afbbc22ff2621181c8576861daaf7667ecf", - "WorkspaceJobsQuery": "637f616d6d4eba194cf80bbb292f579f864d3b16aeb0b40cdf108d8100ba9b1c", - "WorkspaceSchedulesQuery": "213cb3c1a2ffd6d9a6fbe20cd58eab746d53b6ab04d7d498c3c4f1f9d4a850d3", - "WorkspaceSensorsQuery": "2c8ef3f0111c4524514ff762d542810e95d429cc421e60893a3a4bbb39bb9849" + "WorkspaceGraphsQuery": "ccbef870f327b56beb0d781a476c8afbbc22ff2621181c8576861daaf7667ecf" } diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/__stories__/AssetTables.stories.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/__stories__/AssetTables.stories.tsx index ebe087b54a9f5..27c704e6c3e6b 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/__stories__/AssetTables.stories.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/__stories__/AssetTables.stories.tsx @@ -1,14 +1,10 @@ import {MockedProvider} from '@apollo/client/testing'; -import {Box} from '@dagster-io/ui-components'; import {StorybookProvider} from '../../testing/StorybookProvider'; -import {VirtualizedRepoAssetTable} from '../../workspace/VirtualizedRepoAssetTable'; -import {RepoAssetTableFragment} from '../../workspace/types/WorkspaceAssetsQuery.types'; import {AssetsCatalogTable} from '../AssetsCatalogTable'; import { AssetCatalogGroupTableMock, AssetCatalogTableMock, - AssetCatalogTableMockAssets, SingleAssetQueryLastRunFailed, SingleAssetQueryMaterializedStaleAndLate, SingleAssetQueryMaterializedWithLatestRun, @@ -49,22 +45,3 @@ export const GlobalCatalogWithPrefix = () => { ); }; - -export const RepoAssets = () => { - const assets: RepoAssetTableFragment[] = AssetCatalogTableMockAssets.filter( - (a) => !!a.definition, - ).map((a) => ({...a.definition!, assetKey: a.key})); - - return ( - - - - - - - - ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesRoot.tsx index d5f1114c19614..94b2f1835a5cb 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesRoot.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesRoot.tsx @@ -16,8 +16,8 @@ import {useTrackPageView} from '../app/analytics'; import {useDocumentTitle} from '../hooks/useDocumentTitle'; import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; import {RepoFilterButton} from '../instance/RepoFilterButton'; -import {RESOURCE_ENTRY_FRAGMENT} from '../resources/WorkspaceResourcesRoot'; -import {ResourceEntryFragment} from '../resources/types/WorkspaceResourcesRoot.types'; +import {RESOURCE_ENTRY_FRAGMENT} from '../resources/WorkspaceResourcesQuery'; +import {ResourceEntryFragment} from '../resources/types/WorkspaceResourcesQuery.types'; import {SearchInputSpinner} from '../ui/SearchInputSpinner'; import {WorkspaceContext} from '../workspace/WorkspaceContext/WorkspaceContext'; import {WorkspaceLocationNodeFragment} from '../workspace/WorkspaceContext/types/WorkspaceQueries.types'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesTable.tsx index 41c957f99ba25..d9e0de0a0971d 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesTable.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/overview/OverviewResourcesTable.tsx @@ -7,7 +7,7 @@ import { VirtualizedResourceHeader, VirtualizedResourceRow, } from '../resources/VirtualizedResourceRow'; -import {ResourceEntryFragment} from '../resources/types/WorkspaceResourcesRoot.types'; +import {ResourceEntryFragment} from '../resources/types/WorkspaceResourcesQuery.types'; import {Container, Inner} from '../ui/VirtualizedTable'; import {findDuplicateRepoNames} from '../ui/findDuplicateRepoNames'; import {useRepoExpansionState} from '../ui/useRepoExpansionState'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/resources/VirtualizedResourceRow.tsx b/js_modules/dagster-ui/packages/ui-core/src/resources/VirtualizedResourceRow.tsx index 526df1918a041..b948f8ca5bcca 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/resources/VirtualizedResourceRow.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/resources/VirtualizedResourceRow.tsx @@ -3,10 +3,10 @@ import {Link} from 'react-router-dom'; import styled from 'styled-components'; import {succinctType} from './ResourceRoot'; -import {ResourceEntryFragment} from './types/WorkspaceResourcesRoot.types'; import {HeaderCell, HeaderRow, Row, RowCell} from '../ui/VirtualizedTable'; import {RepoAddress} from '../workspace/types'; import {workspacePathFromAddress} from '../workspace/workspacePath'; +import {ResourceEntryFragment} from './types/WorkspaceResourcesQuery.types'; const TEMPLATE_COLUMNS = '1.5fr 1fr 1fr'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/resources/VirtualizedResourceTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/resources/VirtualizedResourceTable.tsx deleted file mode 100644 index 4d1a6a4fb6ce4..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/resources/VirtualizedResourceTable.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import {useVirtualizer} from '@tanstack/react-virtual'; -import {useRef} from 'react'; - -import {VirtualizedResourceHeader, VirtualizedResourceRow} from './VirtualizedResourceRow'; -import {ResourceEntryFragment} from './types/WorkspaceResourcesRoot.types'; -import {Container, Inner} from '../ui/VirtualizedTable'; -import {RepoAddress} from '../workspace/types'; - -interface Props { - repoAddress: RepoAddress; - resources: ResourceEntryFragment[]; -} - -export const VirtualizedResourceTable = ({repoAddress, resources}: Props) => { - const parentRef = useRef(null); - - const rowVirtualizer = useVirtualizer({ - count: resources.length, - getScrollElement: () => parentRef.current, - estimateSize: () => 64, - overscan: 10, - }); - - const totalHeight = rowVirtualizer.getTotalSize(); - const items = rowVirtualizer.getVirtualItems(); - - return ( -
- - - - {items.map(({index, key, size, start}) => { - const row: ResourceEntryFragment = resources[index]!; - return ( - - ); - })} - - -
- ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/resources/WorkspaceResourcesQuery.tsx b/js_modules/dagster-ui/packages/ui-core/src/resources/WorkspaceResourcesQuery.tsx new file mode 100644 index 0000000000000..68692cec39571 --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-core/src/resources/WorkspaceResourcesQuery.tsx @@ -0,0 +1,40 @@ +import {gql} from '../apollo-client'; +import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; + +export const RESOURCE_ENTRY_FRAGMENT = gql` + fragment ResourceEntryFragment on ResourceDetails { + name + description + resourceType + parentResources { + name + } + assetKeysUsing { + path + } + jobsOpsUsing { + jobName + } + schedulesUsing + sensorsUsing + } +`; + +export const WORKSPACE_RESOURCES_QUERY = gql` + query WorkspaceResourcesQuery($selector: RepositorySelector!) { + repositoryOrError(repositorySelector: $selector) { + ... on Repository { + id + name + allTopLevelResourceDetails { + id + ...ResourceEntryFragment + } + } + ...PythonErrorFragment + } + } + + ${PYTHON_ERROR_FRAGMENT} + ${RESOURCE_ENTRY_FRAGMENT} +`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/resources/WorkspaceResourcesRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/resources/WorkspaceResourcesRoot.tsx deleted file mode 100644 index 6b7989a9ca011..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/resources/WorkspaceResourcesRoot.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import {Box, Colors, NonIdealState, Spinner, TextInput} from '@dagster-io/ui-components'; -import {useMemo} from 'react'; - -import {VirtualizedResourceTable} from './VirtualizedResourceTable'; -import {gql, useQuery} from '../apollo-client'; -import { - WorkspaceResourcesQuery, - WorkspaceResourcesQueryVariables, -} from './types/WorkspaceResourcesRoot.types'; -import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; -import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh'; -import {useTrackPageView} from '../app/analytics'; -import {useDocumentTitle} from '../hooks/useDocumentTitle'; -import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; -import {WorkspaceHeader} from '../workspace/WorkspaceHeader'; -import {repoAddressAsHumanString} from '../workspace/repoAddressAsString'; -import {repoAddressToSelector} from '../workspace/repoAddressToSelector'; -import {RepoAddress} from '../workspace/types'; - -export const WorkspaceResourcesRoot = ({repoAddress}: {repoAddress: RepoAddress}) => { - useTrackPageView(); - - const repoName = repoAddressAsHumanString(repoAddress); - useDocumentTitle(`Resources: ${repoName}`); - - const [searchValue, setSearchValue] = useQueryPersistedState({ - queryKey: 'search', - defaults: {search: ''}, - }); - - const selector = repoAddressToSelector(repoAddress); - - const queryResultOverview = useQuery( - WORKSPACE_RESOURCES_QUERY, - { - fetchPolicy: 'network-only', - notifyOnNetworkStatusChange: true, - variables: {selector}, - }, - ); - const {data, loading} = queryResultOverview; - const refreshState = useQueryRefreshAtInterval(queryResultOverview, FIFTEEN_SECONDS); - - const sanitizedSearch = searchValue.trim().toLocaleLowerCase(); - const anySearch = sanitizedSearch.length > 0; - - const resources = useMemo(() => { - if (data?.repositoryOrError.__typename === 'Repository') { - return data.repositoryOrError.allTopLevelResourceDetails; - } - return []; - }, [data]); - - const filteredBySearch = useMemo(() => { - const searchToLower = sanitizedSearch.toLocaleLowerCase(); - return resources.filter(({name}) => name.toLocaleLowerCase().includes(searchToLower)); - }, [resources, sanitizedSearch]); - - const content = () => { - if (loading && !data) { - return ( - - - -
Loading resources…
-
-
- ); - } - - if (!filteredBySearch.length) { - if (anySearch) { - return ( - - - No resources matching {searchValue} were found in {repoName} - - } - /> - - ); - } - - return ( - - - - ); - } - - return ; - }; - - return ( - - - - setSearchValue(e.target.value)} - placeholder="Filter by resource name…" - style={{width: '340px'}} - /> - - {loading && !data ? ( - - - - ) : ( - content() - )} - - ); -}; - -export const RESOURCE_ENTRY_FRAGMENT = gql` - fragment ResourceEntryFragment on ResourceDetails { - name - description - resourceType - parentResources { - name - } - assetKeysUsing { - path - } - jobsOpsUsing { - jobName - } - schedulesUsing - sensorsUsing - } -`; - -const WORKSPACE_RESOURCES_QUERY = gql` - query WorkspaceResourcesQuery($selector: RepositorySelector!) { - repositoryOrError(repositorySelector: $selector) { - ... on Repository { - id - name - allTopLevelResourceDetails { - id - ...ResourceEntryFragment - } - } - ...PythonErrorFragment - } - } - - ${PYTHON_ERROR_FRAGMENT} - ${RESOURCE_ENTRY_FRAGMENT} -`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/resources/types/WorkspaceResourcesRoot.types.ts b/js_modules/dagster-ui/packages/ui-core/src/resources/types/WorkspaceResourcesQuery.types.ts similarity index 100% rename from js_modules/dagster-ui/packages/ui-core/src/resources/types/WorkspaceResourcesRoot.types.ts rename to js_modules/dagster-ui/packages/ui-core/src/resources/types/WorkspaceResourcesQuery.types.ts diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedGraphTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedGraphTable.tsx deleted file mode 100644 index 1b44cdd8b2b4c..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedGraphTable.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import {Box, Caption, Colors} from '@dagster-io/ui-components'; -import {useVirtualizer} from '@tanstack/react-virtual'; -import {useMemo, useRef} from 'react'; -import {Link} from 'react-router-dom'; -import styled from 'styled-components'; - -import {useDelayedRowQuery} from './VirtualizedWorkspaceTable'; -import {RepoAddress} from './types'; -import {SingleGraphQuery, SingleGraphQueryVariables} from './types/VirtualizedGraphTable.types'; -import {workspacePathFromAddress} from './workspacePath'; -import {gql, useLazyQuery} from '../apollo-client'; -import {Container, HeaderCell, HeaderRow, Inner, Row, RowCell} from '../ui/VirtualizedTable'; - -export type Graph = {name: string; path: string; description: string | null}; - -interface Props { - graphs: Graph[]; - repoAddress: RepoAddress; -} - -export const VirtualizedGraphTable = ({repoAddress, graphs}: Props) => { - const parentRef = useRef(null); - - const rowVirtualizer = useVirtualizer({ - count: graphs.length, - getScrollElement: () => parentRef.current, - estimateSize: () => 64, - overscan: 10, - }); - - const totalHeight = rowVirtualizer.getTotalSize(); - const items = rowVirtualizer.getVirtualItems(); - - return ( -
- - - Graph - - - {items.map(({index, key, size, start}) => { - const row: Graph = graphs[index]!; - return ( - - ); - })} - - -
- ); -}; - -interface GraphRowProps { - name: string; - path: string; - description: string | null; - repoAddress: RepoAddress; - height: number; - start: number; -} - -const GraphRow = (props: GraphRowProps) => { - const {name, path, description, repoAddress, start, height} = props; - - const [queryGraph, queryResult] = useLazyQuery( - SINGLE_GRAPH_QUERY, - { - variables: { - selector: { - repositoryName: repoAddress.name, - repositoryLocationName: repoAddress.location, - graphName: name, - }, - }, - }, - ); - - useDelayedRowQuery(queryGraph); - const {data} = queryResult; - - const displayedDescription = useMemo(() => { - if (description) { - return description; - } - if (data?.graphOrError.__typename === 'Graph') { - return data.graphOrError.description; - } - return null; - }, [data, description]); - - return ( - - - - -
- {name} -
- {displayedDescription ? ( -
- - {displayedDescription} - -
- ) : null} -
-
-
-
- ); -}; - -const RowGrid = styled(Box)` - display: grid; - grid-template-columns: 100%; - height: 100%; -`; - -const SINGLE_GRAPH_QUERY = gql` - query SingleGraphQuery($selector: GraphSelector!) { - graphOrError(selector: $selector) { - ... on Graph { - id - name - description - } - } - } -`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedJobTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedJobTable.tsx deleted file mode 100644 index e8acb68258f81..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedJobTable.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import {useVirtualizer} from '@tanstack/react-virtual'; -import {useRef} from 'react'; - -import {VirtualizedJobHeader, VirtualizedJobRow} from './VirtualizedJobRow'; -import {RepoAddress} from './types'; -import {Container, Inner} from '../ui/VirtualizedTable'; - -type Job = {isJob: boolean; name: string}; - -interface Props { - repoAddress: RepoAddress; - jobs: Job[]; -} - -export const VirtualizedJobTable = ({repoAddress, jobs}: Props) => { - const parentRef = useRef(null); - - const rowVirtualizer = useVirtualizer({ - count: jobs.length, - getScrollElement: () => parentRef.current, - estimateSize: () => 64, - overscan: 10, - }); - - const totalHeight = rowVirtualizer.getTotalSize(); - const items = rowVirtualizer.getVirtualItems(); - - return ( -
- - - - {items.map(({index, key, size, start}) => { - const row: Job = jobs[index]!; - return ( - - ); - })} - - -
- ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedRepoAssetTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedRepoAssetTable.tsx deleted file mode 100644 index b3866744e2f75..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedRepoAssetTable.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import {Box, Colors, Icon, IconWrapper, Tag} from '@dagster-io/ui-components'; -import {useVirtualizer} from '@tanstack/react-virtual'; -import {useRef} from 'react'; -import {Link} from 'react-router-dom'; -import styled from 'styled-components'; - -import {VirtualizedAssetHeader, VirtualizedAssetRow} from './VirtualizedAssetRow'; -import {RepoAddress} from './types'; -import {RepoAssetTableFragment} from './types/WorkspaceAssetsQuery.types'; -import {useFlattenedGroupedAssetList} from './useFlattenedGroupedAssetList'; -import {workspacePathFromAddress} from './workspacePath'; -import {Container, Inner, Row} from '../ui/VirtualizedTable'; - -type Asset = RepoAssetTableFragment; - -interface Props { - repoAddress: RepoAddress; - assets: Asset[]; -} - -type RowType = - | {type: 'group'; name: string; assetCount: number} - | {type: 'asset'; id: string; definition: Asset}; - -const UNGROUPED_NAME = 'UNGROUPED'; - -export const VirtualizedRepoAssetTable = ({repoAddress, assets}: Props) => { - const parentRef = useRef(null); - const {flattened, expandedKeys, onToggle} = useFlattenedGroupedAssetList({repoAddress, assets}); - - const rowVirtualizer = useVirtualizer({ - count: flattened.length, - getScrollElement: () => parentRef.current, - estimateSize: (ii: number) => { - const row = flattened[ii]; - return row?.type === 'group' ? 48 : 64; - }, - overscan: 5, - }); - - const totalHeight = rowVirtualizer.getTotalSize(); - const items = rowVirtualizer.getVirtualItems(); - - return ( -
- - - - {items.map(({index, key, size, start}) => { - const row: RowType = flattened[index]!; - const type = row!.type; - return type === 'group' ? ( - - ) : ( - {}} - onRefresh={() => {}} - /> - ); - })} - - -
- ); -}; - -const GroupNameRow = ({ - repoAddress, - groupName, - assetCount, - expanded, - height, - start, - onToggle, -}: { - repoAddress: RepoAddress; - groupName: string; - assetCount: number; - expanded: boolean; - height: number; - start: number; - onToggle: (groupName: string) => void; -}) => { - return ( - onToggle(groupName)} - $open={expanded} - tabIndex={0} - onKeyDown={(e) => { - if (e.code === 'Space' || e.code === 'Enter') { - e.preventDefault(); - onToggle(groupName); - } - }} - > - - - - {groupName === UNGROUPED_NAME ? ( -
Ungrouped assets
- ) : ( - <> - {groupName} - {groupName !== UNGROUPED_NAME ? ( - - - - View lineage - - - - - ) : null} - - )} -
- - {assetCount === 1 ? '1 asset' : `${assetCount} assets`} - - -
-
- ); -}; - -const ClickableRow = styled(Row)<{$open: boolean}>` - cursor: pointer; - - :focus, - :active { - outline: none; - } - - ${IconWrapper}[aria-label="arrow_drop_down"] { - transition: transform 100ms linear; - ${({$open}) => ($open ? null : `transform: rotate(-90deg);`)} - } -`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedScheduleTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedScheduleTable.tsx deleted file mode 100644 index 8b6dd0a1ecd21..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedScheduleTable.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import {useVirtualizer} from '@tanstack/react-virtual'; -import * as React from 'react'; - -import {VirtualizedScheduleHeader, VirtualizedScheduleRow} from './VirtualizedScheduleRow'; -import {RepoAddress} from './types'; -import {BasicInstigationStateFragment} from '../overview/types/BasicInstigationStateFragment.types'; -import {makeScheduleKey} from '../schedules/makeScheduleKey'; -import {Container, Inner} from '../ui/VirtualizedTable'; - -type ScheduleInfo = {name: string; scheduleState: BasicInstigationStateFragment}; - -interface Props { - repoAddress: RepoAddress; - schedules: ScheduleInfo[]; - headerCheckbox: React.ReactNode; - checkedKeys: Set; - onToggleCheckFactory: (path: string) => (values: {checked: boolean; shiftKey: boolean}) => void; -} - -export const VirtualizedScheduleTable = ({ - repoAddress, - schedules, - headerCheckbox, - checkedKeys, - onToggleCheckFactory, -}: Props) => { - const parentRef = React.useRef(null); - - const rowVirtualizer = useVirtualizer({ - count: schedules.length, - getScrollElement: () => parentRef.current, - estimateSize: () => 64, - overscan: 10, - }); - - const totalHeight = rowVirtualizer.getTotalSize(); - const items = rowVirtualizer.getVirtualItems(); - - return ( -
- - - - {items.map(({index, key, size, start}) => { - const row: ScheduleInfo = schedules[index]!; - const scheduleKey = makeScheduleKey(repoAddress, row.name); - return ( - - ); - })} - - -
- ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedSensorTable.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedSensorTable.tsx deleted file mode 100644 index 744018fbb5be7..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/VirtualizedSensorTable.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import {useVirtualizer} from '@tanstack/react-virtual'; -import * as React from 'react'; - -import {VirtualizedSensorHeader, VirtualizedSensorRow} from './VirtualizedSensorRow'; -import {RepoAddress} from './types'; -import {BasicInstigationStateFragment} from '../overview/types/BasicInstigationStateFragment.types'; -import {makeSensorKey} from '../sensors/makeSensorKey'; -import {Container, Inner} from '../ui/VirtualizedTable'; - -type SensorInfo = {name: string; sensorState: BasicInstigationStateFragment}; - -interface Props { - repoAddress: RepoAddress; - sensors: SensorInfo[]; - headerCheckbox: React.ReactNode; - checkedKeys: Set; - onToggleCheckFactory: (path: string) => (values: {checked: boolean; shiftKey: boolean}) => void; -} - -export const VirtualizedSensorTable = ({ - repoAddress, - sensors, - headerCheckbox, - checkedKeys, - onToggleCheckFactory, -}: Props) => { - const parentRef = React.useRef(null); - - const rowVirtualizer = useVirtualizer({ - count: sensors.length, - getScrollElement: () => parentRef.current, - estimateSize: () => 64, - overscan: 10, - }); - - const totalHeight = rowVirtualizer.getTotalSize(); - const items = rowVirtualizer.getVirtualItems(); - - return ( -
- - - - {items.map(({index, key, size, start}) => { - const row: SensorInfo = sensors[index]!; - const sensorKey = makeSensorKey(repoAddress, row.name); - return ( - - ); - })} - - -
- ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceAssetsRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceAssetsRoot.tsx deleted file mode 100644 index da3c4dd1bcc14..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceAssetsRoot.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import {Box, Colors, NonIdealState, Spinner, TextInput} from '@dagster-io/ui-components'; -import {useMemo} from 'react'; - -import {VirtualizedRepoAssetTable} from './VirtualizedRepoAssetTable'; -import {WORKSPACE_ASSETS_QUERY} from './WorkspaceAssetsQuery'; -import {WorkspaceHeader} from './WorkspaceHeader'; -import {repoAddressAsHumanString} from './repoAddressAsString'; -import {repoAddressToSelector} from './repoAddressToSelector'; -import {RepoAddress} from './types'; -import {useQuery} from '../apollo-client'; -import { - WorkspaceAssetsQuery, - WorkspaceAssetsQueryVariables, -} from './types/WorkspaceAssetsQuery.types'; -import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh'; -import {useTrackPageView} from '../app/analytics'; -import {useAssetSearch} from '../assets/useAssetSearch'; -import {useDocumentTitle} from '../hooks/useDocumentTitle'; -import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; - -export const WorkspaceAssetsRoot = ({repoAddress}: {repoAddress: RepoAddress}) => { - useTrackPageView(); - - const repoName = repoAddressAsHumanString(repoAddress); - useDocumentTitle(`Assets: ${repoName}`); - - const selector = repoAddressToSelector(repoAddress); - const [searchValue, setSearchValue] = useQueryPersistedState({ - queryKey: 'search', - defaults: {search: ''}, - }); - - const queryResultOverview = useQuery( - WORKSPACE_ASSETS_QUERY, - { - fetchPolicy: 'network-only', - notifyOnNetworkStatusChange: true, - variables: {selector}, - }, - ); - const {data, loading} = queryResultOverview; - const refreshState = useQueryRefreshAtInterval(queryResultOverview, FIFTEEN_SECONDS); - - const sanitizedSearch = searchValue.trim().toLocaleLowerCase(); - const anySearch = sanitizedSearch.length > 0; - - const assetNodes = useMemo(() => { - if (data?.repositoryOrError.__typename === 'Repository') { - return data.repositoryOrError.assetNodes; - } - return []; - }, [data]); - - const filteredBySearch = useAssetSearch(searchValue, assetNodes); - - const content = () => { - if (loading && !data) { - return ( - - - -
Loading assets…
-
-
- ); - } - - if (!filteredBySearch.length) { - if (anySearch) { - return ( - - - No assets matching {searchValue} were found in {repoName} - - } - /> - - ); - } - - return ( - - - - ); - } - - return ; - }; - - return ( - - - - setSearchValue(e.target.value)} - placeholder="Filter by asset name…" - style={{width: '340px'}} - /> - - {loading && !data ? ( - - - - ) : ( - content() - )} - - ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceContext/WorkspaceQueries.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceContext/WorkspaceQueries.tsx index 660d8adf27a55..d62faca893398 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceContext/WorkspaceQueries.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceContext/WorkspaceQueries.tsx @@ -1,7 +1,7 @@ import {gql} from '../../apollo-client'; import {PYTHON_ERROR_FRAGMENT} from '../../app/PythonErrorFragment'; import {BASIC_INSTIGATION_STATE_FRAGMENT} from '../../overview/BasicInstigationStateFragment'; -import {RESOURCE_ENTRY_FRAGMENT} from '../../resources/WorkspaceResourcesRoot'; +import {RESOURCE_ENTRY_FRAGMENT} from '../../resources/WorkspaceResourcesQuery'; import {SENSOR_SWITCH_FRAGMENT} from '../../sensors/SensorSwitch'; import {REPOSITORY_INFO_FRAGMENT} from '../RepositoryInformation'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceGraphsRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceGraphsRoot.tsx deleted file mode 100644 index 687ef313927cb..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceGraphsRoot.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import {Box, Colors, NonIdealState, Spinner, TextInput} from '@dagster-io/ui-components'; -import {useMemo} from 'react'; - -import {VirtualizedGraphTable} from './VirtualizedGraphTable'; -import {WORSKPACE_GRAPHS_QUERY} from './WorkspaceGraphsQuery'; -import {WorkspaceHeader} from './WorkspaceHeader'; -import {extractGraphsForRepo} from './extractGraphsForRepo'; -import {repoAddressAsHumanString} from './repoAddressAsString'; -import {repoAddressToSelector} from './repoAddressToSelector'; -import {RepoAddress} from './types'; -import {useQuery} from '../apollo-client'; -import { - WorkspaceGraphsQuery, - WorkspaceGraphsQueryVariables, -} from './types/WorkspaceGraphsQuery.types'; -import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh'; -import {useTrackPageView} from '../app/analytics'; -import {useDocumentTitle} from '../hooks/useDocumentTitle'; -import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; - -export const WorkspaceGraphsRoot = ({repoAddress}: {repoAddress: RepoAddress}) => { - useTrackPageView(); - - const repoName = repoAddressAsHumanString(repoAddress); - useDocumentTitle(`Graphs: ${repoName}`); - - const selector = repoAddressToSelector(repoAddress); - const [searchValue, setSearchValue] = useQueryPersistedState({ - queryKey: 'search', - defaults: {search: ''}, - }); - - const queryResultOverview = useQuery( - WORSKPACE_GRAPHS_QUERY, - { - fetchPolicy: 'network-only', - notifyOnNetworkStatusChange: true, - variables: {selector}, - }, - ); - const {data, loading} = queryResultOverview; - const refreshState = useQueryRefreshAtInterval(queryResultOverview, FIFTEEN_SECONDS); - - const sanitizedSearch = searchValue.trim().toLocaleLowerCase(); - const anySearch = sanitizedSearch.length > 0; - - const graphs = useMemo(() => { - const repo = data?.repositoryOrError; - if (!repo || repo.__typename !== 'Repository') { - return []; - } - - return extractGraphsForRepo(repo); - }, [data]); - - const filteredBySearch = useMemo(() => { - const searchToLower = sanitizedSearch.toLocaleLowerCase(); - return graphs.filter(({name}) => name.toLocaleLowerCase().includes(searchToLower)); - }, [graphs, sanitizedSearch]); - - const content = () => { - if (loading && !data) { - return ( - - - -
Loading graphs…
-
-
- ); - } - - if (!filteredBySearch.length) { - if (anySearch) { - return ( - - - No graphs matching {searchValue} were found in {repoName} - - } - /> - - ); - } - - return ( - - - - ); - } - - return ; - }; - - return ( - - - - setSearchValue(e.target.value)} - placeholder="Filter by graph name…" - style={{width: '340px'}} - /> - - {loading && !data ? ( - - - - ) : ( - content() - )} - - ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceHeader.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceHeader.tsx deleted file mode 100644 index 9bb9fee2baf94..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceHeader.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import {Box, Button, Colors, Heading, Icon, PageHeader, Tooltip} from '@dagster-io/ui-components'; -import {Link} from 'react-router-dom'; - -import {WorkspaceTabs} from './WorkspaceTabs'; -import {repoAddressAsHumanString} from './repoAddressAsString'; -import {RepoAddress} from './types'; -import {QueryRefreshState} from '../app/QueryRefresh'; -import { - NO_RELOAD_PERMISSION_TEXT, - ReloadRepositoryLocationButton, -} from '../nav/ReloadRepositoryLocationButton'; - -interface Props { - repoAddress: RepoAddress; - tab: string; - refreshState?: QueryRefreshState; -} - -export const WorkspaceHeader = (props: Props) => { - const {repoAddress, tab, refreshState} = props; - - return ( - - - - Deployment - - - / - - {repoAddressAsHumanString(repoAddress)} - - - } - tabs={} - right={ - { - return ( - - - - ); - }} - /> - } - /> - ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceJobsRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceJobsRoot.tsx deleted file mode 100644 index cedb690d8415a..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceJobsRoot.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import {Box, Colors, NonIdealState, Spinner, TextInput} from '@dagster-io/ui-components'; -import {useMemo} from 'react'; - -import {VirtualizedJobTable} from './VirtualizedJobTable'; -import {useRepository} from './WorkspaceContext/util'; -import {WorkspaceHeader} from './WorkspaceHeader'; -import {repoAddressAsHumanString} from './repoAddressAsString'; -import {repoAddressToSelector} from './repoAddressToSelector'; -import {RepoAddress} from './types'; -import {gql, useQuery} from '../apollo-client'; -import {WorkspaceJobsQuery, WorkspaceJobsQueryVariables} from './types/WorkspaceJobsRoot.types'; -import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; -import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh'; -import {useTrackPageView} from '../app/analytics'; -import {isHiddenAssetGroupJob} from '../asset-graph/Utils'; -import {useDocumentTitle} from '../hooks/useDocumentTitle'; -import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; -import {useBlockTraceUntilTrue} from '../performance/TraceContext'; -import {SearchInputSpinner} from '../ui/SearchInputSpinner'; - -const NO_REPOS_EMPTY_ARR: any[] = []; - -export const WorkspaceJobsRoot = ({repoAddress}: {repoAddress: RepoAddress}) => { - useTrackPageView(); - - const repo = useRepository(repoAddress); - - const repoName = repoAddressAsHumanString(repoAddress); - useDocumentTitle(`Jobs: ${repoName}`); - - const selector = repoAddressToSelector(repoAddress); - const [searchValue, setSearchValue] = useQueryPersistedState({ - queryKey: 'search', - defaults: {search: ''}, - }); - - const queryResultOverview = useQuery( - WORKSPACE_JOBS_QUERY, - { - fetchPolicy: 'network-only', - notifyOnNetworkStatusChange: true, - variables: {selector}, - }, - ); - const {data, loading: queryLoading} = queryResultOverview; - - const refreshState = useQueryRefreshAtInterval(queryResultOverview, FIFTEEN_SECONDS); - - const sanitizedSearch = searchValue.trim().toLocaleLowerCase(); - const anySearch = sanitizedSearch.length > 0; - - const jobs = useMemo(() => { - if (data?.repositoryOrError.__typename === 'Repository') { - return data.repositoryOrError.pipelines; - } - if (repo) { - return repo.repository.pipelines; - } - return NO_REPOS_EMPTY_ARR; - }, [data, repo]); - - const loading = jobs === NO_REPOS_EMPTY_ARR; - - useBlockTraceUntilTrue('WorkspaceJobs', !loading); - - const filteredBySearch = useMemo(() => { - const searchToLower = sanitizedSearch.toLocaleLowerCase(); - return jobs.filter( - ({name}) => !isHiddenAssetGroupJob(name) && name.toLocaleLowerCase().includes(searchToLower), - ); - }, [jobs, sanitizedSearch]); - - const content = () => { - if (loading && !data) { - return ( - - - -
Loading jobs…
-
-
- ); - } - - if (!filteredBySearch.length) { - if (anySearch) { - return ( - - - No jobs matching {searchValue} were found in {repoName} - - } - /> - - ); - } - - return ( - - - - ); - } - - return ; - }; - - const showSearchSpinner = !data && queryLoading; - - return ( - - - - setSearchValue(e.target.value)} - placeholder="Filter by job name…" - style={{width: '340px'}} - rightElement={ - showSearchSpinner ? : undefined - } - /> - - {loading && !data ? ( - - - - ) : ( - content() - )} - - ); -}; - -const WORKSPACE_JOBS_QUERY = gql` - query WorkspaceJobsQuery($selector: RepositorySelector!) { - repositoryOrError(repositorySelector: $selector) { - ... on Repository { - id - name - pipelines { - id - name - isJob - } - } - ...PythonErrorFragment - } - } - - ${PYTHON_ERROR_FRAGMENT} -`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceOpsRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceOpsRoot.tsx deleted file mode 100644 index 708abeb6f05f7..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceOpsRoot.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import {Box} from '@dagster-io/ui-components'; - -import {WorkspaceHeader} from './WorkspaceHeader'; -import {repoAddressAsHumanString} from './repoAddressAsString'; -import {RepoAddress} from './types'; -import {useTrackPageView} from '../app/analytics'; -import {useDocumentTitle} from '../hooks/useDocumentTitle'; -import {OpsRoot} from '../ops/OpsRoot'; - -export const WorkspaceOpsRoot = ({repoAddress}: {repoAddress: RepoAddress}) => { - useTrackPageView(); - - const repoName = repoAddressAsHumanString(repoAddress); - useDocumentTitle(`Ops: ${repoName}`); - - return ( - - - - - ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceSchedulesRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceSchedulesRoot.tsx deleted file mode 100644 index 4cf0febe276ac..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceSchedulesRoot.tsx +++ /dev/null @@ -1,256 +0,0 @@ -import {Box, Colors, NonIdealState, Spinner, TextInput, Tooltip} from '@dagster-io/ui-components'; -import {useMemo} from 'react'; - -import {VirtualizedScheduleTable} from './VirtualizedScheduleTable'; -import {useRepository} from './WorkspaceContext/util'; -import {WorkspaceHeader} from './WorkspaceHeader'; -import {repoAddressAsHumanString} from './repoAddressAsString'; -import {repoAddressToSelector} from './repoAddressToSelector'; -import {RepoAddress} from './types'; -import {gql, useQuery} from '../apollo-client'; -import { - WorkspaceSchedulesQuery, - WorkspaceSchedulesQueryVariables, -} from './types/WorkspaceSchedulesRoot.types'; -import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; -import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh'; -import {useTrackPageView} from '../app/analytics'; -import {useDocumentTitle} from '../hooks/useDocumentTitle'; -import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; -import {useSelectionReducer} from '../hooks/useSelectionReducer'; -import {filterPermissionedInstigationState} from '../instigation/filterPermissionedInstigationState'; -import {BASIC_INSTIGATION_STATE_FRAGMENT} from '../overview/BasicInstigationStateFragment'; -import {ScheduleBulkActionMenu} from '../schedules/ScheduleBulkActionMenu'; -import {makeScheduleKey} from '../schedules/makeScheduleKey'; -import {useFilters} from '../ui/BaseFilters'; -import {CheckAllBox} from '../ui/CheckAllBox'; -import {useInstigationStatusFilter} from '../ui/Filters/useInstigationStatusFilter'; -import {SearchInputSpinner} from '../ui/SearchInputSpinner'; - -// Reuse this reference to distinguish no sensors case from data is still loading case; -const NO_DATA_EMPTY_ARR: any[] = []; - -export const WorkspaceSchedulesRoot = ({repoAddress}: {repoAddress: RepoAddress}) => { - useTrackPageView(); - - const repo = useRepository(repoAddress); - - const repoName = repoAddressAsHumanString(repoAddress); - useDocumentTitle(`Schedules: ${repoName}`); - - const selector = repoAddressToSelector(repoAddress); - const [searchValue, setSearchValue] = useQueryPersistedState({ - queryKey: 'search', - defaults: {search: ''}, - }); - - const runningStateFilter = useInstigationStatusFilter(); - const filters = useMemo(() => [runningStateFilter], [runningStateFilter]); - const {button: filterButton, activeFiltersJsx} = useFilters({filters}); - - const queryResultOverview = useQuery( - WORKSPACE_SCHEDULES_QUERY, - { - fetchPolicy: 'network-only', - notifyOnNetworkStatusChange: true, - variables: {selector}, - }, - ); - const {data, loading: queryLoading} = queryResultOverview; - const refreshState = useQueryRefreshAtInterval(queryResultOverview, FIFTEEN_SECONDS); - - const sanitizedSearch = searchValue.trim().toLocaleLowerCase(); - const anySearch = sanitizedSearch.length > 0; - - const schedules = useMemo(() => { - if (data?.repositoryOrError.__typename === 'Repository') { - return data.repositoryOrError.schedules; - } - if (repo) { - return repo.repository.schedules; - } - return NO_DATA_EMPTY_ARR; - }, [data, repo]); - - const loading = NO_DATA_EMPTY_ARR === schedules; - - const {state: runningState} = runningStateFilter; - const filteredByRunningState = useMemo(() => { - return runningState.size - ? schedules.filter(({scheduleState}) => runningState.has(scheduleState.status)) - : schedules; - }, [schedules, runningState]); - - const filteredBySearch = useMemo(() => { - const searchToLower = sanitizedSearch.toLocaleLowerCase(); - return filteredByRunningState.filter(({name}) => - name.toLocaleLowerCase().includes(searchToLower), - ); - }, [filteredByRunningState, sanitizedSearch]); - - const anySchedulesVisible = filteredBySearch.length > 0; - - const permissionedSchedules = useMemo(() => { - return filteredBySearch.filter(({scheduleState}) => - filterPermissionedInstigationState(scheduleState), - ); - }, [filteredBySearch]); - - const permissionedKeys = useMemo(() => { - return permissionedSchedules.map(({name}) => makeScheduleKey(repoAddress, name)); - }, [permissionedSchedules, repoAddress]); - - const [{checkedIds: checkedKeys}, {onToggleFactory, onToggleAll}] = - useSelectionReducer(permissionedKeys); - - const checkedSchedules = useMemo(() => { - return permissionedSchedules - .filter(({name}) => checkedKeys.has(makeScheduleKey(repoAddress, name))) - .map(({name, scheduleState}) => { - return {repoAddress, scheduleName: name, scheduleState}; - }); - }, [permissionedSchedules, checkedKeys, repoAddress]); - - const permissionedCount = permissionedKeys.length; - const checkedCount = checkedKeys.size; - - const viewerHasAnyInstigationPermission = permissionedKeys.length > 0; - - const content = () => { - if (loading && !data) { - return ( - - - -
Loading schedules…
-
-
- ); - } - - if (!filteredBySearch.length) { - if (anySearch) { - return ( - - - No schedules matching {searchValue} were found in {repoName} - - } - /> - - ); - } - - return ( - - - - ); - } - - return ( - - ) : undefined - } - checkedKeys={checkedKeys} - onToggleCheckFactory={onToggleFactory} - /> - ); - }; - - const showSearchSpinner = queryLoading && !data; - - return ( - - - - - {filterButton} - { - setSearchValue(e.target.value); - onToggleAll(false); - }} - placeholder="Filter by schedule name…" - style={{width: '340px'}} - rightElement={ - showSearchSpinner ? ( - - ) : undefined - } - /> - - - refreshState.refetch()} - /> - - - {activeFiltersJsx.length ? ( - - {activeFiltersJsx} - - ) : null} - {loading && !data ? ( - - - - ) : ( - content() - )} - - ); -}; - -const WORKSPACE_SCHEDULES_QUERY = gql` - query WorkspaceSchedulesQuery($selector: RepositorySelector!) { - repositoryOrError(repositorySelector: $selector) { - ... on Repository { - id - name - schedules { - id - name - description - scheduleState { - id - ...BasicInstigationStateFragment - } - } - } - ...PythonErrorFragment - } - } - - ${BASIC_INSTIGATION_STATE_FRAGMENT} - ${PYTHON_ERROR_FRAGMENT} -`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceSensorsRoot.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceSensorsRoot.tsx deleted file mode 100644 index 9e5b5955c13e3..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/WorkspaceSensorsRoot.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import {Box, Colors, NonIdealState, Spinner, TextInput, Tooltip} from '@dagster-io/ui-components'; -import {useMemo} from 'react'; - -import {VirtualizedSensorTable} from './VirtualizedSensorTable'; -import {useRepository} from './WorkspaceContext/util'; -import {WorkspaceHeader} from './WorkspaceHeader'; -import {repoAddressAsHumanString} from './repoAddressAsString'; -import {repoAddressToSelector} from './repoAddressToSelector'; -import {RepoAddress} from './types'; -import {gql, useQuery} from '../apollo-client'; -import { - WorkspaceSensorsQuery, - WorkspaceSensorsQueryVariables, -} from './types/WorkspaceSensorsRoot.types'; -import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; -import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh'; -import {useTrackPageView} from '../app/analytics'; -import {useDocumentTitle} from '../hooks/useDocumentTitle'; -import {useQueryPersistedState} from '../hooks/useQueryPersistedState'; -import {useSelectionReducer} from '../hooks/useSelectionReducer'; -import {filterPermissionedInstigationState} from '../instigation/filterPermissionedInstigationState'; -import {BASIC_INSTIGATION_STATE_FRAGMENT} from '../overview/BasicInstigationStateFragment'; -import {SensorBulkActionMenu} from '../sensors/SensorBulkActionMenu'; -import {makeSensorKey} from '../sensors/makeSensorKey'; -import {useFilters} from '../ui/BaseFilters'; -import {CheckAllBox} from '../ui/CheckAllBox'; -import {useInstigationStatusFilter} from '../ui/Filters/useInstigationStatusFilter'; -import {SearchInputSpinner} from '../ui/SearchInputSpinner'; - -// Reuse this reference to distinguish no sensors case from data is still loading case; -const NO_DATA_EMPTY_ARR: any[] = []; - -export const WorkspaceSensorsRoot = ({repoAddress}: {repoAddress: RepoAddress}) => { - useTrackPageView(); - - const repo = useRepository(repoAddress); - - const repoName = repoAddressAsHumanString(repoAddress); - useDocumentTitle(`Sensors: ${repoName}`); - - const selector = repoAddressToSelector(repoAddress); - const [searchValue, setSearchValue] = useQueryPersistedState({ - queryKey: 'search', - defaults: {search: ''}, - }); - - const runningStateFilter = useInstigationStatusFilter(); - const filters = useMemo(() => [runningStateFilter], [runningStateFilter]); - const {button: filterButton, activeFiltersJsx} = useFilters({filters}); - - const queryResultOverview = useQuery( - WORKSPACE_SENSORS_QUERY, - { - fetchPolicy: 'network-only', - notifyOnNetworkStatusChange: true, - variables: {selector}, - }, - ); - const {data, loading: queryLoading} = queryResultOverview; - const refreshState = useQueryRefreshAtInterval(queryResultOverview, FIFTEEN_SECONDS); - - const sanitizedSearch = searchValue.trim().toLocaleLowerCase(); - const anySearch = sanitizedSearch.length > 0; - - const sensors = useMemo(() => { - if (data?.repositoryOrError.__typename === 'Repository') { - return data.repositoryOrError.sensors; - } - if (repo) { - return repo.repository.sensors; - } - return NO_DATA_EMPTY_ARR; - }, [repo, data]); - - const loading = NO_DATA_EMPTY_ARR === sensors; - - const {state: runningState} = runningStateFilter; - const filteredByRunningState = useMemo(() => { - return runningState.size - ? sensors.filter(({sensorState}) => runningState.has(sensorState.status)) - : sensors; - }, [sensors, runningState]); - - const filteredBySearch = useMemo(() => { - const searchToLower = sanitizedSearch.toLocaleLowerCase(); - return filteredByRunningState.filter(({name}) => - name.toLocaleLowerCase().includes(searchToLower), - ); - }, [filteredByRunningState, sanitizedSearch]); - - const anySensorsVisible = filteredBySearch.length > 0; - - const permissionedSensors = useMemo(() => { - return filteredBySearch.filter(({sensorState}) => - filterPermissionedInstigationState(sensorState), - ); - }, [filteredBySearch]); - - const permissionedKeys = useMemo(() => { - return permissionedSensors.map(({name}) => makeSensorKey(repoAddress, name)); - }, [permissionedSensors, repoAddress]); - - const [{checkedIds: checkedKeys}, {onToggleFactory, onToggleAll}] = - useSelectionReducer(permissionedKeys); - - const checkedSensors = useMemo(() => { - return permissionedSensors - .filter(({name}) => checkedKeys.has(makeSensorKey(repoAddress, name))) - .map(({name, sensorState}) => { - return {repoAddress, sensorName: name, sensorState}; - }); - }, [permissionedSensors, checkedKeys, repoAddress]); - - const permissionedCount = permissionedKeys.length; - const checkedCount = checkedKeys.size; - - const viewerHasAnyInstigationPermission = permissionedKeys.length > 0; - - const content = () => { - if (loading && !data) { - return ( - - - -
Loading sensors…
-
-
- ); - } - - if (!filteredBySearch.length) { - if (anySearch) { - return ( - - - No sensors matching {searchValue} were found in {repoName} - - } - /> - - ); - } - - return ( - - - - ); - } - - return ( - - ) : undefined - } - checkedKeys={checkedKeys} - onToggleCheckFactory={onToggleFactory} - /> - ); - }; - - const showSearchSpinner = queryLoading && !data; - - return ( - - - - - {filterButton} - setSearchValue(e.target.value)} - placeholder="Filter by sensor name…" - style={{width: '340px'}} - rightElement={ - showSearchSpinner ? ( - - ) : undefined - } - /> - - - refreshState.refetch()} /> - - - {activeFiltersJsx.length ? ( - - {activeFiltersJsx} - - ) : null} - {loading && !data ? ( - - - - ) : ( - content() - )} - - ); -}; - -const WORKSPACE_SENSORS_QUERY = gql` - query WorkspaceSensorsQuery($selector: RepositorySelector!) { - repositoryOrError(repositorySelector: $selector) { - ... on Repository { - id - name - sensors { - id - name - description - sensorState { - id - ...BasicInstigationStateFragment - } - } - } - ...PythonErrorFragment - } - } - - ${BASIC_INSTIGATION_STATE_FRAGMENT} - ${PYTHON_ERROR_FRAGMENT} -`; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/__stories__/VirtualizedJobTable.stories.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/__stories__/VirtualizedJobTable.stories.tsx deleted file mode 100644 index e860dddf9c9d4..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/__stories__/VirtualizedJobTable.stories.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import {Box, TextInput} from '@dagster-io/ui-components'; -import {Meta} from '@storybook/react'; -import faker from 'faker'; -import * as React from 'react'; - -import {useQueryPersistedState} from '../../hooks/useQueryPersistedState'; -import {StorybookProvider} from '../../testing/StorybookProvider'; -import {VirtualizedJobTable} from '../VirtualizedJobTable'; -import {buildRepoAddress} from '../buildRepoAddress'; - -// eslint-disable-next-line import/no-default-export -export default { - title: 'VirtualizedJobTable', - component: VirtualizedJobTable, -} as Meta; - -const mocks = { - Pipeline: () => ({ - isJob: () => true, - description: () => faker.random.words(4), - }), -}; - -export const Standard = () => { - const [searchValue, setSearchValue] = useQueryPersistedState({ - queryKey: 'search', - defaults: {search: ''}, - }); - - const repoAddress = React.useMemo( - () => - buildRepoAddress( - faker.random.word().toLocaleLowerCase(), - faker.random.word().toLocaleLowerCase(), - ), - [], - ); - - const jobs = React.useMemo( - () => - new Array(3000).fill(null).map(() => ({ - name: faker.random.words(2).replace(' ', '-').toLocaleLowerCase(), - isJob: true, - })), - [], - ); - - const onChange = React.useCallback( - (e: React.ChangeEvent) => { - setSearchValue(e.target.value); - }, - [setSearchValue], - ); - - const filtered = React.useMemo(() => { - const searchLower = searchValue.toLocaleLowerCase(); - return jobs.filter(({name}) => name.includes(searchLower)); - }, [searchValue, jobs]); - - return ( - -
- - - - -
-
- ); -}; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/extractGraphsForRepo.tsx b/js_modules/dagster-ui/packages/ui-core/src/workspace/extractGraphsForRepo.tsx index f849d48390f2f..94f7690e49bf3 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/extractGraphsForRepo.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/workspace/extractGraphsForRepo.tsx @@ -1,8 +1,9 @@ -import {Graph} from './VirtualizedGraphTable'; import {RepositoryGraphsFragment} from './types/WorkspaceGraphsQuery.types'; import {COMMON_COLLATOR} from '../app/Util'; import {isHiddenAssetGroupJob} from '../asset-graph/Utils'; +type Graph = {name: string; path: string; description: string | null}; + export const extractGraphsForRepo = (repo: RepositoryGraphsFragment) => { const jobGraphNames = new Set( repo.pipelines.filter((p) => p.isJob && !isHiddenAssetGroupJob(p.name)).map((p) => p.graphName), diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/VirtualizedGraphTable.types.ts b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/VirtualizedGraphTable.types.ts deleted file mode 100644 index f66a46cf9eaa2..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/VirtualizedGraphTable.types.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Generated GraphQL types, do not edit manually. - -import * as Types from '../../graphql/types'; - -export type SingleGraphQueryVariables = Types.Exact<{ - selector: Types.GraphSelector; -}>; - -export type SingleGraphQuery = { - __typename: 'Query'; - graphOrError: - | {__typename: 'Graph'; id: string; name: string; description: string | null} - | {__typename: 'GraphNotFoundError'} - | {__typename: 'PythonError'}; -}; - -export const SingleGraphQueryVersion = 'f1d47e63982c085807a7013c1581574e1284fe12344ea9752349c57aff0e0b2b'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceJobsRoot.types.ts b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceJobsRoot.types.ts deleted file mode 100644 index bcf59cf306cd7..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceJobsRoot.types.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Generated GraphQL types, do not edit manually. - -import * as Types from '../../graphql/types'; - -export type WorkspaceJobsQueryVariables = Types.Exact<{ - selector: Types.RepositorySelector; -}>; - -export type WorkspaceJobsQuery = { - __typename: 'Query'; - repositoryOrError: - | { - __typename: 'PythonError'; - message: string; - stack: Array; - errorChain: Array<{ - __typename: 'ErrorChainLink'; - isExplicitLink: boolean; - error: {__typename: 'PythonError'; message: string; stack: Array}; - }>; - } - | { - __typename: 'Repository'; - id: string; - name: string; - pipelines: Array<{__typename: 'Pipeline'; id: string; name: string; isJob: boolean}>; - } - | {__typename: 'RepositoryNotFoundError'}; -}; - -export const WorkspaceJobsQueryVersion = '637f616d6d4eba194cf80bbb292f579f864d3b16aeb0b40cdf108d8100ba9b1c'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceSchedulesRoot.types.ts b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceSchedulesRoot.types.ts deleted file mode 100644 index fcfd845d6e585..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceSchedulesRoot.types.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Generated GraphQL types, do not edit manually. - -import * as Types from '../../graphql/types'; - -export type WorkspaceSchedulesQueryVariables = Types.Exact<{ - selector: Types.RepositorySelector; -}>; - -export type WorkspaceSchedulesQuery = { - __typename: 'Query'; - repositoryOrError: - | { - __typename: 'PythonError'; - message: string; - stack: Array; - errorChain: Array<{ - __typename: 'ErrorChainLink'; - isExplicitLink: boolean; - error: {__typename: 'PythonError'; message: string; stack: Array}; - }>; - } - | { - __typename: 'Repository'; - id: string; - name: string; - schedules: Array<{ - __typename: 'Schedule'; - id: string; - name: string; - description: string | null; - scheduleState: { - __typename: 'InstigationState'; - id: string; - selectorId: string; - status: Types.InstigationStatus; - hasStartPermission: boolean; - hasStopPermission: boolean; - }; - }>; - } - | {__typename: 'RepositoryNotFoundError'}; -}; - -export const WorkspaceSchedulesQueryVersion = '213cb3c1a2ffd6d9a6fbe20cd58eab746d53b6ab04d7d498c3c4f1f9d4a850d3'; diff --git a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceSensorsRoot.types.ts b/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceSensorsRoot.types.ts deleted file mode 100644 index 7b6e6873ceb40..0000000000000 --- a/js_modules/dagster-ui/packages/ui-core/src/workspace/types/WorkspaceSensorsRoot.types.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Generated GraphQL types, do not edit manually. - -import * as Types from '../../graphql/types'; - -export type WorkspaceSensorsQueryVariables = Types.Exact<{ - selector: Types.RepositorySelector; -}>; - -export type WorkspaceSensorsQuery = { - __typename: 'Query'; - repositoryOrError: - | { - __typename: 'PythonError'; - message: string; - stack: Array; - errorChain: Array<{ - __typename: 'ErrorChainLink'; - isExplicitLink: boolean; - error: {__typename: 'PythonError'; message: string; stack: Array}; - }>; - } - | { - __typename: 'Repository'; - id: string; - name: string; - sensors: Array<{ - __typename: 'Sensor'; - id: string; - name: string; - description: string | null; - sensorState: { - __typename: 'InstigationState'; - id: string; - selectorId: string; - status: Types.InstigationStatus; - hasStartPermission: boolean; - hasStopPermission: boolean; - }; - }>; - } - | {__typename: 'RepositoryNotFoundError'}; -}; - -export const WorkspaceSensorsQueryVersion = '2c8ef3f0111c4524514ff762d542810e95d429cc421e60893a3a4bbb39bb9849';