mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
fix: remove drive component
This commit is contained in:
@@ -6,7 +6,6 @@ import { theme } from './theme'
|
||||
import Login from './components/login'
|
||||
import Header from './components/header'
|
||||
import Home from './components/home'
|
||||
import Drive from './containers/Drive'
|
||||
import Studio from './containers/Studio'
|
||||
import Settings from './containers/Settings'
|
||||
|
||||
@@ -36,7 +35,6 @@ function App() {
|
||||
<Header />
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/SASjsDrive" element={<Drive />} />
|
||||
<Route path="/SASjsStudio" element={<Studio />} />
|
||||
<Route path="/SASjsSettings" element={<Settings />} />
|
||||
<Route path="/SASjsLogon" element={<AuthCode />} />
|
||||
|
||||
@@ -83,12 +83,6 @@ const Header = (props: any) => {
|
||||
onChange={handleTabChange}
|
||||
>
|
||||
<Tab label="Home" value="/" to="/" component={Link} />
|
||||
<Tab
|
||||
label="Drive"
|
||||
value="/SASjsDrive"
|
||||
to="/SASjsDrive"
|
||||
component={Link}
|
||||
/>
|
||||
<Tab
|
||||
label="Studio"
|
||||
value="/SASjsStudio"
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import axios from 'axios'
|
||||
|
||||
import CssBaseline from '@mui/material/CssBaseline'
|
||||
import Box from '@mui/material/Box'
|
||||
|
||||
import SideBar from './sideBar'
|
||||
import Main from './main'
|
||||
|
||||
export interface TreeNode {
|
||||
name: string
|
||||
relativePath: string
|
||||
absolutePath: string
|
||||
children: Array<TreeNode>
|
||||
}
|
||||
|
||||
const Drive = () => {
|
||||
const location = useLocation()
|
||||
const baseUrl = window.location.origin
|
||||
|
||||
const [selectedFilePath, setSelectedFilePath] = useState('')
|
||||
const [directoryData, setDirectoryData] = useState<TreeNode | null>(null)
|
||||
|
||||
const setFilePathOnMount = useCallback(() => {
|
||||
const queryParams = new URLSearchParams(location.search)
|
||||
setSelectedFilePath(queryParams.get('filePath') ?? '')
|
||||
}, [location.search])
|
||||
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(`/SASjsApi/drive/fileTree`)
|
||||
.then((res: any) => {
|
||||
if (res.data && res.data?.status === 'success') {
|
||||
setDirectoryData(res.data.tree)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
setFilePathOnMount()
|
||||
}, [setFilePathOnMount])
|
||||
|
||||
const handleSelect = (node: TreeNode) => {
|
||||
if (node.children.length) return
|
||||
|
||||
if (!node.name.includes('.')) return
|
||||
|
||||
window.history.pushState(
|
||||
'',
|
||||
'',
|
||||
`${baseUrl}/#/SASjsDrive?filePath=${node.relativePath}`
|
||||
)
|
||||
setSelectedFilePath(node.relativePath)
|
||||
}
|
||||
|
||||
const removeFileFromTree = (path: string) => {
|
||||
if (directoryData) {
|
||||
const newTree = JSON.parse(JSON.stringify(directoryData)) as TreeNode
|
||||
findAndRemoveNode(newTree, newTree, path)
|
||||
setDirectoryData(newTree)
|
||||
}
|
||||
}
|
||||
|
||||
const findAndRemoveNode = (
|
||||
node: TreeNode,
|
||||
parentNode: TreeNode,
|
||||
path: string
|
||||
) => {
|
||||
if (node.relativePath === path) {
|
||||
removeNodeFromParent(parentNode, path)
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(node.children)) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
if (findAndRemoveNode(node.children[i], node, path)) return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const removeNodeFromParent = (parent: TreeNode, path: string) => {
|
||||
const index = parent.children.findIndex(
|
||||
(node) => node.relativePath === path
|
||||
)
|
||||
if (index !== -1) {
|
||||
parent.children.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<CssBaseline />
|
||||
<SideBar
|
||||
selectedFilePath={selectedFilePath}
|
||||
directoryData={directoryData}
|
||||
handleSelect={handleSelect}
|
||||
/>
|
||||
<Main
|
||||
selectedFilePath={selectedFilePath}
|
||||
removeFileFromTree={removeFileFromTree}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Drive
|
||||
@@ -1,173 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import axios from 'axios'
|
||||
|
||||
import Editor from 'react-monaco-editor'
|
||||
|
||||
import Box from '@mui/material/Box'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import Stack from '@mui/material/Stack'
|
||||
import Button from '@mui/material/Button'
|
||||
import Toolbar from '@mui/material/Toolbar'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
|
||||
type Props = {
|
||||
selectedFilePath: string
|
||||
removeFileFromTree: (path: string) => void
|
||||
}
|
||||
|
||||
const Main = (props: Props) => {
|
||||
const baseUrl = window.location.origin
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [fileContentBeforeEdit, setFileContentBeforeEdit] = useState('')
|
||||
const [fileContent, setFileContent] = useState('')
|
||||
const [editMode, setEditMode] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (props.selectedFilePath) {
|
||||
setIsLoading(true)
|
||||
axios
|
||||
.get(`/SASjsApi/drive/file?_filePath=${props.selectedFilePath}`)
|
||||
.then((res: any) => {
|
||||
setFileContent(res.data)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
}, [props.selectedFilePath])
|
||||
|
||||
const handleDeleteBtnClick = () => {
|
||||
setIsLoading(true)
|
||||
|
||||
const filePath = props.selectedFilePath
|
||||
|
||||
axios
|
||||
.delete(`/SASjsApi/drive/file?_filePath=${filePath}`)
|
||||
.then((res) => {
|
||||
setFileContent('')
|
||||
props.removeFileFromTree(filePath)
|
||||
window.history.pushState('', '', `${baseUrl}/#/SASjsDrive`)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const handleEditSaveBtnClick = () => {
|
||||
if (!editMode) {
|
||||
setFileContentBeforeEdit(fileContent)
|
||||
setEditMode(true)
|
||||
} else {
|
||||
setIsLoading(true)
|
||||
|
||||
const formData = new FormData()
|
||||
|
||||
const stringBlob = new Blob([fileContent], { type: 'text/plain' })
|
||||
formData.append('file', stringBlob, 'filename.sas')
|
||||
formData.append('filePath', props.selectedFilePath)
|
||||
|
||||
axios
|
||||
.patch(`/SASjsApi/drive/file`, formData)
|
||||
.then((res) => {
|
||||
setEditMode(false)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancelExecuteBtnClick = () => {
|
||||
if (editMode) {
|
||||
setFileContent(fileContentBeforeEdit)
|
||||
setEditMode(false)
|
||||
} else {
|
||||
window.open(
|
||||
`${baseUrl}/SASjsApi/stp/execute?_program=${props.selectedFilePath}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
||||
<Toolbar />
|
||||
<Paper
|
||||
sx={{
|
||||
height: '75vh',
|
||||
padding: '10px',
|
||||
overflow: 'auto',
|
||||
position: 'relative'
|
||||
}}
|
||||
elevation={3}
|
||||
>
|
||||
{isLoading && (
|
||||
<CircularProgress
|
||||
style={{ position: 'absolute', left: '50%', top: '50%' }}
|
||||
/>
|
||||
)}
|
||||
{!isLoading && props?.selectedFilePath && !editMode && (
|
||||
<code style={{ whiteSpace: 'break-spaces' }}>{fileContent}</code>
|
||||
)}
|
||||
{!isLoading && props?.selectedFilePath && editMode && (
|
||||
<Editor
|
||||
height="95%"
|
||||
language="sas"
|
||||
value={fileContent}
|
||||
onChange={(val) => {
|
||||
if (val) setFileContent(val)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
<Stack
|
||||
spacing={3}
|
||||
direction="row"
|
||||
sx={{ justifyContent: 'center', marginTop: '20px' }}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleDeleteBtnClick}
|
||||
disabled={isLoading || !props?.selectedFilePath}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleEditSaveBtnClick}
|
||||
disabled={isLoading || !props?.selectedFilePath}
|
||||
>
|
||||
{!editMode ? 'Edit' : 'Save'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleCancelExecuteBtnClick}
|
||||
disabled={isLoading || !props?.selectedFilePath}
|
||||
>
|
||||
{editMode ? 'Cancel' : 'Execute'}
|
||||
</Button>
|
||||
{props?.selectedFilePath && (
|
||||
<Button
|
||||
variant="contained"
|
||||
component={Link}
|
||||
to={`/SASjsStudio?_program=${props.selectedFilePath}`}
|
||||
>
|
||||
Open in Studio
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Main
|
||||
@@ -1,100 +0,0 @@
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import { makeStyles } from '@mui/styles'
|
||||
|
||||
import Box from '@mui/material/Box'
|
||||
import Drawer from '@mui/material/Drawer'
|
||||
import Toolbar from '@mui/material/Toolbar'
|
||||
import ListItem from '@mui/material/ListItem'
|
||||
import ListItemText from '@mui/material/ListItemText'
|
||||
|
||||
import TreeView from '@mui/lab/TreeView'
|
||||
import TreeItem from '@mui/lab/TreeItem'
|
||||
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight'
|
||||
|
||||
import { TreeNode } from '.'
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
root: {
|
||||
'& .MuiTreeItem-content': {
|
||||
width: 'auto'
|
||||
}
|
||||
},
|
||||
listItem: {
|
||||
padding: 0
|
||||
}
|
||||
}))
|
||||
|
||||
const drawerWidth = 240
|
||||
|
||||
type Props = {
|
||||
selectedFilePath: string
|
||||
directoryData: TreeNode | null
|
||||
handleSelect: (node: TreeNode) => void
|
||||
}
|
||||
|
||||
const SideBar = ({ selectedFilePath, directoryData, handleSelect }: Props) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const defaultExpanded = useMemo(() => {
|
||||
const splittedPath = selectedFilePath.split('/')
|
||||
const arr = ['']
|
||||
let nodeId = ''
|
||||
splittedPath.forEach((path) => {
|
||||
if (path !== '') {
|
||||
nodeId += '/' + path
|
||||
arr.push(nodeId)
|
||||
}
|
||||
})
|
||||
return arr
|
||||
}, [selectedFilePath])
|
||||
|
||||
const renderTree = (nodes: TreeNode) => (
|
||||
<TreeItem
|
||||
classes={{ root: classes.root }}
|
||||
key={nodes.relativePath}
|
||||
nodeId={nodes.relativePath}
|
||||
label={
|
||||
<ListItem
|
||||
className={classes.listItem}
|
||||
onClick={() => handleSelect(nodes)}
|
||||
>
|
||||
<ListItemText primary={nodes.name} />
|
||||
</ListItem>
|
||||
}
|
||||
>
|
||||
{Array.isArray(nodes.children)
|
||||
? nodes.children.map((node) => renderTree(node))
|
||||
: null}
|
||||
</TreeItem>
|
||||
)
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
variant="permanent"
|
||||
sx={{
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
[`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box' }
|
||||
}}
|
||||
>
|
||||
<Toolbar />
|
||||
<Box sx={{ overflow: 'auto' }}>
|
||||
{directoryData && (
|
||||
<TreeView
|
||||
defaultCollapseIcon={<ExpandMoreIcon />}
|
||||
defaultExpandIcon={<ChevronRightIcon />}
|
||||
defaultExpanded={defaultExpanded}
|
||||
selected={defaultExpanded.slice(-1)}
|
||||
>
|
||||
{renderTree(directoryData)}
|
||||
</TreeView>
|
||||
)}
|
||||
</Box>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
export default SideBar
|
||||
Reference in New Issue
Block a user