Skip to content

Commit

Permalink
#38: finish second iteration with file and branch selection
Browse files Browse the repository at this point in the history
  • Loading branch information
m4nv3ru committed Apr 27, 2022
1 parent 8f1cb25 commit dcd1cea
Show file tree
Hide file tree
Showing 10 changed files with 460 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from 'react';
import styles from './FileSelection.scss';
import _ from 'lodash';
import { checkAllChildrenSelected } from '../../sagas/fileTreeOperations';

export default class FileSelection extends React.Component {
constructor(props) {
super(props);
}

render() {
const { onSetFilteredFile, onSetFileFilterMode, files } = this.props;

return (
<div>
<label className="label">Files & Modules</label>
<button className={styles.modeButton} onClick={() => onSetFileFilterMode('EXCLUDE')}>
All
</button>
<button className={styles.modeButton} onClick={() => onSetFileFilterMode('INCLUDE')}>
Clear
</button>
<div className={styles.fileSelection}>
{_.map(files, (file, i) => <FileTreeNode level={0} key={`n_${i}`} node={file} onSetFilteredFile={f => onSetFilteredFile(f)} />)}
</div>
</div>
);
}
}

class FileTreeNode extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: false
};
}

render() {
const { node, level, onSetFilteredFile } = this.props;
const hasChildren = node.children && node.children.length > 0;
const folderIcon = this.state.expanded ? 'fa-folder-open' : 'fa-folder';

return (
((node.type === 'folder' && hasChildren) || node.type === 'file') &&
<div className={level === 0 ? styles.rootLevel : styles.subLevel}>
<button className={`${styles.treeNode} ${hasChildren ? styles.folderTreeNode : styles.fileTreeNode}`}>
<div className={styles.nodeHeader}>
<label className="checkbox">
<input
className="checkbox"
type="checkbox"
checked={checkAllChildrenSelected(node)}
onChange={event => {
console.log(event.target.checked);
onSetFilteredFile({
node: node,
selected: event.target.checked
});
}}
/>
</label>
{hasChildren
? <FolderContent
icon={folderIcon}
name={node.name}
expanded={this.state.expanded}
onClick={() => this.setState({ expanded: !this.state.expanded })}
/>
: <FileContent name={node.name} />}
</div>
</button>
{hasChildren &&
this.state.expanded &&
<div className="nodeChildren">
{_.map(node.children, (child, i) =>
<FileTreeNode key={`n_${i}`} onSetFilteredFile={f => onSetFilteredFile(f)} level={level + 1} node={child} />
)}
</div>}
</div>
);
}
}

const FolderContent = props => {
return (
<div className={styles.nodeHeaderContent} onClick={() => props.onClick()}>
<span className={`fa ${props.icon} ${styles.nodeIcon} `} />
{props.name}
<span className={`fa ${props.expanded ? 'fa-caret-up' : 'fa-caret-down'} ${styles.folderCaret}`} />
</div>
);
};

const FileContent = props => {
return (
<div className={styles.nodeHeaderContent}>
<span className={`fa fa-file ${styles.nodeIcon} `} />
{props.name}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
@import '~bulma';

.fileSelection {
overflow-y: scroll;
max-height: 35vh;
}




.treeNode {
padding: 0.25rem;
width: 100%;
text-align: left;
outline: none;
background: #EEEEEE;
border-radius: 4px;
border: 1px solid white;

span {
color: #183153;
}
}

.nodeHeader {
display: flex;
flex-direction: row;
align-items: center;

}

.folderCaret {
margin-left: auto;
margin-right: 5px;
}

.nodeHeaderContent {
width: 100%;
display: flex;
align-items: center;
}

.nodeIcon {
margin-right: 5px;
}

.modeButton {
padding: 0.20rem;
min-width: 3rem;
border: none;
cursor: pointer;
}

.fileTreeNode {

}

.folderTreeNode {
cursor: pointer;
// Make background a bit lighter
background: #2596be;
}


.subLevel {
margin-left: 0.5rem;
padding-left: 0.5rem;
border-left: 0.1rem solid #CCCCCC;
}

.rootLevel{

}
33 changes: 25 additions & 8 deletions ui/src/visualizations/team-awareness/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,39 @@

import React from 'react';
import { connect } from 'react-redux';
import { setActivityDimensions, setActivityScale, setBranch } from './sagas';
import { setActivityDimensions, setActivityScale, setBranch, setFileFilterMode, setFilteredFiles } from './sagas';
import * as d3 from 'd3';
import { getState } from './util/util';
import _ from 'lodash';
import ActivityTimeline from './components/Timeline/ActivityTimeline';
import styles from './styles.scss';
import FileSelection from './components/FileSelection/FileSelection';

const mapStateToProps = (appState /*, ownProps*/) => {
const { config, data } = getState(appState);

return {
config: {
selectedActivityScale: config.selectedActivityScale,
selectedBranch: config.selectedBranch
},
data: {
files: data.data.files,
fileTree: data.data.fileTree,
branches: data.data.branches,
activityTimeline: data.data.activityTimeline,
yDims: data.data.dataBoundaries
}
};
};
const mapDispatchToProps = dispatch => {
// noinspection JSUnusedGlobalSymbols
return {
onSelectActivityScale: selectActivity => dispatch(setActivityScale(selectActivity)),
onActivityDimensionsRestricted: restrictActivity => dispatch(setActivityDimensions(restrictActivity)),
onSelectBranch: selectActivity => dispatch(setBranch(selectActivity))
onSelectActivityScale: activity => dispatch(setActivityScale(activity)),
onActivityDimensionsRestricted: activity => dispatch(setActivityDimensions(activity)),
onSelectBranch: activity => dispatch(setBranch(activity)),
onSetFilteredFile: activity => dispatch(setFilteredFiles(activity)),
onSetFileFilterMode: activity => dispatch(setFileFilterMode(activity))
};
};

Expand All @@ -37,9 +44,18 @@ class ConfigComponent extends React.Component {
}

render() {
const { onActivityDimensionsRestricted, onSelectActivityScale, config, onSelectBranch } = this.props;
const { activityTimeline, yDims, branches } = this.props.data;
console.log(activityTimeline);
const {
onActivityDimensionsRestricted,
onSelectActivityScale,
config,
data,
onSelectBranch,
onSetFilteredFile,
onSetFileFilterMode
} = this.props;
const { activityTimeline, yDims, branches, fileTree } = data;
const { stackOffsetDiverging } = d3;

return (
<div className={styles.configContainer}>
<form>
Expand Down Expand Up @@ -84,11 +100,12 @@ class ConfigComponent extends React.Component {
resolution={'weeks'}
xAxisCenter={true}
content={activityTimeline && activityTimeline.length > 0 ? activityTimeline : [{ date: 0, activity: 0 }]}
d3offset={d3.stackOffsetDiverging}
d3offset={stackOffsetDiverging}
yDims={_.values(yDims)}
onDimensionsRestricted={dims => onActivityDimensionsRestricted(dims)}
/>
</div>
<FileSelection onSetFileFilterMode={m => onSetFileFilterMode(m)} files={fileTree} onSetFilteredFile={f => onSetFilteredFile(f)} />
</div>
);
}
Expand Down
25 changes: 24 additions & 1 deletion ui/src/visualizations/team-awareness/reducers/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,37 @@

import { handleActions } from 'redux-actions';
import _ from 'lodash';
import { flattenNode } from '../sagas/fileTreeOperations';

export default handleActions(
{
SET_TEAM_AWARENESS_ACTIVITY_SCALE: (state, action) => _.assign({}, state, { selectedActivityScale: action.payload }),
SET_TEAM_AWARENESS_ACTIVITY_DIMENSIONS: (state, action) => _.assign({}, state, action.payload),
SET_TEAM_AWARENESS_BRANCH: (state, action) => _.assign({}, state, { selectedBranch: action.payload })
SET_TEAM_AWARENESS_BRANCH: (state, action) => _.assign({}, state, { selectedBranch: action.payload }),
SET_TEAM_AWARENESS_FILTERED_FILES: (state, action) => {
const nextFilter = new Map();
state.fileFilter.files.forEach(f => nextFilter.set(f.id, f));

const addToMap = file => nextFilter.set(file.id, file);
const deleteFromMap = file => nextFilter.delete(file.id);

let operation = state.fileFilter.mode === 'EXCLUDE' && action.payload.selected === false ? addToMap : deleteFromMap;
if (state.fileFilter.mode === 'INCLUDE') {
operation = action.payload.selected === true ? addToMap : deleteFromMap;
}

flattenNode(action.payload.node).forEach(n => operation(n));
return _.assign({}, state, {
fileFilter: _.assign({}, state.fileFilter, { files: Array.from(nextFilter.values()) })
});
},
SET_TEAM_AWARENESS_FILE_FILTER_MODE: (state, action) => _.assign({}, state, { fileFilter: { mode: action.payload, files: [] } })
},
{
fileFilter: {
mode: 'EXCLUDE',
files: []
},
selectedBranch: 'all',
selectedActivityScale: 'commits',
activityRestricted: false,
Expand Down
19 changes: 5 additions & 14 deletions ui/src/visualizations/team-awareness/reducers/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { handleActions } from 'redux-actions';

export default handleActions(
{
REQUEST_TEAM_AWARENESS_DATA: state => {
return _.assign({}, state, { isFetching: true });
},
REQUEST_TEAM_AWARENESS_DATA: state => _.assign({}, state, { isFetching: true }),
RECEIVE_TEAM_AWARENESS_DATA: (state, action) => {
return _.assign({}, state, {
data: action.payload,
Expand All @@ -16,20 +14,13 @@ export default handleActions(
receivedAt: action.meta.receivedAt
});
},
PROCESS_TEAM_AWARENESS_DATA: (state, action) => {
return _.assign({}, state, {
data: {
branches: state.data.branches,
commits: state.data.commits,
stakeholders: action.payload.stakeholders,
activityTimeline: action.payload.activityTimeline,
dataBoundaries: action.payload.dataBoundaries
}
});
}
PROCESS_TEAM_AWARENESS_DATA: (state, action) => _.assign({}, state, { data: _.assign({}, state.data, action.payload) }),
PROCESS_TEAM_AWARENESS_FILE_BROWSER: (state, action) => _.assign({}, state, { data: _.assign({}, state.data, action.payload) })
},
{
data: {
files: [],
fileTree: [],
branches: [],
commits: [],
stakeholders: []
Expand Down
Loading

0 comments on commit dcd1cea

Please sign in to comment.