import { useAutoAnimate } from "@formkit/auto-animate/react"; import type { AppCategories } from "@prisma/client"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import type { UIEvent } from "react"; import { useEffect, useRef, useState } from "react"; import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams"; import { classNames } from "@calcom/lib"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { AppFrontendPayload as App } from "@calcom/types/App"; import type { CredentialFrontendPayload as Credential } from "@calcom/types/Credential"; import { EmptyScreen } from "../empty-screen"; import { ChevronLeft, ChevronRight, Search } from "../icon"; import { AppCard } from "./AppCard"; export function useShouldShowArrows() { const ref = useRef(null); const [showArrowScroll, setShowArrowScroll] = useState({ left: false, right: false, }); useEffect(() => { const appCategoryList = ref.current; if (appCategoryList && appCategoryList.scrollWidth > appCategoryList.clientWidth) { setShowArrowScroll({ left: false, right: true }); } }, []); const calculateScroll = (e: UIEvent) => { setShowArrowScroll({ left: e.currentTarget.scrollLeft > 0, right: Math.floor(e.currentTarget.scrollWidth) - Math.floor(e.currentTarget.offsetWidth) !== Math.floor(e.currentTarget.scrollLeft), }); }; return { ref, calculateScroll, leftVisible: showArrowScroll.left, rightVisible: showArrowScroll.right }; } type AllAppsPropsType = { apps: (App & { credentials?: Credential[] })[]; searchText?: string; categories: string[]; userAdminTeams?: UserAdminTeams; }; interface CategoryTabProps { selectedCategory: string | null; categories: string[]; searchText?: string; } function CategoryTab({ selectedCategory, categories, searchText }: CategoryTabProps) { const pathname = usePathname(); const searchParams = useSearchParams(); const { t } = useLocale(); const router = useRouter(); const { ref, calculateScroll, leftVisible, rightVisible } = useShouldShowArrows(); const handleLeft = () => { if (ref.current) { ref.current.scrollLeft -= 100; } }; const handleRight = () => { if (ref.current) { ref.current.scrollLeft += 100; } }; return (

{searchText ? t("search") : t("category_apps", { category: (selectedCategory && selectedCategory[0].toUpperCase() + selectedCategory.slice(1)) || t("all"), })}

{leftVisible && ( )}
    calculateScroll(e)} ref={ref}>
  • { if (pathname !== null) { router.replace(pathname); } }} className={classNames( selectedCategory === null ? "bg-emphasis text-default" : "bg-muted text-emphasis", "hover:bg-emphasis min-w-max rounded-md px-4 py-2.5 text-sm font-medium hover:cursor-pointer" )}> {t("all")}
  • {categories.map((cat, pos) => (
  • { if (selectedCategory === cat) { if (pathname !== null) { router.replace(pathname); } } else { const _searchParams = new URLSearchParams(searchParams ?? undefined); _searchParams.set("category", cat); router.replace(`${pathname}?${_searchParams.toString()}`); } }} className={classNames( selectedCategory === cat ? "bg-emphasis text-default" : "bg-muted text-emphasis", "hover:bg-emphasis rounded-md px-4 py-2.5 text-sm font-medium hover:cursor-pointer" )}> {cat[0].toUpperCase() + cat.slice(1)}
  • ))}
{rightVisible && ( )}
); } export function AllApps({ apps, searchText, categories, userAdminTeams }: AllAppsPropsType) { const searchParams = useSearchParams(); const { t } = useLocale(); const [selectedCategory, setSelectedCategory] = useState(null); const [appsContainerRef, enableAnimation] = useAutoAnimate(); const categoryQuery = searchParams?.get("category"); if (searchText) { enableAnimation && enableAnimation(false); } useEffect(() => { const queryCategory = typeof categoryQuery === "string" && categories.includes(categoryQuery) ? categoryQuery : null; setSelectedCategory(queryCategory); // eslint-disable-next-line react-hooks/exhaustive-deps }, [categoryQuery]); const filteredApps = apps .filter((app) => selectedCategory !== null ? app.categories ? app.categories.includes(selectedCategory as AppCategories) : app.category === selectedCategory : true ) .filter((app) => (searchText ? app.name.toLowerCase().includes(searchText.toLowerCase()) : true)) .sort(function (a, b) { if (a.name < b.name) return -1; else if (a.name > b.name) return 1; return 0; }); return (
{filteredApps.length ? (
{filteredApps.map((app) => ( ))}{" "}
) : ( )}
); }