diff --git a/packages/server-admin-ui/src/views/appstore/Grid/cell-renderers/ActionCellRenderer.js b/packages/server-admin-ui/src/views/appstore/Grid/cell-renderers/ActionCellRenderer.js
index be4cf01b0..bec72ad48 100644
--- a/packages/server-admin-ui/src/views/appstore/Grid/cell-renderers/ActionCellRenderer.js
+++ b/packages/server-admin-ui/src/views/appstore/Grid/cell-renderers/ActionCellRenderer.js
@@ -1,12 +1,28 @@
-import React from 'react'
-import { Button, Progress } from 'reactstrap'
+import React, { useState } from 'react'
+import {
+ Button,
+ Progress,
+ Modal,
+ ModalHeader,
+ ModalBody,
+ ModalFooter,
+ ListGroup,
+ ListGroupItem
+} from 'reactstrap'
import { connect } from 'react-redux'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
-import { faTrashCan, faCloudArrowDown } from '@fortawesome/free-solid-svg-icons'
+import {
+ faTrashCan,
+ faCloudArrowDown,
+ faHistory
+} from '@fortawesome/free-solid-svg-icons'
function ActionCellRenderer(props) {
const app = props.data
+ const [showVersionsModal, setShowVersionsModal] = useState(false)
+ const [versions, setVersions] = useState([])
+ const [loadingVersions, setLoadingVersions] = useState(false)
const handleInstallClick = () => {
fetch(
@@ -18,6 +34,59 @@ function ActionCellRenderer(props) {
)
}
+ const handleInstallVersionClick = (version) => {
+ fetch(
+ `${window.serverRoutesPrefix}/appstore/install/${props.data.name}/${version}`,
+ {
+ method: 'POST',
+ credentials: 'include'
+ }
+ )
+ setShowVersionsModal(false)
+ }
+
+ const handleVersionsClick = async () => {
+ setLoadingVersions(true)
+ setShowVersionsModal(true)
+
+ try {
+ // Fetch versions from npm registry
+ const response = await fetch(
+ `https://registry.npmjs.org/${props.data.name}`
+ )
+ const packageData = await response.json()
+
+ if (packageData.versions) {
+ // Get all versions and sort them by semver (newest first)
+ const versionList = Object.keys(packageData.versions)
+ .filter((version) => version !== props.data.version) // Exclude current version
+ .sort((a, b) => {
+ // Simple version comparison - newer versions first
+ const aParts = a.split('.').map(Number)
+ const bParts = b.split('.').map(Number)
+
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
+ const aPart = aParts[i] || 0
+ const bPart = bParts[i] || 0
+
+ if (bPart !== aPart) {
+ return bPart - aPart
+ }
+ }
+ return 0
+ })
+ .slice(0, 20) // Limit to 20 versions
+
+ setVersions(versionList)
+ }
+ } catch (error) {
+ console.error('Failed to fetch versions:', error)
+ setVersions([])
+ } finally {
+ setLoadingVersions(false)
+ }
+ }
+
const handleRemoveClick = () => {
if (confirm(`Are you sure you want to uninstall ${props.data.name}?`)) {
fetch(`${window.serverRoutesPrefix}/appstore/remove/${props.data.name}`, {
@@ -86,20 +155,95 @@ function ActionCellRenderer(props) {
icon={faTrashCan}
onClick={handleRemoveClick}
/>
+
+
Loading versions...
++ No older versions available or failed to load versions. +
+ )} +