From 637161a2df2e8f23487838a159890afbca91e05a Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:34:50 +0000 Subject: [PATCH 1/2] feat: add template export functionality to UI - Add downloadTemplateVersion function to frontend API - Add Export as TAR and Export as ZIP options to template dropdown menu - Implement file download with proper naming convention - Support both tar and zip formats as requested in issue #17859 Fixes #17859 --- site/src/api/api.ts | 25 ++++++++++++++ .../pages/TemplatePage/TemplatePageHeader.tsx | 34 ++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 81931c003c99d..5463ad7a44dd6 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1084,6 +1084,31 @@ class ApiMethods { return response.data; }; + /** + * Downloads a template version as a tar or zip archive + * @param fileId The file ID from the template version's job + * @param format Optional format: "zip" for zip archive, empty/undefined for tar + * @returns Promise that resolves to a Blob containing the archive + */ + downloadTemplateVersion = async ( + fileId: string, + format?: "zip", + ): Promise => { + const params = new URLSearchParams(); + if (format) { + params.set("format", format); + } + + const response = await this.axios.get( + `/api/v2/files/${fileId}?${params.toString()}`, + { + responseType: "blob", + }, + ); + + return response.data; + }; + updateTemplateMeta = async ( templateId: string, data: TypesGen.UpdateTemplateMeta, diff --git a/site/src/pages/TemplatePage/TemplatePageHeader.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.tsx index 54c3c04de8bdf..5002f244c7d01 100644 --- a/site/src/pages/TemplatePage/TemplatePageHeader.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.tsx @@ -26,7 +26,7 @@ import { } from "components/PageHeader/PageHeader"; import { Pill } from "components/Pill/Pill"; import { Stack } from "components/Stack/Stack"; -import { CopyIcon } from "lucide-react"; +import { CopyIcon, DownloadIcon } from "lucide-react"; import { EllipsisVertical, PlusIcon, @@ -34,6 +34,7 @@ import { TrashIcon, } from "lucide-react"; import { linkToTemplate, useLinks } from "modules/navigation"; +import { API } from "api/api"; import type { WorkspacePermissions } from "modules/permissions/workspaces"; import type { FC } from "react"; import { useQuery } from "react-query"; @@ -46,6 +47,7 @@ type TemplateMenuProps = { templateName: string; templateVersion: string; templateId: string; + fileId: string; onDelete: () => void; }; @@ -54,6 +56,7 @@ const TemplateMenu: FC = ({ templateName, templateVersion, templateId, + fileId, onDelete, }) => { const dialogState = useDeletionDialogState(templateId, onDelete); @@ -68,6 +71,24 @@ const TemplateMenu: FC = ({ const templateLink = getLink(linkToTemplate(organizationName, templateName)); + const handleExport = async (format?: "zip") => { + try { + const blob = await API.downloadTemplateVersion(fileId, format); + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + const extension = format === "zip" ? "zip" : "tar"; + link.download = `${templateName}-${templateVersion}.${extension}`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } catch (error) { + console.error("Failed to export template:", error); + // TODO: Show user-friendly error message + } + }; + return ( <> @@ -102,6 +123,16 @@ const TemplateMenu: FC = ({ Duplicate… + + handleExport()}> + + Export as TAR + + + handleExport("zip")}> + + Export as ZIP + = ({ templateId={template.id} templateName={template.name} templateVersion={activeVersion.name} + fileId={activeVersion.job.file_id} onDelete={onDeleteTemplate} /> )} From b82313026590ca0877b57bd060ec3b581ace6e43 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 17:49:45 +0000 Subject: [PATCH 2/2] fix: correct import ordering in TemplatePageHeader.tsx Fix import ordering to comply with biome linting rules by moving API import to the correct position in the import order. --- site/src/pages/TemplatePage/TemplatePageHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/TemplatePage/TemplatePageHeader.tsx b/site/src/pages/TemplatePage/TemplatePageHeader.tsx index 5002f244c7d01..a7ebbf0ad00b1 100644 --- a/site/src/pages/TemplatePage/TemplatePageHeader.tsx +++ b/site/src/pages/TemplatePage/TemplatePageHeader.tsx @@ -1,5 +1,6 @@ import EditIcon from "@mui/icons-material/EditOutlined"; import Button from "@mui/material/Button"; +import { API } from "api/api"; import { workspaces } from "api/queries/workspaces"; import type { AuthorizationResponse, @@ -34,7 +35,6 @@ import { TrashIcon, } from "lucide-react"; import { linkToTemplate, useLinks } from "modules/navigation"; -import { API } from "api/api"; import type { WorkspacePermissions } from "modules/permissions/workspaces"; import type { FC } from "react"; import { useQuery } from "react-query"; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy