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'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);