Skip to content

Commit 05d446e

Browse files
feat: components detail (#691)
1 parent feb697f commit 05d446e

31 files changed

+621
-229
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
},
5252
"devDependencies": {
5353
"@playwright/test": "^1.48.1",
54+
"@tailwindcss/container-queries": "^0.1.1",
5455
"@tailwindcss/forms": "^0.5.7",
5556
"@tailwindcss/typography": "^0.5.10",
5657
"@types/lodash.debounce": "^4.0.9",

pnpm-lock.yaml

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useParams } from "react-router-dom";
2+
import { StackComponentsDetailHeader } from "../../../components/stack-components/component-detail/Header";
3+
import { StackComponentTabs } from "@/components/stack-components/component-detail/Tabs";
4+
import { StackList } from "../../stacks/StackList";
5+
6+
export default function ComponentDetailPage() {
7+
const { componentId } = useParams() as { componentId: string };
8+
9+
return (
10+
<div className="@container">
11+
<StackComponentsDetailHeader isPanel={false} componentId={componentId} />
12+
<StackComponentTabs
13+
isPanel={false}
14+
stacksTabContent={<StackList fixedQueryParams={{ component_id: componentId }} />}
15+
componentId={componentId}
16+
/>
17+
</div>
18+
);
19+
}

src/app/components/columns.tsx

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { CopyButton } from "@/components/CopyButton";
22
import { DisplayDate } from "@/components/DisplayDate";
33
import { InlineAvatar } from "@/components/InlineAvatar";
4+
import { ComponentSheet } from "@/components/stack-components/component-sheet";
45
import { ComponentBadge } from "@/components/stack-components/ComponentBadge";
5-
import { ComponentFallbackDialog } from "@/components/stack-components/ComponentFallbackDialog";
66
import { snakeCaseToTitleCase } from "@/lib/strings";
77
import { sanitizeUrl } from "@/lib/url";
88
import { getUsername } from "@/lib/user";
@@ -29,18 +29,19 @@ export function getComponentList(): ColumnDef<StackComponent>[] {
2929
/>
3030
<div>
3131
<div className="flex items-center gap-1">
32-
<ComponentFallbackDialog
33-
name={name}
34-
type={row.original.body?.type || "orchestrator"}
35-
>
32+
<ComponentSheet componentId={id}>
3633
<button>
3734
<h2 className="text-text-md font-semibold">{name}</h2>
3835
</button>
39-
</ComponentFallbackDialog>
36+
</ComponentSheet>
4037
<CopyButton copyText={name} />
4138
</div>
4239
<div className="flex items-center gap-1">
43-
<p className="text-text-xs text-theme-text-secondary">{id.split("-")[0]}</p>
40+
<ComponentSheet componentId={id}>
41+
<button className="text-text-xs text-theme-text-secondary">
42+
{id.split("-")[0]}
43+
</button>
44+
</ComponentSheet>
4445
<CopyButton copyText={id} />
4546
</div>
4647
</div>

src/app/stacks/ResumeBanner.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { useState } from "react";
2+
import { parseWizardData } from "./create/new-infrastructure/persist";
3+
import { parseWizardData as parseTerraform } from "./create/terraform/persist";
4+
import { ResumeStackBanner } from "./ResumeStackBanner";
5+
import { ResumeTerraformBanner } from "./ResumeTerraformBanner";
6+
7+
export function ResumeBanners() {
8+
const [hasResumeableStack, setResumeableStack] = useState(parseWizardData().success);
9+
const [hasResumeableTerraform, setResumeableTerraform] = useState<boolean>(
10+
parseTerraform().success
11+
);
12+
13+
return (
14+
<div className="space-y-5">
15+
{hasResumeableStack && <ResumeStackBanner setHasResumeableStack={setResumeableStack} />}
16+
{hasResumeableTerraform && (
17+
<ResumeTerraformBanner setHasResumeableTerraform={setResumeableTerraform} />
18+
)}
19+
</div>
20+
);
21+
}

src/app/stacks/StackList.tsx

+7-15
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,19 @@ import { stackQueries } from "@/data/stacks";
66
import { routes } from "@/router/routes";
77
import { useQuery } from "@tanstack/react-query";
88
import { Button, DataTable, Skeleton } from "@zenml-io/react-component-library";
9-
import { useState } from "react";
109
import { Link } from "react-router-dom";
1110
import { getStackColumns } from "./columns";
12-
import { parseWizardData } from "./create/new-infrastructure/persist";
13-
import { parseWizardData as parseTerraform } from "./create/terraform/persist";
14-
import { ResumeStackBanner } from "./ResumeStackBanner";
1511
import { useStacklistQueryParams } from "./service";
16-
import { ResumeTerraformBanner } from "./ResumeTerraformBanner";
12+
import { StackListQueryParams } from "../../types/stack";
1713

18-
export function StackList() {
19-
const [hasResumeableStack, setResumeableStack] = useState(parseWizardData().success);
20-
const [hasResumeableTerraform, setResumeableTerraform] = useState<boolean>(
21-
parseTerraform().success
22-
);
14+
type Props = {
15+
fixedQueryParams?: StackListQueryParams;
16+
};
17+
18+
export function StackList({ fixedQueryParams = {} }: Props) {
2319
const queryParams = useStacklistQueryParams();
2420
const { refetch, data } = useQuery({
25-
...stackQueries.stackList({ ...queryParams, sort_by: "desc:updated" }),
21+
...stackQueries.stackList({ ...queryParams, sort_by: "desc:updated", ...fixedQueryParams }),
2622
throwOnError: true
2723
});
2824

@@ -45,10 +41,6 @@ export function StackList() {
4541
</div>
4642
</div>
4743
<div className="flex flex-col items-center gap-5">
48-
{hasResumeableStack && <ResumeStackBanner setHasResumeableStack={setResumeableStack} />}
49-
{hasResumeableTerraform && (
50-
<ResumeTerraformBanner setHasResumeableTerraform={setResumeableTerraform} />
51-
)}
5244
<div className="w-full">
5345
{data ? (
5446
<DataTable columns={getStackColumns()} data={data.items} />

src/app/stacks/page.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useTourContext } from "@/components/tour/TourContext";
22
import { useEffect } from "react";
33
import { StackList } from "./StackList";
4+
import { ResumeBanners } from "./ResumeBanner";
45

56
export default function StacksPage() {
67
const {
@@ -14,5 +15,10 @@ export default function StacksPage() {
1415
}
1516
}, [tourActive]);
1617

17-
return <StackList />;
18+
return (
19+
<div className="space-y-5">
20+
<ResumeBanners />
21+
<StackList />
22+
</div>
23+
);
1824
}

src/assets/icons/expand-full.svg

+4
Loading

src/components/Pagination.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@ import { useNavigate } from "react-router-dom";
1010
import { ResponsePage } from "@/types/common";
1111

1212
type Props = {
13+
inMemoryHandler?: (page: number) => void;
1314
// Maybe handle this with a generic?
1415
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1516
searchParams: Record<string, any>;
1617
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1718
paginate: Omit<ResponsePage<any>, "items">;
1819
};
1920

20-
export default function Pagination({ paginate, searchParams }: Props) {
21+
export default function Pagination({ paginate, searchParams, inMemoryHandler }: Props) {
2122
const navigate = useNavigate();
2223

2324
const { index, total_pages } = paginate;
2425

2526
function goToPage(page: number) {
27+
if (!!inMemoryHandler) {
28+
inMemoryHandler(page);
29+
return;
30+
}
2631
const queryParams = new URLSearchParams(objectToSearchParams(searchParams));
2732
queryParams.set("page", page.toString());
2833

src/components/SearchField.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { InputHTMLAttributes, forwardRef, useEffect, useMemo, useState } from "r
66
import { useNavigate, useSearchParams } from "react-router-dom";
77

88
type Props = {
9+
inMemoryHandler?: (val: string) => void;
910
searchContains?: boolean;
1011
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1112
searchParams: Record<string, any>;
@@ -14,7 +15,7 @@ type Props = {
1415
export const SearchField = forwardRef<
1516
HTMLInputElement,
1617
InputHTMLAttributes<HTMLInputElement> & Props
17-
>(({ searchParams, searchContains = true, ...rest }, ref) => {
18+
>(({ searchParams, searchContains = true, inMemoryHandler, ...rest }, ref) => {
1819
const navigate = useNavigate();
1920
const [existingParams] = useSearchParams();
2021

@@ -35,6 +36,10 @@ export const SearchField = forwardRef<
3536
}, [debouncedSearch]);
3637

3738
function updateSearchQuery(value: string) {
39+
if (!!inMemoryHandler) {
40+
inMemoryHandler(value);
41+
return;
42+
}
3843
const queryParams = new URLSearchParams({
3944
...Object.fromEntries(existingParams),
4045
...objectToSearchParams(searchParams)

src/components/artifacts/artifact-node-sheet/DetailCards.tsx

+6-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { ExecutionStatusIcon, getExecutionStatusTagColor } from "@/components/Ex
77
import { InlineAvatar } from "@/components/InlineAvatar";
88
import { Key, KeyValue, Value } from "@/components/KeyValue";
99
import { useArtifactVersion } from "@/data/artifact-versions/artifact-version-detail-query";
10-
import { useComponentDetail } from "@/data/components/component-detail-query";
1110
import { usePipelineRun } from "@/data/pipeline-runs/pipeline-run-detail-query";
1211
import { useStepDetail } from "@/data/steps/step-detail-query";
1312
import { routes } from "@/router/routes";
@@ -22,6 +21,8 @@ import {
2221
import { Link } from "react-router-dom";
2322
import { Codesnippet } from "../../CodeSnippet";
2423
import { CollapsibleCard } from "../../CollapsibleCard";
24+
import { useQuery } from "@tanstack/react-query";
25+
import { componentQueries } from "../../../data/components";
2526

2627
type Props = {
2728
artifactVersionId: string;
@@ -177,12 +178,10 @@ export function DataCard({ artifactVersionId }: Props) {
177178
} = useArtifactVersion({ versionId: artifactVersionId });
178179

179180
const artifactStoreId = artifactVersionData?.metadata?.artifact_store_id;
180-
const { data: storeData, isSuccess: isStoreSuccess } = useComponentDetail(
181-
{
182-
componentId: artifactStoreId!
183-
},
184-
{ enabled: !!artifactStoreId }
185-
);
181+
const { data: storeData, isSuccess: isStoreSuccess } = useQuery({
182+
...componentQueries.componentDetail(artifactStoreId!),
183+
enabled: !!artifactStoreId
184+
});
186185

187186
if (isArtifactVersionError) {
188187
return <ErrorFallback err={artifactVersionError} />;

src/components/breadcrumbs/SegmentsBreadcrumbs.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ export const matchSegmentWithRequest = ({ segment, data }: { segment: string; da
2828
components: {
2929
components: { name: "Components" }
3030
},
31+
componentDetail: {
32+
components: { name: "Components" },
33+
component_detail: {
34+
id: data?.id,
35+
name: data?.name
36+
}
37+
},
3138
secrets: {
3239
secrets: { name: "Secrets" }
3340
},
@@ -113,7 +120,8 @@ export const matchSegmentWithTab = (segment: string) => {
113120
metadata: <MetadataIcon className={iconClasses} />,
114121
runs: <RunIcon className={iconClasses} />,
115122
templates: <TemplatesIcon className={iconClasses} />,
116-
stack: <Stack className={iconClasses} />
123+
stack: <Stack className={iconClasses} />,
124+
stacks: <Stack className={iconClasses} />
117125
};
118126

119127
return routeMap[segment] || <Info className="h-5 w-5 fill-theme-text-tertiary" />;

src/components/stack-components/ComponentFallbackDialog.tsx

-78
This file was deleted.

0 commit comments

Comments
 (0)