1
0
mirror of https://github.com/sasjs/server.git synced 2026-04-10 15:43:14 +00:00

chore: rename Web folder to web

This commit is contained in:
2021-10-19 15:26:26 +00:00
parent 3fe475d477
commit a1ae0e3ed0
28 changed files with 2 additions and 2 deletions

0
web/src/App.css Normal file
View File

9
web/src/App.test.tsx Normal file
View File

@@ -0,0 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

31
web/src/App.tsx Normal file
View File

@@ -0,0 +1,31 @@
import React from 'react'
import { Route, BrowserRouter, Switch, Redirect } from 'react-router-dom'
import { ThemeProvider } from '@mui/material/styles'
import { theme } from './theme'
import Header from './components/header'
import Home from './components/home'
import SASjsDrive from './containers/SASjsDrive'
import SASStudio from './containers/SASjsStudio'
function App() {
return (
<ThemeProvider theme={theme}>
<BrowserRouter>
<Header />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/SASjsDrive">
<SASjsDrive />
</Route>
<Route exact path="/SASjsStudio">
<SASStudio />
</Route>
</Switch>
</BrowserRouter>
</ThemeProvider>
)
}
export default App

View File

@@ -0,0 +1,49 @@
import React, { useState } from 'react'
import { Link, useHistory, useLocation } from 'react-router-dom'
import AppBar from '@mui/material/AppBar'
import Toolbar from '@mui/material/Toolbar'
import Tabs from '@mui/material/Tabs'
import Tab from '@mui/material/Tab'
const Header = (props: any) => {
const history = useHistory()
const [tabValue, setTabValue] = useState(0)
const handleTabChange = (event: React.SyntheticEvent, value: number) => {
setTabValue(value)
}
return (
<AppBar
position="fixed"
sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}
>
<Toolbar variant="dense">
<img
src="logo-white.png"
alt="logo"
style={{
width: '50px',
cursor: 'pointer',
marginRight: '25px'
}}
onClick={() => {
setTabValue(0)
history.push('/')
}}
/>
<Tabs
indicatorColor="secondary"
value={tabValue}
onChange={handleTabChange}
>
<Tab label="Home" to="/" component={Link} />
<Tab label="SASjs Drive" to="/SASjsDrive" component={Link} />
<Tab label="SAS Studio" to="/SASjsStudio" component={Link} />
</Tabs>
</Toolbar>
</AppBar>
)
}
export default Header

View File

@@ -0,0 +1,15 @@
import React from 'react'
import CssBaseline from '@mui/material/CssBaseline'
import Box from '@mui/material/Box'
const Home = () => {
return (
<Box sx={{ display: 'flex' }} className="main">
<CssBaseline />
<h2>this is home component</h2>
</Box>
)
}
export default Home

View File

@@ -0,0 +1,21 @@
import React, { useState } from 'react'
import CssBaseline from '@mui/material/CssBaseline'
import Box from '@mui/material/Box'
import Header from '../../components/header'
import SideBar from './sideBar'
import Main from './main'
const SASjsDrive = () => {
const [selectedFilePath, setSelectedFilePath] = useState('')
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<SideBar setSelectedFilePath={setSelectedFilePath} />
<Main selectedFilePath={selectedFilePath} />
</Box>
)
}
export default SASjsDrive

View File

@@ -0,0 +1,121 @@
import React, { useState, useEffect, useRef } from 'react'
import axios from 'axios'
import Editor from '@monaco-editor/react'
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'
const Main = (props: any) => {
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(`${baseUrl}/SASjsApi/files?filePath=${props.selectedFilePath}`)
.then((res: any) => {
setIsLoading(false)
setFileContent(res.data.fileContent)
})
}
}, [props.selectedFilePath])
const handleEditSaveBtnClick = () => {
if (!editMode) {
setFileContentBeforeEdit(fileContent)
setEditMode(true)
} else {
setIsLoading(true)
axios
.post(`${baseUrl}/SASjsApi/files`, {
filePath: props.selectedFilePath,
fileContent: fileContent
})
.then((res) => {
setIsLoading(false)
setEditMode(false)
})
}
}
const handleCancelExecuteBtnClick = () => {
if (editMode) {
setFileContent(fileContentBeforeEdit)
setEditMode(false)
} else {
setIsLoading(true)
axios
.get(`${baseUrl}/SASjsExecutor/do?_program=${props.selectedFilePath}`)
.then((res) => {
setIsLoading(false)
setEditMode(false)
console.log(res)
})
}
}
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%"
value={fileContent}
onChange={(val) => {
if (val) setFileContent(val)
}}
/>
)}
</Paper>
<Stack
spacing={3}
direction="row"
sx={{ justifyContent: 'center', marginTop: '20px' }}
>
<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>
</Stack>
</Box>
)
}
export default Main

View File

@@ -0,0 +1,105 @@
import React, { useState, useEffect } from 'react'
import axios from 'axios'
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'
interface TreeNode {
name: string
relativePath: string
absolutePath: string
children: Array<TreeNode>
}
const useStyles = makeStyles(() => ({
root: {
'& .MuiTreeItem-content': {
width: 'auto'
}
},
listItem: {
padding: 0
}
}))
const drawerWidth = 240
const SideBar = (props: any) => {
const baseUrl = window.location.origin
const classes = useStyles()
const [directoryData, setDirectoryData] = useState<TreeNode | null>(null)
useEffect(() => {
axios.get(`${baseUrl}/SASjsApi/executor`).then((res: any) => {
if (res.data && res.data?.status === 'success') {
setDirectoryData(res.data.tree)
}
})
}, [])
const handleSelect = (node: TreeNode) => {
if (!node.children.length) {
window.history.pushState(
'',
'',
`${baseUrl}/SASjsDrive?filepath=${node.relativePath}`
)
props.setSelectedFilePath(node.relativePath)
}
}
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' }}>
<TreeView
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
{directoryData && renderTree(directoryData)}
</TreeView>
</Box>
</Drawer>
)
}
export default SideBar

View File

@@ -0,0 +1,15 @@
import React from 'react'
import CssBaseline from '@mui/material/CssBaseline'
import Box from '@mui/material/Box'
const SASjsStudio = () => {
return (
<Box sx={{ display: 'flex' }} className="main">
<CssBaseline />
<h2>This is container for SAS studio</h2>
</Box>
)
}
export default SASjsStudio

17
web/src/index.css Normal file
View File

@@ -0,0 +1,17 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.main {
margin-top: 50px;
}

17
web/src/index.tsx Normal file
View File

@@ -0,0 +1,17 @@
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()

1
web/src/react-app-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
web/src/setupTests.ts Normal file
View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

19
web/src/theme/index.js Normal file
View File

@@ -0,0 +1,19 @@
import { createTheme } from '@mui/material/styles'
import palette from './palette'
export const theme = createTheme({
palette,
components: {
MuiTab: {
styleOverrides: {
root: {
fontSize: '21px',
color: palette.white,
'&.Mui-selected': {
color: palette.secondary.main
}
}
}
}
}
})

55
web/src/theme/palette.js Normal file
View File

@@ -0,0 +1,55 @@
import { colors } from '@mui/material'
const white = '#FFFFFF'
const black = '#000000'
const yellow = '#F6E30F'
const palette = {
black,
white,
primary: {
contrastText: white,
main: black
},
secondary: {
contrastText: white,
main: yellow
},
success: {
contrastText: white,
dark: colors.green[900],
main: colors.green[600],
light: colors.green[400]
},
info: {
contrastText: white,
dark: colors.blue[900],
main: colors.blue[600],
light: colors.blue[400]
},
warning: {
contrastText: white,
dark: colors.orange[900],
main: colors.orange[600],
light: colors.orange[400]
},
error: {
contrastText: white,
dark: colors.red[900],
main: colors.red[600],
light: colors.red[400]
},
text: {
primary: colors.blueGrey[900],
secondary: colors.blueGrey[600],
link: colors.blue[600]
},
background: {
default: '#F4F6F8',
paper: white
},
icon: colors.blueGrey[600],
divider: colors.grey[200]
}
export default palette