diff --git a/src/components/ArchestActivityStatusChangeWidgetComponent.js b/src/components/ArchestActivityStatusChangeWidgetComponent.js
new file mode 100644
index 0000000..bc5cf49
--- /dev/null
+++ b/src/components/ArchestActivityStatusChangeWidgetComponent.js
@@ -0,0 +1,57 @@
+import React, {Component} from "react";
+import {BACKEND_ESTIMATOR_API_URL, SUB_ACTIVITY} from "../constants";
+import ArchestHttp from "../modules/archest_http";
+import {Form} from "react-bootstrap";
+import './styles/ArchestActivityStatusChangeWidgetComponent.scss';
+
+const _ = require('lodash');
+
+class ArchestActivityStatusChangeWidgetComponent extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ activity: this.props.activity
+ };
+ this.onChangeStatus = this.onChangeStatus.bind(this);
+ }
+
+ onChangeStatus(element) {
+
+ let urlActivityType = 'activities';
+ if (this.props.type === SUB_ACTIVITY) {
+ urlActivityType = 'sub_activities';
+ }
+
+ ArchestHttp.PATCH(`${BACKEND_ESTIMATOR_API_URL}/${urlActivityType}/${this.props.activity.id}/`, {
+ status: element.target.value,
+ }).then((response) => {
+ this.setState({
+ activity: response.data
+ });
+ }).catch((error) => {
+ console.log(error);
+ });
+ }
+
+ render() {
+ return (
+
+
+
+ Backlog
+ In Progress
+ Completed
+ On Hold
+
+
+
+ );
+ }
+
+}
+
+export default ArchestActivityStatusChangeWidgetComponent;
\ No newline at end of file
diff --git a/src/components/ArchestCalendarComponent.js b/src/components/ArchestCalendarComponent.js
index 2ca560e..f32d496 100644
--- a/src/components/ArchestCalendarComponent.js
+++ b/src/components/ArchestCalendarComponent.js
@@ -2,7 +2,7 @@ import ReactDOM from 'react-dom';
import React, {Component} from "react";
import ArchestAuthEnabledComponent from "./ArchestAuthEnabledComponent";
import ArchestMainContainerComponent from "./ArchestMainContainerComponent";
-import './styles/ArchestTimeline.scss';
+import './styles/ArchestTimelineComponent.scss';
import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from "@fullcalendar/interaction";
diff --git a/src/components/ArchestEstimateActivityComponent.js b/src/components/ArchestEstimateActivityComponent.js
index cea4521..0cbc3be 100644
--- a/src/components/ArchestEstimateActivityComponent.js
+++ b/src/components/ArchestEstimateActivityComponent.js
@@ -16,6 +16,7 @@ class ArchestEstimateActivityComponent extends Component {
featureId: this.props.activity.feature.id,
activityStatus: this.props.activity.status,
activityName: this.props.activity.name,
+ ownerId: this.props.activity.owner ? this.props.activity.owner.id : '',
activityEstimatedTime: this.props.activity.estimated_time,
subActivityTotalHours: 0,
savingData: false,
@@ -37,6 +38,7 @@ class ArchestEstimateActivityComponent extends Component {
ArchestHttp.PATCH(BACKEND_ESTIMATOR_API_URL + "/activities/" + this.state.activityId + "/", {
name: this.state.activityName,
feature_id: this.state.featureId,
+ owner_id: this.state.ownerId,
estimated_time: this.state.activityEstimatedTime,
status: this.state.activityStatus,
}).then(function (response) {
@@ -107,7 +109,8 @@ class ArchestEstimateActivityComponent extends Component {
render() {
const activityId = this.props.activity.id;
- let featureOptions;
+ let featureOptions = [];
+ let resourcesOptions = [];
if (this.props.features) {
featureOptions = this.props.features.map(
@@ -115,6 +118,22 @@ class ArchestEstimateActivityComponent extends Component {
);
}
+ if (this.props.estimateResources) {
+ resourcesOptions = this.props.estimateResources.map(
+ (estimateResource) => {estimateResource.resource.full_name}
+ );
+ }
+
+ if (this.state.ownerId !== '') {
+ let estimateResources = this.props.estimateResources.map((estimateResource) => parseInt(estimateResource.resource.id, 10));
+ if (estimateResources.indexOf(parseInt(this.state.ownerId, 10)) === -1) {
+ resourcesOptions.unshift({this.props.activity.owner.full_name} );
+ }
+ }
+
+ resourcesOptions.unshift({''} );
+
return (
@@ -155,6 +174,7 @@ class ArchestEstimateActivityComponent extends Component {
size="sm"
as="select"
value={this.state.featureId}
+ disabled={!this.props.activity.is_editable}
name="featureId"
onChange={this.handleActivityFormFieldChange}
onBlur={this.saveActivityData}>
@@ -167,7 +187,7 @@ class ArchestEstimateActivityComponent extends Component {
-
+
@@ -183,9 +203,12 @@ class ArchestEstimateActivityComponent extends Component {
-
+
Activity
+
+ Owner
+
Hrs.
@@ -193,19 +216,38 @@ class ArchestEstimateActivityComponent extends Component {
-
+
+
+
+
+
+ {resourcesOptions}
+
+
+
+
@@ -216,6 +258,7 @@ class ArchestEstimateActivityComponent extends Component {
placeholder="Hrs."
size="sm"
value={this.state.activityEstimatedTime}
+ disabled={!this.props.activity.is_editable}
name="activityEstimatedTime"
onChange={this.handleActivityFormFieldChange}
onBlur={this.saveActivityData}/>
@@ -224,6 +267,7 @@ class ArchestEstimateActivityComponent extends Component {
overlay={Auto calculate from Sub Activity
Hours }>
sync
@@ -237,6 +281,7 @@ class ArchestEstimateActivityComponent extends Component {
subActivities={this.props.activity.sub_activities}
subActivityChangeHandler={this.subActivityChangeHandler}
subActivityTotalHours={this.state.subActivityTotalHours}
+ estimateResources={this.props.estimateResources}
/>
diff --git a/src/components/estimate_edit.js b/src/components/ArchestEstimateEditComponent.js
similarity index 76%
rename from src/components/estimate_edit.js
rename to src/components/ArchestEstimateEditComponent.js
index e1a4540..e6eb26b 100644
--- a/src/components/estimate_edit.js
+++ b/src/components/ArchestEstimateEditComponent.js
@@ -5,12 +5,12 @@ import {BACKEND_ESTIMATOR_API_URL} from "../constants";
import ArchestHttp from "../modules/archest_http";
import ArchestMainContainerComponent from "./ArchestMainContainerComponent";
import ArchestEstimateActivityComponent from "./ArchestEstimateActivityComponent";
-import './styles/ArchestEditEstimate.scss';
+import './styles/ArchestEstimateEditComponent.scss';
import autosize from 'autosize';
const _ = require('lodash');
-class EstimateEditComponent extends Component {
+class ArchestEstimateEditComponent extends Component {
constructor(props) {
super(props);
@@ -20,6 +20,7 @@ class EstimateEditComponent extends Component {
redirectTo: false,
estimate: {},
estimateDetails: {},
+ estimateResources: [],
estimateTableData: [],
breadcrumbs: []
};
@@ -34,36 +35,43 @@ class EstimateEditComponent extends Component {
let estimateId = this.props.match.params.estimateId;
- ArchestHttp.GET(BACKEND_ESTIMATOR_API_URL + '/estimates/' + estimateId + '/detailed_view/', {})
- .then(function (response) {
-
- component.setState({
- estimateDetails: response.data.results,
- dataLoaded: true
- });
- })
- .catch(function (error) {
- console.log(error);
- });
-
- ArchestHttp.GET(BACKEND_ESTIMATOR_API_URL + '/estimates/' + estimateId + '/', {})
- .then(function (response) {
- let estimate = response.data;
- component.setState({
- estimate: estimate,
- breadcrumbs: [
- {title: 'Home', url: '/'},
- {
- title: estimate.phase.name + ' - Estimates',
- url: `/phase/${estimate.phase.id}/estimates/`
- },
- {title: estimate.name, url: '#', active: true},
- ]
- });
- })
- .catch(function (error) {
- console.log(error);
+ let requestConfigs = [
+ {
+ name: 'estimateDetailedView',
+ url: `${BACKEND_ESTIMATOR_API_URL}/estimates/${estimateId}/detailed_view/`,
+ params: {}
+ },
+ {
+ name: 'estimate',
+ url: `${BACKEND_ESTIMATOR_API_URL}/estimates/${estimateId}/`,
+ params: {}
+ },
+ {
+ name: 'estimateResources',
+ url: `${BACKEND_ESTIMATOR_API_URL}/estimates/${estimateId}/shared_resources/`,
+ params: {}
+ },
+ ];
+
+ ArchestHttp.BATCH_GET(requestConfigs, (responses) => {
+
+ let estimate = responses.estimate.data;
+
+ component.setState({
+ estimateDetails: responses.estimateDetailedView.data.results,
+ dataLoaded: true,
+ estimateResources: responses.estimateResources.data.results,
+ estimate: estimate,
+ breadcrumbs: [
+ {title: 'Home', url: '/'},
+ {
+ title: estimate.phase.name + ' - Estimates',
+ url: `/phase/${estimate.phase.id}/estimates/`
+ },
+ {title: estimate.name, url: '#', active: true},
+ ]
});
+ });
}
@@ -109,6 +117,7 @@ class EstimateEditComponent extends Component {
key={activity.id}
activity={activity}
features={this.state.estimate.features}
+ estimateResources={this.state.estimateResources}
removeActivityItemHandler={this.removeActivityItem}
/>
);
@@ -169,4 +178,4 @@ class EstimateEditComponent extends Component {
}
-export default EstimateEditComponent;
+export default ArchestEstimateEditComponent;
diff --git a/src/components/ArchestEstimateProgressComponent.js b/src/components/ArchestEstimateProgressComponent.js
index 14d4359..93e3ccb 100644
--- a/src/components/ArchestEstimateProgressComponent.js
+++ b/src/components/ArchestEstimateProgressComponent.js
@@ -2,10 +2,12 @@ import React, {Component} from "react";
import ArchestAuthEnabledComponent from "./ArchestAuthEnabledComponent";
import ArchestMainContainerComponent from "./ArchestMainContainerComponent";
import ArchestFeatureProgressComponent from "./ArchestFeatureProgressComponent";
-import {BACKEND_ESTIMATOR_API_URL} from "../constants";
+import {ACTIVITY, BACKEND_ESTIMATOR_API_URL, SUB_ACTIVITY} from "../constants";
import ArchestHttp from "../modules/archest_http";
-import {Col, Row, Card, Table, ProgressBar, Dropdown} from "react-bootstrap";
+import {Col, Row, Card} from "react-bootstrap";
import './styles/ArchestEstimateProgressComponent.scss';
+import './styles/ArchestEstimateWorkEntriesModalComponent.scss';
+import ArchestEstimateWorkEntriesModalComponent from "./ArchestEstimateWorkEntriesModalComponent";
const _ = require('lodash');
@@ -17,8 +19,10 @@ class ArchestEstimateProgressComponent extends Component {
estimateId: this.props.match.params.estimateId,
estimate: {},
estimateProgress: {},
+ workEntriesModalProps: {show: false, workEntries: [], activityOrSubActivity: {},},
breadcrumbs: []
};
+ this.showWorkEntriesModal = this.showWorkEntriesModal.bind(this);
}
componentDidMount() {
@@ -53,14 +57,59 @@ class ArchestEstimateProgressComponent extends Component {
});
}
+ showWorkEntriesModal(activityOrSubActivity, type) {
+
+ let url = '';
+
+ if (type === ACTIVITY) {
+ url = BACKEND_ESTIMATOR_API_URL + "/activities/" + activityOrSubActivity.id + '/work_entries/';
+ } else if (type === SUB_ACTIVITY) {
+ url = BACKEND_ESTIMATOR_API_URL + "/sub_activities/" + activityOrSubActivity.id + '/work_entries/';
+ } else {
+ return;
+ }
+
+ ArchestHttp.GET(url, {}).then((response) => {
+ this.setState({
+ workEntriesModalProps: {
+ show: true,
+ workEntries: response.data.results,
+ activityOrSubActivity: activityOrSubActivity,
+ onCancel: () => {
+ this.setState({
+ workEntriesModalProps: {
+ show: false,
+ workEntries: [],
+ activityOrSubActivity: {}
+ }
+ });
+ }
+ }
+ });
+ }).catch(function (error) {
+ console.log(error);
+ });
+
+
+ }
+
render() {
let featureProgressComponents = _.map(
this.state.estimateProgress.features,
- feature =>
+ feature =>
);
return (
+
+
+
diff --git a/src/components/ArchestEstimateShareModalComponent.js b/src/components/ArchestEstimateShareModalComponent.js
new file mode 100644
index 0000000..e7cc57d
--- /dev/null
+++ b/src/components/ArchestEstimateShareModalComponent.js
@@ -0,0 +1,158 @@
+import React, {Component} from "react";
+import ArchestAuthEnabledComponent from "./ArchestAuthEnabledComponent";
+import {BACKEND_ESTIMATOR_API_URL} from "../constants";
+import ArchestHttp from "../modules/archest_http";
+import {Modal, Button, Form, Card, Row, Col} from "react-bootstrap";
+import './styles/ArchestEstimateShareModalComponent.scss';
+
+const _ = require('lodash');
+
+class ArchestEstimateShareModalComponent extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ phaseResources: [],
+ dataLoaded: false,
+ resourceSharingOptions: []
+ };
+ this.onCancel = this.onCancel.bind(this);
+ this.onChangeShareOption = this.onChangeShareOption.bind(this);
+ this.shareEstimate = this.shareEstimate.bind(this);
+ }
+
+ onCancel() {
+ this.props.onCancel();
+ this.setState((prevState) => {
+ return {dataLoaded: false}
+ });
+ }
+
+ shareEstimate() {
+ ArchestHttp.PATCH(
+ `${BACKEND_ESTIMATOR_API_URL}/estimates/${this.props.estimateData.estimateId}/shared_resources/`,
+ this.state.resourceSharingOptions
+ ).then((response) => {
+ this.onCancel();
+ });
+ }
+
+ onChangeShareOption(optionSelectElement, resourceId) {
+ let selectedShareOption = optionSelectElement.target.value;
+
+ this.setState(function (prevState) {
+ let sharingOptions = prevState.resourceSharingOptions;
+
+ if (sharingOptions[resourceId] && selectedShareOption === '0') {
+ delete sharingOptions[resourceId];
+ } else {
+ sharingOptions[resourceId] = selectedShareOption;
+ }
+
+ return {resourceSharingOptions: sharingOptions};
+ });
+ }
+
+ render() {
+ if (this.props.show && !this.state.dataLoaded) {
+ let requestConfigs = [
+ {
+ name: 'phaseResources',
+ url: `${BACKEND_ESTIMATOR_API_URL}/phases/${this.props.estimateData.phaseId}/resources/`,
+ params: {}
+ },
+ {
+ name: 'estimateResources',
+ url: `${BACKEND_ESTIMATOR_API_URL}/estimates/${this.props.estimateData.estimateId}/shared_resources/`,
+ params: {}
+ },
+ ];
+
+ ArchestHttp.BATCH_GET(requestConfigs, (responses) => {
+ let phaseResources = responses.phaseResources.data.results;
+ let estimateResources = responses.estimateResources.data.results;
+ let resourceSharingOptions = {};
+
+ for (let i = 0; i < estimateResources.length; i++) {
+ resourceSharingOptions[estimateResources[i].resource.id] = estimateResources[i].access_level;
+ }
+
+
+ for (let i = 0; i < phaseResources.length; i++) {
+ phaseResources[i]['sharedOption'] = 0;
+ if (resourceSharingOptions[phaseResources[i]['id']]) {
+ phaseResources[i]['sharedOption'] = resourceSharingOptions[phaseResources[i]['id']];
+ }
+ }
+
+ this.setState({
+ phaseResources: phaseResources,
+ resourceSharingOptions: resourceSharingOptions,
+ dataLoaded: true
+ });
+ });
+ }
+
+ let estimateSharedResources = _.map(
+ this.state.phaseResources,
+ phaseResource => {
+ let sharedOption = 0;
+ if (this.state.resourceSharingOptions[phaseResource.id]) {
+ sharedOption = this.state.resourceSharingOptions[phaseResource.id];
+ }
+ let disableOptions = false;
+ if (parseInt(this.props.estimateData.estimateOwner.id, 10) === parseInt(phaseResource.id, 10)) {
+ disableOptions = true;
+ sharedOption = 2;
+ }
+ return (
+
+ {phaseResource.full_name}
+
+
+ this.onChangeShareOption(element, phaseResource.id)}>
+
+ View
+ Edit
+
+
+
+
+
+ );
+ }
+ );
+
+ return (
+
+
+
+ Share Estimate
+
+
+
+
+
+ Name
+ Options
+
+
+ {estimateSharedResources}
+
+
+
+
+ Cancel
+ Share
+
+
+
+ );
+ }
+}
+
+export default ArchestEstimateShareModalComponent;
\ No newline at end of file
diff --git a/src/components/ArchestEstimateSubActivitiesComponent.js b/src/components/ArchestEstimateSubActivitiesComponent.js
index 1b05008..4004772 100644
--- a/src/components/ArchestEstimateSubActivitiesComponent.js
+++ b/src/components/ArchestEstimateSubActivitiesComponent.js
@@ -78,6 +78,7 @@ class ArchestEstimateSubActivitiesComponent extends Component {
removeSubActivityItemHandler={this.removeSubActivityItem}
saveSubActivityItemCallback={this.saveSubActivityItemCallback}
subActivityChangeHandler={this.subActivityChangeHandler}
+ estimateResources={this.props.estimateResources}
/>
);
diff --git a/src/components/ArchestEstimateSubActivityItemComponent.js b/src/components/ArchestEstimateSubActivityItemComponent.js
index 4f564c7..f1b6b62 100644
--- a/src/components/ArchestEstimateSubActivityItemComponent.js
+++ b/src/components/ArchestEstimateSubActivityItemComponent.js
@@ -10,6 +10,7 @@ class ArchestEstimateSubActivityItemComponent extends Component {
this.state = {
parentActivityId: this.props.subActivity.parent_id,
subActivityId: this.props.subActivity.id,
+ ownerId: this.props.subActivity.owner ? this.props.subActivity.owner.id : '',
subActivityName: this.props.subActivity.name,
subActivityEstimatedTime: this.props.subActivity.estimated_time,
};
@@ -25,6 +26,7 @@ class ArchestEstimateSubActivityItemComponent extends Component {
this.props.saveSubActivityItemCallback(this.state.subActivityId, {
name: this.state.subActivityName,
estimated_time: this.state.subActivityEstimatedTime,
+ owner_id: this.state.ownerId
});
}
@@ -67,6 +69,24 @@ class ArchestEstimateSubActivityItemComponent extends Component {
render() {
+ let resourcesOptions = [];
+
+ if (this.props.estimateResources) {
+ resourcesOptions = this.props.estimateResources.map(
+ (estimateResource) => {estimateResource.resource.full_name}
+ );
+ }
+
+ if (this.state.ownerId !== '') {
+ let estimateResources = this.props.estimateResources.map((estimateResource) => parseInt(estimateResource.resource.id, 10));
+ if (estimateResources.indexOf(parseInt(this.state.ownerId, 10)) === -1) {
+ resourcesOptions.unshift({this.props.subActivity.owner.full_name} );
+ }
+ }
+
+ resourcesOptions.unshift({''} );
+
return (
@@ -83,7 +103,7 @@ class ArchestEstimateSubActivityItemComponent extends Component {
-
+
+
+
+
+
+ {resourcesOptions}
+
+
+
+
-
diff --git a/src/components/estimate_view.js b/src/components/ArchestEstimateViewComponent.js
similarity index 98%
rename from src/components/estimate_view.js
rename to src/components/ArchestEstimateViewComponent.js
index 7a9d939..f98a1ca 100644
--- a/src/components/estimate_view.js
+++ b/src/components/ArchestEstimateViewComponent.js
@@ -6,7 +6,7 @@ import ArchestHttp from "../modules/archest_http";
import {HotTable} from '@handsontable/react'
import ArchestMainContainerComponent from "./ArchestMainContainerComponent";
-class EstimateViewComponent extends Component {
+class ArchestEstimateViewComponent extends Component {
constructor(props) {
super(props);
@@ -154,4 +154,4 @@ class EstimateViewComponent extends Component {
}
-export default EstimateViewComponent;
+export default ArchestEstimateViewComponent;
diff --git a/src/components/ArchestEstimateWorkEntriesModalComponent.js b/src/components/ArchestEstimateWorkEntriesModalComponent.js
new file mode 100644
index 0000000..9993397
--- /dev/null
+++ b/src/components/ArchestEstimateWorkEntriesModalComponent.js
@@ -0,0 +1,121 @@
+import React, {Component} from "react";
+import ArchestAuthEnabledComponent from "./ArchestAuthEnabledComponent";
+import {Modal, Button, Card, Row, Col, OverlayTrigger, Tooltip, ProgressBar} from "react-bootstrap";
+import './styles/ArchestEstimateWorkEntriesModalComponent.scss';
+import {ACTIVITY_WORK_ENTRY_TYPE, SUB_ACTIVITY_WORK_ENTRY_TYPE} from "../constants";
+
+const _ = require('lodash');
+
+class ArchestEstimateWorkEntriesModalComponent extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ phaseResources: [],
+ dataLoaded: false,
+ resourceSharingOptions: []
+ };
+ this.onCancel = this.onCancel.bind(this);
+ }
+
+ onCancel() {
+ this.props.onCancel();
+ this.setState((prevState) => {
+ return {dataLoaded: false}
+ });
+ }
+
+ render() {
+
+ let activityOrSubActivity = this.props.activityOrSubActivity;
+ let entered_time = 0;
+ let workEntries = _.map(this.props.workEntries, (workEntry) => {
+ if (workEntry.ENTRY_TYPE === ACTIVITY_WORK_ENTRY_TYPE) {
+ entered_time = activityOrSubActivity.entered_time_directly_to_activity;
+ } else if (workEntry.ENTRY_TYPE === SUB_ACTIVITY_WORK_ENTRY_TYPE) {
+ entered_time = activityOrSubActivity.entered_time;
+ }
+ return (
+
+ {workEntry.date}
+ {workEntry.worked_hours.toFixed(2)}
+ {workEntry.owner ? workEntry.owner.full_name : ''}
+ {workEntry.note}
+
+ );
+ });
+ return (
+
+
+
+ Work Entries
+
+
+
+
+
+
+
+
+ Activity/Sub Activity Name
+
+ {activityOrSubActivity.name}
+
+
+
+
+
+
+ Estimated Time
+
+ {activityOrSubActivity.estimated_time} Hrs.
+
+
+
+ Entered Time
+
+ {entered_time} Hrs.
+
+
+
+ Status
+
+ {activityOrSubActivity.status_name}
+
+
+
+ Completion (%)
+
+
+
{activityOrSubActivity.completion_percentage}
+ }>
+
+
+
+
+
+
+
+ Date
+ Worked Hrs.
+ Owner
+ Note
+
+
+ {workEntries}
+
+
+
+
+ Ok
+
+
+
+ );
+ }
+}
+
+export default ArchestEstimateWorkEntriesModalComponent;
\ No newline at end of file
diff --git a/src/components/ArchestFeatureActivityProgressComponent.js b/src/components/ArchestFeatureActivityProgressComponent.js
index 9a74a8c..cb207b5 100644
--- a/src/components/ArchestFeatureActivityProgressComponent.js
+++ b/src/components/ArchestFeatureActivityProgressComponent.js
@@ -1,6 +1,8 @@
import React, {Component} from "react";
-import {Col, Dropdown, OverlayTrigger, ProgressBar, Row, Tooltip} from "react-bootstrap";
+import {Col, OverlayTrigger, ProgressBar, Row, Tooltip} from "react-bootstrap";
import ArchestFeatureSubActivityProgressComponent from "./ArchestFeatureSubActivityProgressComponent";
+import {ACTIVITY} from "../constants";
+import ArchestActivityStatusChangeWidgetComponent from "./ArchestActivityStatusChangeWidgetComponent";
const _ = require('lodash');
@@ -10,13 +12,14 @@ class ArchestFeatureActivityProgressComponent extends Component {
let activity = this.props.activity;
let subActivityComponents = _.map(
activity.sub_activities,
- subActivity =>
+ subActivity =>
);
return (
-
+
{activity.name}
@@ -26,35 +29,40 @@ class ArchestFeatureActivityProgressComponent extends Component {
-
- {activity.estimated_time}
+
+ {activity.owner}
-
-
- Activity {activity.entered_time_directly_to_activity} Hrs, Sub
- Activities {activity.entered_time_to_sub_activities} Hrs
-
- }>
-
- {activity.total_entered_time}
- schedule
-
-
-
-
- {activity.remaining_time}
+
+
+
+ {activity.estimated_time}
+
+
+
+ Activity {activity.entered_time_directly_to_activity} Hrs, Sub
+ Activities {activity.entered_time_to_sub_activities} Hrs
+
+ }>
+
+ this.props.showWorkEntriesCallback(activity, ACTIVITY)}>
+ {activity.total_entered_time}
+
+
+
+
+ {activity.remaining_time}
+
+
+
+
-
-
-
-
-
- Delete
-
-
+
{subActivityComponents}
diff --git a/src/components/ArchestFeatureProgressComponent.js b/src/components/ArchestFeatureProgressComponent.js
index 136ac72..5724349 100644
--- a/src/components/ArchestFeatureProgressComponent.js
+++ b/src/components/ArchestFeatureProgressComponent.js
@@ -10,7 +10,7 @@ class ArchestFeatureProgressComponent extends Component {
let feature = this.props.feature;
let activityComponents = _.map(
feature.activities,
- activity =>
+ activity =>
);
return (
@@ -20,24 +20,40 @@ class ArchestFeatureProgressComponent extends Component {
{feature.name}
+
+
+
+
+ Times
+
+
-
+
Activity/Sub Activity
- Completion (%)
+ Progress (%)
-
- Est.
+
+ Owner
-
- Ent.
-
-
- Rem.
+
+
+
+
+ Estimated
+
+
+ Entered
+
+
+ Remaining
+
+
+
- Actions
+ Status
diff --git a/src/components/ArchestFeatureSubActivityProgressComponent.js b/src/components/ArchestFeatureSubActivityProgressComponent.js
index 716dcd5..8e12834 100644
--- a/src/components/ArchestFeatureSubActivityProgressComponent.js
+++ b/src/components/ArchestFeatureSubActivityProgressComponent.js
@@ -1,5 +1,7 @@
import React, {Component} from "react";
-import {Col, Dropdown, OverlayTrigger, ProgressBar, Row, Tooltip} from "react-bootstrap";
+import {Col, OverlayTrigger, ProgressBar, Row, Tooltip} from "react-bootstrap";
+import {SUB_ACTIVITY} from "../constants";
+import ArchestActivityStatusChangeWidgetComponent from "./ArchestActivityStatusChangeWidgetComponent";
class ArchestFeatureSubActivityProgressComponent extends Component {
@@ -7,7 +9,7 @@ class ArchestFeatureSubActivityProgressComponent extends Component {
let subActivity = this.props.subActivity;
return (
-
+
{subActivity.name}
@@ -17,24 +19,34 @@ class ArchestFeatureSubActivityProgressComponent extends Component {
-
- {subActivity.estimated_time}
+
+ {subActivity.owner}
-
- {subActivity.entered_time}
-
-
- {subActivity.remaining_time}
+
+
+
+
+ {subActivity.estimated_time}
+
+
+ this.props.showWorkEntriesCallback(subActivity, SUB_ACTIVITY)}>
+ {subActivity.entered_time}
+
+
+
+
+
+ {subActivity.remaining_time}
+
+
+
+
-
-
-
-
-
- Delete
-
-
+
);
diff --git a/src/components/home.js b/src/components/ArchestHomeComponent.js
similarity index 97%
rename from src/components/home.js
rename to src/components/ArchestHomeComponent.js
index 851fff3..011fab6 100644
--- a/src/components/home.js
+++ b/src/components/ArchestHomeComponent.js
@@ -8,7 +8,7 @@ import ArchestMainContainerComponent from "./ArchestMainContainerComponent";
const _ = require('lodash');
-class HomeComponent extends Component {
+class ArchestHomeComponent extends Component {
constructor(props) {
super(props);
@@ -109,4 +109,4 @@ class HomeComponent extends Component {
}
}
-export default HomeComponent;
+export default ArchestHomeComponent;
diff --git a/src/components/login.js b/src/components/ArchestLoginComponent.js
similarity index 97%
rename from src/components/login.js
rename to src/components/ArchestLoginComponent.js
index 8b32a17..8dcfa06 100644
--- a/src/components/login.js
+++ b/src/components/ArchestLoginComponent.js
@@ -6,7 +6,7 @@ import ArchestHttp from "../modules/archest_http";
import ArchestAuth from "../modules/archest_auth";
-class LoginComponent extends Component {
+class ArchestLoginComponent extends Component {
constructor(props) {
super(props);
@@ -105,4 +105,4 @@ class LoginComponent extends Component {
}
}
-export default LoginComponent;
+export default ArchestLoginComponent;
diff --git a/src/components/logout.js b/src/components/ArchestLogoutComponent.js
similarity index 80%
rename from src/components/logout.js
rename to src/components/ArchestLogoutComponent.js
index 0712354..81504c7 100644
--- a/src/components/logout.js
+++ b/src/components/ArchestLogoutComponent.js
@@ -2,7 +2,7 @@ import React, {Component} from "react";
import {LOCAL_STORAGE_TOKEN_KEY} from "../constants";
-class LogoutComponent extends Component {
+class ArchestLogoutComponent extends Component {
render() {
localStorage.removeItem(LOCAL_STORAGE_TOKEN_KEY);
@@ -15,4 +15,4 @@ class LogoutComponent extends Component {
}
}
-export default LogoutComponent;
+export default ArchestLogoutComponent;
diff --git a/src/components/navbar.js b/src/components/ArchestNavbarComponent.js
similarity index 94%
rename from src/components/navbar.js
rename to src/components/ArchestNavbarComponent.js
index 44b9216..b7dfb2e 100644
--- a/src/components/navbar.js
+++ b/src/components/ArchestNavbarComponent.js
@@ -5,7 +5,7 @@ import {Navbar, Nav, OverlayTrigger, Tooltip} from "react-bootstrap";
const _ = require('lodash');
-class NavbarComponent extends Component {
+class ArchestNavbarComponent extends Component {
render() {
if (!_.includes(PATHS_WITH_NO_NAVBAR, this.props.location.pathname)) {
@@ -41,4 +41,4 @@ class NavbarComponent extends Component {
}
}
-export default withRouter(NavbarComponent);
+export default withRouter(ArchestNavbarComponent);
diff --git a/src/components/phase_estimates.js b/src/components/ArchestPhaseEstimatesComponent.js
similarity index 58%
rename from src/components/phase_estimates.js
rename to src/components/ArchestPhaseEstimatesComponent.js
index 0399c67..131f295 100644
--- a/src/components/phase_estimates.js
+++ b/src/components/ArchestPhaseEstimatesComponent.js
@@ -5,10 +5,12 @@ import {BACKEND_ESTIMATOR_API_URL} from "../constants";
import ArchestHttp from "../modules/archest_http";
import {Redirect} from "react-router-dom";
import ArchestMainContainerComponent from "./ArchestMainContainerComponent";
+import './styles/ArchestPhaseEstimatesComponent.scss';
+import ArchestEstimateShareModalComponent from "./ArchestEstimateShareModalComponent";
const _ = require('lodash');
-class PhaseEstimatesComponent extends Component {
+class ArchestPhaseEstimatesComponent extends Component {
constructor(props) {
super(props);
@@ -18,7 +20,10 @@ class PhaseEstimatesComponent extends Component {
phase: {},
phaseEstimateList: [],
modalProps: {},
- breadcrumbs: []
+ breadcrumbs: [],
+ shareEstimateModalProps: {
+ show: false
+ }
};
this.addNewEstimate = this.addNewEstimate.bind(this);
this.showAddEstimateModal = this.showAddEstimateModal.bind(this);
@@ -103,6 +108,25 @@ class PhaseEstimatesComponent extends Component {
})
}
+ showShareEstimateModal(estimatedId, estimateOwner) {
+ this.setState({
+ shareEstimateModalProps: {
+ show: true,
+ estimateId: estimatedId,
+ estimateOwner: estimateOwner,
+ onCancel: () => {
+ this.setState({
+ shareEstimateModalProps: {
+ show: false,
+ estimateId: estimatedId,
+ estimateOwner: estimateOwner,
+ }
+ });
+ }
+ }
+ });
+ }
+
deleteEstimate(estimateId) {
let component = this;
@@ -143,6 +167,15 @@ class PhaseEstimatesComponent extends Component {
return (
+
@@ -178,49 +211,59 @@ class PhaseEstimatesComponent extends Component {
getPhaseEstimateInfoListItem(estimate) {
return (
-
-
{estimate.name}
- Delete
- }>
- this.showDeleteEstimateModal(estimate.id)}
- style={{'float': 'right', 'marginLeft': '10px'}} variant="outline-danger" size="sm">
-
-
-
- View
- }>
- this.handleEstimateNavigationBtnClick(estimate, 'view')}
- style={{'float': 'right', 'marginLeft': '10px'}} variant="outline-primary" size="sm">
-
-
-
- Progress
- }>
- this.handleEstimateNavigationBtnClick(estimate, 'progress')}
- style={{'float': 'right', 'marginLeft': '10px'}} variant="outline-primary" size="sm">
-
-
-
- Edit
- }>
- this.handleEstimateNavigationBtnClick(estimate, 'edit')}
- style={{'float': 'right'}} variant="outline-primary" size="sm">
-
-
-
-
- {estimate.owner.full_name}
+
+
+
+ {estimate.name}
+
+
+
+
+
+ Progress}>
+ this.handleEstimateNavigationBtnClick(estimate, 'progress')}
+ className="material-icons md-18 archest-phase-estimates-icon">view_list
+
+
+
+
+ Sheet View}>
+ this.handleEstimateNavigationBtnClick(estimate, 'view')}
+ className="material-icons md-18 archest-phase-estimates-icon">grid_on
+
+
+
+
+ Edit}>
+ this.handleEstimateNavigationBtnClick(estimate, 'edit')}
+ className="material-icons md-18 archest-phase-estimates-icon">edit
+
+
+
+ Share}>
+ this.showShareEstimateModal(estimate.id, estimate.owner)}
+ className="material-icons md-18 archest-phase-estimates-icon">folder_shared
+
+
+
+ Delete}>
+ this.showDeleteEstimateModal(estimate.id)}
+ className="material-icons md-18 archest-phase-estimates-icon">delete
+
+
+
+
+
+
+
+ {estimate.owner.full_name}
+
+
+
);
}
}
-export default PhaseEstimatesComponent;
+export default ArchestPhaseEstimatesComponent;
diff --git a/src/components/ArchestTimelineComponent.js b/src/components/ArchestTimelineComponent.js
index 935ff56..a9c45a1 100644
--- a/src/components/ArchestTimelineComponent.js
+++ b/src/components/ArchestTimelineComponent.js
@@ -4,7 +4,7 @@ import ArchestMainContainerComponent from "./ArchestMainContainerComponent";
import {BACKEND_ESTIMATOR_API_URL} from "../constants";
import ArchestHttp from "../modules/archest_http";
import ArchestWorkEntryComponent from "./ArchestWorkEntryComponent";
-import './styles/ArchestTimeline.scss';
+import './styles/ArchestTimelineComponent.scss';
import ArchestWorkEntryFormComponent from "./ArchestWorkEntryFormComponent";
const _ = require('lodash');
diff --git a/src/components/styles/ArchestActivityStatusChangeWidgetComponent.scss b/src/components/styles/ArchestActivityStatusChangeWidgetComponent.scss
new file mode 100644
index 0000000..f7fe11a
--- /dev/null
+++ b/src/components/styles/ArchestActivityStatusChangeWidgetComponent.scss
@@ -0,0 +1,8 @@
+.archest-activity-completion-widget-form {
+ width: 180%;
+ margin-left: -20%;
+}
+
+.archest-activity-completion-widget-form-group > select {
+ border: none;
+}
\ No newline at end of file
diff --git a/src/components/styles/ArchestEditEstimate.scss b/src/components/styles/ArchestEstimateEditComponent.scss
similarity index 89%
rename from src/components/styles/ArchestEditEstimate.scss
rename to src/components/styles/ArchestEstimateEditComponent.scss
index 56c124c..ebaf160 100644
--- a/src/components/styles/ArchestEditEstimate.scss
+++ b/src/components/styles/ArchestEstimateEditComponent.scss
@@ -52,7 +52,7 @@
.archest-activity-estimated-time-sync-icon {
margin-top: 0.4rem;
margin-left: 0.3rem;
- font-size: 1.3rem;
+ font-size: 1.1rem;
color: $info;
font-weight: bold;
}
@@ -71,7 +71,7 @@
color: $danger;
margin-top: 0.45rem;
margin-left: 0.5rem;
- font-size: 0.9rem;
+ font-size: 0.7rem;
}
.archest-sub-activity-item-delete-btn:hover {
@@ -88,7 +88,7 @@
}
.archest-activity-feature-name-form-group {
- width: 108%;
+ width: 109.5%;
}
.archest-activity-delete-btn {
@@ -123,3 +123,17 @@
.archest-activity-sub-activities-save-loading-icon {
margin-top: -10px;
}
+
+.archest-activity-owner-name-form-group {
+ margin-left: -30%;
+ width: 124%;
+}
+
+.archest-sub-activity-item-activity-owner-form-group {
+ margin-left: -25%;
+ width: 130%;
+}
+
+#archest-activity-owner-label {
+ margin-left: -20%;
+}
diff --git a/src/components/styles/ArchestEstimateProgressComponent.scss b/src/components/styles/ArchestEstimateProgressComponent.scss
index 57eea89..7eb94dc 100644
--- a/src/components/styles/ArchestEstimateProgressComponent.scss
+++ b/src/components/styles/ArchestEstimateProgressComponent.scss
@@ -16,14 +16,8 @@
color: #ffffff;
}
-.archest-feature-progress-sub-activity:hover, .archest-feature-progress-sub-activity-name:hover {
- background-color: $gray-300;
- cursor: default;
-}
-
-.archest-feature-progress-activity:hover, .archest-feature-progress-activity-name:hover {
- background-color: $info;
- color: #ffffff;
+.archest-feature-progress-sub-activity:hover, .archest-feature-progress-sub-activity-name:hover, .archest-feature-progress-activity:hover, .archest-feature-progress-activity-name:hover {
+ background-color: $gray-200;
cursor: default;
}
@@ -38,6 +32,18 @@
font-size: 0.95em;
}
+.archest-feature-progress-activity-entered > p {
+ text-decoration: underline;
+ color: $info;
+ cursor: pointer;
+}
+
+.archest-feature-progress-sub-activity-entered > p {
+ text-decoration: underline;
+ color: $info;
+ cursor: pointer;
+}
+
.archest-feature-progress-activity-entered-icon {
font-size: 1.1em;
margin-left: 5px;
@@ -50,4 +56,42 @@
.archest-feature-progress-activity-completion, .archest-feature-progress-sub-activity-completion {
padding-top: 7px;
-}
\ No newline at end of file
+}
+
+.archest-feature-progress-activity-completion > .progress, .archest-feature-progress-sub-activity-completion > .progress {
+ width: 80%;
+}
+
+.archest-feature-progress-time-main-heading-row {
+ margin-left: -28%;
+}
+
+.archest-feature-progress-time-sub-heading {
+ padding: 0;
+ font-size: 0.75em;
+ margin-top: 5px;
+}
+
+.archest-feature-progress-activity-remaining-danger, .archest-feature-progress-sub-activity-remaining-danger {
+ color: $danger;
+}
+
+#archest-feature-progress-owner-heading {
+ margin-left: -4%;
+}
+
+.archest-feature-progress-activity-owner {
+ margin-left: -4%;
+}
+
+.archest-feature-progress-sub-activity-owner {
+ margin-left: -4%;
+}
+
+.archest-feature-progress-times-heading {
+ margin-left: -3%;
+}
+
+.archest-feature-progress-activity-estimated, .archest-feature-progress-sub-activity-estimated {
+ margin-left: -7%;
+}
diff --git a/src/components/styles/ArchestEstimateShareModalComponent.scss b/src/components/styles/ArchestEstimateShareModalComponent.scss
new file mode 100644
index 0000000..3ab9e87
--- /dev/null
+++ b/src/components/styles/ArchestEstimateShareModalComponent.scss
@@ -0,0 +1,6 @@
+@import "../../res/archest-default-theme/variables.scss";
+@import "common/ArchestModalStyles.scss";
+
+.archest-estimate-share-modal-resource-row-share-options-group {
+ margin-bottom: 4px;
+}
diff --git a/src/components/styles/ArchestEstimateWorkEntriesModalComponent.scss b/src/components/styles/ArchestEstimateWorkEntriesModalComponent.scss
new file mode 100644
index 0000000..c21e9d3
--- /dev/null
+++ b/src/components/styles/ArchestEstimateWorkEntriesModalComponent.scss
@@ -0,0 +1,19 @@
+@import "../../res/archest-default-theme/variables.scss";
+@import "common/ArchestModalStyles.scss";
+
+.archest-estimate-work-entries-modal-work-entry-row {
+ padding-bottom: 5px;
+ border-bottom: 1px solid $gray-200;
+ padding-top: 5px;
+}
+
+.archest-estimate-work-entries-modal-work-entry-row:hover {
+ background-color: $gray-200;
+}
+
+.archest-estimate-work-entries-modal-activity-info-label {
+ color: $info;
+ font-size: 0.9em;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
\ No newline at end of file
diff --git a/src/components/styles/ArchestPhaseEstimatesComponent.scss b/src/components/styles/ArchestPhaseEstimatesComponent.scss
new file mode 100644
index 0000000..2722e27
--- /dev/null
+++ b/src/components/styles/ArchestPhaseEstimatesComponent.scss
@@ -0,0 +1,13 @@
+@import "../../res/archest-default-theme/variables.scss";
+
+.archest-phase-estimates-icon {
+ margin-top: 4px;
+}
+
+.archest-phase-estimates-icon:hover {
+ cursor: pointer;
+}
+
+.archest-phase-estimates-icons-row{
+ margin-left: 5px;
+}
\ No newline at end of file
diff --git a/src/components/styles/ArchestTimeline.scss b/src/components/styles/ArchestTimelineComponent.scss
similarity index 100%
rename from src/components/styles/ArchestTimeline.scss
rename to src/components/styles/ArchestTimelineComponent.scss
diff --git a/src/components/styles/common/ArchestModalStyles.scss b/src/components/styles/common/ArchestModalStyles.scss
new file mode 100644
index 0000000..8a99c5e
--- /dev/null
+++ b/src/components/styles/common/ArchestModalStyles.scss
@@ -0,0 +1,30 @@
+@import "../../../res/archest-default-theme/variables.scss";
+
+.archest-modal-card {
+ box-shadow: none !important;
+}
+
+.archest-modal-body, .archest-modal-header, .archest-modal-footer {
+ border: none !important;
+}
+
+.archest-modal-headings {
+ font-weight: bold;
+ font-size: 0.95em;
+}
+
+.archest-modal-body {
+ font-size: 0.88em;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.archest-modal-header {
+ background-color: $info;
+ color: #ffffff;
+}
+
+.archest-modal-title {
+ font-size: 1.2rem;
+ margin-left: 10px
+}
diff --git a/src/constants.js b/src/constants.js
index ac8ce2d..b92cbc1 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -6,3 +6,6 @@ export const HANDSONTABLE_KEY = 'non-commercial-and-evaluation';
export const SUB_ACTIVITY_WORK_ENTRY_TYPE = 'SUB_ACTIVITY_WORK_ENTRY_TYPE';
export const ACTIVITY_WORK_ENTRY_TYPE = 'ACTIVITY_WORK_ENTRY_TYPE';
+
+export const ACTIVITY = 'ACTIVITY';
+export const SUB_ACTIVITY = 'SUB_ACTIVITY';
diff --git a/src/index.js b/src/index.js
index 6e308c4..b1145c9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,39 +4,36 @@ import './res/archest-default-theme/index.scss';
import './res/icons/font/css/open-iconic-bootstrap.scss';
import '../node_modules/handsontable/dist/handsontable.full.min.css';
import * as serviceWorker from './serviceWorker';
-import HomeComponent from "./components/home";
-import LoginComponent from "./components/login";
-import LogoutComponent from "./components/logout";
import {BrowserRouter as Router, Route} from "react-router-dom";
-import NavbarComponent from "./components/navbar";
-import PhaseEstimatesComponent from "./components/phase_estimates";
-import EstimateViewComponent from "./components/estimate_view";
-import EstimateEditComponent from "./components/estimate_edit";
+import ArchestHomeComponent from "./components/ArchestHomeComponent";
+import ArchestLoginComponent from "./components/ArchestLoginComponent";
+import ArchestLogoutComponent from "./components/ArchestLogoutComponent";
+import ArchestNavbarComponent from "./components/ArchestNavbarComponent";
+import ArchestPhaseEstimatesComponent from "./components/ArchestPhaseEstimatesComponent";
+import ArchestEstimateViewComponent from "./components/ArchestEstimateViewComponent";
+import ArchestEstimateEditComponent from "./components/ArchestEstimateEditComponent";
import ArchestEstimateProgressComponent from "./components/ArchestEstimateProgressComponent";
-import TimelineComponent from "./components/ArchestTimelineComponent.js";
+import ArchestTimelineComponent from "./components/ArchestTimelineComponent.js";
import ArchestCalendarComponent from "./components/ArchestCalendarComponent";
ReactDOM.render(
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
,
document.getElementById('root'),
);
-// If you want your app to work offline and load faster, you can change
-// unregister() to register() below. Note this comes with some pitfalls.
-// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
diff --git a/src/res/archest-default-theme/index.scss b/src/res/archest-default-theme/index.scss
index 279cc8f..a1886cc 100644
--- a/src/res/archest-default-theme/index.scss
+++ b/src/res/archest-default-theme/index.scss
@@ -92,4 +92,19 @@ ol.breadcrumb > .breadcrumb-item {
margin-bottom: 20px;
}
+/* Rules for sizing the icon. */
+.material-icons.md-12 { font-size: 12px; }
+.material-icons.md-18 { font-size: 18px; }
+.material-icons.md-24 { font-size: 24px; }
+.material-icons.md-36 { font-size: 36px; }
+.material-icons.md-48 { font-size: 48px; }
+
+/* Rules for using icons as black on a light background. */
+.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
+.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
+
+/* Rules for using icons as white on a dark background. */
+.material-icons.md-light { color: rgba(255, 255, 255, 1); }
+.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
+
@import "~bootstrap/scss/bootstrap";
\ No newline at end of file