Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ const ConfigDetailsPage = dynamic(
)
);

const ConfigDetailsIndexRedirect = dynamic(
import(
"@flanksource-ui/pages/config/details/ConfigDetailsIndexRedirect"
).then((mod) => mod.ConfigDetailsIndexRedirect)
);

const ConfigDetailsChangesPage = dynamic(
import("@flanksource-ui/pages/config/details/ConfigDetailsChangesPage").then(
(mod) => mod.ConfigDetailsChangesPage
Expand Down Expand Up @@ -897,6 +903,19 @@ export function IncidentManagerRoutes({ sidebar }: { sidebar: ReactNode }) {
<Route path=":id">
<Route
index
element={
<ErrorBoundary>
{withAuthorizationAccessCheck(
<ConfigDetailsIndexRedirect />,
tables.database,
"read",
true
)}
</ErrorBoundary>
}
/>
<Route
path="spec"
element={
<ErrorBoundary>
{withAuthorizationAccessCheck(
Expand Down
1 change: 1 addition & 0 deletions src/api/services/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type ViewListItem = {
namespace?: string;
title?: string;
icon?: string;
ordinal?: number;
};

export const getAllViews = (
Expand Down
4 changes: 2 additions & 2 deletions src/components/Configs/ConfigDetailsTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type ConfigDetailsTabsProps = {
isLoading?: boolean;
pageTitlePrefix: string;
activeTabName:
| "Catalog"
| "Spec"
| "Changes"
| "Insights"
| "Relationships"
Expand All @@ -33,7 +33,7 @@ export function ConfigDetailsTabs({
children,
isLoading = false,
pageTitlePrefix,
activeTabName = "Catalog",
activeTabName = "Spec",
className = "p-2"
}: ConfigDetailsTabsProps) {
const { id } = useParams();
Expand Down
46 changes: 40 additions & 6 deletions src/components/Configs/ConfigTabsLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@ import { ConfigItem } from "../../api/types/configs";
import { getViewsByConfigId } from "../../api/services/views";
import { useQuery } from "@tanstack/react-query";
import { Icon } from "@flanksource-ui/ui/Icons/Icon";
import { ReactNode } from "react";

export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]) {
type ConfigDetailsTab = {
label: ReactNode;
key: string;
path: string;
icon?: ReactNode;
search?: string;
};

export function useConfigDetailsTabs(
countSummary?: ConfigItem["summary"]
): ConfigDetailsTab[] {
const { id } = useParams<{ id: string }>();

const { data: views = [] } = useQuery({
Expand All @@ -14,8 +25,8 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]) {
enabled: !!id
});

const staticTabs = [
{ label: "Config", key: "Catalog", path: `/catalog/${id}` },
const staticTabs: ConfigDetailsTab[] = [
{ label: "Spec", key: "Spec", path: `/catalog/${id}/spec` },
{
label: (
<>
Expand Down Expand Up @@ -68,12 +79,35 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]) {
}
];

const viewTabs = views.map((view) => ({
const hasExplicitOrdering = views.some((view) => view.ordinal != null);

const orderedViews = hasExplicitOrdering
? [...views].sort((a, b) => {
const aOrdinal = a.ordinal ?? Number.MAX_SAFE_INTEGER;
const bOrdinal = b.ordinal ?? Number.MAX_SAFE_INTEGER;

if (aOrdinal !== bOrdinal) {
return aOrdinal - bOrdinal;
}

const aLabel = a.title || a.name;
const bLabel = b.title || b.name;

return aLabel.localeCompare(bLabel);
})
: views;

const viewTabs: ConfigDetailsTab[] = orderedViews.map((view) => ({
label: view.title || view.name,
key: view.id,
path: `/catalog/${id}/view/${view.id}`,
icon: <Icon name={view.icon} />
icon: <Icon name={view.icon || "workflow"} />
}));

return [...staticTabs, ...viewTabs];
if (viewTabs.length === 0) {
return staticTabs;
}

// Views configured for a config should appear ahead of the built-in tabs.
return [...viewTabs, ...staticTabs];
}
59 changes: 59 additions & 0 deletions src/pages/config/details/ConfigDetailsIndexRedirect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useGetConfigByIdQuery } from "@flanksource-ui/api/query-hooks";
import { useConfigDetailsTabs } from "@flanksource-ui/components/Configs/ConfigTabsLinks";
import { Loading } from "@flanksource-ui/ui/Loading";
import { useEffect } from "react";
import { useLocation, useNavigate, useParams } from "react-router-dom";

export function ConfigDetailsIndexRedirect() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const location = useLocation();

const idParam = id ?? "";

const {
data: configDetails,
isLoading: isConfigLoading,
isError: isConfigError
} = useGetConfigByIdQuery(idParam);

const tabs = useConfigDetailsTabs(configDetails?.summary);

useEffect(() => {
if (!id || tabs.length === 0) {
return;
}

const firstTab = tabs[0];

if (!firstTab || location.pathname === firstTab.path) {
return;
}

navigate(
{
pathname: firstTab.path,
search: firstTab.search ?? location.search
},
{ replace: true }
);
}, [id, tabs, navigate, location.pathname, location.search]);

if (isConfigLoading && tabs.length === 0) {
return (
<div className="flex h-full items-center justify-center">
<Loading />
</div>
);
}

if (isConfigError) {
return (
<div className="flex h-full items-center justify-center text-gray-500">
Failed to load config
</div>
);
}

return null;
}
2 changes: 1 addition & 1 deletion src/pages/config/details/ConfigDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export function ConfigDetailsPage() {
pageTitlePrefix={"Catalog"}
isLoading={isLoading}
refetch={refetch}
activeTabName="Catalog"
activeTabName="Spec"
className=""
>
<div className={`relative flex h-full flex-1 flex-col pb-0`}>
Expand Down
Loading