/* eslint-disable operator-assignment */
import React, { useState, useEffect, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getGamesSettings, getCashoutNumberofSlots } from '../../selectors/settings';
import { PERMISSIONS, GAMES_SETTINGS_FIELDS, SVG_ICONS, CONFIRM_MODAL_CONTENT, MODALS } from '../../constants';
import { checkPermisson, getCurrency, getActiveModal, getNavigatedLink, isFetched } from '../../selectors/common';
import SVGComponent from '../../components/SVG/SVGComponent';
import { parsePoints, parseDecimal } from '../../utils/common';
import validateFields from '../../utils/validation';
import ConfirmationModal from '../../components/Modals/ConfirmationModal';
import { aSetNavigationInfo, aSetOpenModal } from '../../reducers/common';
import { aGetGamesSettings } from '../../reducers/settings';
import { aSaveGamesSettings } from '../../reducers/actions';
import { withRouterHooks } from '../../utils/router';
/**
* @class
* @param navigate
* @param navigate.navigate
* @property {object} props
* @property {Array} gamesSettings All games settings data
* @property {boolean} checkPermisson Check if user have
* permission for specific action or view
* @property {string} currency Currency sign
* @property {string} activeModal Active modal
* @property {object} modalData Modal data
* @property {Function} getGamesSettings Call API for games settings
* @property {Function} saveLeaguesSettings Call API to save changes
* @property {Function} setOpenModal Open modal
* @property {object} nextNavigationLink link for navigation
* @property {Function} setNavigationInfo set navigation info for unsaved data
*/
const GamesSettings = ({ navigate }) => {
/**
* @member {object}
* @property {boolean} hasChanges
* @property {object|null} currentData
* @property {Array|null} multipleBonusPercentages
* @property {object} errorMgs
*/
const [state, setState] = useState({
hasChanges: false,
currentData: null,
multipleBonusPercentages: null,
errorMgs: {},
});
const dispatch = useDispatch();
const editPermission = useSelector((stateSelector) => checkPermisson(stateSelector, PERMISSIONS.GAMES_SETTINGS_EDIT));
const gamesSettings = useSelector(getGamesSettings);
const currency = useSelector(getCurrency);
const activeModal = useSelector(getActiveModal);
const nextNavigationLink = useSelector(getNavigatedLink);
const isFetchedSelector = useSelector(isFetched);
const cashoutNumberofSlots = useSelector(getCashoutNumberofSlots);
/**
* Get game settings data
*
* @returns {void}
*/
useEffect(() => {
dispatch(aGetGamesSettings());
return () => {
dispatch(aSetNavigationInfo({}));
};
}, [dispatch]);
/**
* Load original data
*
* @function
* @returns {void}
*/
const refresh = useCallback(() => {
const currentData = { ...gamesSettings };
GAMES_SETTINGS_FIELDS.AmtLimitSettings.map((field) => {
currentData[field.code] = currentData[field.code] / 100;
});
currentData.cashoutNumberOfSlotsList = cashoutNumberofSlots;
setState((prevState) => ({
...prevState,
hasChanges: false,
currentData,
multipleBonusPercentages: [...gamesSettings.multipleBonusPercentages],
errorMgs: {},
}));
}, [gamesSettings, cashoutNumberofSlots]);
/**
* Update current data when api is fetched
* Update check for modified fields
*
* @function
* @param {object} prevProps
* @param {object} prevState
* @returns {void}
*/
useEffect(() => {
if (state.hasChanges === false && gamesSettings && state.currentData === null) {
refresh();
}
}, [gamesSettings, state.hasChanges, refresh, state.currentData]);
useEffect(() => {
dispatch(aSetNavigationInfo({ hasFieldDataModified: state.hasChanges }));
}, [state.hasChanges, dispatch]);
/**
* Handle change on inputs
*
* @function
* @param {object} field
* @param {Event} e
* @returns {void}
*/
const handleInputChange = (field, e) => {
const value = e.target.value;
const temp = state.currentData;
temp[field.code] = field.getValue(value);
setState((prevState) => ({
...prevState,
currentData: temp,
hasChanges: true,
}));
};
/**
* Handle change on multiple bonus inputs
*
* @function
* @param {Event} e
* @returns {void}
*/
const handleMultipleBonusChange = (e) => {
const multipleBonusPercentages = [...state.multipleBonusPercentages];
const code = parseInt(e.target.id);
const value = e.target.value;
multipleBonusPercentages[code] = parseInt(value.replace(/[,.]/g, '')) || 0;
setState((prevState) => ({
...prevState,
hasChanges: true,
multipleBonusPercentages,
}));
};
/**
* Save process - validation before save
*
* @function
*/
// eslint-disable-next-line sonarjs/cognitive-complexity
const saveProcess = () => {
const validationParams = [];
const currentData = {};
Object.keys(GAMES_SETTINGS_FIELDS).map((objectKey) => {
GAMES_SETTINGS_FIELDS[objectKey].map((key) => {
validationParams.push({
label: key.name,
fieldName: key.code,
// eslint-disable-next-line max-len
value: key.isBoostPercentage
? Number(parseDecimal(state?.[objectKey]?.[key.code], 100))
: key.float
? parseDecimal(state.currentData[key.code], 100)
: state.currentData[key.code] || 0,
rules: key.validationRules,
comparatorValue: key.comparatorValue ?? state.currentData.couponMinStake,
comparatorMaxValue:
key.code === 'matchLength'
? state.currentData.roundInterval - key.comparatorMinValue
: key.comparatorMaxValue,
minValue: key.minValue ?? state.currentData.couponMinStake,
});
currentData[key.code] = key.currency ? state.currentData[key.code] * 100 : state.currentData[key.code];
});
});
validateFields(validationParams)
.then(() => {
currentData.multipleBonusPercentages = state.multipleBonusPercentages;
dispatch(aSaveGamesSettings(currentData));
setState((prevState) => ({
...prevState,
hasChanges: false,
errorMgs: {},
}));
})
.catch((errorMgs) => {
setState((prevState) => ({
...prevState,
errorMgs,
}));
})
.finally(() => {
if (activeModal === MODALS.SAVE_BEFORE_NAVIGATION && Object.keys(state.errorMgs).length) {
dispatch(aSetOpenModal({ modal: '' }));
dispatch(aSetNavigationInfo({ hasFieldDataModified: state.hasChanges }));
}
});
};
/**
* @function
* @param {object} validationParams
* @returns {object}
*/
const processErrorMessages = (validationParams) => {
const errorMessages = { ...state.errorMgs };
const fieldName = validationParams[0]?.fieldName;
delete errorMessages[fieldName];
return errorMessages;
};
const validateInputField = (fieldName, key) => {
const validationParams = [];
const validationParam = GAMES_SETTINGS_FIELDS[key]?.find((param) => param.name === fieldName);
if (validationParam) {
validationParams.push({
label: validationParam.name,
fieldName: validationParam.code,
// eslint-disable-next-line max-len
value: validationParam.isBoostPercentage
? Number(parseDecimal(state?.[key]?.[validationParam.code], 100))
: validationParam.float
? parseDecimal(state.currentData[validationParam.code], 100)
: state.currentData[validationParam.code] || 0,
rules: validationParam.validationRules,
comparatorValue: validationParam.comparatorValue ?? state.currentData.couponMinStake,
comparatorMaxValue:
validationParam.code === 'matchLength'
? state.currentData.roundInterval - validationParam.comparatorMinValue
: validationParam.comparatorMaxValue,
minValue: validationParam.minValue ?? state.currentData.couponMinStake,
});
}
validateFields(validationParams)
.then(() => {
const updatedErrorMessages = processErrorMessages(validationParams);
setState((prevState) => ({
...prevState,
errorMgs: updatedErrorMessages,
}));
})
.catch((errorMgs) => {
setState((prevState) => ({
...prevState,
errorMgs: {
...prevState.errorMgs,
...errorMgs,
},
}));
});
};
/**
* Render all imput settings fields
*
* @function
* @param {object} field
* @param {string} key
* @returns {view}
*/
// eslint-disable-next-line sonarjs/cognitive-complexity
const renderInputSettings = (field, key) => {
if (state.currentData) {
const value = field.currency
? parsePoints(state.currentData[field.code], ',')
: field.decimal
? parseDecimal(state.currentData[field.code], 1)
: field.float
? parseDecimal(state.currentData[field.code], 100)
: state.currentData[field.code] &&
// eslint-disable-next-line no-restricted-globals
!isNaN(parseInt(state.currentData[field.code]))
? parseInt(state.currentData[field.code])
: 0;
return (
<div className="user__settings-row" key={field.name}>
<div>
<div className="d-flex justify-content-end">
<div className="user__settings-txt">{field.name}</div>
<div className="user__settings-icon ml-20">
<SVGComponent className="icon-info" src={`${SVG_ICONS.utility}#info`} />
<div className="user__settings-icon-info">{field.info}</div>
</div>
</div>
</div>
<div className={`user__settings-input-wrap ${state.errorMgs[field.code] && 'error'}`}>
{field.isSelectField ? (
<select
value={value}
disabled={field.disabled || ''}
className="user__settings-input"
onChange={handleInputChange.bind(this, field)}
>
{state.currentData[field.codeSelectField]?.map((valueOption) => (
<option key={`option-${valueOption?.id}`} value={valueOption?.id}>
{valueOption?.label}
</option>
))}
</select>
) : (
<input
id={`input-${field.name}`}
className="user__settings-input"
value={value}
onChange={handleInputChange.bind(this, field)}
name={field.name}
onBlur={validateInputField.bind(this, field.name, key)}
/>
)}
{field.currency === true ? <div className="currency">{currency}</div> : null}
{state.errorMgs[field.code]}
</div>
<div className="user__settings-info">{field.desc?.replace('%s', currency)}</div>
</div>
);
}
return null;
};
/**
* Render multiple boost input
*
* @function
* @returns {view}
*/
const renderMultipleBoost = () => {
const col = [];
let index = 0;
const multipleBonusPercentages = state.multipleBonusPercentages;
if (multipleBonusPercentages) {
for (let i = 0; i < 20; i += 5) {
const row = [];
for (let j = 0; j < 5; j += 1) {
index = i + j;
const value = parseDecimal(multipleBonusPercentages[index], 100);
row.push(
<div className="user__settings-row" key={index} disabled={index === 0}>
<div className="text-right">{index + 1}</div>
<div className={`user__settings-input-wrap sm ${state.errorMgs[index] !== undefined ? 'error' : ''}`}>
<input
id={index}
name={`multipleBoostPercentage-${index}`}
className="user__settings-input"
value={value}
onChange={handleMultipleBonusChange}
onBlur={validateInputField.bind(this, `Percentage [${index + 1}]`, 'multipleBonusPercentages')}
/>
{state.errorMgs?.[index] !== undefined && state.errorMgs[index]}
</div>
<div>%</div>
</div>
);
}
col.push(
<div className="col-sm-3" key={i}>
{row}
</div>
);
}
}
return <div className="row mt-30 no-gutters">{col}</div>;
};
/**
* Render buttons
*
* @returns {view}
*/
const renderButtons = () => (
<div className="d-flex justify-content-end mt-20">
<button
id="gameSettings_save"
type="button"
className="btn btn-primary"
onClick={saveProcess}
disabled={!state.hasChanges}
>
Save
</button>
<button
id="gameSettings_cancel"
type="button"
className="btn btn-secondary ml-3"
onClick={refresh}
disabled={!state.hasChanges}
>
Cancel
</button>
</div>
);
/**
* close modal and navigate
*
* @returns {void}
*/
const unsavedNavigation = () => {
dispatch(aSetOpenModal({ modal: '' }));
navigate(nextNavigationLink);
};
/**
* Render
*
* @returns {view}
*/
if (isFetchedSelector) {
return (
<>
<div className="user mt-20">
<div className="d-flex">
<div className="user__settings">
<div className="user__settings-title">Time settings</div>
<hr className="line-spacer" />
<div className="user__settings-wrap">
{GAMES_SETTINGS_FIELDS.TimeSettings.map((field) => renderInputSettings(field, 'TimeSettings'))}
</div>
</div>
<div className="user__settings ml-20">
<div className="user__settings-title">Cashout settings</div>
<hr className="line-spacer" />
<div className="user__settings-wrap" />
{GAMES_SETTINGS_FIELDS.CashoutSettings.map((field) => renderInputSettings(field, 'CashoutSettings'))}
</div>
</div>
<div className="user__settings mt-20">
<div className="user__settings-title">Amount limit settings</div>
<hr className="line-spacer" />
<div className="user__settings-wrap">
{GAMES_SETTINGS_FIELDS.AmtLimitSettings.map((field) => renderInputSettings(field, 'AmtLimitSettings'))}
</div>
</div>
<div className="user__settings mt-20">
<div className="user__settings-title">Bonus Settings</div>
<hr className="line-spacer my-4" />
<div className="user__settings-wrap">
{GAMES_SETTINGS_FIELDS.BonusSettings.map((field) => renderInputSettings(field, 'BonusSettings'))}
<hr className="line-spacer my-4" />
<div className="d-flex justify-content-between align-items-center">
<div className="user__settings-title">Multiple Boost percentages</div>
<div className="user__settings-icon">
<SVGComponent className="icon-info" src={`${SVG_ICONS.utility}#info`} />
<div className="user__settings-icon-info">
Bonus amount percentage for number of events in a coupon.
</div>
</div>
</div>
{renderMultipleBoost()}
</div>
</div>
<hr className="line-spacer" />
{editPermission ? renderButtons() : null}
</div>
{activeModal === MODALS.SAVE_BEFORE_NAVIGATION ? (
<ConfirmationModal
modalData={CONFIRM_MODAL_CONTENT.SAVE_CHANGES}
cancel={unsavedNavigation}
confirmation={saveProcess}
cancelBtnText="Cancel"
confirmBtnText="Save"
logo
/>
) : null}
</>
);
}
return null;
};
export default withRouterHooks(GamesSettings);