React Basic

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 10

import React, { useState, useEffect, useReducer } from 'react';

import PropTypes from 'prop-types';


import { withIntl } from 'i18n/ReactIntlProviderWrapper';
import { withStyles } from '@ellucian/react-design-system/core/styles';
import {
Typography,
Grid,
Card,
Table,
TableHead,
TableBody,
TableRow,
CircularProgress,
TableCell
} from '@ellucian/react-design-system/core';
import { useDispatch } from 'react-redux';
import { useHistory, useLocation } from '@ellucian/react-router-dom';
import SearchData from 'components/searchData/SearchData';
import ConfirmationDialogBox from
'components/confirmationDialog/ConfirmationDialogBox';
import AlertMessage from 'components/alertMessage/AlertMessage';
import DisplayErrorMessages from
'components/displayErrorMessage/DisplayErrorMessages';
import GenericReferenceDataColumns from
'components/referenceData/genericReferenceDataColumns/GenericReferenceDataColumns';
import ActionButton from 'components/actionButton/ActionButton';
import RowEditButton from 'components/referenceData/rowEditButton/RowEditButton';
import DeleteRowButton from
'components/referenceData/deleteRowButton/DeleteRowButton';
import ReferenceDataTableHeader from
'components/referenceData/referenceDataTableheader/ReferenceDataTableHeader';
import AidYearColumns from
'components/referenceData/aidYearColumns/AidYearColumns';
import { genericReferenceDataValueColumns } from
'page/referenceData/referenceDataValue/constant';
import { setRefAidYearList } from 'components/aidYearList/aidYearAction';
import { statusReducer, initialState } from '../reducer';
import { defaultResults, aidYearColumns } from './constant';
import { genericRefDatastyles, permRefDataCreate, permRefDataModify } from
'../constant';
import { handleFieldChange, handleAdd, handleAllEdit, handleCancel, handleEdit,
handleRowDelete } from '../helper';
import { postReferenceDataApi, putReferenceDataApi, getReferenceTableData } from
'../apiHandler';
import referenceDataApiService from 'service/referenceDataApiService';
import { apiStatus } from 'service/apiResponseStatus';
import { permission } from 'utils/permissions';
import { getCustomID, findDuplicateByIndex, findDuplicate } from 'utils/utils';

const AidYearPage = (props) => {


const {
intl,
classes: { card, loadingIndicator, table, refHeading, headerWidth },
userInfo,
userInfo: { locale, tenantId },
extensionInfo: {
configuration: { refDataApiUrl }
},
data: { getEllucianInternalJwt },
pageControl: { setErrorMessage, setExitPrompt }
} = props;

const history = useHistory();


const { pathname } = useLocation();
console.log(pathname, 'pathname')
const reduxDispatch = useDispatch();
const path = pathname.toString().split('/');
const tableName = path[7];
const title = getCustomID(path);
const customId = title.split(' ').join('');
const [tableData, setTableData] = useState([]);
const [results, setResults] = useState([defaultResults]);
const [checkValueChange, setCheckValueChange] = useState(false);
const [loader, setLoader] = useState(false);
const [state, dispatch] = useReducer(statusReducer, initialState);
const [updatedItemArray, setUpdatedItemArray] = useState([]);
const [alert, setAlert] = useState({ alertType: 'info', text: '',
autoHideDuration: null, open: false });
const [openConfirmationDialog, setOpenConfirmationDialog] = useState(false);
const [buttonLoader, setButtonLoader] = useState(false);
const canCreateRecord = permission(permRefDataCreate);
const canModifyRecord = permission(permRefDataModify);
const [isPreseededData, setIsPreseededData] = useState(false);

// state used to update the mergedAidYearColmnObj holding headers and styles, so


it is updated asynchronously when the component is rendered
const [mergedAidYearColmnObj, setMergedAidYearColmnObj] = useState([]);

const {
isCodeEditable,
hasUserMadeChanges,
isEditButtonDisabled,
saveClicked,
isSaveButtonDisplayed,
isSaveButtonDisabled,
isRowEditDisabled,
isConditonalAddDisabled,
validateRecordOnSave
} = state;

/**
* API call to get the reference table data
* Returns the response body which is used to populate the reference table data
* @async
* @param {boolean} isSortOrderAsc - The sorting order.
* @returns {*} response body
*/
const getTableData = async (isSortOrderAsc) => {
await getReferenceTableData(
tableName,
refDataApiUrl,
tenantId,
locale,
getEllucianInternalJwt,
setTableData,
setResults,
setLoader,
isSortOrderAsc
);
};

/**
* Event Handler to add the editable rows in the table when clicked on Add button
*/
const onClickAdd = () => {
handleAdd(dispatch, results, defaultResults, setResults, setTableData);
};

/**
* calling api with field values when table does not have any validation errors
* Returns the response body which is used to display the corresponding alert
message
* @param {*} api - api
*/
const updateReferenceDataValuesResponse = async (api) => {
const validation = results?.every((item) => {
return (
item?.description?.trim() &&
item.properties?.startDate &&
item.properties?.endDate &&
new Date(item.properties?.startDate) < new Date(item.properties?.endDate)
);
});
if (validation) {
if (updatedItemArray.length > 0) {
dispatch({ type: 'save' });
const putApiResponse = await putReferenceDataApi(
api,
refDataApiUrl,
tenantId,
updatedItemArray,
tableName,
locale,
getEllucianInternalJwt,
setAlert,
dispatch,
setLoader,
setUpdatedItemArray,
setCheckValueChange,
setButtonLoader,
intl
);
if (putApiResponse === apiStatus.success) {
getTableData(false);
}
} else {
setAlert({
alertType: 'info',
text: intl.formatMessage({ id: 'No-Changes-Error' }),
autoHideDuration: null,
open: true
});
results.forEach((res) => {
if (!res.isEdit) {
res.isEdit = true;
}
});
dispatch({ type: 'successResponse' });
}
} else {
dispatch({ type: 'helperText' });
}
};

/**
* calling api with field values when table does not have any validation errors
* Returns the response body which is used to display the corresponding alert
message
* @param {*} api - api
*/
const createReferenceDataValuesResponse = async (api) => {
const newRecords = [];
const validation = results.reduce((reducer, item) => {
// reducer - The reducer method is called on the results array with an
initial value of true
const {
isAdd,
code,
description,
properties,
properties: { startDate, endDate }
} = item;

if (isAdd) {
// push the newly added object and its attributes
newRecords.push({ code, description, properties });

// validate the object value


const value = code && description?.trim() && startDate && endDate && new
Date(startDate) < new Date(endDate);

// intially reducer is set to true


// when ever validation fails, value is set to false, the reducer is
updated to false
return value && reducer;
}

// when isAdd is false we will just return the current reducer value
return reducer;
}, true);

const foundDuplicateCode = findDuplicate(results, 'code');

if (validation && !foundDuplicateCode) {


dispatch({ type: 'save' });
const postApiResponse = await postReferenceDataApi(
api,
refDataApiUrl,
tenantId,
newRecords,
locale,
tableName,
getEllucianInternalJwt,
setAlert,
dispatch,
setLoader,
setUpdatedItemArray,
setCheckValueChange,
setButtonLoader,
'',
setErrorMessage,
intl
);
if (postApiResponse === apiStatus.success) {
getTableData(false);
}
} else {
dispatch({ type: 'helperText' });
}
};

/**
* Event handler to save the updated data
*
*/
const handleSave = () => {
if (hasUserMadeChanges) {
// edit record
if (!isCodeEditable) {

updateReferenceDataValuesResponse(referenceDataApiService.updateReferenceDataValues
);
}
// add record
else {

createReferenceDataValuesResponse(referenceDataApiService.createReferenceDataValues
);
}
}
};

const onClickEditAll = () => {


handleAllEdit(dispatch, results, setResults);
};

/**
* Updates the state of textfield values on change event
*
* @param {*} index - index of the object
*/

const onClickEdit = (index) => {


handleEdit(index, results, dispatch);
};

/**
* Event handler to display the dialog with Yes or No options, when updates are
made and not saved
*
*/
const onClickCancel = () => {
handleCancel(hasUserMadeChanges, saveClicked, setOpenConfirmationDialog,
history, checkValueChange);
};
const styleError = (spanStyle, saveButtonValidator, index) => {
const errRecords = results.filter((item) => {
const {
code,
description,
properties: { startDate, endDate },
styleErrMsg
} = item;
if (
code &&
!findDuplicateByIndex(results, 'code', index).length &&
description?.trim() &&
startDate &&
new Date(startDate) < new Date(endDate)
) {
item.styleErrMsg = null;
} else if (saveButtonValidator) {
item.styleErrMsg = <span className={spanStyle}></span>;
}
return styleErrMsg;
});
return saveButtonValidator && errRecords.length ? <span
className={spanStyle}></span> : null;
};

const handleChange = (index, event, field) => {


setCheckValueChange(true);
handleFieldChange(
index,
event,
field,
results,
updatedItemArray,
setUpdatedItemArray,
setResults,
setTableData,
isPreseededData,
setIsPreseededData
);
};

/**
* function is executed on click of cancel in ConfirmationDialogBox Box
*
* setLoader - state is used to display loader
* getTableData - function is used to get the table values
* dispatch - used to set the table values
* setCheckValueChange - state is used to check if there is any unsaved Changes
* setOpenConfirmationDialog - state used to close/open ConfirmationDialogBox Box
*/
const handleClose = () => {
setOpenConfirmationDialog(false);
setCheckValueChange(false);
setLoader(true);
getTableData(false);
dispatch({ type: 'successResponse' });
};
const onClickDelete = (index) => {
handleRowDelete(index, results, setResults, dispatch, setCheckValueChange);
};

/**
* Function to merge the AidYear columns with Generic columns
* Adding style object to each header
* set the state, so that the mergedAidYearColmnObj value is updated when
component is rendered asynchronously on UseEffect
*/
const applyAidYearHeaderStyleObj = () => {
const mergedResults = genericReferenceDataValueColumns.concat(aidYearColumns);
mergedResults.forEach((data) => {
data.styleObject = headerWidth;
});
setMergedAidYearColmnObj(mergedResults);
};

useEffect(() => {
setLoader(true);
getTableData(false);
applyAidYearHeaderStyleObj();
}, []);

useEffect(() => {
reduxDispatch(setRefAidYearList(results));
}, [results]);

useEffect(() => {
setExitPrompt(checkValueChange);
}, [checkValueChange]);

return (
<React.Fragment>
<AlertMessage
alertType={alert.alertType}
id={`${customId}_Alert`}
open={alert.open}
autoHideDuration={alert.autoHideDuration}
text={alert.text}
userInfo={userInfo}
setAlert={setAlert}
/>
<Typography variant="h2" id={`${customId}_Title`} className={refHeading}>
{title}
</Typography>
{!loader && (
<Grid container spacing={20}>
<Grid item xs={6} sm={6}>
<SearchData
id={`${customId}_searchBox`}
tableData={tableData}
searchResults={results}
setSearchResults={setResults}
placeholder={intl.formatMessage({ id: 'Search-Label' })}
searchKey="code"
disabled={hasUserMadeChanges}
/>
</Grid>
<ActionButton
intl={intl}
customId={customId}
isSaveButtonDisplayed={isSaveButtonDisplayed}
handleCancel={onClickCancel}
canModifyRecord={canModifyRecord}
isSaveButtonDisabled={isSaveButtonDisabled}
canCreateRecord={canCreateRecord}
handleSave={handleSave}
handleAllEdit={onClickEditAll}
results={results}
isEditButtonDisabled={isEditButtonDisabled}
isAddButtonDisabled={isConditonalAddDisabled}
handleAdd={onClickAdd}
addText="Create-Aid-Year"
buttonLoader={buttonLoader}
/>
</Grid>
)}
{!loader ? (
<Card className={card}>
<Table stickyHeader id={`${customId}_Table`} className={table}>
<TableHead>
<TableRow>
<ReferenceDataTableHeader customId={customId} intl={intl}
tableHeadersObject={mergedAidYearColmnObj} />
<TableCell></TableCell>
</TableRow>
</TableHead>
{results?.length ? (
<TableBody>
{results?.map((refDataValue, index) => {
const {
code,
isAdd,
isEdit,
codeEdit,
description,
properties,
properties: { startDate, endDate }
} = refDataValue;
return (
<TableRow key={`${customId}_Key_${index}`}>
<GenericReferenceDataColumns
customId={customId}
refDataValue={refDataValue}
state={state}
intl={intl}
results={results}
handleChange={handleChange}
index={index}
styleError={styleError}
codeFieldType="number"
/>
<AidYearColumns
customId={customId}
results={results}
setResults={setResults}
userInfo={locale}
handleChange={handleChange}
setTableData={setTableData}
updatedRefDataValues={{ code, isEdit, codeEdit,
description, properties }}
state={state}
index={index}
intl={intl}
styleError={styleError}
/>

{/* isAdd is used to check whether data is on create flow


with new row's inserted */}
{isAdd ? (
<DeleteRowButton
index={index}
results={results}
setResults={setResults}
dispatch={dispatch}
onClickDelete={onClickDelete}
isDeleteButtonDisabled={isSaveButtonDisabled}
/>
) : (
<RowEditButton
intl={intl}
customId={customId}
canModifyRecord={canModifyRecord}
isRowEditDisabled={isRowEditDisabled}
onClickEdit={onClickEdit}
results={results}
updatedDescription={{ code, isEdit, codeEdit,
description, properties }}
atLeastOneErrorExists={Boolean(
validateRecordOnSave &&
(!code || !description || !startDate || !endDate ||
startDate >= endDate)
)}
/>
)}
</TableRow>
);
})}
</TableBody>
) : (
<TableBody>
<TableRow id="NoResults">
<DisplayErrorMessages userInfo={userInfo} />
</TableRow>
</TableBody>
)}
</Table>
{openConfirmationDialog && (
<ConfirmationDialogBox
open={openConfirmationDialog}
setOpen={setOpenConfirmationDialog}
userInfo={userInfo}
intl={intl}
closeHandler={handleClose}
displayCardText={intl.formatMessage({ id: 'Confirmation-Dialog-
TextField' })}
/>
)}
</Card>
) : (
<CircularProgress className={loadingIndicator} />
)}
</React.Fragment>
);
};

AidYearPage.propTypes = {
intl: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
userInfo: PropTypes.object.isRequired,
extensionInfo: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
pageControl: PropTypes.object.isRequired
};

export default withIntl(withStyles(genericRefDatastyles)(AidYearPage));

You might also like