diff --git a/package-lock.json b/package-lock.json
index bc6f08664..31916f976 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8344,11 +8344,6 @@
"resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
"integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU="
},
- "lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
- },
"lodash.defaults": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz",
@@ -14663,6 +14658,11 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
"integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
},
+ "uuid-v4": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/uuid-v4/-/uuid-v4-0.1.0.tgz",
+ "integrity": "sha1-YtezEEBvbOz+oVKMafHo4LzsWjo="
+ },
"v8flags": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz",
diff --git a/package.json b/package.json
index 14b0b263c..161a39068 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,8 @@
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.3",
"redux-thunk": "^2.2.0",
- "semantic-ui-react": "^0.78.2"
+ "semantic-ui-react": "^0.78.2",
+ "uuid-v4": "^0.1.0"
},
"devDependencies": {
"@types/jest": "^22.2.2",
diff --git a/src/components/rangeUsePlan/edit/EditRupGrazingSchedule.js b/src/components/rangeUsePlan/edit/EditRupGrazingSchedule.js
new file mode 100644
index 000000000..7203ba303
--- /dev/null
+++ b/src/components/rangeUsePlan/edit/EditRupGrazingSchedule.js
@@ -0,0 +1,204 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+// import cloneDeep from 'lodash.clonedeep';
+import { Table, Button, Icon, TextArea, Form, Dropdown, Message } from 'semantic-ui-react';
+// import { calcCrownTotalAUMs, roundTo1Decimal } from '../../../handlers';
+// import { handleGrazingScheduleValidation } from '../../../handlers/validation';
+import * as strings from '../../../constants/strings';
+import { roundTo1Decimal, getObjValues } from '../../../utils';
+import EditRupGrazingScheduleEntry from './EditRupGrazingScheduleEntry';
+// import { ConfirmationModal } from '../../common';
+
+const propTypes = {
+ schedule: PropTypes.shape({ grazingScheduleEntries: PropTypes.array }).isRequired,
+ scheduleIndex: PropTypes.number.isRequired,
+ onScheduleClicked: PropTypes.func.isRequired,
+ activeScheduleIndex: PropTypes.number.isRequired,
+ grazingScheduleEntriesMap: PropTypes.shape({}).isRequired,
+ authorizedAUMs: PropTypes.number.isRequired,
+ crownTotalAUMs: PropTypes.number.isRequired,
+ yearOptions: PropTypes.arrayOf(PropTypes.object).isRequired,
+ pasturesMap: PropTypes.shape({}).isRequired,
+ livestockTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
+ // usages: PropTypes.arrayOf(PropTypes.object).isRequired,
+ // yearOptions: PropTypes.arrayOf(PropTypes.object).isRequired,
+ // pastures: PropTypes.arrayOf(PropTypes.object).isRequired,
+ // handleScheduleChange: PropTypes.func.isRequired,
+ // handleScheduleDelete: PropTypes.func.isRequired,
+ // handleScheduleCopy: PropTypes.func.isRequired,
+ // deleteRupScheduleEntry: PropTypes.func.isRequired,
+ // isDeletingSchedule: PropTypes.bool.isRequired,
+ // isDeletingScheduleEntry: PropTypes.bool.isRequired,
+};
+
+class EditRupSchedule extends Component {
+ state = {
+ // isDeleteScheduleModalOpen: false,
+ }
+
+ onScheduleClicked = () => {
+ const { scheduleIndex, onScheduleClicked } = this.props;
+ onScheduleClicked(scheduleIndex);
+ }
+
+ onNarativeChanged = () => {
+
+ }
+ onNewRowClick = (scheduleIndex) => () => {
+
+ }
+
+ onScheduleCopyClicked = () => () => {
+
+ }
+ renderScheduleEntries = (grazingScheduleEntries = [], scheduleIndex) => {
+ const {
+ schedule,
+ pasturesMap,
+ livestockTypes,
+ isDeletingScheduleEntry,
+ } = this.props;
+ const { year } = schedule;
+ const pastures = getObjValues(pasturesMap);
+ const pastureOptions = pastures.map((pasture) => {
+ const { id, name } = pasture || {};
+ return {
+ key: id,
+ value: id,
+ text: name,
+ };
+ });
+ const livestockTypeOptions = livestockTypes.map((lt) => {
+ const { id, name } = lt || {};
+ return {
+ key: id,
+ value: id,
+ text: name,
+ };
+ });
+
+ return grazingScheduleEntries.map((entry, entryIndex) => {
+ // const key = `schedule${scheduleIndex}entry${entry.key || entry.id}`;
+ return (
+
+ );
+ });
+ }
+ render() {
+ const {
+ schedule,
+ scheduleIndex,
+ activeScheduleIndex,
+ grazingScheduleEntriesMap,
+ authorizedAUMs,
+ crownTotalAUMs,
+ yearOptions,
+ } = this.props;
+ // const { isDeleteScheduleModalOpen } = this.state;
+ const { year, grazingScheduleEntries: ids } = schedule;
+ const grazingScheduleEntries = ids.map(id => grazingScheduleEntriesMap[id]);
+ const narative = (schedule && schedule.narative) || '';
+ const isScheduleActive = activeScheduleIndex === scheduleIndex;
+ const roundedCrownTotalAUMs = roundTo1Decimal(crownTotalAUMs);
+ const copyOptions = yearOptions.map(o => ({ ...o, onClick: this.onScheduleCopyClicked(o) })) || [];
+ const isCrownTotalAUMsError = crownTotalAUMs > authorizedAUMs;
+
+ return (
+
+
+
+
+ }
+ icon={null}
+ pointing="right"
+ loading={false}
+ disabled={false}
+ >
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+ {strings.PASTURE}
+ {strings.LIVESTOCK_TYPE}
+ {strings.NUM_OF_ANIMALS}
+ {strings.DATE_IN}
+ {strings.DATE_OUT}
+ {strings.DAYS}
+ {strings.GRACE_DAYS}
+ {strings.PLD}
+ {strings.CROWN_AUMS}
+
+
+ {this.renderScheduleEntries(grazingScheduleEntries, scheduleIndex)}
+
+
+
+
+
Authorized AUMs
+
{authorizedAUMs}
+
Total AUMs
+
+ {roundedCrownTotalAUMs}
+
+
+
Schedule Description
+
+
+
+ );
+ }
+}
+
+EditRupSchedule.propTypes = propTypes;
+export default EditRupSchedule;
diff --git a/src/components/rangeUsePlan/edit/EditRupGrazingScheduleEntry.js b/src/components/rangeUsePlan/edit/EditRupGrazingScheduleEntry.js
new file mode 100644
index 000000000..ac5073957
--- /dev/null
+++ b/src/components/rangeUsePlan/edit/EditRupGrazingScheduleEntry.js
@@ -0,0 +1,244 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Pikaday from 'pikaday';
+import { Table, Dropdown, Input, Icon } from 'semantic-ui-react';
+import * as utils from '../../../utils';
+import { DELETE_SCHEDULE_ENTRY_FOR_AH_CONTENT, DELETE_SCHEDULE_ENTRY_FOR_AH_HEADER } from '../../../constants/strings';
+
+import { DATE_FORMAT } from '../../../constants/variables';
+import { ConfirmationModal } from '../../common';
+
+const propTypes = {
+ year: PropTypes.number.isRequired,
+ entry: PropTypes.shape({}).isRequired,
+ entryIndex: PropTypes.number.isRequired,
+ pasturesMap: PropTypes.shape({}).isRequired,
+ pastureOptions: PropTypes.arrayOf(PropTypes.object).isRequired,
+ livestockTypes: PropTypes.arrayOf(PropTypes.object).isRequired,
+ livestockTypeOptions: PropTypes.arrayOf(PropTypes.object).isRequired,
+ // handleScheduleEntryChange: PropTypes.func.isRequired,
+ // handleScheduleEntryCopy: PropTypes.func.isRequired,
+ // handleScheduleEntryDelete: PropTypes.func.isRequired,
+ // isDeletingScheduleEntry: PropTypes.bool.isRequired,
+};
+
+class EditRupScheduleEntry extends Component {
+ state = {
+ isDeleteScheduleEntryModalOpen: false,
+ }
+
+ componentDidMount() {
+ const { entry, year } = this.props;
+ const { dateIn: din, dateOut: dout } = entry;
+ const dateIn = din ? new Date(din) : null;
+ const dateOut = dout ? new Date(dout) : null;
+ const minDate = new Date(`${year}-01-02`);
+ const maxDate = new Date(`${year + 1}-01-01`);
+
+ this.pikaDayDateIn = new Pikaday({
+ field: this.dateInRef,
+ format: DATE_FORMAT.SCHEUDLE_ENTRY,
+ minDate,
+ maxDate: dateOut || maxDate,
+ defaultDate: minDate, // the initial date to view when first opened
+ onSelect: this.handleDateChange('dateIn'),
+ });
+ if (dateIn) this.pikaDayDateIn.setDate(dateIn);
+
+ this.pikaDayDateOut = new Pikaday({
+ field: this.dateOutRef,
+ format: DATE_FORMAT.SCHEUDLE_ENTRY,
+ minDate: dateIn || minDate,
+ maxDate,
+ defaultDate: minDate,
+ onSelect: this.handleDateChange('dateOut'),
+ });
+ if (dateOut) this.pikaDayDateOut.setDate(dateOut);
+ }
+
+ setDateInRef = (ref) => { this.dateInRef = ref; }
+ setDateOutRef = (ref) => { this.dateOutRef = ref; }
+
+ handleNumberOnly = (e) => {
+ if (!(e.charCode >= 48 && e.charCode <= 57)) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+
+ handleDateChange = key => (date) => {
+ // const { entry, entryIndex, handleScheduleEntryChange } = this.props;
+ // entry[key] = formatDateFromUTC(date);
+
+ // // prevent users from inputting wrong dates
+ // if (this.pikaDayDateIn && key === 'dateOut') {
+ // this.pikaDayDateIn.setMaxDate(date);
+ // } else if (this.pikaDayDateOut && key === 'dateIn') {
+ // this.pikaDayDateOut.setMinDate(date);
+ // }
+ // handleScheduleEntryChange(entry, entryIndex);
+ }
+
+ handleNumberInput = key => (e) => {
+ const { value } = e.target;
+ const { entry, entryIndex, handleScheduleEntryChange } = this.props;
+ entry[key] = Number(value);
+
+ // handleScheduleEntryChange(entry, entryIndex);
+ }
+
+ handlePastureDropdown = (e, { value: pastureId }) => {
+ const {
+ entry,
+ entryIndex,
+ handleScheduleEntryChange,
+ pastures,
+ } = this.props;
+
+ entry.pastureId = pastureId;
+ const { graceDays } = pastures.find(p => p.id === pastureId);
+ entry.graceDays = graceDays;
+ // handleScheduleEntryChange(entry, entryIndex);
+ }
+
+ handleLiveStockTypeDropdown = (e, { value: livestockTypeId }) => {
+ const {
+ entry,
+ entryIndex,
+ handleScheduleEntryChange,
+ } = this.props;
+
+ entry.livestockTypeId = livestockTypeId;
+ // handleScheduleEntryChange(entry, entryIndex);
+ }
+
+ render() {
+ const {
+ entry,
+ entryIndex,
+ pasturesMap,
+ pastureOptions,
+ livestockTypes,
+ livestockTypeOptions,
+ // isDeletingScheduleEntry,
+ } = this.props;
+
+ const {
+ pastureId,
+ livestockTypeId,
+ livestockCount,
+ dateIn,
+ dateOut,
+ graceDays,
+ } = entry || {};
+
+ const days = utils.calcDateDiff(dateOut, dateIn, false);
+ const pasture = pasturesMap[pastureId];
+ const pldPercent = pasture && pasture.pldPercent;
+ const livestockType = livestockTypes.find(lt => lt.id === livestockTypeId);
+ const auFactor = livestockType && livestockType.auFactor;
+
+ const totalAUMs = utils.calcTotalAUMs(livestockCount, days, auFactor);
+ const pldAUMs = utils.roundTo1Decimal(utils.calcPldAUMs(totalAUMs, pldPercent));
+ const crownAUMs = utils.roundTo1Decimal(utils.calcCrownAUMs(totalAUMs, pldAUMs));
+
+ const entryOptions = [
+ { key: `entry${entryIndex}option1`, text: 'Copy', onClick: this.onCopyEntryClicked },
+ { key: `entry${entryIndex}option2`, text: 'Delete', onClick: this.openDeleteScheduleEntryConfirmationModal },
+ ];
+
+ const isPastureDropdownError = pastureId === undefined;
+ const isLivestockTypeDropdownError = livestockTypeId === undefined;
+ const isLivestockCountError = livestockCount <= 0;
+ const isDateInError = dateIn === undefined;
+ const isDateOutError = dateOut === undefined;
+
+ return (
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {utils.presentNullValue(days, false)}
+
+
+
+
+
+ {utils.presentNullValue(pldAUMs, false)}
+ {utils.presentNullValue(crownAUMs, false)}
+
+ }
+ options={entryOptions}
+ icon={null}
+ pointing="right"
+ />
+
+
+ );
+ }
+}
+
+EditRupScheduleEntry.propTypes = propTypes;
+export default EditRupScheduleEntry;
diff --git a/src/components/rangeUsePlan/edit/EditRupGrazingSchedules.js b/src/components/rangeUsePlan/edit/EditRupGrazingSchedules.js
index 8608d532b..e32c49c03 100644
--- a/src/components/rangeUsePlan/edit/EditRupGrazingSchedules.js
+++ b/src/components/rangeUsePlan/edit/EditRupGrazingSchedules.js
@@ -4,10 +4,10 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import { Dropdown } from 'semantic-ui-react';
import { NOT_PROVIDED } from '../../../constants/strings';
-// import EditRupGrazingSchedule from './EditRupGrazingSchedule';
-import { ELEMENT_ID } from '../../../constants/variables';
+import EditRupGrazingSchedule from './EditRupGrazingSchedule';
+import { ELEMENT_ID, REFERENCE_KEY } from '../../../constants/variables';
import { deleteRupSchedule, deleteRupScheduleEntry } from '../../../actionCreators';
-import { createEmptyArray } from '../../../utils';
+import * as utils from '../../../utils';
const propTypes = {
plan: PropTypes.shape({}).isRequired,
@@ -21,21 +21,22 @@ const propTypes = {
export class EditRupGrazingSchedules extends Component {
constructor(props) {
super(props);
- const { plan, grazingSchedulesMap } = this.props;
+ // const { plan, grazingSchedulesMap } = this.props;
this.state = {
- yearOptions: this.getInitialYearOptions(plan, grazingSchedulesMap),
+ yearOptions: this.getInitialYearOptions(),
activeScheduleIndex: 0,
};
}
- getInitialYearOptions = (plan, grazingSchedulesMap) => {
+ getInitialYearOptions = () => {
+ const { plan, grazingSchedulesMap } = this.props;
const { planStartDate, planEndDate, grazingSchedules: grazingScheduleIds } = plan || {};
if (planStartDate && planEndDate) {
// set up year options
const planStartYear = new Date(planStartDate).getFullYear();
const planEndYear = new Date(planEndDate).getFullYear();
const length = (planEndYear - planStartYear) + 1;
- return createEmptyArray(length)
+ return utils.createEmptyArray(length)
.map((v, i) => (
{
key: planStartYear + i,
@@ -59,12 +60,74 @@ export class EditRupGrazingSchedules extends Component {
this.setState({ activeScheduleIndex: newIndex });
}
+ renderSchedule = (schedule, scheduleIndex) => {
+ const {
+ usages,
+ references,
+ pasturesMap,
+ grazingScheduleEntriesMap,
+ } = this.props;
+ const { yearOptions, activeScheduleIndex } = this.state;
+ const grazingScheduleEntries = utils.getObjValues(grazingScheduleEntriesMap);
+ const { id, year } = schedule;
+ const yearUsage = usages.find(u => u.year === year);
+ const authorizedAUMs = yearUsage && yearUsage.authorizedAum;
+ const livestockTypes = references[REFERENCE_KEY.LIVESTOCK_TYPE];
+ const crownTotalAUMs = utils.calcCrownTotalAUMs(grazingScheduleEntries, pasturesMap, livestockTypes);
+
+ return (
+
+ );
+ }
render() {
+ const { yearOptions } = this.state;
+ const { plan, grazingSchedulesMap } = this.props;
+ const { grazingSchedules: grazingScheduleIds } = plan;
+ const grazingSchedules = grazingScheduleIds.map(id => grazingSchedulesMap[id]);
return (
-
- schedules
+
+
+
+
+ {
+ grazingSchedules.length === 0 ? (
+ {NOT_PROVIDED}
+ ) : (
+ grazingSchedules.map(this.renderSchedule)
+ )
+ }
+
- )
+ );
}
}
diff --git a/src/constants/actionTypes.js b/src/constants/actionTypes.js
index 5ccdfe636..805e031d7 100644
--- a/src/constants/actionTypes.js
+++ b/src/constants/actionTypes.js
@@ -13,7 +13,9 @@ export const SET_AGREEMENT_FILTER = 'SET_AGREEMENT_FILTER';
export const STORE_PLAN = 'STORE_PLAN';
export const UPDATE_PLAN = 'UPDATE_PLAN';
export const ADD_GRAZING_SCHEDULE = 'ADD_GRAZING_SCHEDULE';
+export const UPDATE_GRAZING_SCHEDULE = 'UPDATE_GRAZING_SCHEDULE';
export const ADD_GRAZING_SCHEDULE_ENTRY = 'ADD_GRAZING_SCHEDULE_ENTRY';
+export const UPDATE_GRAZING_SCHEDULE_ENTRY = 'UPDATE_GRAZING_SCHEDULE_ENTRY';
export const STORE_REFERENCES = 'STORE_REFERENCES';
export const STORE_ZONES = 'STORE_ZONES';
diff --git a/src/reducers/grazingScheduleEntriesReducer.js b/src/reducers/grazingScheduleEntriesReducer.js
index d3c58e650..cccfccb09 100644
--- a/src/reducers/grazingScheduleEntriesReducer.js
+++ b/src/reducers/grazingScheduleEntriesReducer.js
@@ -1,4 +1,4 @@
-import { STORE_PLAN, ADD_GRAZING_SCHEDULE_ENTRY } from '../constants/actionTypes';
+import { STORE_PLAN, ADD_GRAZING_SCHEDULE_ENTRY, UPDATE_GRAZING_SCHEDULE_ENTRY } from '../constants/actionTypes';
const storeGrazingScheduleEntries = (state, action) => {
const { grazingScheduleEntries } = action.payload.entities;
@@ -21,6 +21,10 @@ const addGrazingScheduleEntry = (state, action) => {
};
};
+const updateGrazingScheduleEntry = (state, action) => (
+ addGrazingScheduleEntry(state, action)
+);
+
const grazingScheduleEntriesReducer = (state = {
byId: {},
}, action) => {
@@ -29,6 +33,8 @@ const grazingScheduleEntriesReducer = (state = {
return storeGrazingScheduleEntries(state, action);
case ADD_GRAZING_SCHEDULE_ENTRY:
return addGrazingScheduleEntry(state, action);
+ case UPDATE_GRAZING_SCHEDULE_ENTRY:
+ return updateGrazingScheduleEntry(state, action);
default:
return state;
}
diff --git a/src/reducers/grazingSchedulesReducer.js b/src/reducers/grazingSchedulesReducer.js
index 2cbc6251d..44bceb68d 100644
--- a/src/reducers/grazingSchedulesReducer.js
+++ b/src/reducers/grazingSchedulesReducer.js
@@ -1,4 +1,4 @@
-import { STORE_PLAN, ADD_GRAZING_SCHEDULE, ADD_GRAZING_SCHEDULE_ENTRY } from '../constants/actionTypes';
+import { STORE_PLAN, ADD_GRAZING_SCHEDULE, ADD_GRAZING_SCHEDULE_ENTRY, UPDATE_GRAZING_SCHEDULE } from '../constants/actionTypes';
const storeGrazingSchedules = (state, action) => {
const { grazingSchedules } = action.payload.entities;
@@ -21,6 +21,10 @@ const addGrazingSchedule = (state, action) => {
};
};
+const updateGrazingSchedule = (state, action) => (
+ addGrazingSchedule(state, action)
+);
+
const addGrazingScheduleEntry = (state, action) => {
const { grazingScheduleId, grazingScheduleEntry } = action.payload;
const grazingSchedule = { ...state.byId[grazingScheduleId] };
@@ -46,6 +50,8 @@ const grazingSchedulesReducer = (state = {
return addGrazingSchedule(state, action);
case ADD_GRAZING_SCHEDULE_ENTRY:
return addGrazingScheduleEntry(state, action);
+ case UPDATE_GRAZING_SCHEDULE:
+ return updateGrazingSchedule(state, action);
default:
return state;
}