Files
prowler/ui/app/(prowler)/providers/page.tsx
T
Pablo Fernandez Guerra (PFE) d23c2f3b53 refactor(ui): standardize "Providers" wording across UI and docs (#10971)
Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:39:54 +02:00

118 lines
4.0 KiB
TypeScript

import { Suspense } from "react";
import { ProvidersAccountsView } from "@/components/providers";
import { SkeletonTableProviders } from "@/components/providers/table";
import { Skeleton } from "@/components/shadcn/skeleton/skeleton";
import { ContentLayout } from "@/components/ui";
import { FilterTransitionWrapper } from "@/contexts";
import { SearchParamsProps } from "@/types";
import { ProviderGroupsContent } from "./provider-groups-content";
import { ProviderPageTabs } from "./provider-page-tabs";
import { getProviderTab } from "./provider-page-tabs.shared";
import { loadProvidersAccountsViewData } from "./providers-page.utils";
export default async function Providers({
searchParams,
}: {
searchParams: Promise<SearchParamsProps>;
}) {
const resolvedSearchParams = await searchParams;
const activeTab = getProviderTab(resolvedSearchParams.tab);
// Exclude `tab` from the Suspense key so switching tabs doesn't re-suspend
const { tab: _, ...paramsWithoutTab } = resolvedSearchParams || {};
const searchParamsKey = JSON.stringify(paramsWithoutTab);
return (
<ContentLayout title="Providers" icon="lucide:cloud-cog">
<FilterTransitionWrapper>
<ProviderPageTabs
activeTab={activeTab}
providersContent={
<Suspense
key={`providers-${searchParamsKey}`}
fallback={<ProvidersTableFallback />}
>
<ProvidersTabContent searchParams={resolvedSearchParams} />
</Suspense>
}
providerGroupsContent={
<Suspense
key={`groups-${searchParamsKey}`}
fallback={<ProviderGroupsFallback />}
>
<ProviderGroupsContent searchParams={resolvedSearchParams} />
</Suspense>
}
/>
</FilterTransitionWrapper>
</ContentLayout>
);
}
const ProvidersTableFallback = () => {
return (
<div className="flex flex-col gap-6">
<div className="flex flex-wrap items-center gap-4">
{/* ProviderTypeSelector */}
<Skeleton className="h-[52px] min-w-[200px] flex-1 rounded-lg md:max-w-[280px]" />
{/* Organizations filter */}
<Skeleton className="h-[52px] max-w-[240px] min-w-[180px] flex-1 rounded-lg" />
{/* Provider Groups filter */}
<Skeleton className="h-[52px] max-w-[240px] min-w-[180px] flex-1 rounded-lg" />
{/* Status filter */}
<Skeleton className="h-[52px] max-w-[240px] min-w-[180px] flex-1 rounded-lg" />
{/* Action buttons */}
<div className="ml-auto flex flex-wrap gap-4">
<Skeleton className="h-9 w-[160px] rounded-md" />
<Skeleton className="h-9 w-[120px] rounded-md" />
</div>
</div>
<SkeletonTableProviders />
</div>
);
};
const ProviderGroupsFallback = () => {
return (
<div className="grid min-h-[50vh] grid-cols-1 items-start gap-8 md:grid-cols-12">
<div className="col-span-1 md:col-span-4">
<div className="flex flex-col gap-4">
<Skeleton className="h-7 w-48 rounded" />
<Skeleton className="h-4 w-64 rounded" />
<Skeleton className="h-10 w-full rounded-md" />
<Skeleton className="h-10 w-full rounded-md" />
<Skeleton className="h-10 w-full rounded-md" />
<Skeleton className="h-10 w-32 rounded-md" />
</div>
</div>
<div className="col-span-1 md:col-span-1" />
<div className="col-span-1 md:col-span-6">
<SkeletonTableProviders />
</div>
</div>
);
};
const ProvidersTabContent = async ({
searchParams,
}: {
searchParams: SearchParamsProps;
}) => {
const providersView = await loadProvidersAccountsViewData({
searchParams,
isCloud: process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true",
});
return (
<ProvidersAccountsView
isCloud={process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true"}
filters={providersView.filters}
providers={providersView.providers}
metadata={providersView.metadata}
rows={providersView.rows}
/>
);
};