mirror of
https://github.com/sasjs/server.git
synced 2025-12-11 19:44:35 +00:00
feat(log): use improved log for SAS run time only
This commit is contained in:
@@ -23,6 +23,7 @@ import LogTabWithIcons from './internal/components/log/logTabWithIcons'
|
|||||||
import { usePrompt } from '../../utils/hooks'
|
import { usePrompt } from '../../utils/hooks'
|
||||||
import { getLanguageFromExtension } from './internal/helper'
|
import { getLanguageFromExtension } from './internal/helper'
|
||||||
import useEditor from './internal/hooks/useEditor'
|
import useEditor from './internal/hooks/useEditor'
|
||||||
|
import { RunTimeType } from '../../context/appContext'
|
||||||
|
|
||||||
const StyledTabPanel = styled(TabPanel)(() => ({
|
const StyledTabPanel = styled(TabPanel)(() => ({
|
||||||
padding: '10px'
|
padding: '10px'
|
||||||
@@ -110,7 +111,11 @@ const SASjsEditor = ({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
const logWithErrorsOrWarnings = log?.errors.length || log?.warnings.length
|
// INFO: variable indicating if selected run type is SAS if there are any errors or warnings in the log
|
||||||
|
const logWithErrorsOrWarnings =
|
||||||
|
selectedRunTime === RunTimeType.SAS &&
|
||||||
|
log &&
|
||||||
|
(log?.errors.length !== 0 || log?.warnings.length !== 0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ width: '100%', typography: 'body1', marginTop: '50px' }}>
|
<Box sx={{ width: '100%', typography: 'body1', marginTop: '50px' }}>
|
||||||
@@ -155,6 +160,11 @@ const SASjsEditor = ({
|
|||||||
icon={
|
icon={
|
||||||
logWithErrorsOrWarnings ? <LogTabWithIcons log={log} /> : ''
|
logWithErrorsOrWarnings ? <LogTabWithIcons log={log} /> : ''
|
||||||
}
|
}
|
||||||
|
onClick={() => {
|
||||||
|
const logWrapper = document.querySelector(`#logWrapper`)
|
||||||
|
|
||||||
|
if (logWrapper) logWrapper.scrollTop = 0
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<StyledTab
|
<StyledTab
|
||||||
label={
|
label={
|
||||||
@@ -205,7 +215,9 @@ const SASjsEditor = ({
|
|||||||
</Paper>
|
</Paper>
|
||||||
</StyledTabPanel>
|
</StyledTabPanel>
|
||||||
<StyledTabPanel value="log">
|
<StyledTabPanel value="log">
|
||||||
{log && <LogComponent log={log} />}
|
{log && (
|
||||||
|
<LogComponent log={log} selectedRunTime={selectedRunTime} />
|
||||||
|
)}
|
||||||
</StyledTabPanel>
|
</StyledTabPanel>
|
||||||
<StyledTabPanel value="webout">
|
<StyledTabPanel value="webout">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ListItemText } from '@mui/material'
|
|||||||
import { makeStyles } from '@mui/styles'
|
import { makeStyles } from '@mui/styles'
|
||||||
import Highlight from 'react-highlight'
|
import Highlight from 'react-highlight'
|
||||||
import { LogObject } from '../../../../../utils'
|
import { LogObject } from '../../../../../utils'
|
||||||
|
import { RunTimeType } from '../../../../../context/appContext'
|
||||||
|
|
||||||
const useStyles: any = makeStyles((theme: any) => ({
|
const useStyles: any = makeStyles((theme: any) => ({
|
||||||
expansionDescription: {
|
expansionDescription: {
|
||||||
@@ -30,10 +31,11 @@ const useStyles: any = makeStyles((theme: any) => ({
|
|||||||
|
|
||||||
interface LogComponentProps {
|
interface LogComponentProps {
|
||||||
log: LogObject
|
log: LogObject
|
||||||
|
selectedRunTime: RunTimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
const LogComponent = (props: LogComponentProps) => {
|
const LogComponent = (props: LogComponentProps) => {
|
||||||
const { log } = props
|
const { log, selectedRunTime } = props
|
||||||
|
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
@@ -63,77 +65,91 @@ const LogComponent = (props: LogComponentProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
id="logWrapper"
|
{selectedRunTime === RunTimeType.SAS ? (
|
||||||
style={{ overflowY: 'auto', maxHeight: 'calc(100vh - 130px)' }}
|
|
||||||
>
|
|
||||||
<div style={{ backgroundColor: 'white' }}>
|
|
||||||
<div
|
<div
|
||||||
style={{
|
id="logWrapper"
|
||||||
display: 'flex',
|
style={{ overflowY: 'auto', maxHeight: 'calc(100vh - 130px)' }}
|
||||||
flexDirection: 'row',
|
>
|
||||||
justifyContent: 'center',
|
<div style={{ backgroundColor: 'white' }}>
|
||||||
alignItems: 'center',
|
<div
|
||||||
gap: 10
|
style={{
|
||||||
}}
|
display: 'flex',
|
||||||
></div>
|
flexDirection: 'row',
|
||||||
<div style={{ paddingBottom: 10 }}>
|
justifyContent: 'center',
|
||||||
<TreeView
|
alignItems: 'center',
|
||||||
defaultCollapseIcon={<ExpandMore />}
|
gap: 10
|
||||||
defaultExpandIcon={<ChevronRight />}
|
}}
|
||||||
>
|
></div>
|
||||||
{log?.errors.length && (
|
<div style={{ paddingBottom: 10 }}>
|
||||||
<TreeItem
|
<TreeView
|
||||||
nodeId="errors"
|
defaultCollapseIcon={<ExpandMore />}
|
||||||
label={
|
defaultExpandIcon={<ChevronRight />}
|
||||||
<Typography color="error">
|
|
||||||
{`Errors (${log.errors.length})`}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{log.errors &&
|
{log?.errors.length !== 0 && (
|
||||||
log.errors.map((error, ind) => (
|
<TreeItem
|
||||||
<TreeItem
|
nodeId="errors"
|
||||||
nodeId={`error_${ind}`}
|
label={
|
||||||
label={<ListItemText primary={error} />}
|
<Typography color="error">
|
||||||
key={`error_${ind}`}
|
{`Errors (${log.errors.length})`}
|
||||||
onClick={() => goToLogLine('error', ind)}
|
</Typography>
|
||||||
/>
|
}
|
||||||
))}
|
>
|
||||||
</TreeItem>
|
{log.errors &&
|
||||||
)}
|
log.errors.map((error, ind) => (
|
||||||
{log?.warnings.length && (
|
<TreeItem
|
||||||
<TreeItem
|
nodeId={`error_${ind}`}
|
||||||
nodeId="warnings"
|
label={<ListItemText primary={error} />}
|
||||||
label={
|
key={`error_${ind}`}
|
||||||
<Typography>{`Warnings (${log.warnings.length})`}</Typography>
|
onClick={() => goToLogLine('error', ind)}
|
||||||
}
|
/>
|
||||||
>
|
))}
|
||||||
{log.warnings &&
|
</TreeItem>
|
||||||
log.warnings.map((warning, ind) => (
|
)}
|
||||||
<TreeItem
|
{log?.warnings.length !== 0 && (
|
||||||
nodeId={`warning_${ind}`}
|
<TreeItem
|
||||||
label={<ListItemText primary={warning} />}
|
nodeId="warnings"
|
||||||
key={`warning_${ind}`}
|
label={
|
||||||
onClick={() => goToLogLine('warning', ind)}
|
<Typography>{`Warnings (${log.warnings.length})`}</Typography>
|
||||||
/>
|
}
|
||||||
))}
|
>
|
||||||
</TreeItem>
|
{log.warnings &&
|
||||||
)}
|
log.warnings.map((warning, ind) => (
|
||||||
</TreeView>
|
<TreeItem
|
||||||
</div>
|
nodeId={`warning_${ind}`}
|
||||||
</div>
|
label={<ListItemText primary={warning} />}
|
||||||
|
key={`warning_${ind}`}
|
||||||
|
onClick={() => goToLogLine('warning', ind)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</TreeItem>
|
||||||
|
)}
|
||||||
|
</TreeView>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
id={`log_container`}
|
id={`log_container`}
|
||||||
variant="h5"
|
variant="h5"
|
||||||
className={classes.expansionDescription}
|
className={classes.expansionDescription}
|
||||||
>
|
>
|
||||||
<Highlight className={'html'} innerHTML={true}>
|
<Highlight className={'html'} innerHTML={true}>
|
||||||
{decodeHtml(log?.body || '')}
|
{decodeHtml(log?.body || '')}
|
||||||
</Highlight>
|
</Highlight>
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<h2>Log</h2>
|
||||||
|
<pre
|
||||||
|
id="log"
|
||||||
|
style={{ overflow: 'auto', height: 'calc(100vh - 220px)' }}
|
||||||
|
>
|
||||||
|
{log}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,14 @@ const LogTabWithIcons = (props: LogTabProps) => {
|
|||||||
gap: 6,
|
gap: 6,
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
|
||||||
const logWrapper = document.querySelector(`#logWrapper`)
|
|
||||||
|
|
||||||
if (logWrapper) logWrapper.scrollTop = 0
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span>log</span>
|
<span>log</span>
|
||||||
{errors.length && <ErrorOutline color="error" style={{ fontSize: 20 }} />}
|
{errors.length !== 0 && (
|
||||||
{warnings.length && <Warning style={{ fontSize: 20 }} />}{' '}
|
<ErrorOutline color="error" style={{ fontSize: 20 }} />
|
||||||
|
)}
|
||||||
|
{warnings.length !== 0 && (
|
||||||
|
<Warning style={{ fontSize: 20, color: 'green' }} />
|
||||||
|
)}{' '}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
useSnackbar,
|
useSnackbar,
|
||||||
useStateWithCallback
|
useStateWithCallback
|
||||||
} from '../../../../utils/hooks'
|
} from '../../../../utils/hooks'
|
||||||
|
import { parseErrorsAndWarnings, LogObject } from '../../../../utils'
|
||||||
|
|
||||||
const SASJS_LOGS_SEPARATOR =
|
const SASJS_LOGS_SEPARATOR =
|
||||||
'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784'
|
'SASJS_LOGS_SEPARATOR_163ee17b6ff24f028928972d80a26784'
|
||||||
@@ -41,10 +42,12 @@ const useEditor = ({
|
|||||||
|
|
||||||
const [prevFileContent, setPrevFileContent] = useStateWithCallback('')
|
const [prevFileContent, setPrevFileContent] = useStateWithCallback('')
|
||||||
const [fileContent, setFileContent] = useState('')
|
const [fileContent, setFileContent] = useState('')
|
||||||
const [log, setLog] = useState('')
|
const [log, setLog] = useState<LogObject>()
|
||||||
const [webout, setWebout] = useState('')
|
const [webout, setWebout] = useState('')
|
||||||
const [runTimes, setRunTimes] = useState<string[]>([])
|
const [runTimes, setRunTimes] = useState<string[]>([])
|
||||||
const [selectedRunTime, setSelectedRunTime] = useState('')
|
const [selectedRunTime, setSelectedRunTime] = useState<RunTimeType>(
|
||||||
|
RunTimeType.SAS
|
||||||
|
)
|
||||||
const [selectedFileExtension, setSelectedFileExtension] = useState('')
|
const [selectedFileExtension, setSelectedFileExtension] = useState('')
|
||||||
const [openFilePathInputModal, setOpenFilePathInputModal] = useState(false)
|
const [openFilePathInputModal, setOpenFilePathInputModal] = useState(false)
|
||||||
const [showDiff, setShowDiff] = useState(false)
|
const [showDiff, setShowDiff] = useState(false)
|
||||||
@@ -150,6 +153,13 @@ const useEditor = ({
|
|||||||
const runCode = useCallback(
|
const runCode = useCallback(
|
||||||
(code: string) => {
|
(code: string) => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
|
// Scroll to bottom of log
|
||||||
|
const logElement = document.getElementById('log')
|
||||||
|
if (logElement) logElement.scrollTop = logElement.scrollHeight
|
||||||
|
|
||||||
|
setIsLoading(false)
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post(`/SASjsApi/code/execute`, {
|
.post(`/SASjsApi/code/execute`, {
|
||||||
code: programPathInjection(
|
code: programPathInjection(
|
||||||
@@ -160,8 +170,18 @@ const useEditor = ({
|
|||||||
runTime: selectedRunTime
|
runTime: selectedRunTime
|
||||||
})
|
})
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
|
const { errors, warnings, logLines } = parseErrorsAndWarnings(
|
||||||
|
res.data.split(SASJS_LOGS_SEPARATOR)[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
const log: LogObject = {
|
||||||
|
body: logLines.join(`\n`),
|
||||||
|
errors,
|
||||||
|
warnings
|
||||||
|
}
|
||||||
|
|
||||||
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
setWebout(res.data.split(SASJS_LOGS_SEPARATOR)[0] ?? '')
|
||||||
setLog(res.data.split(SASJS_LOGS_SEPARATOR)[1] ?? '')
|
setLog(log)
|
||||||
setTab('log')
|
setTab('log')
|
||||||
|
|
||||||
// Scroll to bottom of log
|
// Scroll to bottom of log
|
||||||
@@ -249,7 +269,7 @@ const useEditor = ({
|
|||||||
}, [appContext.runTimes])
|
}, [appContext.runTimes])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (runTimes.length) setSelectedRunTime(runTimes[0])
|
if (runTimes.length) setSelectedRunTime(runTimes[0] as RunTimeType)
|
||||||
}, [runTimes])
|
}, [runTimes])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -280,7 +300,6 @@ const useEditor = ({
|
|||||||
const content = localStorage.getItem('fileContent') ?? ''
|
const content = localStorage.getItem('fileContent') ?? ''
|
||||||
setFileContent(content)
|
setFileContent(content)
|
||||||
}
|
}
|
||||||
setLog('')
|
|
||||||
setWebout('')
|
setWebout('')
|
||||||
setTab('code')
|
setTab('code')
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -294,7 +313,9 @@ const useEditor = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fileExtension = selectedFileExtension.toLowerCase()
|
const fileExtension = selectedFileExtension.toLowerCase()
|
||||||
if (runTimes.includes(fileExtension)) setSelectedRunTime(fileExtension)
|
|
||||||
|
if (runTimes.includes(fileExtension))
|
||||||
|
setSelectedRunTime(fileExtension as RunTimeType)
|
||||||
}, [selectedFileExtension, runTimes])
|
}, [selectedFileExtension, runTimes])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const parseErrorsAndWarnings = (log: string) => {
|
|||||||
errorLines.push(line)
|
errorLines.push(line)
|
||||||
|
|
||||||
logLines[index] =
|
logLines[index] =
|
||||||
`<font id="error_${errorLines.length - 1}">` +
|
`<font id="error_${errorLines.length - 1}" style="color: red;">` +
|
||||||
logLines[index] +
|
logLines[index] +
|
||||||
'</font>'
|
'</font>'
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ export const parseErrorsAndWarnings = (log: string) => {
|
|||||||
warningLines.push(line)
|
warningLines.push(line)
|
||||||
|
|
||||||
logLines[index] =
|
logLines[index] =
|
||||||
`<font id="warning_${warningLines.length - 1}">` +
|
`<font id="warning_${warningLines.length - 1}" style="color: green;">` +
|
||||||
logLines[index] +
|
logLines[index] +
|
||||||
'</font>'
|
'</font>'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user