setEditing(true)} />
+ )}
+ >
+ )}
>
);
};
diff --git a/client/src/Project/IssueDetails/Tracking/Styles.js b/client/src/Project/Board/IssueDetails/EstimateTracking/Styles.js
similarity index 55%
rename from client/src/Project/IssueDetails/Tracking/Styles.js
rename to client/src/Project/Board/IssueDetails/EstimateTracking/Styles.js
index 7c683af..765787e 100644
--- a/client/src/Project/IssueDetails/Tracking/Styles.js
+++ b/client/src/Project/Board/IssueDetails/EstimateTracking/Styles.js
@@ -1,7 +1,6 @@
import styled from 'styled-components';
import { color, font, mixin } from 'shared/utils/styles';
-import { Icon } from 'shared/components';
export const TrackingLink = styled.div`
padding: 4px 4px 2px 0;
@@ -13,41 +12,6 @@ export const TrackingLink = styled.div`
}
`;
-export const Tracking = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-`;
-
-export const WatchIcon = styled(Icon)`
- color: ${color.textMedium};
-`;
-
-export const Right = styled.div`
- width: 90%;
-`;
-
-export const BarCont = styled.div`
- height: 5px;
- border-radius: 4px;
- background: ${color.backgroundMedium};
-`;
-
-export const Bar = styled.div`
- height: 5px;
- border-radius: 4px;
- background: ${color.primary};
- transition: all 0.1s;
- width: ${props => props.width}%;
-`;
-
-export const Values = styled.div`
- display: flex;
- justify-content: space-between;
- padding-top: 3px;
- ${font.size(14.5)};
-`;
-
export const ModalContents = styled.div`
padding: 20px 25px 25px;
`;
diff --git a/client/src/Project/Board/IssueDetails/EstimateTracking/TrackingWidget/Styles.js b/client/src/Project/Board/IssueDetails/EstimateTracking/TrackingWidget/Styles.js
new file mode 100644
index 0000000..aa22997
--- /dev/null
+++ b/client/src/Project/Board/IssueDetails/EstimateTracking/TrackingWidget/Styles.js
@@ -0,0 +1,39 @@
+import styled from 'styled-components';
+
+import { color, font } from 'shared/utils/styles';
+import { Icon } from 'shared/components';
+
+export const TrackingWidget = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+`;
+
+export const WatchIcon = styled(Icon)`
+ color: ${color.textMedium};
+`;
+
+export const Right = styled.div`
+ width: 90%;
+`;
+
+export const BarCont = styled.div`
+ height: 5px;
+ border-radius: 4px;
+ background: ${color.backgroundMedium};
+`;
+
+export const Bar = styled.div`
+ height: 5px;
+ border-radius: 4px;
+ background: ${color.primary};
+ transition: all 0.1s;
+ width: ${props => props.width}%;
+`;
+
+export const Values = styled.div`
+ display: flex;
+ justify-content: space-between;
+ padding-top: 3px;
+ ${font.size(14.5)};
+`;
diff --git a/client/src/Project/Board/IssueDetails/EstimateTracking/TrackingWidget/index.jsx b/client/src/Project/Board/IssueDetails/EstimateTracking/TrackingWidget/index.jsx
new file mode 100644
index 0000000..2142106
--- /dev/null
+++ b/client/src/Project/Board/IssueDetails/EstimateTracking/TrackingWidget/index.jsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { isNil } from 'lodash';
+
+import { TrackingWidget, WatchIcon, Right, BarCont, Bar, Values } from './Styles';
+
+const propTypes = {
+ issue: PropTypes.object.isRequired,
+};
+
+const ProjectBoardIssueDetailsTrackingWidget = ({ issue }) => (
+
+
+
+
+
+
+
+ {issue.timeSpent ? `${issue.timeSpent}h logged` : 'No time logged'}
+ {renderRemainingOrEstimate(issue)}
+
+
+
+);
+
+const calculateTrackingBarWidth = ({ timeSpent, timeRemaining, estimate }) => {
+ if (!timeSpent) {
+ return 0;
+ }
+ if (isNil(timeRemaining) && isNil(estimate)) {
+ return 100;
+ }
+ if (!isNil(timeRemaining)) {
+ return (timeSpent / (timeSpent + timeRemaining)) * 100;
+ }
+ if (!isNil(estimate)) {
+ return Math.min((timeSpent / estimate) * 100, 100);
+ }
+};
+
+const renderRemainingOrEstimate = ({ timeRemaining, estimate }) => {
+ if (isNil(timeRemaining) && isNil(estimate)) {
+ return null;
+ }
+ if (!isNil(timeRemaining)) {
+ return {`${timeRemaining}h remaining`}
;
+ }
+ if (!isNil(estimate)) {
+ return {`${estimate}h estimated`}
;
+ }
+};
+
+ProjectBoardIssueDetailsTrackingWidget.propTypes = propTypes;
+
+export default ProjectBoardIssueDetailsTrackingWidget;
diff --git a/client/src/Project/Board/IssueDetails/EstimateTracking/index.jsx b/client/src/Project/Board/IssueDetails/EstimateTracking/index.jsx
new file mode 100644
index 0000000..1cccd11
--- /dev/null
+++ b/client/src/Project/Board/IssueDetails/EstimateTracking/index.jsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { isNil } from 'lodash';
+
+import { InputDebounced, Modal, Button } from 'shared/components';
+
+import TrackingWidget from './TrackingWidget';
+import { SectionTitle } from '../Styles';
+import {
+ TrackingLink,
+ ModalContents,
+ ModalTitle,
+ Inputs,
+ InputCont,
+ InputLabel,
+ Actions,
+} from './Styles';
+
+const propTypes = {
+ issue: PropTypes.object.isRequired,
+ updateIssue: PropTypes.func.isRequired,
+};
+
+const ProjectBoardIssueDetailsEstimateTracking = ({ issue, updateIssue }) => (
+ <>
+ Original Estimate (hours)
+ {renderHourInput('estimate', issue, updateIssue)}
+
+ Time Tracking
+ (
+
+
+
+ )}
+ renderContent={modal => (
+
+ Time tracking
+
+
+
+ Time spent (hours)
+ {renderHourInput('timeSpent', issue, updateIssue)}
+
+
+ Time remaining (hours)
+ {renderHourInput('timeRemaining', issue, updateIssue)}
+
+
+
+
+
+
+ )}
+ />
+ >
+);
+
+const renderHourInput = (fieldName, issue, updateIssue) => (
+ {
+ const value = stringValue.trim() ? Number(stringValue) : null;
+ updateIssue({ [fieldName]: value });
+ }}
+ />
+);
+
+ProjectBoardIssueDetailsEstimateTracking.propTypes = propTypes;
+
+export default ProjectBoardIssueDetailsEstimateTracking;
diff --git a/client/src/Project/IssueDetails/Loader.jsx b/client/src/Project/Board/IssueDetails/Loader.jsx
similarity index 100%
rename from client/src/Project/IssueDetails/Loader.jsx
rename to client/src/Project/Board/IssueDetails/Loader.jsx
diff --git a/client/src/Project/IssueDetails/Priority/Styles.js b/client/src/Project/Board/IssueDetails/Priority/Styles.js
similarity index 100%
rename from client/src/Project/IssueDetails/Priority/Styles.js
rename to client/src/Project/Board/IssueDetails/Priority/Styles.js
diff --git a/client/src/Project/Board/IssueDetails/Priority/index.jsx b/client/src/Project/Board/IssueDetails/Priority/index.jsx
new file mode 100644
index 0000000..0bdbe45
--- /dev/null
+++ b/client/src/Project/Board/IssueDetails/Priority/index.jsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { IssuePriority, IssuePriorityCopy } from 'shared/constants/issues';
+import { Select, IssuePriorityIcon } from 'shared/components';
+
+import { SectionTitle } from '../Styles';
+import { Priority, Label } from './Styles';
+
+const propTypes = {
+ issue: PropTypes.object.isRequired,
+ updateIssue: PropTypes.func.isRequired,
+};
+
+const ProjectBoardIssueDetailsPriority = ({ issue, updateIssue }) => (
+ <>
+ Priority
+