From faa6cce135b7e6481bff4a15123cdeb27bcc283d Mon Sep 17 00:00:00 2001 From: Ashley Date: Thu, 14 Nov 2024 18:13:43 -0500 Subject: [PATCH] Copilot Landing Page Filters (#53113) Co-authored-by: Hector Alfaro --- src/landings/components/CategoryLanding.tsx | 70 ++++++++--- .../components/CookBookArticleCard.tsx | 7 +- src/landings/components/CookBookFilter.tsx | 114 +++++++++++++----- 3 files changed, 144 insertions(+), 47 deletions(-) diff --git a/src/landings/components/CategoryLanding.tsx b/src/landings/components/CategoryLanding.tsx index b3b4913f00c9..07e294ed6de9 100644 --- a/src/landings/components/CategoryLanding.tsx +++ b/src/landings/components/CategoryLanding.tsx @@ -4,7 +4,6 @@ import cx from 'classnames' import { CookBookArticleCard } from './CookBookArticleCard' import { CookBookFilter } from './CookBookFilter' -//import { useTranslation } from 'src/languages/components/useTranslation' import { DefaultLayout } from 'src/frame/components/DefaultLayout' import { ArticleTitle } from 'src/frame/components/article/ArticleTitle' import { Lead } from 'src/frame/components/ui/Lead' @@ -12,32 +11,65 @@ import { useCategoryLandingContext } from 'src/frame/components/context/Category import { ClientSideRedirects } from 'src/rest/components/ClientSideRedirects' import { RestRedirect } from 'src/rest/components/RestRedirect' import { Breadcrumbs } from 'src/frame/components/page-header/Breadcrumbs' -import { ArticleCardItems } from '../types' +import { ArticleCardItems } from 'src/landings/types' export const CategoryLanding = () => { const router = useRouter() - //const { t } = useTranslation('toc') const { title, intro, tocItems } = useCategoryLandingContext() // tocItems contains directories and its children, we only want the child articles const onlyFlatItems: ArticleCardItems = tocItems.flatMap((item) => item.childTocItems || []) - const [searchResults, setSearchResults] = useState(onlyFlatItems) + const [searchQuery, setSearchQuery] = useState('') + const [selectedCategory, setSelectedCategory] = useState('All') + const [selectedComplexity, setSelectedComplexity] = useState('All') - const handleSearch = (query: string) => { - const results = onlyFlatItems.filter((token) => { - return Object.values(token).some((value) => { - if (typeof value === 'string') { - return value.toLowerCase().includes(query.toLowerCase()) - } else if (Array.isArray(value)) { - return value.some((item) => item.toLowerCase().includes(query.toLowerCase())) - } - return false + const applyFilters = () => { + let results = onlyFlatItems + + if (searchQuery) { + results = results.filter((token) => { + return Object.values(token).some((value) => { + if (typeof value === 'string') { + return value.toLowerCase().includes(searchQuery.toLowerCase()) + } else if (Array.isArray(value)) { + return value.some((item) => item.toLowerCase().includes(searchQuery.toLowerCase())) + } + return false + }) }) - }) - setSearchResults(results) + } + + if (selectedCategory !== 'All') { + results = results.filter((item) => item.category?.includes(selectedCategory)) + } + + if (selectedComplexity !== 'All') { + results = results.filter((item) => item.complexity?.includes(selectedComplexity)) + } + + return results + } + + const searchResults = applyFilters() + + const handleSearch = (query: string) => { + setSearchQuery(query) } + const handleFilter = (option: string, type: 'category' | 'complexity') => { + if (type === 'category') { + setSelectedCategory(option) + } else if (type === 'complexity') { + setSelectedComplexity(option) + } + } + + const handleResetFilter = () => { + setSearchQuery('') + setSelectedCategory('All') + setSelectedComplexity('All') + } return ( {router.route === '/[versionId]/rest/[category]' && } @@ -65,7 +97,12 @@ export const CategoryLanding = () => {

Explore {searchResults.length} prompt articles

- +
    @@ -80,6 +117,7 @@ export const CategoryLanding = () => { ...(item.category || []), ...(item.complexity || []), ]} + url={item.fullPath} /> ))} diff --git a/src/landings/components/CookBookArticleCard.tsx b/src/landings/components/CookBookArticleCard.tsx index f89bdd282419..417ec6771e9f 100644 --- a/src/landings/components/CookBookArticleCard.tsx +++ b/src/landings/components/CookBookArticleCard.tsx @@ -1,4 +1,4 @@ -import { Label, LabelGroup } from '@primer/react' +import { Label, LabelGroup, Link } from '@primer/react' import { BugIcon } from '@primer/octicons-react' type Props = { @@ -55,6 +55,7 @@ export const CookBookArticleCard = ({ tags = defaultProps.tags, description = defaultProps.description, image = '', + url, spotlight = false, }: Props) => { return ( @@ -66,7 +67,9 @@ export const CookBookArticleCard = ({ {spotlight ? setImage(image) : null} {spotlight ? setIcon('none') : setIcon(icon)}
    -

    {title}

    + +

    {title}

    +
    {description}
    {tags.map((tag, index) => ( diff --git a/src/landings/components/CookBookFilter.tsx b/src/landings/components/CookBookFilter.tsx index dbd70c81d5bd..160a11f3a625 100644 --- a/src/landings/components/CookBookFilter.tsx +++ b/src/landings/components/CookBookFilter.tsx @@ -1,16 +1,52 @@ -import { TextInput, ActionMenu, ActionList, Button } from '@primer/react' +import { TextInput, ActionMenu, ActionList, Button, Box } from '@primer/react' import { SearchIcon } from '@primer/octicons-react' -import { useRef, useEffect } from 'react' +import { useRef, useEffect, useState } from 'react' import { ArticleCardItems } from '#src/landings/types.ts' type Props = { tokens: ArticleCardItems onSearch: (query: string) => void isSearchOpen?: boolean + handleFilter: (option: string, type: 'category' | 'complexity') => void + handleResetFilter: () => void } -export const CookBookFilter = ({ onSearch, isSearchOpen }: Props) => { +export const CookBookFilter = ({ + onSearch, + isSearchOpen, + tokens, + handleFilter, + handleResetFilter, +}: Props) => { + const categories: string[] = ['All', ...new Set(tokens.flatMap((item) => item.category || []))] + const complexities: string[] = [ + 'All', + ...new Set(tokens.flatMap((item) => item.complexity || [])), + ] + + const [selectedCategory, setSelectedCategory] = useState(0) + const [selectedComplexity, setSelectedComplexity] = useState(0) + const inputRef = useRef(null) + + const onFilter = (option: string, type: 'category' | 'complexity', index: number) => { + if (type === 'category') { + setSelectedCategory(index) + } else if (type === 'complexity') { + setSelectedComplexity(index) + } + handleFilter(option, type) + } + + const onResetFilter = () => { + setSelectedCategory(0) + setSelectedComplexity(0) + handleResetFilter() + if (inputRef.current) { + inputRef.current.value = '' + } + } + useEffect(() => { if (isSearchOpen) { inputRef.current?.focus() @@ -38,43 +74,63 @@ export const CookBookFilter = ({ onSearch, isSearchOpen }: Props) => {
    - Category + + Category: + {' '} + {categories[selectedCategory]} - - - Item 1 - Item 2 - Item 3 + + + {categories.map((category, index) => ( + onFilter(category, 'category', index)} + > + {category} + + ))} - - Complexity + + + Complexity: + {' '} + {complexities[selectedComplexity]} - - - Item 1 - Item 2 - Item 3 + + + {complexities.map((complexity, index) => ( + onFilter(complexity, 'complexity', index)} + > + {complexity} + + ))} - - - Industry - - - - Item 1 - Item 2 - Item 3 - - - -