webapp/pages/feedback/buyer/index.js

import React, {useState} from 'react';
import {styled} from '@mui/material/styles';
import {Meteor} from 'meteor/meteor';
import Groups from '/common/db/group';
import Visits from '/common/db/visit';
import Units from '/common/db/unit';
import Projects from '/common/db/project';
import BuyerUnits from '/common/db/buyerUnit';
const {ShowAssigneeDetails} = require('/components/generic/ShowAssigneeDetails');
import {Config} from '/config';
import {withTracker} from '/common/components/withTracker';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
import format from '/common/utils/date/toStr';
import queryStringToObjectUsingQS from '/common/utils/queryStringToObjectUsingQS';
import projectAmenities from '/common/info/project/amenities';
import feedbackBuyerGeneral from '/common/info/feedback/buyer/general';
import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import {Formik, Form} from 'formik';
import Input from '/components/forms/Input';
import FeedBack from './feedback';
import getName from '/common/utils/info/getName';
import FRating from './FRating';
import PropTypes from 'prop-types';
import getIndPhonesOrEmails from '/common/utils/getIndPhonesOrEmails';

const PREFIX = 'BuyerFeedback';

const classes = {
	container: `${PREFIX}-container`,
	itemContainer: `${PREFIX}-itemContainer`,
	tWhite: `${PREFIX}-tWhite`,
	tRed: `${PREFIX}-tRed`,
	generic: `${PREFIX}-generic`,
	paperGeneric: `${PREFIX}-paperGeneric`,
	paperUnit: `${PREFIX}-paperUnit`,
	alignCenter: `${PREFIX}-alignCenter`,
	margins: `${PREFIX}-margins`,
	lineHeightGrid: `${PREFIX}-lineHeightGrid`,
	toggleButton: `${PREFIX}-toggleButton`,
	formControl: `${PREFIX}-formControl`,
};

const StyledGrid = styled(Grid)((
	{
		theme,
	},
) => ({
	padding: '1rem 0rem',
	[`& .${classes.container}`]: {
		paddingTop: '2rem',
	},
	[`& .${classes.itemContainer}`]: {
		backgroundColor: '#e0454b',
		height: 'auto',
	},
	[`& .${classes.tWhite}`]: {
		color: 'white',
	},
	[`& .${classes.tRed}`]: {
		color: 'red',
	},
	[`& .${classes.generic}`]: {
		padding: '0.25rem',
	},
	[`& .${classes.paperUnit}`]: {
		margin: '0.5rem 0rem',
	},
	[`& .${classes.alignCenter}`]: {
		display: 'flex',
		justifyContent: 'flex-end',
		[theme.breakpoints.down('md')]: {
			display: 'flex',
			justifyContent: 'center',
		},
	},
	[`& .${classes.margins}`]: {
		marginTop: '0rem !important',
		marginLeft: '0rem',
	},
	[`& .${classes.lineHeightGrid}`]: {
		marginBottom: '1.0rem !important',
	},
	[`& .${classes.toggleButton}`]: {
		padding: '0.3rem 2.1rem',
		marginBottom: '1rem',
		fontSize: 'inherit',
		[theme.breakpoints.down('md')]: {
			padding: '0.6rem 3.2rem',
			width: '50%',
		},
	},
	[`& .${classes.formControl}`]: {
		[theme.breakpoints.down('md')]: {
			width: '100%',
			height: '3rem',
		},
	},
}));

const defaultOptions = [{value: 'yes', label: 'YES'}, {value: 'no', label: 'NO'}];

/**
 * This function categorizes the amenities.
 * @param {array} amenities -array of amenities available in unit.
 * @returns {Set} ret -set of amenities name or their category.
 */
const checkAmenities = (amenities) => {
	let ret = [];
	amenities.map(a => {
		let cname = getName(projectAmenities, a.name, 'category');
		cname = cname === a.name ? getName(projectAmenities, a.name, 'name') : cname;
		ret.addToSet(cname);
	});
	return ret;
};
function BuyerFeedbackForm(props) {
	const {fullData, updateFeedbackSubmitted, visitIds} = props;
	const topRef = React.useRef(null);
	const scrollToTop = function() {
		if (topRef.current) topRef.current.scrollIntoView(false);
	};
	return (
		<StyledGrid container item xs={12}>
			<Formik initialValues={fullData}>
				{({values}) => (
					<Form autoComplete='off' id='buyer-feedback'>
						<Paper className={classes.paperGeneric} elevation={0} ref={topRef} variant='outlined'>
							<Grid container justifyContent='space-around'>
								<Grid item xs={11}>
									<Grid container justifyContent='space-between' spacing={0}>
										<Grid item lg={8} md={8} sm={8} xs={12}>
											<Typography className={classes.lineHeightGrid}>
												{`Please rate your ${((fullData.unitsVisited || []).length - 1) > 1 ? 'visits' : 'visit'} completed on ${((fullData.unitsVisited || [])[0] || {}).completedOn}: `}
											</Typography>
										</Grid>
										<Input
											FormControlProps={{className: classes.formControl}}
											base
											className={classes.formControl}
											components={{input: FRating}}
											container={{xs: 12, sm: 4, md: 4, lg: 3, className: classes.alignCenter}}
											name='visitRating'
										/>
									</Grid>
									<Grid container justifyContent='space-between' spacing={0}>
										<Grid item lg={8} md={8} sm={8} xs={12}>
											<Typography className={classes.lineHeightGrid}>
												{feedbackBuyerGeneral.visitPunctuality}
											</Typography>
										</Grid>
										<Input
											FormControlProps={{className: classes.formControl}}
											ToggleButtonProps={{className: classes.toggleButton}}
											className={classes.formControl}
											container={{xs: 12, sm: 4, md: 4, lg: 3, className: classes.alignCenter}}
											fast={false}
											name='visitPunctuality'
											options={defaultOptions}
											required
											type='buttons'
										/>
									</Grid>
								</Grid>
							</Grid>
						</Paper>
						<Paper className={classes.paperUnit} elevation={0} id='unit-specific-feedBack' variant='outlined'>
							<FeedBack
								defaultValue={fullData}
								length={values.unitsVisited.length}
								scrollToTop={scrollToTop}
								unitsVisited={values.unitsVisited}
								updateFeedbackSubmitted={updateFeedbackSubmitted}
								visitIds={visitIds}
							/>
						</Paper>
					</Form>
				)}
			</Formik>
		</StyledGrid>
	);
}
BuyerFeedbackForm.propTypes = {
	fullData: PropTypes.object,
	updateFeedbackSubmitted: PropTypes.func,
	visitIds: PropTypes.array,
};
// eslint-disable-next-line react/no-multi-comp
function BuyerVisitFeedbackWrapper(props) {
	const [feedbackSubmitted, setfeedbackSubmitted] = useState(false);
	const {data, user} = props;
	if (!data) return null;
	return (
		<StyledGrid container justifyContent='space-around'>
			<Grid item lg={8} md={9} xs={12}>
				<Grid className={classes.container} item xs={12}>
					<Grid alignItems='center' className={classes.itemContainer} container direction='row' justifyContent='space-around'>
						<Grid item lg={4} md={8} xs={10}>
							<Typography align='center' className={classes.tWhite} variant='h4'>
								Help Us Improve
							</Typography>
						</Grid>
					</Grid>
					<Grid className={classes.generic} container justifyContent='space-around'>
						{data.linkExpired ? (
							<Grid item xs={12}>
								<Typography align='center' className={classes.tRed} style={{marginTop: '2rem'}}>
									Sorry! This link is either invalid or expired.
								</Typography>
							</Grid>
						)
							: (
								<>
									{Boolean(user._id && user._id !== data.individual._id) && (
										<Grid item lg={10} md={10} xs={10}>
											<Typography align='center' className={classes.tRed} variant='subtitle1'>
												You are not logged in with that user on which this link was sent. Anyhow you can proceed but you will not be able to see that user&apos;s contents, kindly logout and login.
											</Typography>
										</Grid>
									)}
									{feedbackSubmitted ? (
										<Paper elevation={0} style={{width: '100%'}} variant='outlined'>
											<Grid item xs={12}>
												<Typography align='center' style={{margin: '5rem 0rem 3rem', color: 'green'}} variant='h5'>
													Thank You! For Your Valuable FeedBack.
												</Typography>
											</Grid>
											<Grid item style={{textAlign: 'center', marginBottom: '2rem'}} xs={12}>
												<Link to='/dashboard'>
													{' '}
													<Typography align='center' style={{textDecoration: 'underline'}} variant='caption'>
														Go To Your Dashboard
													</Typography>
												</Link>
											</Grid>
										</Paper>
									)
										: (
											<Grid item xs={12}>
												<BuyerFeedbackForm fullData={data} initialValues={data} updateFeedbackSubmitted={(flag) => { setfeedbackSubmitted({flag}); }} visitIds={data.visitIds}/>
											</Grid>
										)}
									<ShowAssigneeDetails
										assignees={data.assignees}
										className='left-align shadow margin-bottom-1'
										relationship='buyer'
									/>
								</>
							)}
					</Grid>
				</Grid>
			</Grid>
		</StyledGrid>
	);
}
BuyerVisitFeedbackWrapper.propTypes = {
	data: PropTypes.object,
	user: PropTypes.object,
};
const BuyerVisitFeedbackWithTracker = withTracker(({location, setFeedbackPageData}) => {
	if (Meteor.isClient) return {};

	let urlParams = queryStringToObjectUsingQS(location.search), data = {linkExpired: true};
	if (Object.keys(urlParams || {}).length < 2) {
		setFeedbackPageData({type: 'FEEDBACK__SET_DATA', data});
		return {data};
	}
	let group = Groups.findOne({_id: urlParams.gId}, {fields: {members: 1, relationship: 1, name: 1, identifier: 1}});
	if (!group) {
		setFeedbackPageData({type: 'FEEDBACK__SET_DATA', data});
		return {data};
	}
	const {default: Assignees} = require('/common/db/assignees');
	let assignees = Assignees.find({'doc.collection': 'buyers', 'doc.id': group._id}, {fields: {userIds: 1}}).fetch();
	group.assignees = assignees;
	let individualIndex = group.members.indexOfKey('decisionMaker', 'designation');
	if (!~individualIndex) {
		setFeedbackPageData({type: 'FEEDBACK__SET_DATA', data});
		return {data};
	}
	let indId = group.members[individualIndex].id;
	let ind = Meteor.users.findOne({_id: indId, 'authTokens.feedBack': {$elemMatch: {token: urlParams.token, expiresAt: {$gt: new ServerDate()}}}}, {fields: {'profile.name': 1, identifier: 1, 'profile.emails': 1, emails: 1, phones: 1, 'profile.phones': 1, authTokens: 1}});
	let linkExpired;
	if (!ind) {
		setFeedbackPageData({type: 'FEEDBACK__SET_DATA', data});
		return {data};
	}
	let phoneOptions = getIndPhonesOrEmails(ind, 'phones', true).map(p => ({value: p.number, label: p.number}));
	let tokenIndex = ind.authTokens.feedBack.indexOfKey(urlParams.token, 'token');
	let visitIds = ind.authTokens.feedBack[tokenIndex].visitIds;

	let visits = Visits.find({_id: {$in: visitIds}}, {fields: {assignedTo: 1, unitId: 1, end: 1, relationship: 1, process: 1, buyerUnitId: 1}}).fetch();

	data = {visitIds: visitIds, visitRating: (urlParams.visitRating && !isNaN(urlParams.visitRating)) ? parseInt(urlParams.visitRating, 10) : 0, visitPunctuality: '', unitsVisited: [], group, individual: ind, assignees, linkExpired, urlParams, remark: ''};
	const unitField = {
		builtUpArea: 1, 'cancellation.flag': 1, 'cancellation.status': 1, configId: 1, constructedOn: 1, cover: 1, facing: 1, floorplans: 1, plotArea: 1, project: 1, propertyType: 1, 'location.gps': 1, 'location.locality': 1, sample: 1, 'saleInfo.price': 1, 'saleInfo.group.id': 1, slug: 1, 'spaces.bedrooms.number': 1, 'spaces.bathrooms.number': 1, 'cancellation.temporary': 1, 'sale.status': 1, identifier: 1, 'assignedTo.userId': 1,
	};

	const {default: Feedback} = require('/common/db/feedback');
	const feedback = Feedback.findOne({'visits.id': {$in: visitIds}});

	visits.map(function(v) {
		const unit = Units.findOne({_id: v.unitId}, {fields: unitField});
		const buyerUnit = BuyerUnits.findOne({_id: v.buyerUnitId}, {fields: {cancellation: 1, processDetails: 1}});

		let amenitiesAvailable = [];
		if ((unit.project || {}).id) {
			const project = Projects.findOne({_id: unit.project.id}) || {};
			if ((project.amenities || []).length) amenitiesAvailable = checkAmenities(project.amenities);
		}
		const visit = feedback?.visits?.find(m => m.id === v._id) || {};

		data.unitsVisited.push({unitAccToReq: visit ? visit.unitAccToReq : '', unitQueryResolved: visit ? visit.unitQueryResolved : '', amenitiesTour: visit ? visit.amenitiesTour : '', locationAssistance: visit ? visit.locationAssistance : '', basicInfoExplained: visit ? visit.basicInfoExplained : '',
			type: v.process, unit, unitDecision: visit ? visit.unitDecision : buyerUnit.cancellation.flag ? 'unitCancelled' : buyerUnit.processDetails[3].status, amenitiesAvailable, id: v._id, unitId: v.unitId, completedOn: format(v.end, Config.dateFormats.short), buyerUnitId: v.buyerUnitId, laision: v.assignedTo, projectId: (unit.project || {}).id,
		});
	});
	//pushing extra parameter for last slider of phoneverification
	data.unitsVisited.push({phoneOptions, expectedUserId: ind._id, unit: {}, remark: ''}); //unit is required due to ssr issue with imagegallery
	setFeedbackPageData({type: 'FEEDBACK__SET_DATA', data});
	return {data};
})(BuyerVisitFeedbackWrapper);

const {setFeedbackPageData} = require('/redux/actions');

export default connect(({feedbackPageData}) => feedbackPageData, dispatch => ({setFeedbackPageData(data) { dispatch(setFeedbackPageData(data)); }}))(BuyerVisitFeedbackWithTracker);