diff --git a/packages/web/app/src/pages/target-checks.tsx b/packages/web/app/src/pages/target-checks.tsx index b7e0c4cc0d..10ed66dcd2 100644 --- a/packages/web/app/src/pages/target-checks.tsx +++ b/packages/web/app/src/pages/target-checks.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useQuery } from 'urql'; import { Page, TargetLayout } from '@/components/layouts/target'; import { BadgeRounded } from '@/components/ui/badge'; @@ -13,6 +13,7 @@ import { Spinner } from '@/components/ui/spinner'; import { Switch } from '@/components/ui/switch'; import { TimeAgo } from '@/components/ui/time-ago'; import { graphql } from '@/gql'; +import { ProjectType } from '@/gql/graphql'; import { cn } from '@/lib/utils'; import { ExternalLinkIcon } from '@radix-ui/react-icons'; import { @@ -81,6 +82,12 @@ const Navigation = ( schemaCheckId?: string; } & SchemaCheckFilters, ) => { + const search = useMemo(() => { + return { + filter_changed: props.showOnlyChanged, + filter_failed: props.showOnlyFailed, + }; + }, [props.showOnlyChanged, props.showOnlyFailed]); const [query] = useQuery({ query: SchemaChecks_NavigationQuery, variables: { @@ -95,83 +102,87 @@ const Navigation = ( }, }); + const onLoadMore = useCallback(() => { + props.onLoadMore(query.data?.target?.schemaChecks.pageInfo.endCursor ?? ''); + }, [query.data?.target?.schemaChecks.pageInfo.endCursor, props.onLoadMore]); + + if (query.fetching) { + return ( +
+ +
+ ); + } + + if (!query.data?.target?.schemaChecks) { + return null; + } + return ( <> - {query.fetching || !query.data?.target?.schemaChecks ? null : ( - <> - {query.data.target.schemaChecks.edges.map(edge => ( -
- ( +
+ +

+ {edge.node.meta?.commit ?? edge.node.id} +

+ {edge.node.meta?.author ? ( +
+ {edge.node.meta.author} +
+ ) : null} +
+
-

- {edge.node.meta?.commit ?? edge.node.id} -

- {edge.node.meta?.author ? ( -
- {edge.node.meta.author} -
- ) : null} -
-
- - -
+ + +
- {edge.node.serviceName ? ( -
- {edge.node.serviceName} -
- ) : null} + {edge.node.serviceName ? ( +
+ {edge.node.serviceName}
- - {edge.node.githubRepository && edge.node.meta ? ( - - associated with Git commit - ) : null}
- ))} - {props.isLastPage && query.data.target.schemaChecks.pageInfo.hasNextPage && ( - - )} - + associated with Git commit + + ) : null} +
+ ))} + {props.isLastPage && query.data.target.schemaChecks.pageInfo.hasNextPage && ( + )} ); @@ -222,25 +233,30 @@ const ChecksPageQuery = graphql(` } `); -function ChecksPageContent(props: { - organizationSlug: string; - projectSlug: string; - targetSlug: string; -}) { - const [paginationVariables, setPaginationVariables] = useState>(() => [ - null, - ]); - const navigate = useNavigate(); +function useTargetCheckUrlParams() { const { schemaCheckId } = useParams({ strict: false /* allows to read the $schemaCheckId param of its child route */, }) as { schemaCheckId?: string }; const search = useSearch({ from: '/authenticated/$organizationSlug/$projectSlug/$targetSlug/checks', }) as { - filter_changed: boolean; - filter_failed: boolean; + filter_changed?: boolean; + filter_failed?: boolean; + }; + return { + showOnlyChanged: search.filter_changed ?? false, + showOnlyFailed: search.filter_failed ?? false, + schemaCheckId, + rawSearch: search, }; - const { filter_changed: showOnlyChanged, filter_failed: showOnlyFailed } = search; +} + +function ChecksPageContent(props: { + organizationSlug: string; + projectSlug: string; + targetSlug: string; +}) { + const { showOnlyChanged, showOnlyFailed, schemaCheckId } = useTargetCheckUrlParams(); const [query] = useQuery({ query: ChecksPageQuery, @@ -265,77 +281,39 @@ function ChecksPageContent(props: { ); } - const [isLoading] = useDebouncedLoader(query.fetching || query.stale); + const isLoading = query.fetching || query.stale; + const renderLoading = useDebouncedLoader(isLoading); const [hasSchemaChecks, setHasSchemaChecks] = useState( !!query.data?.target?.schemaChecks?.edges?.length, ); useEffect(() => { - if (!query.stale && !query.fetching) { + if (!isLoading) { setHasSchemaChecks(!!query.data?.target?.schemaChecks?.edges?.length); } - }, [query.fetching, query.stale, !query.data?.target?.schemaChecks?.edges?.length]); + }, [isLoading, !query.data?.target?.schemaChecks?.edges?.length]); const hasFilteredSchemaChecks = !!query.data?.target?.filteredSchemaChecks?.edges?.length; const hasActiveSchemaCheck = !!schemaCheckId; - - const handleShowOnlyFilterChange = () => { - void navigate({ - search: { - ...search, - filter_changed: !showOnlyChanged, - }, - }); - }; - - const handleShowOnlyFilterFailed = () => { - void navigate({ - search: { - ...search, - filter_failed: !showOnlyFailed, - }, - }); - }; - - const loadMore = (cursor: string) => setPaginationVariables(cursors => [...cursors, cursor]); + const [paginationVariables, setPaginationVariables] = useState>(() => [ + null, + ]); + const onLoadMore = (cursor: string) => setPaginationVariables(cursors => [...cursors, cursor]); return ( <> -
+
Schema Checks Recently checked schemas.
- {hasSchemaChecks ? ( -
-
-
- - -
-
- - -
-
+ {/* if done loading and there are schema checks found associated w this target */} + {hasSchemaChecks && ( + {hasFilteredSchemaChecks ? (
{paginationVariables.map((cursor, index) => ( @@ -346,7 +324,7 @@ function ChecksPageContent(props: { schemaCheckId={schemaCheckId} after={cursor} isLastPage={index + 1 === paginationVariables.length} - onLoadMore={loadMore} + onLoadMore={onLoadMore} key={cursor ?? 'first'} showOnlyChanged={showOnlyChanged} showOnlyFailed={showOnlyFailed} @@ -354,57 +332,115 @@ function ChecksPageContent(props: { ))}
) : ( - !query.fetching && - !query.stale && ( + !isLoading && (
No schema checks found with the current filters
) )} -
- ) : ( - !query.fetching && - !query.stale && ( -
-
- {!hasActiveSchemaCheck && ( - - )} -
- - Learn how to check your first schema - -
- ) + + )} + {!hasSchemaChecks && !isLoading && ( + )} - {isLoading && ( + {renderLoading && (
)}
- - {hasActiveSchemaCheck ? ( - schemaCheckId ? ( - - ) : null - ) : hasSchemaChecks ? ( + {hasSchemaChecks && } + {hasSchemaChecks && !hasActiveSchemaCheck && ( - ) : null} + )} ); } +function NoSchemaChecks(props: { projectType: ProjectType | null }) { + return ( + <> +
+ +
+ + Learn how to check your first schema + + + ); +} + +/** + * Renders the section of the checks page for when there are checks existing in the backend + */ +function SchemaChecksSideNav(props: { + organizationSlug: string; + projectSlug: string; + targetSlug: string; + children: ReactNode; +}) { + const navigate = useNavigate(); + const { showOnlyChanged, showOnlyFailed, rawSearch } = useTargetCheckUrlParams(); + + const handleShowOnlyFilterChange = () => { + void navigate({ + search: { + ...rawSearch, + filter_changed: !showOnlyChanged, + }, + replace: true, + }); + }; + + const handleShowOnlyFilterFailed = () => { + void navigate({ + search: { + ...rawSearch, + filter_failed: !showOnlyFailed, + }, + replace: true, + }); + }; + + return ( +
+
+
+ + +
+
+ + +
+
+ {props.children} +
+ ); +} + export function TargetChecksPage(props: { organizationSlug: string; projectSlug: string; @@ -426,7 +462,7 @@ export function TargetChecksPage(props: { ); } -const useDebouncedLoader = (isLoading: boolean, delay = 500) => { +const useDebouncedLoader = (isLoading: boolean, delay = 500): boolean => { const [showLoadingIcon, setShowLoadingIcon] = useState(false); const timerRef = useRef | undefined>(undefined); @@ -448,5 +484,5 @@ const useDebouncedLoader = (isLoading: boolean, delay = 500) => { }; }, [isLoading, delay]); - return [showLoadingIcon]; + return showLoadingIcon; };