MetaPhase-Consulting/State-TalentMAP

View on GitHub
src/Components/AssignmentsSeparations/Assignment/Assignment.jsx

Summary

Maintainability
C
1 day
Test Coverage
F
2%
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import FA from 'react-fontawesome';
import PropTypes from 'prop-types';
import { get, isEmpty } from 'lodash';
import { useDataLoader } from 'hooks';
import { getResult } from 'utilities';
import { EMPTY_FUNCTION, POSITION_DETAILS } from 'Constants/PropTypes';
import {
  NO_BUREAU, NO_GRADE, NO_ORG, NO_POSITION_NUMBER, NO_POSITION_TITLE, NO_POST,
  NO_STATUS, NO_TOUR_END_DATE, NO_VALUE,
} from 'Constants/SystemMessages';
import { assignmentSeparationAction } from 'actions/assignment';
import { positionsFetchData, resetPositionsFetchData } from 'actions/positions';
import Alert from 'Components/Alert';
import Spinner from 'Components/Spinner';
import CheckBox from 'Components/CheckBox';
import MonthYearInput from 'Components/MonthYearInput';
import InteractiveElement from 'Components/InteractiveElement';
import PositionExpandableContent from 'Components/PositionExpandableContent';
import api from '../../../api';
import { panelMeetingLink } from '../AssignmentsSeparations';

const Assignment = (props) => {
  const {
    perdet,
    data,
    isNew,
    toggleModal,
    setDisableOtherEdits,
    disableOtherEdits,
    employee,
  } = props;

  const dispatch = useDispatch();

  // ====================== Data Retrieval ======================

  const asgId = data?.ASG_SEQ_NUM;
  const revisionNum = data?.ASGD_REVISION_NUM;

  useEffect(() => {
    dispatch(resetPositionsFetchData());
  }, []);

  const [refetch, setRefetch] = useState(true);
  const ep = `/fsbid/assignment_history/${perdet}/assignments/${asgId}/?revision_num=${revisionNum}`;
  const { data: detailsData, loading: detailsLoading, error: detailsErrored } = useDataLoader(
    api().get,
    `${ep}${(asgId && revisionNum) ? '' : '&ignore_params=true'}`,
    true,
    undefined,
    refetch,
  );

  const details = detailsData?.data?.QRY_GETASGDTL_REF?.[0];
  const statusOptions = detailsData?.data?.QRY_LSTASGS_REF;
  const actionOptions = detailsData?.data?.QRY_LSTLAT_REF;
  const todOptions = detailsData?.data?.QRY_LSTTOD_REF;
  const travelOptions = detailsData?.data?.QRY_LSTTF_REF;
  const fundingOptions = detailsData?.data?.QRY_LSTBUREAUS_REF;
  const waiverOptions = detailsData?.data?.QRY_LSTWRT_REF;


  // ====================== View Mode ======================

  const sections = {
    /* eslint-disable quote-props */
    subheading: [
      { 'Position Number': getResult(data, 'POS_NUM_TXT') || NO_POSITION_NUMBER },
      { 'Position Title': getResult(data, 'POS_TITLE_TXT') || NO_POSITION_TITLE },
    ],
    bodyPrimary: [
      { 'Status': getResult(data, 'ASGS_CODE') || NO_STATUS },
      { 'Bureau': getResult(data, 'ORGS_SHORT_DESC') || NO_BUREAU },
      { 'Location': getResult(data, 'POS_LOCATION_CODE') || NO_POST },
      { 'ETA': get(data, 'ASGD_ETA_DATE') || NO_VALUE },
      { 'DIP': getResult(data, 'DIP_CODE') || NO_VALUE },
      { 'Memo Sent': getResult(data, 'MEMO_LAST_SENT_DATE') || NO_VALUE },
      { 'Note Sent': getResult(data, 'NOTE_LAST_SENT_DATE') || NO_VALUE },
      { 'TED': get(data, 'ASGD_ETD_TED_DATE') || NO_TOUR_END_DATE },
      { 'Grade': getResult(data, 'GRD_CD') || NO_GRADE },
      { 'Pay Plan': getResult(data, 'PPL_CODE') || NO_VALUE },
    ],
    /* eslint-enable quote-props */
  };

  // ====================== Edit Mode ======================

  const [editMode, setEditMode] = useState(isNew);
  const [inputClass, setInputClass] = useState('input-default');

  // ----- Position Number State Management -----

  // Position Number Search
  // For search pos results when creating new card
  const [selectedPositionNumber, setPositionNumber] = useState('');
  const pos_results = useSelector(state => state.positions);
  const pos_results_loading = useSelector(state => state.positionsIsLoading);
  const pos_results_errored = useSelector(state => state.positionsHasErrored);

  const getTOD = () => (
    pos_results?.todo_tod_code || pos_results?.bt_tod_code || ''
  );

  const addPositionNum = () => {
    if (selectedPositionNumber) {
      dispatch(positionsFetchData(`limit=50&page=1&position_num=${selectedPositionNumber}`));
    }
  };

  useEffect(() => {
    if (pos_results_loading) {
      setInputClass('loading-animation--3');
    } else if (pos_results_errored) {
      setInputClass('input-error');
    } else if (isEmpty(pos_results) && selectedPositionNumber.length) {
      setInputClass('input-error');
    } else {
      setInputClass('input-default');
    }
  }, [pos_results, pos_results_loading, pos_results_errored]);

  if (isNew) {
    /* eslint-disable quote-props */
    sections.subheading = [
      { 'Position Number': pos_results?.pos_num_text || NO_POSITION_NUMBER },
      { 'Position Title': pos_results?.pos_title_desc || NO_POSITION_TITLE },
      { 'Bureau': pos_results?.pos_bureau_short_desc || NO_BUREAU },
      { 'Location': pos_results?.pos_location_code || NO_POST },
      { 'Org': pos_results?.pos_org_short_desc || NO_ORG },
      { 'Grade': pos_results?.pos_grade_code || NO_GRADE },
      { 'Pay Plan': pos_results?.pos_pay_plan_code || NO_GRADE },
    ];
    /* eslint-enable quote-props */
  }

  const [status, setStatus] = useState('');
  const [action, setAction] = useState('');
  const [ted, setTED] = useState('');
  const [eta, setETA] = useState('');
  const [tod, setTOD] = useState('');
  const [travel, setTravel] = useState('');
  const [funding, setFunding] = useState('');
  const [adj, setAdj] = useState('');
  const [todOther, setTodOther] = useState('');
  const [todMonths, setTodMonths] = useState('');
  const [salaryReimbursement, setSalaryReimbursement] = useState(false);
  const [travelReimbursement, setTravelReimbursement] = useState(false);
  const [training, setTraining] = useState(false);
  const [criticalNeed, setCriticalNeed] = useState(false);
  const [waiver, setWaiver] = useState('');
  const [sent, setSent] = useState('');
  const [panelMeetingDate, setPanelMeetingDate] = useState(null);

  useEffect(() => {
    if (editMode) {
      setDisableOtherEdits(editMode);
      setStatus(details?.ASGS_CODE || 'EF'); // Default to "Effective"
      setAction(details?.LAT_CODE || '');
      setTED(details?.ASGD_ETD_TED_DATE || '');
      setETA(details?.ASGD_ETA_DATE || '');
      setTOD(isNew ? getTOD() : (details?.TOD_CODE || ''));
      setTravel(details?.TF_CD || '');
      setFunding(details?.ASGD_ORG_CODE || '');
      setAdj(details?.ASGD_ADJUST_MONTHS_NUM || '');
      setTodOther(details?.ASGD_TOD_OTHER_TEXT || '');
      setTodMonths(details?.ASGD_TOD_MONTHS_NUM || '');
      setSalaryReimbursement(details?.ASGD_SALARY_REIMBURSE_IND === 'Y');
      setTravelReimbursement(details?.ASGD_TRAVEL_REIMBURSE_IND === 'Y');
      setTraining(details?.ASGD_TRAINING_IND === 'Y');
      setCriticalNeed(details?.ASGD_CRITICAL_NEED_IND === 'Y');
      setWaiver(details?.WRT_CODE_RR_REPAY || 'N'); // Default to "Not Used"
      setSent(details?.NOTE_LAST_SENT_DATE || '');
      setPanelMeetingDate(details?.PMD_DTTM ?
        new Date(details?.PMD_DTTM) : null);
    }
  }, [editMode]);

  const onSubmitForm = () => {
    const commonFields = {
      tod_months_num: todMonths || null,
      tod_other_text: todOther || null,
      tod_adjust_months_num: adj || null,
      eta,
      etd: ted,
      tod,
      salary_reimburse_ind: salaryReimbursement ? 'Y' : 'N',
      travel_reimburse_ind: travelReimbursement ? 'Y' : 'N',
      training_ind: training ? 'Y' : 'N',
      org_code: funding,
      status_code: status,
      lat_code: action,
      travel_code: travel,
      rr_repay_ind: waiver,
    };
    if (isNew) {
      const onCreateSuccess = () => {
        toggleModal(false);
      };
      dispatch(assignmentSeparationAction(
        {
          ...commonFields,
          employee: perdet,
          position: pos_results.pos_seq_num,
        },
        perdet,
        null, // Use Create Endpoint (No Seq Num)
        false, // Use Assignment Endpoint
        onCreateSuccess,
      ));
    } else {
      const onUpdateSuccess = () => {
        setDisableOtherEdits(false);
        setRefetch(!refetch); // Refetch Details on Success
      };
      dispatch(assignmentSeparationAction(
        {
          ...commonFields,
          asg_id: asgId,
          revision_num: revisionNum,
          critical_need_ind: criticalNeed ? 'Y' : 'N',
          updated_date: details?.ASGD_UPDATE_DATE,
        },
        perdet,
        asgId, // Use Update Endpoint (Has Seq Num)
        false, // Use Assignment Endpoint
        onUpdateSuccess,
      ));
    }
  };

  const form = {
    /* eslint-disable quote-props */
    staticBody: [
      { 'Position Number': getResult(data, 'POS_NUM_TXT') || NO_POSITION_NUMBER },
      { 'Position Title': getResult(data, 'POS_TITLE_TXT') || NO_POSITION_TITLE },
      { 'Bureau': getResult(data, 'ORGS_SHORT_DESC') || NO_BUREAU },
      { 'Location': getResult(data, 'POS_LOCATION_CODE') || NO_POST },
      { 'Grade': getResult(data, 'GRD_CD') || NO_GRADE },
      { 'Pay Plan': getResult(data, 'PPL_CODE') || NO_GRADE },
    ],
    inputBody:
      <div className="position-form">
        <div className="position-form--inputs">
          {isNew &&
            <div className="position-form--label-input-container position-number-container">
              <label htmlFor="pos-num">Position Number</label>
              <input
                id="pos-num"
                name="add"
                className={inputClass}
                onChange={value => setPositionNumber(value.target.value)}
                onKeyPress={e => (e.key === 'Enter' ? addPositionNum() : null)}
                type="add"
                value={selectedPositionNumber}
                placeholder="Add by Position Number"
              />
              <InteractiveElement
                className="add-pos-num-icon"
                onClick={addPositionNum}
                role="button"
                title="Add position"
                type="span"
              >
                <FA name="plus" />
              </InteractiveElement>
            </div>
          }
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-statuses">Status</label>
            <select
              id="assignment-statuses"
              value={status}
              onChange={(e) => setStatus(e?.target.value)}
            >
              <option value="" disabled>
                Select Status
              </option>
              {statusOptions?.map(s => (
                <option key={s.ASGS_CODE} value={s.ASGS_CODE}>
                  {s.ASGS_DESC_TEXT}
                </option>
              ))}
            </select>
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-actions">Action</label>
            <select
              id="assignment-actions"
              value={action}
              onChange={(e) => setAction(e?.target.value)}
            >
              <option value="" disabled>
                Select Action
              </option>
              {actionOptions?.map(a => (
                <option key={a.LAT_CODE} value={a.LAT_CODE}>
                  {a.LAT_ABBR_DESC_TEXT}
                </option>
              ))}
            </select>
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="ETA">ETA</label>
            <MonthYearInput value={eta} onChange={setETA} />
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="TED">TED</label>
            <MonthYearInput value={ted} onChange={setTED} />
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-tod">Tour of Duty</label>
            <select
              id="assignment-tod"
              value={tod}
              onChange={(e) => setTOD(e?.target.value)}
            >
              <option value="" disabled>
                Select TOD
              </option>
              {todOptions?.map(t => (
                <option key={t.TOD_CODE} value={t.TOD_CODE}>
                  {t.TOD_DESC_TEXT}
                </option>
              ))}
            </select>
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-adj">ADJ</label>
            <input
              id="assignment-adj"
              value={adj}
              onChange={(e) => setAdj(e?.target.value)}
            />
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-travel">Travel</label>
            <select
              id="assignment-travel"
              value={travel}
              onChange={(e) => setTravel(e?.target.value)}
            >
              <option value="" disabled>
                Select Travel
              </option>
              {travelOptions?.map(t => (
                <option key={t.TF_CODE} value={t.TF_CODE}>
                  {t.TF_SHORT_DESC_TEXT}
                </option>
              ))}
            </select>
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-funding">Alt Funding</label>
            <select
              id="assignment-funding"
              value={funding}
              onChange={(e) => setFunding(e?.target.value)}
            >
              <option value="" disabled>
                Select Funding Org
              </option>
              {fundingOptions?.map(f => (
                <option key={f.ORG_CODE} value={f.ORG_CODE}>
                  {f.ORGS_SHORT_DESC}
                </option>
              ))}
            </select>
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="tod-other">TOD Other</label>
            <input
              id="tod-other"
              value={todOther}
              onChange={(e) => setTodOther(e?.target.value)}
              disabled
            />
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="tod-months">TOD Months</label>
            <input
              id="tod-months"
              value={todMonths}
              onChange={(e) => setTodMonths(e?.target.value)}
              disabled
            />
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="panel-meeting-date">Panel Meeting Date</label>
            {panelMeetingLink(details?.PMI_SEQ_NUM, panelMeetingDate, true)}
          </div>
          <div className="position-form--label-input-container height-80">
            <CheckBox
              id={`salary-reimbursement-${data.id ?? 'create'}`}
              label="Salary Reimbursement"
              value={salaryReimbursement}
              className="mt-40"
              excludeTmCheckboxClass
              onChange={() => setSalaryReimbursement(!salaryReimbursement)}
            />
          </div>
          <div className="position-form--label-input-container height-80">
            <CheckBox
              id={`travel-reimbursement-${data.id ?? 'create'}`}
              label="Travel Reimbursement"
              value={travelReimbursement}
              className="mt-40"
              excludeTmCheckboxClass
              onChange={() => setTravelReimbursement(!travelReimbursement)}
            />
          </div>
          <div className="position-form--label-input-container height-80">
            <CheckBox
              id={`training-${data.id ?? 'create'}`}
              label="Training"
              value={training}
              className="mt-40"
              excludeTmCheckboxClass
              onChange={() => setTraining(!training)}
            />
          </div>
          <div className="position-form--label-input-container height-80">
            <CheckBox
              id={`critical-need-${data.id ?? 'create'}`}
              label="Critical Need"
              value={details?.ASGD_CRITICAL_NEED_IND === 'Y' || false}
              className="mt-40"
              excludeTmCheckboxClass
              disabled={isNew}
            />
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-waiver">Waiver</label>
            <select
              id="assignment-waiver"
              value={waiver}
              onChange={(e) => setWaiver(e?.target.value)}
            >
              <option value="" disabled>
                Select Waiver
              </option>
              {waiverOptions?.map(w => (
                <option key={w.WRT_CODE} value={w.WRT_CODE}>
                  {w.WRT_DESC}
                </option>
              ))}
            </select>
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="assignment-sent">Sent</label>
            <input
              id="assignment-sent"
              value={sent}
              onChange={(e) => setSent(e?.target.value)}
              disabled
            />
          </div>
          <div className="position-form--label-input-container">
            <label htmlFor="diplomatic-title">Diplomatic Title</label>
            <input
              id="diplomatic-title"
              value={data?.DIPLOMATIC_TITLE || NO_VALUE}
              disabled
            />
          </div>
        </div>
      </div>,
    cancelText: 'Are you sure you want to discard all changes made to this Assignment?',
    handleSubmit: onSubmitForm,
    handleCancel: () => { toggleModal(false); setDisableOtherEdits(false); },
    handleEdit: {
      editMode,
      setEditMode: isNew ? null : setEditMode,
      disableEdit: disableOtherEdits,
    },
    // TO-DO: DIP, MEMO, NOTE
    /* eslint-enable quote-props */
  };

  const getOverlay = () => {
    if (detailsLoading) {
      if (isNew) {
        return <Spinner type="standard-center" size="small" />;
      }
    } else if (detailsLoading) {
      return (
        <div className="loading-animation--5">
          <div className="loading-message pbl-20">
            Loading additional data
          </div>
        </div>
      );
    } else if (detailsErrored) {
      return <Alert type="error" title="Error loading data" messages={[{ body: 'Please try again.' }]} />;
    }
    return false;
  };

  return (
    <div className={`position-content--container min-height-${isNew ? '150' : '50'}`}>
      {employee}
      {getOverlay() ||
        <PositionExpandableContent
          sections={sections}
          form={form}
          saveText={isNew ? 'Create Assignment' : 'Save Assignment'}
        />
      }
    </div>
  );
};

Assignment.propTypes = {
  data: POSITION_DETAILS.isRequired,
  isNew: PropTypes.bool,
  // cycle: PropTypes.shape({
  //   cycle_name: PropTypes.string,
  // }).isRequired,
  toggleModal: PropTypes.func,
  perdet: PropTypes.string,
  setDisableOtherEdits: PropTypes.func,
  disableOtherEdits: PropTypes.bool,
  employee: PropTypes.shape(),
};

Assignment.defaultProps = {
  data: {},
  isNew: false,
  toggleModal: EMPTY_FUNCTION,
  perdet: '',
  setDisableOtherEdits: EMPTY_FUNCTION,
  disableOtherEdits: false,
  employee: undefined,
};

export default Assignment;