({
loader: {
display: 'flex',
@@ -19,6 +21,8 @@ const styles = () => ({
margin: 0
},
message: {
+ ...defaultFont,
+ fontSize: 14,
padding: 0,
margin: 0
}
diff --git a/app/components/common/styles/appLogo.js b/app/components/common/styles/appLogo.js
index 2f863242..f0868cdf 100755
--- a/app/components/common/styles/appLogo.js
+++ b/app/components/common/styles/appLogo.js
@@ -6,7 +6,8 @@ const styles = theme => ({
display: 'flex',
flexDirection: 'column',
width: '100%',
- maxWidth: 290
+ maxWidth: 290,
+ padding: theme.spacing(2)
},
logo_animation_wrapper: {
position: 'relative',
@@ -72,7 +73,7 @@ const styles = theme => ({
stroke: lighten(theme.palette.secondary.main, 0.2)
},
warningColor: {
- stroke: lighten(theme.palette.warning.main, 0.2)
+ stroke: lighten(theme.palette.error.main, 0.2)
},
errorColor: {
stroke: lighten(theme.palette.error.main, 0.2)
diff --git a/app/components/common/styles/appTabs.js b/app/components/common/styles/appTabs.js
index c60d5b53..d87e581d 100755
--- a/app/components/common/styles/appTabs.js
+++ b/app/components/common/styles/appTabs.js
@@ -10,15 +10,10 @@ const styles = theme => ({
},
tabLabel: {
...defaultFont,
- fontSize: 16,
- paddingBottom: theme.spacing(1)
+ fontSize: 16
},
noMargin: {
margin: 0
- },
- appBar: {
- marginLeft: theme.spacing(0.5),
- marginRight: theme.spacing(0.5)
}
});
diff --git a/app/components/common/styles/installFromSources.js b/app/components/common/styles/installFromSources.js
index 2dae38bd..f9b2eeb1 100755
--- a/app/components/common/styles/installFromSources.js
+++ b/app/components/common/styles/installFromSources.js
@@ -28,7 +28,7 @@ const styles = theme => ({
},
warningColor: {
color: theme.palette.common.white,
- backgroundColor: theme.palette.warning.light
+ backgroundColor: theme.palette.error.light
},
errorColor: {
color: theme.palette.common.white,
diff --git a/app/components/views/common/styles/searchBox.js b/app/components/common/styles/searchBox.js
similarity index 73%
rename from app/components/views/common/styles/searchBox.js
rename to app/components/common/styles/searchBox.js
index aded36bd..175bed4e 100755
--- a/app/components/views/common/styles/searchBox.js
+++ b/app/components/common/styles/searchBox.js
@@ -16,31 +16,32 @@ const styles = theme => ({
border: '1px solid'
},
searchIcon: {
+ cursor: 'pointer',
width: theme.spacing(1) * 6,
height: '100%',
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- color: theme.palette.common.white
+ paddingLeft: theme.spacing(2),
+ color: theme.palette.common.white,
+ zIndex: 9999
},
inputRoot: {
color: 'inherit',
width: '100%',
fontFamily: 'inherit',
- fontSize: 14,
- lineHeight: '1em'
+ fontSize: 20,
+ lineHeight: '0.75em'
},
inputInput: {
- color: '#fff',
- paddingTop: theme.spacing(1),
- paddingRight: theme.spacing(1),
- paddingBottom: theme.spacing(1),
- paddingLeft: theme.spacing(1) * 10,
+ color: theme.palette.common.white,
+ paddingLeft: theme.spacing(10),
+ paddingRight: theme.spacing(10),
transition: theme.transitions.create('width'),
- width: 145,
+ width: 175,
'&:focus': {
- width: 275
+ width: 220
}
}
});
diff --git a/app/components/common/styles/terminalStyles.js b/app/components/common/styles/terminalStyles.js
deleted file mode 100755
index 18722c55..00000000
--- a/app/components/common/styles/terminalStyles.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { defaultFont } from 'styles/variables';
-
-const styles = theme => ({
- root: {
- width: '100%'
- },
- top: {
- ...defaultFont,
- height: '22px',
- background: 'linear-gradient(0deg, #d8d8d8, #ececec)',
- borderTop: '1px solid white',
- borderBottom: '1px solid #b3b3b3',
- borderTopLeftRadius: '5px',
- borderTopRightRadius: '5px',
- color: 'rgba(0, 0, 0, 0.7)',
- fontSize: '13px',
- lineHeight: '22px',
- textAlign: 'center'
- },
- buttons: {
- position: 'absolute',
- float: 'left',
- margin: '0 8px'
- },
- button: {
- padding: 0,
- margin: 0,
- marginRight: 4,
- width: 12,
- height: 12,
- backgroundColor: 'transparent',
- border: '1px solid rgba(0, 0, 0, 0.2)',
- borderRadius: '6px',
- color: 'rgba(0, 0, 0, 0.5)'
- },
- close: {
- backgroundColor: '#ff6159'
- },
- maximize: {
- backgroundColor: '#25cc3e'
- },
- minimize: {
- backgroundColor: '#ffbf2f'
- },
- terminal: {
- display: 'flex',
- flexDirection: 'column',
- padding: theme.spacing(0.5),
- backgroundColor: 'black',
- opacity: '0.7',
- minHeight: 150,
- overflowY: 'scroll',
- color: 'white',
- fontFamily: "'Source Code Pro', monospace",
- fontWeight: '200',
- fontSize: '14px',
- whiteSpace: '-o-pre-wrap',
- wordWrap: 'break-word',
- borderBottomLeftRadius: 5,
- borderBottomRightRadius: 5,
- '& > span': {
- width: '100%',
- padding: theme.spacing(1)
- }
- }
-});
-
-export default styles;
diff --git a/app/components/views/audit/components/Advisories.js b/app/components/views/audit/components/Advisories.js
index a3d53be7..02fd3366 100755
--- a/app/components/views/audit/components/Advisories.js
+++ b/app/components/views/audit/components/Advisories.js
@@ -110,7 +110,7 @@ const Advisories = ({ classes, data, handleAudit, vulnerabilities }) => {
};
return (
-
+
{
{name}}
+ title={{name}}
className={classes.cardHeader}
subheader={
-
+
{title}
}
diff --git a/app/components/views/audit/components/ListTypes.js b/app/components/views/audit/components/ListTypes.js
index b29d49f9..a66aa689 100755
--- a/app/components/views/audit/components/ListTypes.js
+++ b/app/components/views/audit/components/ListTypes.js
@@ -1,7 +1,7 @@
/* eslint-disable no-unused-vars */
import React from 'react';
-import { objectOf, oneOfType, string, array, object, number } from 'prop-types';
+import { objectOf, oneOfType, string, array, object, func, number } from 'prop-types';
import { withStyles } from '@material-ui/core';
import { groupBy } from 'ramda';
@@ -57,17 +57,17 @@ const ListTypes = ({ classes, theme, data, vulnerabilities }) => {
{
value: critical,
name: iMessage('label', 'critical'),
- color: 'danger'
+ color: 'error'
},
{
value: high,
name: iMessage('label', 'high'),
- color: 'warning'
+ color: 'secondary'
},
{
value: moderate,
name: iMessage('label', 'moderate'),
- color: 'secondary'
+ color: 'primary'
},
{
value: low,
@@ -77,11 +77,11 @@ const ListTypes = ({ classes, theme, data, vulnerabilities }) => {
{
value: info,
name: iMessage('label', 'info'),
- color: 'success'
+ color: 'secondary'
}
];
const totalVulnerabilities = typesData.reduce((acc, type) => acc + type.value, 0);
- console.log(theme.palette)
+
return (
@@ -124,7 +124,7 @@ const ListTypes = ({ classes, theme, data, vulnerabilities }) => {
ListTypes.propTypes = {
classes: objectOf(string).isRequired,
- theme: objectOf(oneOfType([string, object, array])).isRequired,
+ theme: objectOf(oneOfType([string, object, array, func])).isRequired,
data: objectOf(oneOfType([string, array, object])).isRequired,
vulnerabilities: objectOf(number).isRequired
};
diff --git a/app/components/views/audit/components/Summary.js b/app/components/views/audit/components/Summary.js
index 46752af3..49248195 100755
--- a/app/components/views/audit/components/Summary.js
+++ b/app/components/views/audit/components/Summary.js
@@ -28,7 +28,6 @@ import { iMessage } from 'commons/utils';
import styles from '../styles/summary';
const Summary = ({ classes, data, onClose }) => {
- console.log(data);
const dataKeys = Object.keys(data);
const dataValues = Object.values(data);
diff --git a/app/components/views/audit/styles/listTypes.js b/app/components/views/audit/styles/listTypes.js
index 2f29973e..b20d8439 100755
--- a/app/components/views/audit/styles/listTypes.js
+++ b/app/components/views/audit/styles/listTypes.js
@@ -19,7 +19,9 @@ const styles = theme => ({
marginLeft: theme.spacing(1),
},
chip: {
- fontSize: 20
+ fontSize: 20,
+ color: theme.palette.common.white,
+ backgroundColor: theme.palette.common.neutral
}
});
diff --git a/app/components/views/common/Init.js b/app/components/views/common/Init.js
index 91f83b53..ffe9218e 100755
--- a/app/components/views/common/Init.js
+++ b/app/components/views/common/Init.js
@@ -1,125 +1,64 @@
-import React from 'react';
+import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { remote } from 'electron';
import { useState } from 'react';
-import { useDispatch } from 'redux-react-hook';
import { withStyles } from '@material-ui/core/styles';
+import { directoryParameters } from 'commons/parameters';
+import { iMessage } from 'commons/utils';
-import Checkbox from '@material-ui/core/Checkbox';
+import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
-import Divider from '@material-ui/core/Divider';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
-
-import { directoryParameters } from 'commons/parameters';
-import { iMessage } from 'commons/utils';
-import { runInit, runLock } from 'models/npm/actions';
import styles from './styles/initForm';
-const InitView = ({ classes, onClose }) => {
- const [type, setType] = useState('init');
+const Init = ({ classes, enableInit }) => {
const [initOptions, setInitOptions] = useState({ directory: null });
- const dispatch = useDispatch();
+ const { directory } = initOptions;
- const { directory } = initOptions || {};
-
- const startInitFlow = () =>
+ const startInitFlow = useCallback(() =>
remote.dialog.showOpenDialog(
remote.getCurrentWindow(),
directoryParameters,
- filePath =>
- filePath &&
- setInitOptions({
- ...initOptions,
- directory: filePath[0]
- })
- );
-
- const npmLock = () => {
- dispatch(
- runLock({
- ipcEvent: 'npm-init-lock',
- cmd: ['install'],
- packageLock: true,
- directory
- })
- );
-
- onClose();
- };
-
- const npmInit = () => {
- dispatch(
- runInit({
- ipcEvent: 'npm-init',
- cmd: ['init'],
- directory
- })
- );
-
- onClose();
- };
+ filePath => {
+ if (filePath) {
+ setInitOptions({
+ ...initOptions,
+ directory: filePath[0]
+ })
+ enableInit(filePath[0])
+ }
+ }
+ ), [initOptions, enableInit]);
- return (
-
-
- {iMessage('info', 'createPackageJsonHelperText')}
-
-
-
{!extraneous && group && !fromSearch && (
-
+
{group}
)}
@@ -134,7 +133,6 @@ PackageItem.propTypes = {
isOutdated: bool,
inOperation: bool,
hasError: bool,
- inPackageJson: bool
};
export default withStyles(styles)(PackageItem);
diff --git a/app/components/views/packages/Toolbar.js b/app/components/views/packages/Toolbar.js
index 225b9a1e..6b9d2dcc 100755
--- a/app/components/views/packages/Toolbar.js
+++ b/app/components/views/packages/Toolbar.js
@@ -55,89 +55,95 @@ const ToolbarView = ({
const dispatch = useDispatch();
const packagesOutdatedNames = outdated.map(pkg => pkg.name);
- const openFilters = (e, close) => {
- const { target } = e || {};
+ const openFilters = useCallback(
+ (e, close) => {
+ const { target } = e || {};
- setAnchorEl(close ? null : target);
- toggleFilters(!filtersOn);
- scrollWrapper(0);
- };
+ setAnchorEl(close ? null : target);
+ toggleFilters(!filtersOn);
+ scrollWrapper(0);
+ },
+ [filtersOn, scrollWrapper]
+ );
- const clearAllFilters = () => {
+ const clearAllFilters = useCallback(() => {
if (filteredByNamePackages.length) {
setFilteredByNamePackages([]);
}
dispatch(clearFilters());
- };
-
- const handleAction = (action, force, latest) => {
- let pkgOptions;
+ }, [filteredByNamePackages, setFilteredByNamePackages, dispatch]);
- if (action === 'clearFilters') {
- return clearAllFilters();
- }
-
- if (!selected.length) {
- return;
- }
+ const handleAction = useCallback(
+ (action, force, latest) => {
+ let pkgOptions;
- if (mode === 'local' && action === 'install' && !force) {
- return toggleOptions({
- open: true,
- single: false
- });
- }
+ if (action === 'clearFilters') {
+ return clearAllFilters();
+ }
- if (action === 'install') {
- if (mode === 'local') {
- pkgOptions = selected.map(packageName => {
- const packageDetails = packagesData.find(
- packageDataDetails => packageDataDetails.name === packageName
- );
- const { __group } = packageDetails;
+ if (!selected.length) {
+ return;
+ }
- return [PACKAGE_GROUPS[__group]];
+ if (mode === 'local' && action === 'install' && !force) {
+ return toggleOptions({
+ open: true,
+ single: false
});
}
- return dispatch(
- installMultiplePackages({
- ipcEvent: 'npm-install',
- cmd: selected.map(() => 'install'),
- multiple: true,
- pkgOptions: mode === 'local' ? pkgOptions : null,
- packages: latest
- ? selected.map(selectedPackage => `${selectedPackage}@latest`)
- : selected
- })
- );
- }
+ if (action === 'install') {
+ if (mode === 'local') {
+ pkgOptions = selected.map(packageName => {
+ const packageDetails = packagesData.find(
+ packageDataDetails => packageDataDetails.name === packageName
+ );
+ const { __group } = packageDetails;
- if (action === 'uninstall') {
- return dispatch(
- uninstallPackages({
- ipcEvent: 'npm-uninstall',
- cmd: ['uninstall'],
- multiple: true,
- packages: selected
- })
- );
- }
+ return [PACKAGE_GROUPS[__group]];
+ });
+ }
- if (action === 'update') {
- return dispatch(
- updatePackages({
- ipcEvent: 'npm-update',
- cmd: ['update'],
- multiple: true,
- packages: selected
- })
- );
- }
+ return dispatch(
+ installMultiplePackages({
+ ipcEvent: 'npm-install',
+ cmd: selected.map(() => 'install'),
+ multiple: true,
+ pkgOptions: mode === 'local' ? pkgOptions : null,
+ packages: latest
+ ? selected.map(selectedPackage => `${selectedPackage}@latest`)
+ : selected
+ })
+ );
+ }
+
+ if (action === 'uninstall') {
+ return dispatch(
+ uninstallPackages({
+ ipcEvent: 'npm-uninstall',
+ cmd: ['uninstall'],
+ multiple: true,
+ packages: selected
+ })
+ );
+ }
+
+ if (action === 'update') {
+ return dispatch(
+ updatePackages({
+ ipcEvent: 'npm-update',
+ cmd: ['update'],
+ multiple: true,
+ packages: selected
+ })
+ );
+ }
- return false;
- };
+ return false;
+ },
+ [selected, packagesData, mode, clearAllFilters, toggleOptions, dispatch]
+ );
const renderAction = action =>
switchcase({
@@ -159,13 +165,13 @@ const ToolbarView = ({
})('none')(action);
const renderToolbarActions = () => (
-
+ <>
{!fromSearch && total ? (
) : null}
-
+ >
);
const hasUpdatedPackages = useCallback(
diff --git a/app/components/views/packages/styles/packageItem.js b/app/components/views/packages/styles/packageItem.js
index 74b723e2..87214e93 100755
--- a/app/components/views/packages/styles/packageItem.js
+++ b/app/components/views/packages/styles/packageItem.js
@@ -1,71 +1,68 @@
-import { darken, lighten } from "@material-ui/core/styles/colorManipulator";
-import { flexContainer, defaultFont } from "styles/variables";
+import { flexContainer, defaultFont } from 'styles/variables';
const styles = theme => ({
flexContainer: {
...flexContainer,
- alignItems: "center",
- justifyContent: "space-between"
+ alignItems: 'center',
+ justifyContent: 'space-between'
},
flexContainerCell: {
...flexContainer,
- flexDirection: "column",
- justifyContent: "flex-start",
- alignItems: "flex-start"
+ flexDirection: 'column',
+ justifyContent: 'flex-start',
+ alignItems: 'flex-start'
},
tableRow: {
- border: "none",
- padding: 10,
- lineHeight: "1.1",
- verticalAlign: "middle",
- "&:hover": {
- cursor: "pointer"
+ border: 'none',
+ padding: theme.spacing(1),
+ lineHeight: '1.1',
+ verticalAlign: 'middle',
+ '&:hover': {
+ cursor: 'pointer'
}
},
tableCell: {
...defaultFont,
- fontSize: "1rem",
- textAlign: "center",
- "& p": {
- overflowWrap: "break-word"
+ textAlign: 'center',
+ '& p': {
+ overflowWrap: 'break-word'
}
},
loader: {
marginLeft: theme.spacing(1)
},
name: {
- ...defaultFont,
- [theme.breakpoints.up("lg")]: {
- width: "auto"
+ [theme.breakpoints.up('lg')]: {
+ width: 'auto'
},
- [theme.breakpoints.down("md")]: {
+ [theme.breakpoints.down('md')]: {
width: 200
},
- whiteSpace: "nowrap",
- overflow: "hidden",
- textOverflow: "ellipsis",
- textAlign: "left"
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ textAlign: 'left'
},
group: {
- color: darken(theme.palette.secondary.light, 0.3)
+ color: theme.palette.primary.main
},
statusMissing: {
- color: darken(theme.palette.secondary.main, 0.1)
+ color: theme.palette.secondary.main
},
statusOK: {
- color: lighten("#00b300", 0.3)
+ color: '#00b300'
},
statusPeerMissing: {
- color: darken(theme.palette.secondary.main, 0.1)
+ color: theme.palette.secondary.main
},
statusOutdated: {
- color: lighten(theme.palette.warning.main, 0.3)
+ color: theme.palette.error.main
},
statusExtraneous: {
- color: darken(theme.palette.secondary.main, 0.1)
+ color: theme.palette.secondary.main
},
statusError: {
- color: darken(theme.palette.secondary.main, 0.1)
+ color: theme.palette.secondary.main
}
});
diff --git a/app/components/views/packages/styles/packages.js b/app/components/views/packages/styles/packages.js
index b666059d..78cc8074 100755
--- a/app/components/views/packages/styles/packages.js
+++ b/app/components/views/packages/styles/packages.js
@@ -2,7 +2,7 @@ import { darken, lighten } from '@material-ui/core/styles/colorManipulator';
import { flexContainer, defaultFont } from 'styles/variables';
const styles = theme => ({
- root: {
+ paper: {
width: '100%',
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
@@ -81,7 +81,6 @@ const styles = theme => ({
},
tableCell: {
...defaultFont,
- fontSize: '1rem',
textAlign: 'center',
'& p': {
overflowWrap: 'break-word'
@@ -128,7 +127,7 @@ const styles = theme => ({
color: darken(theme.palette.secondary.main, 0.1)
},
statusOutdated: {
- color: lighten(theme.palette.warning.main, 0.3)
+ color: lighten(theme.palette.error.main, 0.3)
},
statusExtraneous: {
color: darken(theme.palette.secondary.main, 0.1)
@@ -144,7 +143,6 @@ const styles = theme => ({
},
name: {
...defaultFont,
- fontSize: '1rem',
[theme.breakpoints.up('md')]: {
maxWidth: '100%'
},
diff --git a/app/components/views/sidebar/Sidebar.js b/app/components/views/sidebar/Sidebar.js
new file mode 100755
index 00000000..13cc908e
--- /dev/null
+++ b/app/components/views/sidebar/Sidebar.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import { string, objectOf, func, bool, object, arrayOf } from 'prop-types';
+import { withStyles } from '@material-ui/styles';
+
+import { AppTabs, AppLogo } from 'components/common/';
+import {
+ PackagesTab,
+ ActionsTab,
+ HistoryTab
+} from 'components/views/sidebar/tabs';
+import styles from './styles/sidebar';
+
+const Sidebar = ({
+ classes,
+ loading,
+ mode,
+ history,
+ loadDirectory,
+ updatedAt,
+ tabPackagesData,
+ installPackagesFromJson,
+ dedupe
+}) => (
+
+ );
+
+Sidebar.propTypes = {
+ classes: objectOf(string).isRequired,
+ mode: string.isRequired,
+ loading: bool,
+ history: arrayOf(object),
+ loadDirectory: func.isRequired,
+ installPackagesFromJson: func.isRequired,
+ dedupe: func.isRequired,
+ updatedAt: string,
+ tabPackagesData: arrayOf(object)
+};
+
+export default withStyles(styles)(Sidebar);
diff --git a/app/components/views/sidebar/index.js b/app/components/views/sidebar/index.js
new file mode 100755
index 00000000..8f6f607f
--- /dev/null
+++ b/app/components/views/sidebar/index.js
@@ -0,0 +1,5 @@
+/* eslint-disable import/prefer-default-export */
+
+import Sidebar from './Sidebar';
+
+export { Sidebar };
diff --git a/app/components/views/sidebar/styles/sidebar.js b/app/components/views/sidebar/styles/sidebar.js
new file mode 100755
index 00000000..5bb8c1d7
--- /dev/null
+++ b/app/components/views/sidebar/styles/sidebar.js
@@ -0,0 +1,13 @@
+const styles = theme => ({
+ root: {
+ width: '100%',
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column'
+ },
+ spacer: {
+ margin: theme.spacing(1, 0)
+ }
+});
+
+export default styles;
diff --git a/app/components/views/sidebar/tabs/Actions.js b/app/components/views/sidebar/tabs/Actions.js
index 943af1d4..166b8c34 100755
--- a/app/components/views/sidebar/tabs/Actions.js
+++ b/app/components/views/sidebar/tabs/Actions.js
@@ -1,7 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
-
import Tooltip from '@material-ui/core/Tooltip';
import IconButton from '@material-ui/core/IconButton';
import List from '@material-ui/core/List';
@@ -10,85 +9,87 @@ import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
import ArrowRightIcon from '@material-ui/icons/ArrowRightAlt';
import Typography from '@material-ui/core/Typography';
-
import { iMessage } from 'commons/utils';
+
import styles from './styles/actions';
-const ActionsTab = ({
- classes,
- mode,
- installPackagesFromJson,
- toggleDialog
-}) => (
-
-
-
-
- {iMessage('action', 'npmInstall')}
-
- }
- secondary={
-
- {iMessage('info', 'npmInstallInfo')}
-
- }
- />
-
-
-
-
-
-
-
-
- {iMessage('action', 'npmDoctor')}
-
+const ActionsTab = ({ classes, mode, onInstallPackagesFromJson, onDedupe }) =>
+
+
+
+ {iMessage('action', 'npmInstall')}
+
+ }
+ secondary={
+
+ {iMessage('info', 'npmInstallInfo')}
+
+ }
+ />
+
+
- {iMessage('info', 'npmDoctorInfo')}
-
+ >
+
+
+
+
+
+
+ {iMessage('action', 'npmDedupe')}
+
+ }
+ secondary={
+
+ {iMessage('info', 'npmDedupeInfo')}
+
+ }
+ />
+
+
-
-
-
-
toggleDialog(true)}
- >
-
-
-
-
-
-
-
-
-);
+ >
+
+
+
+
+
+
ActionsTab.propTypes = {
classes: PropTypes.objectOf(PropTypes.string).isRequired,
- installPackagesFromJson: PropTypes.func.isRequired,
- toggleDialog: PropTypes.func,
+ onInstallPackagesFromJson: PropTypes.func.isRequired,
+ onDedupe: PropTypes.func.isRequired,
mode: PropTypes.string
};
diff --git a/app/components/views/sidebar/tabs/History.js b/app/components/views/sidebar/tabs/History.js
index 977bd0c5..9f52350c 100755
--- a/app/components/views/sidebar/tabs/History.js
+++ b/app/components/views/sidebar/tabs/History.js
@@ -10,10 +10,9 @@ import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
-import ListItemIcon from '@material-ui/core/ListItemIcon';
import ArrowRightIcon from '@material-ui/icons/ArchiveOutlined';
-import FolderIcon from '@material-ui/icons/FolderOpen';
+import { iMessage } from 'commons/utils'
import styles from './styles/history';
@@ -24,7 +23,7 @@ const HistoryTab = ({ classes, directories, onClick }) => (
No history
+ {iMessage('info', 'noHistory')}
}
/>
@@ -37,14 +36,9 @@ const HistoryTab = ({ classes, directories, onClick }) => (
.join('/');
return (
-
-
-
-
-
-
-
-
+
{dir.name}
@@ -56,15 +50,14 @@ const HistoryTab = ({ classes, directories, onClick }) => (
}
/>
-
-
-
onClick(dir.directory)}
- >
-
-
-
+
+ onClick(dir.directory)}
+ disableRipple
+ >
+
+
diff --git a/app/components/views/sidebar/tabs/Packages.js b/app/components/views/sidebar/tabs/Packages.js
index 936064ee..d6586158 100755
--- a/app/components/views/sidebar/tabs/Packages.js
+++ b/app/components/views/sidebar/tabs/Packages.js
@@ -6,39 +6,77 @@ import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
+import Divider from '@material-ui/core/Divider';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import CardActions from '@material-ui/core/CardActions';
import Typography from '@material-ui/core/Typography';
+import UpdateIcon from '@material-ui/icons/Update';
+import { AppLoader } from 'components/common';
+
+import { iMessage } from "commons/utils";
import styles from './styles/packages';
-const PackagesTab = ({ classes, items }) => (
-
-
- {items &&
- items.map(item => (
-
-
-
- {item.primaryText}
-
-
- }
- />
-
-
- {item.secondaryText}
-
-
-
- ))}
-
-
+const PackagesTab = ({ classes, items, loading, updatedAt }) => (
+
+
+
+ {iMessage('title', 'overview')}
+
+
+
+
+
+ {items &&
+ items.map(item => (
+
+
+
+ {item.primaryText}
+
+
+ }
+ />
+
+
+ {item.secondaryText}
+
+
+
+ ))}
+
+
+
+
+
-
-
-
-
toggleDrawer(!drawerOpen)} />
-
+
+
+
+
+
+
+
{switchcase({
packages: () => ,
- problems: () => (
-
- ),
- audit: () =>
+ problems: () => ,
+ audit: () => ,
+ doctor: () => ,
+ notifications: () =>
})()(activePage)}
-
- {snackbar && snackbar.open && (
-
- dispatch(
- setSnackbar({
- open: false,
- message: null,
- type: "info"
- })
- )
- }
- ClickAwayListenerProps={{
- onClickAway: () =>
- dispatch(
- setSnackbar({
- open: false,
- message: null,
- type: "info"
- })
- )
- }}
- >
-
- dispatch(
- setSnackbar({
- open: false,
- message: null,
- type: "info"
- })
- )
- }
- />
-
- )}
+
+
+
);
};
AppLayout.propTypes = {
- classes: PropTypes.objectOf(PropTypes.string).isRequired
+ classes: objectOf(string).isRequired
};
-export default withStyles(styles)(AppLayout);
+export default withStyles(styles, {
+ withTheme: true
+})(AppLayout);
diff --git a/app/containers/AppNavigationBar.js b/app/containers/AppNavigationBar.js
new file mode 100755
index 00000000..a56d4033
--- /dev/null
+++ b/app/containers/AppNavigationBar.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import cn from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import { useMappedState, useDispatch } from 'redux-react-hook';
+import { objectOf, string } from 'prop-types';
+import { setActivePage } from 'models/ui/actions';
+import { NavigationBar } from 'components/views/navigationBar';
+
+import styles from './styles/appNavigationBar';
+
+const mapState = ({
+ common: { mode, directory },
+ ui: {
+ activePage,
+ loaders: {
+ loader: { loading }
+ }
+ },
+ npm: { env }
+}) => ({
+ activePage,
+ mode,
+ directory,
+ env,
+ loading
+});
+
+const AppNavigationBar = ({ classes, className }) => {
+ const { env, mode, directory, activePage } = useMappedState(mapState);
+
+ const dispatch = useDispatch();
+
+ const setActivePageHandler = page =>
+ dispatch(
+ setActivePage({
+ page,
+ paused: true
+ })
+ );
+
+ return (
+
+
+
+ );
+};
+
+AppNavigationBar.propTypes = {
+ classes: objectOf(string).isRequired,
+ className: string
+};
+
+export default withStyles(styles)(AppNavigationBar);
diff --git a/app/containers/AppNotifications.js b/app/containers/AppNotifications.js
new file mode 100755
index 00000000..8aa4b6ba
--- /dev/null
+++ b/app/containers/AppNotifications.js
@@ -0,0 +1,150 @@
+import React from 'react';
+import { useState, useCallback, useEffect } from 'react';
+import { useMappedState, useDispatch } from 'redux-react-hook';
+
+import Dialog from '@material-ui/core/Dialog';
+import DialogActions from '@material-ui/core/DialogActions';
+import DialogContent from '@material-ui/core/DialogContent';
+import Button from '@material-ui/core/Button';
+
+import { Notifications } from 'components/views/notifications';
+import { installMultiplePackages } from 'models/packages/actions';
+import { clearInstallOptions } from 'models/common/actions';
+import { DialogOptionsView } from 'components/views/packages';
+import { iMessage } from 'commons/utils';
+
+const mapState = ({
+ notifications: { notifications },
+ common: {
+ operations: { packagesInstallOptions }
+ } }) => ({
+ notifications,
+ packagesInstallOptions
+ });
+
+const AppNotifications = () => {
+ const [selected, setSelected] = useState([]);
+ const [selectedPackagesNames, setSelectedPackagesNames] = useState([]);
+ const [options, toggleOptions] = useState({
+ open: false,
+ single: false,
+ name: null,
+ version: null
+ });
+ const { notifications, packagesInstallOptions } = useMappedState(mapState);
+ const dispatch = useDispatch();
+
+ const handleSelectAll = useCallback(
+ e => {
+ let selectedNotifications;
+
+ if (e.target.checked) {
+ selectedNotifications = notifications.map(
+ notification => notification.id
+ );
+ } else {
+ selectedNotifications = [];
+ }
+
+ setSelected(selectedNotifications);
+ },
+ [notifications]
+ );
+
+ const handleSelectOne = useCallback(
+ (e, id) => {
+ const selectedIndex = selected.indexOf(id);
+ let newSelected = [];
+
+ if (selectedIndex === -1) {
+ newSelected = newSelected.concat(selected, id);
+ } else if (selectedIndex === 0) {
+ newSelected = newSelected.concat(selected.slice(1));
+ } else if (selectedIndex === selected.length - 1) {
+ newSelected = newSelected.concat(selected.slice(0, -1));
+ } else if (selectedIndex > 0) {
+ newSelected = newSelected.concat(
+ selected.slice(0, selectedIndex),
+ selected.slice(selectedIndex + 1)
+ );
+ }
+
+ setSelected(newSelected);
+ },
+ [selected]
+ );
+
+ const handleCancel = useCallback(() => {
+ dispatch(clearInstallOptions());
+ toggleOptions({
+ open: false,
+ single: false,
+ name: null,
+ version: null
+ });
+ }, [dispatch]);
+
+ const handleInstall = useCallback(
+ () =>
+ dispatch(
+ installMultiplePackages({
+ ipcEvent: 'npm-install',
+ cmd: selectedPackagesNames.map(() => 'install'),
+ multiple: true,
+ packages: selectedPackagesNames.map(pkgName => `${pkgName}@latest`)
+ })
+ ),
+ [selectedPackagesNames, dispatch]
+ );
+
+ useEffect(() => {
+ const packagesNames = selected.length
+ ? selected.map(notificationId => {
+ const { requiredName } = notifications.find(
+ notification => notification.id === notificationId
+ );
+
+ return requiredName;
+ })
+ : [];
+
+ setSelectedPackagesNames(packagesNames);
+ }, [selected, notifications]);
+
+ return (
+ <>
+
+ toggleOptions({
+ ...options,
+ open: true
+ })
+ }
+ handleSelectAll={handleSelectAll}
+ handleSelectOne={handleSelectOne}
+ />
+
+ >
+ );
+};
+
+export default AppNotifications;
diff --git a/app/containers/AppSidebar.js b/app/containers/AppSidebar.js
index a9cfaec1..0a5c0002 100755
--- a/app/containers/AppSidebar.js
+++ b/app/containers/AppSidebar.js
@@ -1,106 +1,96 @@
import React from 'react';
-import PropTypes from 'prop-types';
-import { useState, useEffect } from 'react';
+import cn from 'classnames';
+import { objectOf, string } from 'prop-types';
+import { useState, useEffect, useCallback } from 'react';
import { useDispatch, useMappedState } from 'redux-react-hook';
import { ipcRenderer } from 'electron';
import { withStyles } from '@material-ui/core/styles';
-
-import Tooltip from '@material-ui/core/Tooltip';
import Drawer from '@material-ui/core/Drawer';
-import List from '@material-ui/core/List';
-import ListItem from '@material-ui/core/ListItem';
-import ListItemText from '@material-ui/core/ListItemText';
-import Dialog from '@material-ui/core/Dialog';
-import DialogActions from '@material-ui/core/DialogActions';
-import DialogContent from '@material-ui/core/DialogContent';
-import Button from '@material-ui/core/Button';
-import Divider from '@material-ui/core/Divider';
-import Card from '@material-ui/core/Card';
-import CardContent from '@material-ui/core/CardContent';
-import CardActions from '@material-ui/core/CardActions';
-import Typography from '@material-ui/core/Typography';
-import UpdateIcon from '@material-ui/icons/Update';
-
-import { AppTabs, AppLogo } from 'components/common/';
-import {
- PackagesTab,
- ActionsTab,
- HistoryTab
-} from 'components/views/sidebar/tabs';
-import { navigatorParameters } from 'commons/parameters';
-import { iMessage, showDialog } from 'commons/utils';
-import { installPackage } from 'models/packages/actions';
+import { Sidebar } from 'components/views/sidebar';
import { setActivePage } from 'models/ui/actions';
+import { installPackageJson } from 'models/packages/actions'
import { setMode } from 'models/common/actions';
+import { runDedupe } from 'models/npm/actions';
+import { iMessage, shrinkDirectory, showDialog } from 'commons/utils'
-import Doctor from './Doctor';
import styles from './styles/appSidebar';
const mapState = ({
- notifications: { notifications },
+ common: { mode, directory },
packages: {
packagesData,
packagesOutdated,
metadata: { lastUpdatedAt }
},
+ notifications: {
+ notifications
+ },
ui: {
- activePage,
loaders: {
loader: { loading }
}
}
}) => ({
- activePage,
+ mode,
+ directory,
loading,
lastUpdatedAt,
- notifications,
packagesData,
- packagesOutdated
+ packagesOutdated,
+ notifications
});
-const AppSidebar = ({
- classes,
- mode,
- directory,
- fullDirectory,
- ...restProps
-}) => {
- const [openedDirectories, setOpenedDirectories] = useState([]);
- const [open, toggleDialog] = useState(false);
+const AppSidebar = ({ classes, className }) => {
+ const [history, updateHistory] = useState([]);
+ const dispatch = useDispatch();
const {
- activePage,
- notifications,
+ mode,
+ directory,
+ lastUpdatedAt,
+ loading,
packagesData,
packagesOutdated,
- lastUpdatedAt,
- loading
+ notifications
} = useMappedState(mapState);
- const dispatch = useDispatch();
- useEffect(() => {
- ipcRenderer.on('loaded-packages-close', (event, directories) =>
- setOpenedDirectories(directories)
- );
-
- return () => ipcRenderer.removeAllListeners(['loaded-packages-close']);
- }, []);
+ const packagesItems = [
+ {
+ name: 'total-packages',
+ primaryText: 'Total',
+ secondaryText: packagesData.length,
+ color: 'secondary',
+ primary: true
+ },
+ {
+ name: 'outdated-packages',
+ primaryText: 'Outdated',
+ secondaryText: packagesOutdated.length,
+ color: 'warning',
+ warning: true
+ },
+ {
+ name: 'notifications',
+ primaryText: 'Problems',
+ secondaryText: notifications.length,
+ color: 'error',
+ error: true
+ }
+ ];
- const loadDirectory = () => {
- const dialogHandler = filePath => {
- dispatch(
- setActivePage({
- page: 'packages',
- paused: false
- })
- );
- dispatch(setMode({ mode: 'local', directory: filePath.join('') }));
- };
+ const loadDirectory = useCallback(directoryPath => {
+ dispatch(setActivePage({ page: 'packages', paused: false }));
+ dispatch(
+ setMode({
+ mode: 'local',
+ directory: directoryPath
+ })
+ );
+ }, [dispatch]);
- return showDialog(dialogHandler, { mode: 'file', ...navigatorParameters });
- };
+ const installPackagesFromJson = useCallback(() => {
+ const shrinkedDirectory = directory && shrinkDirectory(directory);
- const installPackagesFromJson = () => {
const dialogOptions = {
title: 'Confirmation',
type: 'question',
@@ -112,153 +102,77 @@ const AppSidebar = ({
const dialogHandler = () =>
dispatch(
- installPackage({
+ installPackageJson({
ipcEvent: 'install',
cmd: ['install'],
packageJson: true,
+ multiple: false,
mode,
- directory: fullDirectory
+ directory: shrinkedDirectory
})
);
return showDialog(dialogHandler, dialogOptions);
- };
+ }, [mode, directory, dispatch]);
- const packagesItems = [
- {
- name: 'total-packages',
- primaryText: 'Total',
- secondaryText: packagesData.length,
- color: 'secondary',
- primary: true
- },
- {
- name: 'outdated-packages',
- primaryText: 'Outdated',
- secondaryText: packagesOutdated.length,
- color: 'warning',
- warning: true
- },
- {
- name: 'problems-packages',
- primaryText: 'Problems',
- secondaryText: notifications ? notifications.length : 0,
- color: 'error',
- error: true
- }
- ];
+ const dedupe = useCallback(() => {
+ const dialogOptions = {
+ title: 'Confirmation',
+ type: 'question',
+ message: iMessage('confirmation', 'actionRun', {
+ '%name%': 'npm dedupe'
+ }),
+ buttons: ['Cancel', 'Run']
+ };
+
+ const dialogHandler = () =>
+ dispatch(
+ runDedupe({
+ ipcEvent: 'dedupe',
+ cmd: ['dedupe']
+ })
+ );
+
+ return showDialog(dialogHandler, dialogOptions);
+ }, [dispatch]);
+
+ useEffect(() => {
+ ipcRenderer.on('loaded-packages-close', (event, directories) =>
+ updateHistory(directories)
+ );
+
+ return () => ipcRenderer.removeAllListeners(['loaded-packages-close']);
+ }, []);
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
- loadDirectory()}
- >
- {iMessage('action', 'analyze')}
-
-
-
-
-
-
-
-
-
-
-
- {iMessage('title', 'overview')}
-
-
-
-
-
-
-
-
-
- {iMessage('info', 'updatedAt')}
- {lastUpdatedAt !== null ? lastUpdatedAt : '...'}
-
-
-
-
-
- {
- dispatch(
- setActivePage({ page: 'packages', paused: false })
- );
- dispatch(
- setMode({
- mode: 'local',
- directory: projectDirectory
- })
- );
- }}
- loading={loading}
- />
-
-
-
-
-
-
- >
+
+
+
);
};
AppSidebar.propTypes = {
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- mode: PropTypes.string,
- loading: PropTypes.bool,
- name: PropTypes.string,
- version: PropTypes.string,
- description: PropTypes.string,
- directory: PropTypes.string,
- lastUpdatedAt: PropTypes.string,
- fullDirectory: PropTypes.string
+ classes: objectOf(string).isRequired,
+ className: string
};
export default withStyles(styles)(AppSidebar);
diff --git a/app/containers/AppTopBar.js b/app/containers/AppTopBar.js
new file mode 100755
index 00000000..cd116696
--- /dev/null
+++ b/app/containers/AppTopBar.js
@@ -0,0 +1,179 @@
+import React from 'react';
+import { useState, useCallback } from 'react';
+import { withStyles } from '@material-ui/core/styles';
+import { objectOf, string } from 'prop-types';
+import { useMappedState, useDispatch } from 'redux-react-hook';
+import cn from 'classnames';
+
+import DialogTitle from '@material-ui/core/DialogTitle';
+import Dialog from '@material-ui/core/Dialog';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogActions from '@material-ui/core/DialogActions';
+import Button from '@material-ui/core/Button';
+import Divider from '@material-ui/core/Divider';
+
+import { Init, Settings } from 'components/views/common/';
+import { TopBar } from 'components/views/topBar/';
+
+import { showDialog } from 'commons/utils';
+import { setActivePage } from 'models/ui/actions';
+import { setMode } from 'models/common/actions';
+import { runInit } from 'models/npm/actions';
+import { navigatorParameters } from 'commons/parameters';
+import { iMessage } from 'commons/utils';
+
+import styles from './styles/appTopBar';
+
+const mapState = ({
+ common: { mode, directory, activePage },
+ notifications: { notifications },
+ ui: {
+ loaders: {
+ loader: { loading }
+ }
+ },
+ npm: { env: {
+ metricsRegistry,
+ auditLevel,
+ cache
+ } }
+}) => ({
+ activePage,
+ notifications,
+ mode,
+ directory,
+ metricsRegistry,
+ auditLevel,
+ cache,
+ loading
+});
+
+const AppTopBar = ({ classes, className }) => {
+ const {
+ metricsRegistry,
+ auditLevel,
+ cache,
+ mode,
+ directory,
+ notifications,
+ loading,
+ activePage
+ } = useMappedState(mapState);
+
+ const [initFlow, toggleInitFlow] = useState({
+ show: false,
+ directory: null
+ })
+ const [dialog, setDialog] = useState({
+ open: false,
+ title: '',
+ active: null
+ });
+
+ const dispatch = useDispatch();
+
+ const onLoadDirectory = useCallback(() => {
+ const dialogHandler = filePath => {
+ dispatch(
+ setActivePage({
+ page: 'packages',
+ paused: false
+ })
+ );
+ dispatch(setMode({ mode: 'local', directory: filePath.join('') }));
+ };
+
+ return showDialog(dialogHandler, { mode: 'file', ...navigatorParameters });
+ }, [dispatch]);
+
+ const closeDialog = useCallback(() => {
+ setDialog({ ...dialog, open: false, active: null, title: '' })
+ toggleInitFlow({
+ ...initFlow,
+ show: false
+ })
+ }, [dialog, initFlow])
+
+ const onNpmInit = useCallback(() => {
+ dispatch(
+ runInit({
+ ipcEvent: 'npm-init',
+ cmd: ['init'],
+ directory: initFlow.directory || null
+ })
+ );
+
+ closeDialog();
+ }, [dispatch, closeDialog, initFlow])
+
+ const setActivePageHandler = page =>
+ dispatch(
+ setActivePage({
+ page,
+ paused: true
+ })
+ );
+
+ return (
+
+
setDialog({ ...dialog, open: true, active: 'Init', title: iMessage('title', 'createPackageJson') })}
+ onShowSettings={() => setDialog({ ...dialog, open: true, active: 'Settings', title: iMessage('title', 'settings') })}
+ />
+
+
+ );
+};
+
+AppTopBar.propTypes = {
+ classes: objectOf(string).isRequired,
+ className: string
+};
+
+export default withStyles(styles)(AppTopBar);
diff --git a/app/containers/Audit.js b/app/containers/Audit.js
index 3d4422c8..de8a8dfc 100755
--- a/app/containers/Audit.js
+++ b/app/containers/Audit.js
@@ -42,13 +42,7 @@ const mapState = ({
});
const Audit = ({ classes }) => {
- const dispatch = useDispatch();
const { loading, message, mode, result } = useMappedState(mapState);
- const [status, setStatus] = useState({
- type: 'init',
- options: {}
- });
-
const [metadataValues, setMetadata] = useState({
dependencies: 0,
devDependencies: 0,
@@ -56,8 +50,8 @@ const Audit = ({ classes }) => {
vulnerabilities: null,
advisories: null
});
+ const dispatch = useDispatch();
const { content, error } = result || {};
- const defaultOptions = { text: iMessage('info', 'npmAuditInfo') };
const auditRun = option =>
dispatch(
@@ -70,27 +64,46 @@ const Audit = ({ classes }) => {
})
);
+ const dialogText = mode === 'global' ? iMessage('warning', 'noGlobalAudit') : iMessage('info', 'npmAuditInfo');
+ const dialogActionText = iMessage('action', 'runAudit');
+
const initOptions = {
- ...defaultOptions,
- detail: mode === 'global' ? iMessage('warning', 'noGlobalAudit') : null,
- actionText: iMessage('action', 'runAudit'),
+ text: dialogText,
+ actionText: dialogActionText,
actionHandler: () => auditRun(),
- actionDisabled: mode === 'global'
+ actionDisabled: mode === 'global',
+ color: 'primary'
};
+ const [status, setStatus] = useState({
+ type: 'init',
+ options: initOptions
+ });
+
// set data
useEffect(() => {
const { metadata, advisories } = content || {};
- if (!content && !loading) {
+ if (error) {
+ const { summary, code } = error || {};
+
+ const errorOptions = {
+ text: summary,
+ code
+ };
+
setStatus({
- type: 'init',
- options: initOptions
+ type: 'error',
+ options: errorOptions
});
return;
}
+ if (!content && !loading) {
+ return;
+ }
+
if (!metadata && !advisories) {
setStatus({
type: 'dialog'
@@ -114,28 +127,11 @@ const Audit = ({ classes }) => {
advisories
});
- setStatus({
- type: 'audit'
- });
- }, [content, initOptions, loading]);
-
- // set error
- useEffect(() => {
- if (error) {
- const { summary, code } = error || {};
-
- const errorOptions = {
- ...initOptions,
- text: summary,
- code
- };
-
- setStatus({
- type: 'error',
- options: errorOptions
- });
- }
- }, [error, initOptions]);
+ setStatus(options => ({
+ type: 'audit',
+ options
+ }));
+ }, [content, loading, error]);
const { type, options } = status;
const {
@@ -146,6 +142,8 @@ const Audit = ({ classes }) => {
advisories
} = metadataValues || {};
+ const noVulnerabilities = vulnerabilities && Object.values(vulnerabilities).reduce((total, v) => total + v, 0);
+
return (
<>
@@ -154,7 +152,7 @@ const Audit = ({ classes }) => {
{type === 'init' && }
{type === 'audit' && (
<>
-
+
{
- 0 ?
+ /> : }
>
)}