diff --git a/packages/web/app/src/components/target/explorer/common.tsx b/packages/web/app/src/components/target/explorer/common.tsx index a7a5dc90f5b..f0f249d5cfe 100644 --- a/packages/web/app/src/components/target/explorer/common.tsx +++ b/packages/web/app/src/components/target/explorer/common.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, ReactNode, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import React, { ReactElement, ReactNode, useMemo } from 'react'; import { clsx } from 'clsx'; import { PulseIcon, UsersIcon } from '@/components/ui/icon'; import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; @@ -6,78 +6,24 @@ import { Skeleton } from '@/components/ui/skeleton'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Markdown } from '@/components/v2/markdown'; import { FragmentType, graphql, useFragment } from '@/gql'; -import { SupergraphMetadataList_SupergraphMetadataFragmentFragment } from '@/gql/graphql'; import { formatNumber, toDecimal } from '@/lib/hooks'; import { cn } from '@/lib/utils'; import { capitalize } from '@/utils'; -import { ChatBubbleIcon } from '@radix-ui/react-icons'; import { Link as NextLink, useRouter } from '@tanstack/react-router'; -import { useArgumentListToggle, useSchemaExplorerContext } from './provider'; +import { useDescriptionsVisibleToggle } from './provider'; import { SupergraphMetadataList } from './super-graph-metadata'; +import { useExplorerFieldFiltering } from './utils'; -const noop = () => {}; +export function Description(props: { description: string }) { + const { isDescriptionsVisible } = useDescriptionsVisibleToggle(); -function useCollapsibleList(list: ReadonlyArray, max: number, defaultValue: boolean) { - const [collapsed, setCollapsed] = React.useState(defaultValue === true && list.length > max); - const expand = React.useCallback(() => { - setCollapsed(false); - }, [setCollapsed]); - - if (collapsed) { - return [list.slice(0, max), collapsed, expand] as const; - } - - return [list, collapsed, noop] as const; -} - -function Description(props: { description: string }) { return ( - - - - - - - - - - ); -} - -export function DescriptionInline(props: { description: string }) { - const ref = useRef(null); - const [canExpand, setCanExpand] = useState(false); - const [isExpanded, setIsExpanded] = useState(false); - - useLayoutEffect(() => { - if (ref.current) { - setCanExpand(ref.current.scrollHeight > ref.current.clientHeight); - } - }, [props.description]); - - return ( -
- - {canExpand ? ( - setIsExpanded(prev => !prev)} - > - {isExpanded ? 'Show less' : 'Show more'} - - ) : null} +
+
); } @@ -142,7 +88,7 @@ export function SchemaExplorerUsageStats(props: { - {Array.isArray(usage.topOperations) ? ( + {Array.isArray(usage.topOperations) && ( @@ -177,7 +123,7 @@ export function SchemaExplorerUsageStats(props: { ))}
- ) : null} + )}
)} @@ -232,36 +178,6 @@ export function SchemaExplorerUsageStats(props: { ); } -const GraphQLFields_FieldFragment = graphql(` - fragment GraphQLFields_FieldFragment on GraphQLField { - name - description - type - isDeprecated - deprecationReason - usage { - total - ...SchemaExplorerUsageStats_UsageFragment - } - args { - ...GraphQLArguments_ArgumentFragment - } - supergraphMetadata { - ...SupergraphMetadataList_SupergraphMetadataFragment - } - } -`); - -const GraphQLArguments_ArgumentFragment = graphql(` - fragment GraphQLArguments_ArgumentFragment on GraphQLArgument { - name - description - type - isDeprecated - deprecationReason - } -`); - const GraphQLInputFields_InputFieldFragment = graphql(` fragment GraphQLInputFields_InputFieldFragment on GraphQLInputField { name @@ -331,12 +247,13 @@ export function GraphQLTypeCard(props: { GraphQLTypeCard_SupergraphMetadataFragment, props.supergraphMetadata, ); + return (
-
{props.kind}
+
{props.kind}
- {props.description ? : null} + {props.description && }
- {Array.isArray(props.implements) && props.implements.length > 0 ? ( -
+ {Array.isArray(props.implements) && props.implements.length > 0 && ( +
implements
{props.implements.map(t => ( @@ -363,8 +280,8 @@ export function GraphQLTypeCard(props: { ))}
- ) : null} - {props.usage && typeof props.totalRequests !== 'undefined' ? ( + )} + {props.usage && typeof props.totalRequests !== 'undefined' && ( - ) : null} - {supergraphMetadata ? ( + )} + {supergraphMetadata && ( - ) : null} + )}
{props.children}
); } -function GraphQLArguments(props: { - parentCoordinate: string; - args: FragmentType[]; - styleDeprecated: boolean; - organizationSlug: string; - projectSlug: string; - targetSlug: string; -}) { - const args = useFragment(GraphQLArguments_ArgumentFragment, props.args); - const [isCollapsedGlobally] = useArgumentListToggle(); - const [collapsed, setCollapsed] = React.useState(isCollapsedGlobally); - const hasMoreThanTwo = args.length > 2; - const showAll = hasMoreThanTwo && !collapsed; - - React.useEffect(() => { - setCollapsed(isCollapsedGlobally); - }, [isCollapsedGlobally, setCollapsed]); - - if (showAll) { - return ( - - ( -
- {args.map(arg => { - const coordinate = `${props.parentCoordinate}.${arg.name}`; - return ( -
- - - {arg.name} - - - {': '} - - {arg.description ? : null} -
- ); - })} -
- ) -
- ); - } - - return ( - - ( - - {args.slice(0, 2).map(arg => { - const coordinate = `${props.parentCoordinate}.${arg.name}`; - return ( - - - - {arg.name} - - - {': '} - - - ); - })} - {hasMoreThanTwo ? ( - setCollapsed(prev => !prev)} - > - {props.args.length - 2} hidden - - ) : null} - - ) - - ); -} - export function GraphQLTypeCardListItem(props: { children: ReactNode; index: number; @@ -510,163 +325,6 @@ export function GraphQLTypeCardListItem(props: { ); } -export function GraphQLFields(props: { - typeName: string; - fields: Array>; - totalRequests?: number; - collapsed?: boolean; - targetSlug: string; - projectSlug: string; - organizationSlug: string; - filterValue?: string; - warnAboutUnusedArguments: boolean; - warnAboutDeprecatedArguments: boolean; - styleDeprecated: boolean; -}) { - const { totalRequests, filterValue /** filterMeta */ } = props; - const fieldsFromFragment = useFragment(GraphQLFields_FieldFragment, props.fields); - const { hasMetadataFilter, metadata: filterMeta } = useSchemaExplorerContext(); - - const sortedAndFilteredFields = useMemo(() => { - return fieldsFromFragment - .filter(field => { - let matchesFilter = true; - if (filterValue) { - matchesFilter &&= field.name.toLowerCase().includes(filterValue); - } - if (filterMeta.length) { - const matchesMeta = - field.supergraphMetadata && - ( - field.supergraphMetadata as SupergraphMetadataList_SupergraphMetadataFragmentFragment - ).metadata?.some(m => hasMetadataFilter(m.name, m.content)); - matchesFilter &&= !!matchesMeta; - } - return matchesFilter; - }) - .sort( - // Sort by usage DESC, name ASC - (a, b) => b.usage.total - a.usage.total || a.name.localeCompare(b.name), - ); - }, [fieldsFromFragment, filterValue, filterMeta]); - const [fields, collapsed, expand] = useCollapsibleList( - sortedAndFilteredFields, - 5, - props.collapsed ?? false, - ); - - return ( - -
- {fields.map((field, i) => { - const coordinate = `${props.typeName}.${field.name}`; - const isUsed = field.usage.total > 0; - const hasUnusedArguments = field.args.length > 0; - const showsUnusedSchema = typeof totalRequests !== 'number'; - const isDeprecated = field.isDeprecated; - - return ( - -
-
-
- {props.warnAboutUnusedArguments && - isUsed && - hasUnusedArguments && - showsUnusedSchema ? ( - - - This field is used but the presented arguments are not. - - - * - - - ) : null} - {props.warnAboutDeprecatedArguments && !isDeprecated ? ( - - - This field is not deprecated but the presented arguments are. - - - * - - - ) : null} - - - {field.name} - - - {field.args.length > 0 ? ( - - ) : null} - : - -
-
- {field.supergraphMetadata ? ( -
- -
- ) : null} - {typeof totalRequests === 'number' ? ( - - ) : null} -
-
- {field.description ? : null} -
-
- ); - })} - {collapsed && sortedAndFilteredFields.length > fields.length ? ( - - Show {sortedAndFilteredFields.length - fields.length} more fields - - ) : null} -
-
- ); -} - export function GraphQLInputFields(props: { typeName: string; fields: FragmentType[]; @@ -675,33 +333,12 @@ export function GraphQLInputFields(props: { projectSlug: string; organizationSlug: string; styleDeprecated: boolean; - filterValue?: string; }): ReactElement { const fields = useFragment(GraphQLInputFields_InputFieldFragment, props.fields); - const { filterValue } = props; - const { hasMetadataFilter, metadata: filterMeta } = useSchemaExplorerContext(); - const sortedAndFilteredFields = useMemo(() => { - return fields - .filter(field => { - let matchesFilter = true; - if (filterValue) { - matchesFilter &&= field.name.toLowerCase().includes(filterValue); - } - if (filterMeta.length) { - const matchesMeta = - field.supergraphMetadata && - ( - field.supergraphMetadata as SupergraphMetadataList_SupergraphMetadataFragmentFragment - ).metadata?.some(m => hasMetadataFilter(m.name, m.content)); - matchesFilter &&= !!matchesMeta; - } - return matchesFilter; - }) - .sort( - // Sort by usage DESC, name ASC - (a, b) => b.usage.total - a.usage.total || a.name.localeCompare(b.name), - ); - }, [fields, filterValue, filterMeta]); + + const sortedAndFilteredFields = useExplorerFieldFiltering({ + fields, + }); return (
@@ -735,7 +372,7 @@ export function GraphQLInputFields(props: { type={field.type} />
- {typeof props.totalRequests === 'number' ? ( + {typeof props.totalRequests === 'number' && ( - ) : null} + )}
- {field.description ? : null} + {field.description && } ); @@ -754,7 +391,7 @@ export function GraphQLInputFields(props: { ); } -function GraphQLTypeAsLink(props: { +export function GraphQLTypeAsLink(props: { type: string; className?: string; organizationSlug: string; diff --git a/packages/web/app/src/components/target/explorer/enum-type.tsx b/packages/web/app/src/components/target/explorer/enum-type.tsx index ddb2a232103..73cb3a72271 100644 --- a/packages/web/app/src/components/target/explorer/enum-type.tsx +++ b/packages/web/app/src/components/target/explorer/enum-type.tsx @@ -3,7 +3,7 @@ import { FragmentType, graphql, useFragment } from '@/gql'; import { useRouter } from '@tanstack/react-router'; import { DeprecationNote, - DescriptionInline, + Description, GraphQLTypeCard, GraphQLTypeCardListItem, LinkToCoordinatePage, @@ -99,16 +99,16 @@ export function GraphQLEnumTypeComponent(props: { {value.name} - {value.description ? : null} + {value.description && } - {value.supergraphMetadata ? ( + {value.supergraphMetadata && ( - ) : null} + )} ))} diff --git a/packages/web/app/src/components/target/explorer/filter.tsx b/packages/web/app/src/components/target/explorer/filter.tsx index 2f410b5cc95..f4d8d3efb60 100644 --- a/packages/web/app/src/components/target/explorer/filter.tsx +++ b/packages/web/app/src/components/target/explorer/filter.tsx @@ -27,7 +27,11 @@ import { useLocation, useRouter, } from '@tanstack/react-router'; -import { useArgumentListToggle, usePeriodSelector, useSchemaExplorerContext } from './provider'; +import { + useDescriptionsVisibleToggle, + usePeriodSelector, + useSchemaExplorerContext, +} from './provider'; const TypeFilter_AllTypes = graphql(` query TypeFilter_AllTypes( @@ -195,28 +199,28 @@ export function DateRangeFilter() { ); } -export function ArgumentVisibilityFilter() { - const [collapsed, toggleCollapsed] = useArgumentListToggle(); +export function DescriptionsVisibilityFilter() { + const { isDescriptionsVisible, toggleDescriptionsVisible } = useDescriptionsVisibleToggle(); return (
-
- List of arguments is collapsed by default. You can toggle this setting to display all - arguments. + Descriptions are not visible by default. You can toggle this setting to display all + descriptions.
@@ -320,7 +324,7 @@ export function MetadataFilter(props: { options: Array<{ name: string; values: s > {props.options.map(({ name, values }, i) => ( - {i > 0 ? : null} + {i > 0 && } { diff --git a/packages/web/app/src/components/target/explorer/graphql-arguments.tsx b/packages/web/app/src/components/target/explorer/graphql-arguments.tsx new file mode 100644 index 00000000000..951f9b7c42e --- /dev/null +++ b/packages/web/app/src/components/target/explorer/graphql-arguments.tsx @@ -0,0 +1,66 @@ +import { FragmentType, graphql, useFragment } from '@/gql'; +import { DeprecationNote, Description, GraphQLTypeAsLink, LinkToCoordinatePage } from './common'; +import { useDescriptionsVisibleToggle } from './provider'; + +export const GraphQLArguments_ArgumentFragment = graphql(` + fragment GraphQLArguments_ArgumentFragment on GraphQLArgument { + name + description + type + isDeprecated + deprecationReason + } +`); + +export function GraphQLArguments(props: { + parentCoordinate: string; + args: FragmentType[]; + styleDeprecated: boolean; + organizationSlug: string; + projectSlug: string; + targetSlug: string; +}) { + const args = useFragment(GraphQLArguments_ArgumentFragment, props.args); + + const { isDescriptionsVisible } = useDescriptionsVisibleToggle(); + + return ( + + ( +
+ {args.map(arg => { + const coordinate = `${props.parentCoordinate}.${arg.name}`; + return ( +
+ + + {arg.name} + + + {': '} + + {arg.description && isDescriptionsVisible && ( + + )} +
+ ); + })} +
+ ) +
+ ); +} diff --git a/packages/web/app/src/components/target/explorer/graphql-fields.tsx b/packages/web/app/src/components/target/explorer/graphql-fields.tsx new file mode 100644 index 00000000000..3b9bcdb8bb8 --- /dev/null +++ b/packages/web/app/src/components/target/explorer/graphql-fields.tsx @@ -0,0 +1,154 @@ +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { FragmentType, graphql, useFragment } from '@/gql'; +import { + DeprecationNote, + Description, + GraphQLTypeAsLink, + GraphQLTypeCardListItem, + LinkToCoordinatePage, + SchemaExplorerUsageStats, +} from './common'; +import { GraphQLArguments } from './graphql-arguments'; +import { SupergraphMetadataList } from './super-graph-metadata'; +import { useExplorerFieldFiltering } from './utils'; + +const GraphQLFields_FieldFragment = graphql(` + fragment GraphQLFields_FieldFragment on GraphQLField { + name + description + type + isDeprecated + deprecationReason + usage { + total + ...SchemaExplorerUsageStats_UsageFragment + } + args { + ...GraphQLArguments_ArgumentFragment + } + supergraphMetadata { + ...SupergraphMetadataList_SupergraphMetadataFragment + } + } +`); + +export function GraphQLFields(props: { + typeName: string; + fields: Array>; + totalRequests?: number; + targetSlug: string; + projectSlug: string; + organizationSlug: string; + warnAboutUnusedArguments: boolean; + warnAboutDeprecatedArguments: boolean; + styleDeprecated: boolean; +}) { + const { totalRequests } = props; + const fieldsFromFragment = useFragment(GraphQLFields_FieldFragment, props.fields); + + const sortedAndFilteredFields = useExplorerFieldFiltering({ + fields: fieldsFromFragment, + }); + + return ( + +
+ {sortedAndFilteredFields.map((field, i) => { + const coordinate = `${props.typeName}.${field.name}`; + const isUsed = field.usage.total > 0; + const hasArguments = field.args.length > 0; + const showsUnusedSchema = typeof totalRequests !== 'number'; + const isDeprecated = field.isDeprecated; + + return ( + +
+
+
+ {props.warnAboutUnusedArguments && + isUsed && + hasArguments && + showsUnusedSchema && ( + + + This field is used but the presented arguments are not. + + + * + + + )} + {props.warnAboutDeprecatedArguments && !isDeprecated && ( + + + This field is not deprecated but the presented arguments are. + + + * + + + )} + + + {field.name} + + + {field.args.length > 0 && ( + + )} + : + +
+
+ {field.supergraphMetadata && ( +
+ +
+ )} + {typeof totalRequests === 'number' && ( + + )} +
+
+ {field.description && } +
+
+ ); + })} +
+
+ ); +} diff --git a/packages/web/app/src/components/target/explorer/interface-type.tsx b/packages/web/app/src/components/target/explorer/interface-type.tsx index 32a1bcb4abc..652999b309a 100644 --- a/packages/web/app/src/components/target/explorer/interface-type.tsx +++ b/packages/web/app/src/components/target/explorer/interface-type.tsx @@ -1,6 +1,6 @@ import { FragmentType, graphql, useFragment } from '@/gql'; -import { useRouter } from '@tanstack/react-router'; -import { GraphQLFields, GraphQLTypeCard } from './common'; +import { GraphQLTypeCard } from './common'; +import { GraphQLFields } from './graphql-fields'; const GraphQLInterfaceTypeComponent_TypeFragment = graphql(` fragment GraphQLInterfaceTypeComponent_TypeFragment on GraphQLInterfaceType { @@ -29,12 +29,6 @@ export function GraphQLInterfaceTypeComponent(props: { warnAboutDeprecatedArguments: boolean; styleDeprecated: boolean; }) { - const router = useRouter(); - const searchObj = router.latestLocation.search; - const search = - 'search' in searchObj && typeof searchObj.search === 'string' - ? searchObj.search.toLowerCase() - : undefined; const ttype = useFragment(GraphQLInterfaceTypeComponent_TypeFragment, props.type); return ( ; totalRequests?: number; - collapsed?: boolean; organizationSlug: string; projectSlug: string; targetSlug: string; @@ -31,12 +30,6 @@ export function GraphQLObjectTypeComponent(props: { styleDeprecated: boolean; }) { const ttype = useFragment(GraphQLObjectTypeComponent_TypeFragment, props.type); - const router = useRouter(); - const searchObj = router.latestLocation.search; - const search = - 'search' in searchObj && typeof searchObj.search === 'string' - ? searchObj.search.toLowerCase() - : undefined; return ( ({ - isArgumentListCollapsed: true, - setArgumentListCollapsed: () => {}, + isDescriptionsVisible: true, + setDescriptionsVisible: () => {}, dataRetentionInDays: 7, startDate: startOfDay(subDays(new UTCDate(), 7)), period: defaultPeriod, @@ -72,9 +72,9 @@ export function SchemaExplorerProvider({ children }: { children: ReactNode }): R [dataRetentionInDays], ); - const [isArgumentListCollapsed, setArgumentListCollapsed] = useLocalStorageJson( + const [isDescriptionsVisible, setDescriptionsVisible] = useLocalStorageJson( 'hive:schema-explorer:collapsed', - z.boolean().default(true), + z.boolean().default(false), ); const [period, setPeriod] = useLocalStorageJson( 'hive:schema-explorer:period-1', @@ -86,8 +86,8 @@ export function SchemaExplorerProvider({ children }: { children: ReactNode }): R return ( { - setArgumentListCollapsed(!isArgumentListCollapsed); - }, [setArgumentListCollapsed, isArgumentListCollapsed]); +export function useDescriptionsVisibleToggle() { + const { isDescriptionsVisible, setDescriptionsVisible } = useSchemaExplorerContext(); + const toggleDescriptionsVisible = useCallback(() => { + setDescriptionsVisible(!isDescriptionsVisible); + }, [setDescriptionsVisible, isDescriptionsVisible]); - return [isArgumentListCollapsed, toggle] as const; + return { isDescriptionsVisible, toggleDescriptionsVisible }; } export function usePeriodSelector() { diff --git a/packages/web/app/src/components/target/explorer/scalar-type.tsx b/packages/web/app/src/components/target/explorer/scalar-type.tsx index 3ffbb3e22b6..fce680d15eb 100644 --- a/packages/web/app/src/components/target/explorer/scalar-type.tsx +++ b/packages/web/app/src/components/target/explorer/scalar-type.tsx @@ -34,9 +34,9 @@ export function GraphQLScalarTypeComponent(props: { >
- {typeof ttype.description === 'string' ? : null} + {typeof ttype.description === 'string' && }
- {typeof props.totalRequests === 'number' ? ( + {typeof props.totalRequests === 'number' && ( - ) : null} + )}
); diff --git a/packages/web/app/src/components/target/explorer/super-graph-metadata.tsx b/packages/web/app/src/components/target/explorer/super-graph-metadata.tsx index 5e750d52c63..5c39d7c23d7 100644 --- a/packages/web/app/src/components/target/explorer/super-graph-metadata.tsx +++ b/packages/web/app/src/components/target/explorer/super-graph-metadata.tsx @@ -60,7 +60,7 @@ function SubgraphChip(props: { > {props.text} - {props.metadata?.length ? * : null} + {props.metadata?.length && *} ); @@ -196,7 +196,7 @@ export function SupergraphMetadataList(props: {
{meta} {previewItems}{' '} - {allItems ? ( + {allItems && ( @@ -220,7 +220,7 @@ export function SupergraphMetadataList(props: { + {allItems.length - previewItems.length} more - ) : null} + )}
); } diff --git a/packages/web/app/src/components/target/explorer/union-type.tsx b/packages/web/app/src/components/target/explorer/union-type.tsx index 6931be9d697..08591788dda 100644 --- a/packages/web/app/src/components/target/explorer/union-type.tsx +++ b/packages/web/app/src/components/target/explorer/union-type.tsx @@ -47,7 +47,7 @@ export function GraphQLUnionTypeComponent(props: { {ttype.members.map((member, i) => (
{member.name}
- {typeof props.totalRequests === 'number' ? ( + {typeof props.totalRequests === 'number' && ( - ) : null} - {member.supergraphMetadata ? ( + )} + {member.supergraphMetadata && ( - ) : null} + )}
))} diff --git a/packages/web/app/src/components/target/explorer/utils.ts b/packages/web/app/src/components/target/explorer/utils.ts new file mode 100644 index 00000000000..e9a341e8b8d --- /dev/null +++ b/packages/web/app/src/components/target/explorer/utils.ts @@ -0,0 +1,41 @@ +import { useMemo } from 'react'; +import { useRouter } from '@tanstack/react-router'; +import { + GraphQlFields_FieldFragmentFragment, + GraphQlInputFields_InputFieldFragmentFragment, + SupergraphMetadataList_SupergraphMetadataFragmentFragment, +} from '../../../gql/graphql'; +import { useSchemaExplorerContext } from './provider'; + +export function useExplorerFieldFiltering< + T extends GraphQlFields_FieldFragmentFragment | GraphQlInputFields_InputFieldFragmentFragment, +>({ fields }: { fields: T[] }) { + const { hasMetadataFilter, metadata: filterMeta } = useSchemaExplorerContext(); + + const router = useRouter(); + const searchObj = router.latestLocation.search; + const search = + 'search' in searchObj && typeof searchObj.search === 'string' + ? searchObj.search.toLowerCase() + : undefined; + + return useMemo(() => { + return fields + .filter(field => { + let doesMatchFilter = true; + if (search) { + doesMatchFilter &&= field.name.toLowerCase().includes(search); + } + if (filterMeta.length) { + const doesMatchMeta = + field.supergraphMetadata && + ( + field.supergraphMetadata as SupergraphMetadataList_SupergraphMetadataFragmentFragment + ).metadata?.some(m => hasMetadataFilter(m.name, m.content)); + doesMatchFilter &&= !!doesMatchMeta; + } + return doesMatchFilter; + }) + .sort((a, b) => b.usage.total - a.usage.total || a.name.localeCompare(b.name)); + }, [fields, search, filterMeta, hasMetadataFilter]); +} diff --git a/packages/web/app/src/pages/target-explorer-type.tsx b/packages/web/app/src/pages/target-explorer-type.tsx index 9aeb14cdebb..b01ead08794 100644 --- a/packages/web/app/src/pages/target-explorer-type.tsx +++ b/packages/web/app/src/pages/target-explorer-type.tsx @@ -7,8 +7,8 @@ import { } from '@/components/target/explorer/common'; import { GraphQLEnumTypeComponent } from '@/components/target/explorer/enum-type'; import { - ArgumentVisibilityFilter, DateRangeFilter, + DescriptionsVisibilityFilter, FieldByNameFilter, MetadataFilter, SchemaVariantFilter, @@ -237,7 +237,7 @@ function TypeExplorerPageContent(props: { /> - + - + **Tip**: All filter fields are optional - combine them as needed for your search requirements + + --- + + *See `ProductFilterInput` type for complete field definitions and constraints* + """ + filter: ProductFilterInput! + ): [ProductItf!]! """ Get products filtered by category and price range """ - products(category: String, minPrice: Float, maxPrice: Float): [ProductItf!]! + products( + """ + Optional category name to filter products + """ + category: String + """ + Minimum price threshold (inclusive) + """ + minPrice: Float + """ + Maximum price threshold (inclusive) + """ + maxPrice: Float + ): [ProductItf!]! """ Get products filtered by physical dimensions and shipping class """ productsByDimensions( + """ + Minimum weight in pounds (inclusive) + """ minWeight: Float + """ + Maximum weight in pounds (inclusive) + """ maxWeight: Float + """ + Size description to match (e.g., 'Small', 'Large', '10x5x3') + """ size: String + """ + Shipping class filter (STANDARD, EXPRESS, or OVERNIGHT) + """ shippingClass: ShippingClass ): [ProductItf!]! } diff --git a/scripts/seed-schemas/federated/reviews.graphql b/scripts/seed-schemas/federated/reviews.graphql index 9657ec8992b..85c75e5ea56 100644 --- a/scripts/seed-schemas/federated/reviews.graphql +++ b/scripts/seed-schemas/federated/reviews.graphql @@ -68,16 +68,39 @@ type Query { """ Get a specific review by ID """ - review(id: Int!): Review + review( + """ + Unique identifier of the review to retrieve + """ + id: Int! + ): Review """ Get reviews filtered by product and rating range """ - reviews(productUpc: String, minRating: Int, maxRating: Int): [Review] + reviews( + """ + Optional Universal Product Code to filter reviews by product + """ + productUpc: String + """ + Minimum rating threshold (inclusive) + """ + minRating: Int + """ + Maximum rating threshold (inclusive) + """ + maxRating: Int + ): [Review] } type Mutation { """ Create a new product review """ - createReview(input: CreateReviewInput!): Review! + createReview( + """ + Review creation data including product UPC, rating, and optional body text + """ + input: CreateReviewInput! + ): Review! } diff --git a/scripts/seed-schemas/federated/users.graphql b/scripts/seed-schemas/federated/users.graphql index 7a71de62798..1ea5f76cb85 100644 --- a/scripts/seed-schemas/federated/users.graphql +++ b/scripts/seed-schemas/federated/users.graphql @@ -96,11 +96,30 @@ input UpdateUserInput { extend type Query { me: User - user(id: ID!): User + user( + """ + Unique identifier of the user to retrieve + """ + id: ID! + ): User users: [User] } extend type Mutation { - createUser(input: CreateUserInput!): User! - updateUser(id: ID!, input: UpdateUserInput!): User + createUser( + """ + User creation data including email, name, and optional alias + """ + input: CreateUserInput! + ): User! + updateUser( + """ + Unique identifier of the user to update + """ + id: ID! + """ + User update data including optional name and alias changes + """ + input: UpdateUserInput! + ): User }