diff --git a/website/integrations/apps-logo.png b/website/integrations/apps-logo.png deleted file mode 100644 index cc0ba49b1372..000000000000 Binary files a/website/integrations/apps-logo.png and /dev/null differ diff --git a/website/integrations/index.mdx b/website/integrations/index.mdx index 9029a9486334..3874d1f13c98 100644 --- a/website/integrations/index.mdx +++ b/website/integrations/index.mdx @@ -2,18 +2,6 @@ title: Integrations overview --- -There are two main types of integrations with authentik: **Applications** and **Sources**. +import IntegrationsIndex from "@site/src/components/Integrations/IntegrationsIndex"; -## Applications - -authentik integrates with many applications. For a full list, and to learn more about adding documentation for a new application, refer to the [Applications](../integrations/services/index.mdx) documentation. - -![](./apps-logo.png) - -## Sources - -In addition to applications, authentik also integrates with external sources, including federated directories like Active Directory and through protocols such as LDAP, OAuth, SAML, and SCIM sources. Sources are a way for authentik to use external credentials for authentication and verification. Sources in authentik can also be used for social logins, using external providers such as Facebook, Twitter, etc. - -To learn more, refer to the [Sources](https://docs.goauthentik.io/docs/users-sources/sources/index) documentation. - -![](./sources-logo.png) + diff --git a/website/integrations/sources-logo.png b/website/integrations/sources-logo.png deleted file mode 100644 index 52fa99383417..000000000000 Binary files a/website/integrations/sources-logo.png and /dev/null differ diff --git a/website/src/components/Integrations/IntegrationsIndex.tsx b/website/src/components/Integrations/IntegrationsIndex.tsx new file mode 100644 index 000000000000..56db66d7ad96 --- /dev/null +++ b/website/src/components/Integrations/IntegrationsIndex.tsx @@ -0,0 +1,138 @@ +import React, { useMemo } from "react"; +import { useDocsSidebar } from "@docusaurus/plugin-content-docs/client"; +import type { + PropSidebar, + PropSidebarItem, + PropSidebarItemLink, + PropSidebarItemCategory, +} from "@docusaurus/plugin-content-docs"; +import clsx from "clsx"; +import DocCard from "@theme/DocCard"; + +const IGNORED_URLS = new Set(["/integrations/", "/integrations/services/"]); +const CARD_CLASSES = "col col--4 margin-bottom--lg"; + +interface CategoryGroup { + category: string; + items: PropSidebarItemLink[]; +} + +/** + * Type guard to determine if a sidebar item is a category + */ +const isCategory = (item: PropSidebarItem): item is PropSidebarItemCategory => { + return item.type === "category"; +}; + +/** + * Type guard to determine if a sidebar item is a link + */ +const isLink = (item: PropSidebarItem): item is PropSidebarItemLink => { + return item.type === "link"; +}; + +/** + * Custom hook to fetch and process all integration items from the sidebar + * Groups items by their parent category + */ +function useGroupedIntegrations(): CategoryGroup[] { + const sidebar = useDocsSidebar(); + + return useMemo(() => { + if (!sidebar) { + console.warn("No sidebar found for integrations"); + return []; + } + + /** + * Recursively processes sidebar items to maintain category structure + */ + const processItems = ( + items: PropSidebarItem[], + parentCategory?: string, + ): CategoryGroup[] => { + const groups: CategoryGroup[] = []; + const uncategorizedItems: PropSidebarItemLink[] = []; + + items.forEach((item) => { + if (isCategory(item)) { + // Process category items + const categoryGroups = processItems(item.items, item.label); + groups.push(...categoryGroups); + } else if (isLink(item) && !IGNORED_URLS.has(item.href)) { + // Collect links + if (parentCategory) { + const existingGroup = groups.find( + (g) => g.category === parentCategory, + ); + if (existingGroup) { + existingGroup.items.push(item); + } else { + groups.push({ + category: parentCategory, + items: [item], + }); + } + } else { + uncategorizedItems.push(item); + } + } + }); + + // Add uncategorized items if any exist + if (uncategorizedItems.length > 0) { + groups.push({ category: "Other", items: uncategorizedItems }); + } + + return groups; + }; + + const sidebarItems = Array.isArray(sidebar.items) ? sidebar.items : []; + return processItems(sidebarItems); + }, [sidebar]); +} + +/** + * Component to render a category section + */ +function CategorySection({ category, items }: CategoryGroup) { + return ( +
+

{category}

+
+ {items.map((item) => ( +
+ +
+ ))} +
+
+ ); +} + +/** + * Main component that renders the categorized integrations index page + */ +function IntegrationsIndex() { + const groupedIntegrations = useGroupedIntegrations(); + + return ( +
+ {groupedIntegrations.length > 0 ? ( + groupedIntegrations.map((group) => ( + + )) + ) : ( +
+

No integrations found.

+
+ )} +
+ ); +} + +export default IntegrationsIndex;