From aa50ed4d4c5eb255e787d0d587f49802ad984abb Mon Sep 17 00:00:00 2001 From: Amit Amrutiya Date: Thu, 2 Jan 2025 09:54:24 +0530 Subject: [PATCH 1/4] feat(download): enhance download functionality with dynamic URL generation Signed-off-by: Amit Amrutiya --- src/custom/CatalogDetail/ActionButton.tsx | 11 +++++---- src/custom/CatalogDetail/helper.ts | 28 ++++++++++++++++++----- src/custom/CatalogDetail/types.ts | 2 ++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/custom/CatalogDetail/ActionButton.tsx b/src/custom/CatalogDetail/ActionButton.tsx index 0141e736..aa5fe223 100644 --- a/src/custom/CatalogDetail/ActionButton.tsx +++ b/src/custom/CatalogDetail/ActionButton.tsx @@ -4,7 +4,7 @@ import { CopyIcon, DeleteIcon, EditIcon, KanvasIcon, PublishIcon } from '../../i import Download from '../../icons/Download/Download'; import { charcoal, useTheme } from '../../theme'; import { Pattern } from '../CustomCatalog/CustomCard'; -import { downloadFilter, downloadYaml } from './helper'; +import { downloadPattern, downloadYaml, getValidSorceType } from './helper'; import { ActionButton, StyledActionWrapper, UnpublishAction } from './style'; import { RESOURCE_TYPES } from './types'; @@ -13,6 +13,7 @@ interface ActionButtonsProps { details: Pattern; type: string; isCloneLoading: boolean; + getDownloadUrl: (sorceType: string, id: string) => string; handleClone: (name: string, id: string) => void; handleUnpublish: () => void; isCloneDisabled: boolean; @@ -34,6 +35,7 @@ const ActionButtons: React.FC = ({ isCloneDisabled, showUnpublishAction, handleUnpublish, + getDownloadUrl, showOpenPlaygroundAction, onOpenPlaygroundClick, showInfoAction, @@ -42,6 +44,7 @@ const ActionButtons: React.FC = ({ handleDelete }) => { const cleanedType = type.replace('my-', '').replace(/s$/, ''); + const sorceType = getValidSorceType(type); const theme = useTheme(); return ( @@ -83,9 +86,9 @@ const ActionButtons: React.FC = ({ color: theme.palette.text.default }} onClick={() => - cleanedType === RESOURCE_TYPES.FILTERS - ? downloadFilter(details.id, details.name) - : downloadYaml(details.pattern_file, details.name) + cleanedType === RESOURCE_TYPES.VIEWS + ? downloadYaml(details.pattern_file, details.name) + : downloadPattern(details.id, details.name, sorceType, getDownloadUrl) } > diff --git a/src/custom/CatalogDetail/helper.ts b/src/custom/CatalogDetail/helper.ts index 2d6601f7..e3a43455 100644 --- a/src/custom/CatalogDetail/helper.ts +++ b/src/custom/CatalogDetail/helper.ts @@ -34,14 +34,17 @@ export function slugify(str: string): string { return str; } -export const downloadFilter = (id: string, name: string): void => { - const dataUri = `${process.env.API_ENDPOINT_PREFIX}/api/content/filters/download/${id}`; - - // Add the .wasm extension to the filename - const fileNameWithExtension = name + '.wasm'; +export const downloadPattern = ( + id: string, + name: string, + sorceType: string, + getDownloadUrl: (sorceType: string, id: string) => string +): void => { + const downloadUrl = getDownloadUrl(sorceType, id); + const fileNameWithExtension = `${name}.yaml`; const linkElement = document.createElement('a'); - linkElement.setAttribute('href', dataUri); + linkElement.setAttribute('href', downloadUrl); linkElement.setAttribute('download', fileNameWithExtension); linkElement.click(); linkElement.remove(); @@ -59,3 +62,16 @@ export const formatDate = (date: Date) => { const formattedDate = new Date(date).toLocaleDateString('en-US', options); return formattedDate; }; + +export const getValidSorceType = (type: string): string => { + if (type === 'my-designs' || type === 'catalog') { + return 'patterns'; + } + if (type === 'my-filters') { + return 'filters'; + } + if (type === 'my-views') { + return 'views'; + } + return ''; +}; diff --git a/src/custom/CatalogDetail/types.ts b/src/custom/CatalogDetail/types.ts index 1cd2adc7..165c6845 100644 --- a/src/custom/CatalogDetail/types.ts +++ b/src/custom/CatalogDetail/types.ts @@ -37,6 +37,8 @@ export const RESOURCE_TYPES = { VIEWS: 'view' }; +export const PATTERNS = 'patterns'; + export type ContentClassType = { community: { icon: React.ComponentType; From bd2f0fd4e9a0adbbbd611346d08dab84815513ee Mon Sep 17 00:00:00 2001 From: Amit Amrutiya Date: Thu, 2 Jan 2025 09:54:49 +0530 Subject: [PATCH 2/4] feat(download): add getDownloadUrl prop to LeftPanel and DesignTable components Signed-off-by: Amit Amrutiya --- src/custom/CatalogDetail/LeftPanel.tsx | 5 ++++- src/custom/Workspaces/DesignTable.tsx | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/custom/CatalogDetail/LeftPanel.tsx b/src/custom/CatalogDetail/LeftPanel.tsx index 47b844f9..03682785 100644 --- a/src/custom/CatalogDetail/LeftPanel.tsx +++ b/src/custom/CatalogDetail/LeftPanel.tsx @@ -27,6 +27,7 @@ interface LeftPanelProps { handleInfoClick?: () => void; showDeleteAction?: boolean; handleDelete: () => void; + getDownloadUrl: (sorceType: string, id: string) => string; } const LeftPanel: React.FC = ({ @@ -48,7 +49,8 @@ const LeftPanel: React.FC = ({ showInfoAction = false, handleInfoClick, showDeleteAction = false, - handleDelete + handleDelete, + getDownloadUrl }) => { const theme = useTheme(); @@ -95,6 +97,7 @@ const LeftPanel: React.FC = ({ handleInfoClick={handleInfoClick} showDeleteAction={showDeleteAction} handleDelete={handleDelete} + getDownloadUrl={getDownloadUrl} /> {showTechnologies && ( void; + getDownloadUrl: (sorceType: string, id: string) => string; handlePublish: (publishModal: PublishModalState, data: any) => void; publishModalHandler: any; handleUnpublishModal: (design: Pattern, modalRef: React.RefObject) => void; @@ -83,6 +84,7 @@ const DesignTable: React.FC = ({ handleShowDetails, handleUnpublishModal, handleWorkspaceDesignDeleteModal, + getDownloadUrl, publishModalHandler, isCopyLinkAllowed, isDeleteAllowed, @@ -122,6 +124,7 @@ const DesignTable: React.FC = ({ handleCopyUrl, handleClone, handleShowDetails, + getDownloadUrl, isCopyLinkAllowed, isDeleteAllowed, isDownloadAllowed, From bdf190ea15492e14368ee6c7bc2639777a4d83a5 Mon Sep 17 00:00:00 2001 From: Amit Amrutiya Date: Thu, 2 Jan 2025 09:55:03 +0530 Subject: [PATCH 3/4] feat(download): update download functionality to use downloadPattern and include getDownloadUrl prop Signed-off-by: Amit Amrutiya --- .../DesignTableColumnConfig.tsx | 8 +++++--- src/custom/CatalogDesignTable/columnConfig.tsx | 15 +++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx b/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx index ad884cee..2d6725de 100644 --- a/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx +++ b/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx @@ -3,8 +3,8 @@ import { PLAYGROUND_MODES } from '../../constants/constants'; import { ChainIcon, CopyIcon, KanvasIcon, PublishIcon } from '../../icons'; import Download from '../../icons/Download/Download'; import { CHARCOAL } from '../../theme'; -import { downloadYaml, slugify } from '../CatalogDetail/helper'; -import { RESOURCE_TYPES } from '../CatalogDetail/types'; +import { downloadPattern, slugify } from '../CatalogDetail/helper'; +import { PATTERNS, RESOURCE_TYPES } from '../CatalogDetail/types'; import { Pattern } from '../CustomCatalog/CustomCard'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; import { ColView } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx'; @@ -25,6 +25,7 @@ interface ColumnConfigProps { handleCopyUrl: (type: string, name: string, id: string) => void; handleClone: (name: string, id: string) => void; handleShowDetails: (designId: string, designName: string) => void; + getDownloadUrl: (sorceType: string, id: string) => string; isDownloadAllowed: boolean; isCopyLinkAllowed: boolean; isDeleteAllowed: boolean; @@ -53,6 +54,7 @@ export const createDesignsColumnsConfig = ({ handleCopyUrl, handleClone, handleShowDetails, + getDownloadUrl, isUnpublishAllowed, isCopyLinkAllowed, isDeleteAllowed, @@ -167,7 +169,7 @@ export const createDesignsColumnsConfig = ({ const actionsList = [ { title: 'Download', - onClick: () => downloadYaml(rowData?.pattern_file, rowData?.name), + onClick: () => downloadPattern(rowData.id, rowData.name, PATTERNS, getDownloadUrl), disabled: !isDownloadAllowed, icon: }, diff --git a/src/custom/CatalogDesignTable/columnConfig.tsx b/src/custom/CatalogDesignTable/columnConfig.tsx index bcdf1c45..3d7d016e 100644 --- a/src/custom/CatalogDesignTable/columnConfig.tsx +++ b/src/custom/CatalogDesignTable/columnConfig.tsx @@ -13,8 +13,8 @@ import { PublishIcon, TwitterIcon } from '../../icons'; -import { downloadFilter, downloadYaml } from '../CatalogDetail/helper'; -import { RESOURCE_TYPES } from '../CatalogDetail/types'; +import { downloadPattern } from '../CatalogDetail/helper'; +import { PATTERNS } from '../CatalogDetail/types'; import { Pattern } from '../CustomCatalog/CustomCard'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; import { ColView } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx/responsive-column'; @@ -48,13 +48,13 @@ interface ColumnConfigProps { handleUnpublish?: (design: Pattern) => void; maxWidth?: boolean; getCatalogUrl: (type: string, name: string) => string; - type?: string; theme?: any; showUnpublish?: boolean; showOpenPlayground?: boolean; currentUserId?: string; isCloneDisabled?: boolean; isUnpublishDisabled?: boolean; + getDownloadUrl: (sorceType: string, id: string) => string; } interface ActionItem { @@ -74,7 +74,7 @@ export const createDesignColumns = ({ handleUnpublish = () => {}, maxWidth = true, getCatalogUrl, - type, + getDownloadUrl, theme, showUnpublish, currentUserId, @@ -82,7 +82,6 @@ export const createDesignColumns = ({ isUnpublishDisabled, showOpenPlayground }: ColumnConfigProps): MUIDataTableColumn[] => { - const cleanedType = type?.replace('my-', '').replace(/s$/, ''); return [ { name: 'id', @@ -260,11 +259,7 @@ export const createDesignColumns = ({ }, { title: 'Download', - onClick: () => { - cleanedType === RESOURCE_TYPES.FILTERS - ? downloadFilter(rowData.id, rowData.name) - : downloadYaml(rowData.pattern_file, rowData.name); - }, + onClick: () => downloadPattern(rowData.id, rowData.name, PATTERNS, getDownloadUrl), icon: }, { From 4d4bebffe2c25722d0513088d1a8b3214d54b96b Mon Sep 17 00:00:00 2001 From: Amit Amrutiya Date: Thu, 2 Jan 2025 14:10:23 +0530 Subject: [PATCH 4/4] feat: remove the soruce content logic with the id only Signed-off-by: Amit Amrutiya --- .../DesignTableColumnConfig.tsx | 12 +++++------- src/custom/CatalogDesignTable/columnConfig.tsx | 5 ++--- src/custom/CatalogDetail/ActionButton.tsx | 13 ++++++------- src/custom/CatalogDetail/LeftPanel.tsx | 2 +- src/custom/CatalogDetail/helper.ts | 18 ++---------------- src/custom/CatalogDetail/types.ts | 8 +++++--- src/custom/Workspaces/DesignTable.tsx | 2 +- 7 files changed, 22 insertions(+), 38 deletions(-) diff --git a/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx b/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx index 2d6725de..17c26b20 100644 --- a/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx +++ b/src/custom/CatalogDesignTable/DesignTableColumnConfig.tsx @@ -4,7 +4,7 @@ import { ChainIcon, CopyIcon, KanvasIcon, PublishIcon } from '../../icons'; import Download from '../../icons/Download/Download'; import { CHARCOAL } from '../../theme'; import { downloadPattern, slugify } from '../CatalogDetail/helper'; -import { PATTERNS, RESOURCE_TYPES } from '../CatalogDetail/types'; +import { RESOURCE_TYPES } from '../CatalogDetail/types'; import { Pattern } from '../CustomCatalog/CustomCard'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; import { ColView } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx'; @@ -25,7 +25,7 @@ interface ColumnConfigProps { handleCopyUrl: (type: string, name: string, id: string) => void; handleClone: (name: string, id: string) => void; handleShowDetails: (designId: string, designName: string) => void; - getDownloadUrl: (sorceType: string, id: string) => string; + getDownloadUrl: (id: string) => string; isDownloadAllowed: boolean; isCopyLinkAllowed: boolean; isDeleteAllowed: boolean; @@ -169,7 +169,7 @@ export const createDesignsColumnsConfig = ({ const actionsList = [ { title: 'Download', - onClick: () => downloadPattern(rowData.id, rowData.name, PATTERNS, getDownloadUrl), + onClick: () => downloadPattern(rowData.id, rowData.name, getDownloadUrl), disabled: !isDownloadAllowed, icon: }, @@ -177,7 +177,7 @@ export const createDesignsColumnsConfig = ({ title: 'Copy Link', disabled: rowData.visibility === 'private' || !isCopyLinkAllowed, onClick: () => { - handleCopyUrl(RESOURCE_TYPES.DESIGNS, rowData?.name, rowData?.id); + handleCopyUrl(RESOURCE_TYPES.DESIGN, rowData?.name, rowData?.id); }, icon: }, @@ -187,9 +187,7 @@ export const createDesignsColumnsConfig = ({ window.open( `https://playground.meshery.io/extension/meshmap?mode=${ PLAYGROUND_MODES.DESIGNER - }&type=${RESOURCE_TYPES.DESIGNS}&id=${rowData?.id}&name=${slugify( - rowData?.name - )}`, + }&type=${RESOURCE_TYPES.DESIGN}&id=${rowData?.id}&name=${slugify(rowData?.name)}`, '_blank' ); }, diff --git a/src/custom/CatalogDesignTable/columnConfig.tsx b/src/custom/CatalogDesignTable/columnConfig.tsx index 3d7d016e..06cca4e0 100644 --- a/src/custom/CatalogDesignTable/columnConfig.tsx +++ b/src/custom/CatalogDesignTable/columnConfig.tsx @@ -14,7 +14,6 @@ import { TwitterIcon } from '../../icons'; import { downloadPattern } from '../CatalogDetail/helper'; -import { PATTERNS } from '../CatalogDetail/types'; import { Pattern } from '../CustomCatalog/CustomCard'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; import { ColView } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx/responsive-column'; @@ -54,7 +53,7 @@ interface ColumnConfigProps { currentUserId?: string; isCloneDisabled?: boolean; isUnpublishDisabled?: boolean; - getDownloadUrl: (sorceType: string, id: string) => string; + getDownloadUrl: (id: string) => string; } interface ActionItem { @@ -259,7 +258,7 @@ export const createDesignColumns = ({ }, { title: 'Download', - onClick: () => downloadPattern(rowData.id, rowData.name, PATTERNS, getDownloadUrl), + onClick: () => downloadPattern(rowData.id, rowData.name, getDownloadUrl), icon: }, { diff --git a/src/custom/CatalogDetail/ActionButton.tsx b/src/custom/CatalogDetail/ActionButton.tsx index aa5fe223..bf601a09 100644 --- a/src/custom/CatalogDetail/ActionButton.tsx +++ b/src/custom/CatalogDetail/ActionButton.tsx @@ -4,16 +4,16 @@ import { CopyIcon, DeleteIcon, EditIcon, KanvasIcon, PublishIcon } from '../../i import Download from '../../icons/Download/Download'; import { charcoal, useTheme } from '../../theme'; import { Pattern } from '../CustomCatalog/CustomCard'; -import { downloadPattern, downloadYaml, getValidSorceType } from './helper'; +import { downloadPattern, downloadYaml } from './helper'; import { ActionButton, StyledActionWrapper, UnpublishAction } from './style'; -import { RESOURCE_TYPES } from './types'; +import { FILTERS, VIEWS } from './types'; interface ActionButtonsProps { actionItems: boolean; details: Pattern; type: string; isCloneLoading: boolean; - getDownloadUrl: (sorceType: string, id: string) => string; + getDownloadUrl: (id: string) => string; handleClone: (name: string, id: string) => void; handleUnpublish: () => void; isCloneDisabled: boolean; @@ -44,7 +44,6 @@ const ActionButtons: React.FC = ({ handleDelete }) => { const cleanedType = type.replace('my-', '').replace(/s$/, ''); - const sorceType = getValidSorceType(type); const theme = useTheme(); return ( @@ -86,16 +85,16 @@ const ActionButtons: React.FC = ({ color: theme.palette.text.default }} onClick={() => - cleanedType === RESOURCE_TYPES.VIEWS + cleanedType === VIEWS ? downloadYaml(details.pattern_file, details.name) - : downloadPattern(details.id, details.name, sorceType, getDownloadUrl) + : downloadPattern(details.id, details.name, getDownloadUrl) } > Download - {cleanedType !== RESOURCE_TYPES.FILTERS && ( + {cleanedType !== FILTERS && ( void; showDeleteAction?: boolean; handleDelete: () => void; - getDownloadUrl: (sorceType: string, id: string) => string; + getDownloadUrl: (id: string) => string; } const LeftPanel: React.FC = ({ diff --git a/src/custom/CatalogDetail/helper.ts b/src/custom/CatalogDetail/helper.ts index e3a43455..b43477bf 100644 --- a/src/custom/CatalogDetail/helper.ts +++ b/src/custom/CatalogDetail/helper.ts @@ -37,10 +37,9 @@ export function slugify(str: string): string { export const downloadPattern = ( id: string, name: string, - sorceType: string, - getDownloadUrl: (sorceType: string, id: string) => string + getDownloadUrl: (id: string) => string ): void => { - const downloadUrl = getDownloadUrl(sorceType, id); + const downloadUrl = getDownloadUrl(id); const fileNameWithExtension = `${name}.yaml`; const linkElement = document.createElement('a'); @@ -62,16 +61,3 @@ export const formatDate = (date: Date) => { const formattedDate = new Date(date).toLocaleDateString('en-US', options); return formattedDate; }; - -export const getValidSorceType = (type: string): string => { - if (type === 'my-designs' || type === 'catalog') { - return 'patterns'; - } - if (type === 'my-filters') { - return 'filters'; - } - if (type === 'my-views') { - return 'views'; - } - return ''; -}; diff --git a/src/custom/CatalogDetail/types.ts b/src/custom/CatalogDetail/types.ts index 165c6845..2255a6f5 100644 --- a/src/custom/CatalogDetail/types.ts +++ b/src/custom/CatalogDetail/types.ts @@ -32,12 +32,14 @@ export interface Theme { } export const RESOURCE_TYPES = { - DESIGNS: 'design', - FILTERS: 'filter', - VIEWS: 'view' + DESIGN: 'design', + FILTER: 'filter', + VIEW: 'view' }; export const PATTERNS = 'patterns'; +export const FILTERS = 'filters'; +export const VIEWS = 'views'; export type ContentClassType = { community: { diff --git a/src/custom/Workspaces/DesignTable.tsx b/src/custom/Workspaces/DesignTable.tsx index 3fe4278e..e099cc57 100644 --- a/src/custom/Workspaces/DesignTable.tsx +++ b/src/custom/Workspaces/DesignTable.tsx @@ -39,7 +39,7 @@ export interface DesignTableProps { workspaceName: string, workspaceId: string ) => void; - getDownloadUrl: (sorceType: string, id: string) => string; + getDownloadUrl: (id: string) => string; handlePublish: (publishModal: PublishModalState, data: any) => void; publishModalHandler: any; handleUnpublishModal: (design: Pattern, modalRef: React.RefObject) => void;