diff --git a/ui/v2.5/src/components/FrontPage/FilteredRecommendationRow.tsx b/ui/v2.5/src/components/FrontPage/FilteredRecommendationRow.tsx new file mode 100644 index 000000000..8cf27a625 --- /dev/null +++ b/ui/v2.5/src/components/FrontPage/FilteredRecommendationRow.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import Slider from "@ant-design/react-slick"; +import { ListFilterModel } from "src/models/list-filter/filter"; +import { getSlickSliderSettings } from "src/core/recommendations"; +import { RecommendationRow } from "../FrontPage/RecommendationRow"; +import { FormattedMessage } from "react-intl"; +import { PatchComponent } from "src/patch"; +import { UnsupportedCriterion } from "src/models/list-filter/criteria/criterion"; +import { PopoverCard, WarningHoverPopover } from "../Shared/HoverPopover"; + +interface IProps { + className?: string; + isTouch: boolean; + filter: ListFilterModel; + heading: string; + count: number; + loading: boolean; + url: string; +} + +export const FilteredRecommendationRow: React.FC = PatchComponent( + "FilteredRecommendationRow", + (props) => { + const cardCount = props.count; + + const unsupportedCriteria = props.filter.criteria.filter( + (criterion) => criterion instanceof UnsupportedCriterion + ); + + const header = unsupportedCriteria.length ? ( +
+ {props.heading} + + c.criterionOption.type) + .join(", "), + }} + /> + + } + /> +
+ ) : ( + props.heading + ); + + if (!props.loading && !cardCount) { + return null; + } + + return ( + + + + } + > + + {props.children} + + + ); + } +); diff --git a/ui/v2.5/src/components/FrontPage/RecommendationRow.tsx b/ui/v2.5/src/components/FrontPage/RecommendationRow.tsx index 115d8642a..97e43f294 100644 --- a/ui/v2.5/src/components/FrontPage/RecommendationRow.tsx +++ b/ui/v2.5/src/components/FrontPage/RecommendationRow.tsx @@ -3,7 +3,7 @@ import { PatchComponent } from "src/patch"; interface IProps { className?: string; - header: string; + header: React.ReactNode; link: JSX.Element; } diff --git a/ui/v2.5/src/components/Galleries/GalleryRecommendationRow.tsx b/ui/v2.5/src/components/Galleries/GalleryRecommendationRow.tsx index b56b48c36..3df07b643 100644 --- a/ui/v2.5/src/components/Galleries/GalleryRecommendationRow.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryRecommendationRow.tsx @@ -1,13 +1,9 @@ import React from "react"; -import { Link } from "react-router-dom"; import { useFindGalleries } from "src/core/StashService"; -import Slider from "@ant-design/react-slick"; import { GalleryCard } from "./GalleryCard"; import { ListFilterModel } from "src/models/list-filter/filter"; -import { getSlickSliderSettings } from "src/core/recommendations"; -import { RecommendationRow } from "../FrontPage/RecommendationRow"; -import { FormattedMessage } from "react-intl"; import { PatchComponent } from "src/patch"; +import { FilteredRecommendationRow } from "../FrontPage/FilteredRecommendationRow"; interface IProps { isTouch: boolean; @@ -19,40 +15,29 @@ export const GalleryRecommendationRow: React.FC = PatchComponent( "GalleryRecommendationRow", (props) => { const result = useFindGalleries(props.filter); - const cardCount = result.data?.findGalleries.count; - - if (!result.loading && !cardCount) { - return null; - } + const count = result.data?.findGalleries.count ?? 0; return ( - - - - } + heading={props.header} + url={`/galleries?${props.filter.makeQueryParameters()}`} + count={count} + loading={result.loading} + isTouch={props.isTouch} + filter={props.filter} > - - {result.loading - ? [...Array(props.filter.itemsPerPage)].map((i) => ( -
- )) - : result.data?.findGalleries.galleries.map((g) => ( - - ))} -
-
+ {result.loading + ? [...Array(props.filter.itemsPerPage)].map((i) => ( +
+ )) + : result.data?.findGalleries.galleries.map((g) => ( + + ))} + ); } ); diff --git a/ui/v2.5/src/components/Groups/GroupRecommendationRow.tsx b/ui/v2.5/src/components/Groups/GroupRecommendationRow.tsx index 228cb3467..b9e523b34 100644 --- a/ui/v2.5/src/components/Groups/GroupRecommendationRow.tsx +++ b/ui/v2.5/src/components/Groups/GroupRecommendationRow.tsx @@ -1,13 +1,9 @@ import React from "react"; -import { Link } from "react-router-dom"; import { useFindGroups } from "src/core/StashService"; -import Slider from "@ant-design/react-slick"; import { GroupCard } from "./GroupCard"; import { ListFilterModel } from "src/models/list-filter/filter"; -import { getSlickSliderSettings } from "src/core/recommendations"; -import { RecommendationRow } from "../FrontPage/RecommendationRow"; -import { FormattedMessage } from "react-intl"; import { PatchComponent } from "src/patch"; +import { FilteredRecommendationRow } from "../FrontPage/FilteredRecommendationRow"; interface IProps { isTouch: boolean; @@ -19,40 +15,26 @@ export const GroupRecommendationRow: React.FC = PatchComponent( "GroupRecommendationRow", (props: IProps) => { const result = useFindGroups(props.filter); - const cardCount = result.data?.findGroups.count; - - if (!result.loading && !cardCount) { - return null; - } + const count = result.data?.findGroups.count ?? 0; return ( - - - - } + heading={props.header} + url={`/groups?${props.filter.makeQueryParameters()}`} + count={count} + loading={result.loading} + isTouch={props.isTouch} + filter={props.filter} > - - {result.loading - ? [...Array(props.filter.itemsPerPage)].map((i) => ( -
- )) - : result.data?.findGroups.groups.map((g) => ( - - ))} -
-
+ {result.loading + ? [...Array(props.filter.itemsPerPage)].map((i) => ( +
+ )) + : result.data?.findGroups.groups.map((g) => ( + + ))} + ); } ); diff --git a/ui/v2.5/src/components/Images/ImageRecommendationRow.tsx b/ui/v2.5/src/components/Images/ImageRecommendationRow.tsx index 6499be894..0541e5934 100644 --- a/ui/v2.5/src/components/Images/ImageRecommendationRow.tsx +++ b/ui/v2.5/src/components/Images/ImageRecommendationRow.tsx @@ -1,13 +1,9 @@ import React from "react"; -import { Link } from "react-router-dom"; import { useFindImages } from "src/core/StashService"; -import Slider from "@ant-design/react-slick"; import { ListFilterModel } from "src/models/list-filter/filter"; -import { getSlickSliderSettings } from "src/core/recommendations"; -import { RecommendationRow } from "../FrontPage/RecommendationRow"; -import { FormattedMessage } from "react-intl"; import { ImageCard } from "./ImageCard"; import { PatchComponent } from "src/patch"; +import { FilteredRecommendationRow } from "../FrontPage/FilteredRecommendationRow"; interface IProps { isTouch: boolean; @@ -19,40 +15,26 @@ export const ImageRecommendationRow: React.FC = PatchComponent( "ImageRecommendationRow", (props: IProps) => { const result = useFindImages(props.filter); - const cardCount = result.data?.findImages.count; - - if (!result.loading && !cardCount) { - return null; - } + const count = result.data?.findImages.count ?? 0; return ( - - - - } + heading={props.header} + url={`/images?${props.filter.makeQueryParameters()}`} + count={count} + loading={result.loading} + isTouch={props.isTouch} + filter={props.filter} > - - {result.loading - ? [...Array(props.filter.itemsPerPage)].map((i) => ( -
- )) - : result.data?.findImages.images.map((i) => ( - - ))} -
-
+ {result.loading + ? [...Array(props.filter.itemsPerPage)].map((i) => ( +
+ )) + : result.data?.findImages.images.map((i) => ( + + ))} + ); } ); diff --git a/ui/v2.5/src/components/List/FilterTags.tsx b/ui/v2.5/src/components/List/FilterTags.tsx index 5597cae79..28c9f77fa 100644 --- a/ui/v2.5/src/components/List/FilterTags.tsx +++ b/ui/v2.5/src/components/List/FilterTags.tsx @@ -6,10 +6,17 @@ import React, { useRef, } from "react"; import { Badge, BadgeProps, Button, Overlay, Popover } from "react-bootstrap"; -import { Criterion } from "src/models/list-filter/criteria/criterion"; +import { + Criterion, + UnsupportedCriterion, +} from "src/models/list-filter/criteria/criterion"; import { FormattedMessage, useIntl } from "react-intl"; import { Icon } from "../Shared/Icon"; -import { faMagnifyingGlass, faTimes } from "@fortawesome/free-solid-svg-icons"; +import { + faExclamationTriangle, + faMagnifyingGlass, + faTimes, +} from "@fortawesome/free-solid-svg-icons"; import { BsPrefixProps, ReplaceProps } from "react-bootstrap/esm/helpers"; import { CustomFieldsCriterion } from "src/models/list-filter/criteria/custom-fields"; import { useDebounce } from "src/hooks/debounce"; @@ -38,9 +45,20 @@ export const FilterTag: React.FC<{ label: React.ReactNode; onClick: React.MouseEventHandler; onRemove: React.MouseEventHandler; -}> = ({ className, label, onClick, onRemove }) => { + unsupported?: boolean; +}> = ({ className, label, onClick, onRemove, unsupported }) => { + function handleClick(e: React.MouseEvent) { + if (unsupported) { + return; + } + onClick(e); + } + return ( - + + {unsupported && ( + + )} {label}