refactor: Feedback CustomerDetail

feature/price_manager
Lei OT 1 year ago
parent c7f29f4999
commit 6ad342cb2e

@ -51,8 +51,8 @@ const router = createBrowserRouter([
{ path: "account/change-password", element: <ChangePassword />}, { path: "account/change-password", element: <ChangePassword />},
{ path: "account/profile", element: <AccountProfile />}, { path: "account/profile", element: <AccountProfile />},
{ path: "feedback", element: <FeedbackIndex />}, { path: "feedback", element: <FeedbackIndex />},
{ path: "feedback/:GRI_SN/:RefNo", element: <FeedbackDetail />},
{ path: "feedback/:GRI_SN/:CII_SN/:RefNo", element: <FeedbackCustomerDetail />}, { path: "feedback/:GRI_SN/:CII_SN/:RefNo", element: <FeedbackCustomerDetail />},
{ path: "feedback/:GRI_SN/:RefNo", element: <FeedbackDetail />},
{ path: "report", element: <ReportIndex />}, { path: "report", element: <ReportIndex />},
{ path: "notice", element: <NoticeIndex />}, { path: "notice", element: <NoticeIndex />},
{ path: "notice/:CCP_BLID", element: <NoticeDetail />}, { path: "notice/:CCP_BLID", element: <NoticeDetail />},

@ -9,6 +9,62 @@ import { devtools } from 'zustand/middleware';
const { HT_HOST } = config; const { HT_HOST } = config;
export const getFeedbackDetail = async (VEI_SN, GRI_SN) => {
const { errcode, Result, Result1 } = await fetchJSON(`${HT_HOST}/service-Cooperate/Cooperate/getFeedbackDetail`, { VEI_SN, GRI_SN });
return errcode !== 0 ? {} : { feedbackRate: Result, feedbackReview: Result1 };
};
export const getCustomerFeedbackDetail = async (VEI_SN, GRI_SN, CII_SN) => {
const json = await fetchJSON(`${HT_HOST}/service-CooperateSOA/get_feedback_service_item`, { VEI_SN, GRI_SN, city_sn: CII_SN, lgc: 1 });
const _feedbackItemList = json.feedbackItemList;
const itemGroup = groupBy(json.feedbackItemList, 'type');
const serviceItem = {
HWO_Guide: itemGroup?.W || [],
HWO_Driver: itemGroup?.Y || [],
HWO_Activity: [
...(itemGroup['7'] || []),
...(itemGroup.G || []),
...(itemGroup.C || []),
...(itemGroup.A || []).map((ele) => ({ ...ele, Describe: ele.name })),
],
};
const OtherThoughts = json.feedbackEvaluation[0]?.otherComments || '';
const PhotoPermission = json.feedbackEvaluation[0]?.usePhotos || '';
const signatureData = json.feedbackEvaluation[0]?.signatureDataUrl || '';
const cityName = json.group[0]?.cityName || '';
return { ...serviceItem, OtherThoughts, PhotoPermission, signatureData, cityName }; // feedbackServiceRate
};
export const getFeedbackImages = async (VEI_SN, GRI_SN) => {
const { errcode, result } = await fetchJSON(`${HT_HOST}/service-fileServer/ListFile`, { VEI_SN, GRI_SN });
return errcode !== 0 ? {} : result.map((data, index) => {
return {
uid: -index, //用负数,防止添加删除的时候错误
name: data.file_name,
status: "done",
url: data.file_url,
};
});
};
export const removeFeedbackImages = async (fileurl) => {
const { errcode, Result } = await fetchJSON(`${HT_HOST}/service-fileServer/FileDelete`, { fileurl });
return errcode !== 0 ? {} : Result;
};
export const getFeedbackInfo = async (VEI_SN, GRI_SN) => {
const { errcode, Result } = await fetchJSON(`${HT_HOST}/service-Cooperate/Cooperate/getVEIFeedbackInfo`, { VEI_SN, GRI_SN });
return errcode !== 0 ? {} : Result;
};
export const postFeedbackInfo = async (VEI_SN, GRI_SN, EOI_SN, info_content) => {
const postbody = { VEI_SN, GRI_SN, EOI_SN, FeedbackInfo: info_content };
const formData = new FormData();
Object.keys(postbody).forEach(key => {
formData.append(key, postbody[key]);
});
const { errcode, Result } = await postForm(`${HT_HOST}/service-CooperateSOA/FeedbackInfo`, formData);
return errcode !== 0 ? {} : Result;
};
const initialState = { const initialState = {
loading: false, loading: false,
search_date_start: dayjs().subtract(2, 'M').startOf('M'), search_date_start: dayjs().subtract(2, 'M').startOf('M'),
@ -37,6 +93,7 @@ const useFeedbackStore = create(
async fetchFeedbackList(veisn, EOI_Group_Name, TimeStart, TimeEnd) { async fetchFeedbackList(veisn, EOI_Group_Name, TimeStart, TimeEnd) {
const { setLoading, setFeedbackList } = get(); const { setLoading, setFeedbackList } = get();
setLoading(true);
const searchParams = { const searchParams = {
PageSize: 2000, PageSize: 2000,
PageIndex: 1, PageIndex: 1,

@ -70,8 +70,8 @@ export function fetchText(url) {
export function fetchJSON(url, data = {}) { export function fetchJSON(url, data = {}) {
const initParams = getRequestInitParams(); const initParams = getRequestInitParams();
Object.assign(data, initParams); const params4get = Object.assign({}, initParams, data);
const params = data ? new URLSearchParams(data).toString() : ''; const params = params4get ? new URLSearchParams(params4get).toString() : '';
const ifp = url.includes('?') ? '&' : '?'; const ifp = url.includes('?') ? '&' : '?';
const headerObj = getRequestHeader(); const headerObj = getRequestHeader();
const fUrl = params !== '' ? `${url}${ifp}${params}` : url; const fUrl = params !== '' ? `${url}${ifp}${params}` : url;
@ -90,6 +90,12 @@ export function fetchJSON(url, data = {}) {
} }
export function postForm(url, data) { export function postForm(url, data) {
const initParams = getRequestInitParams();
Object.keys(initParams).forEach(key => {
if (! data.has(key)) {
data.append(key, initParams[key]);
}
});
const headerObj = getRequestHeader() const headerObj = getRequestHeader()
return fetch(url, { return fetch(url, {
method: 'POST', method: 'POST',

@ -1,67 +1,73 @@
import { useParams, useNavigate } from "react-router-dom"; import { useParams, useNavigate } from 'react-router-dom';
import { useEffect } from "react"; import { useEffect, useState } from 'react';
import { observer } from "mobx-react"; import { Row, Col, Space, Button, Divider, Form, Typography, Rate, Radio, Upload, Input, App, Card } from 'antd';
import { toJS, runInAction } from "mobx"; import { PlusOutlined } from '@ant-design/icons';
import { Row, Col, Space, Button, Divider, Form, Typography, Rate, Radio, Upload, Input, App, Card } from "antd"; import * as config from '@/config';
import { useStore } from "../../stores/StoreContext.js"; import useAuthStore from '@/stores/Auth';
import { PlusOutlined } from "@ant-design/icons"; import { getFeedbackDetail, getCustomerFeedbackDetail, getFeedbackImages, getFeedbackInfo, removeFeedbackImages, postFeedbackInfo } from '@/stores/Feedback';
const { Title, Text, Paragraph } = Typography; const { Title, Text, Paragraph } = Typography;
import * as config from "@/config";
function Detail() { function Detail() {
const navigate = useNavigate(); const navigate = useNavigate();
const { GRI_SN, RefNo, CII_SN } = useParams(); const { GRI_SN, RefNo, CII_SN } = useParams();
const { feedbackStore, authStore } = useStore(); const [travelAgencyId, token] = useAuthStore((state) => [state.loginUser.travelAgencyId, state.loginUser.token]);
const { feedbackReview, feedbackImages, feedbackInfo, feedbackServiceRate: feedbackRate } = feedbackStore; const desc = ['none', 'Unacceptable', 'Poor', 'Fair', 'Very Good', 'Excellent'];
const desc = ["none", "Unacceptable", "Poor", "Fair", "Very Good", "Excellent"];
const { notification } = App.useApp(); const { notification } = App.useApp();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [feedbackRate, setFeedbackRate] = useState({});
const [feedbackReview, setFeedbackReview] = useState({});
const [feedbackImages, setFeedbackImages] = useState([]);
const [feedbackInfo, setFeedbackInfo] = useState({});
useEffect(() => { useEffect(() => {
console.info("Detail.useEffect: " + GRI_SN); console.info('Detail.useEffect: ' + GRI_SN);
feedbackStore.getFeedbackDetail(authStore.login.travelAgencyId, GRI_SN); getFeedbackDetail(travelAgencyId, GRI_SN).then((res) => {
feedbackStore.getCustomerFeedbackDetail(authStore.login.travelAgencyId, GRI_SN, CII_SN); setFeedbackRate(res.feedbackRate);
feedbackStore.getFeedbackImages(authStore.login.travelAgencyId, GRI_SN); setFeedbackReview(res.feedbackReview);
feedbackStore.getFeedbackInfo(authStore.login.travelAgencyId, GRI_SN).then(v => { });
getCustomerFeedbackDetail(travelAgencyId, GRI_SN, CII_SN).then((res) => setFeedbackRate(res));
getFeedbackImages(travelAgencyId, GRI_SN).then((res) => setFeedbackImages(res));
getFeedbackInfo(travelAgencyId, GRI_SN).then((v) => {
form.setFieldsValue({ info_content: v.EEF_Content }); form.setFieldsValue({ info_content: v.EEF_Content });
setFeedbackInfo(v);
}); });
}, [GRI_SN]); }, [GRI_SN]);
const HWO_Guide = feedbackRate && feedbackRate.HWO_Guide ? feedbackRate.HWO_Guide : []; const HWO_Guide = feedbackRate && feedbackRate.HWO_Guide ? feedbackRate.HWO_Guide : [];
const HWO_Driver = feedbackRate && feedbackRate.HWO_Driver ? feedbackRate.HWO_Driver : []; const HWO_Driver = feedbackRate && feedbackRate.HWO_Driver ? feedbackRate.HWO_Driver : [];
const HWO_Activity = feedbackRate && feedbackRate.HWO_Activity ? feedbackRate.HWO_Activity : []; const HWO_Activity = feedbackRate && feedbackRate.HWO_Activity ? feedbackRate.HWO_Activity : [];
const OtherThoughts = feedbackRate && feedbackRate.OtherThoughts ? feedbackRate.OtherThoughts : ""; const OtherThoughts = feedbackRate && feedbackRate.OtherThoughts ? feedbackRate.OtherThoughts : '';
const signatureData = feedbackRate && feedbackRate.signatureData ? feedbackRate.signatureData : ""; const signatureData = feedbackRate && feedbackRate.signatureData ? feedbackRate.signatureData : '';
const PhotoPermission = feedbackRate?.PhotoPermission === 1; const PhotoPermission = feedbackRate?.PhotoPermission === 1;
const cityName = feedbackRate.cityName; const cityName = feedbackRate?.cityName || '';
const ECI_Content = feedbackReview && feedbackReview.ECI_Content ? feedbackReview.ECI_Content : "None"; const ECI_Content = feedbackReview && feedbackReview.ECI_Content ? feedbackReview.ECI_Content : 'None';
const fileList = toJS(feedbackImages); const fileList = feedbackImages;
const handleChange = info => { const handleChange = (info) => {
let newFileList = [...info.fileList]; let newFileList = [...info.fileList];
newFileList = newFileList.map(file => { newFileList = newFileList.map((file) => {
if (file.response && file.response.result) { if (file.response && file.response.result) {
file.url = file.response.result.file_url; file.url = file.response.result.file_url;
} }
return file; return file;
}); });
runInAction(() => { setFeedbackImages(newFileList);
feedbackStore.feedbackImages = newFileList;
});
}; };
const handRemove = info => { const handRemove = (info) => {
return feedbackStore.removeFeedbackImages(info.url); return removeFeedbackImages(info.url);
}; };
const onFinish = values => { const onFinish = (values) => {
console.log("Success:", values); // console.log("Success:", values);
if (values) { if (values) {
feedbackStore.postFeedbackInfo(feedbackInfo.EEF_VEI_SN, feedbackInfo.EEF_GRI_SN, feedbackInfo.EEF_EOI_SN, values.info_content).then(() => { postFeedbackInfo(feedbackInfo.EEF_VEI_SN, feedbackInfo.EEF_GRI_SN, feedbackInfo.EEF_EOI_SN, values.info_content).then(() => {
notification.success({ notification.success({
message: `Notification`, message: `Notification`,
description: "Submit Successful", description: 'Submit Successful',
placement: "top", placement: 'top',
duration: 4, duration: 4,
}); });
}); });
@ -69,13 +75,11 @@ function Detail() {
}; };
return ( return (
<Space direction="vertical" style={{ width: "100%" }}> <Space direction='vertical' style={{ width: '100%' }}>
<Row gutter={16}> <Row gutter={16}>
<Col span={20}> <Col span={20}></Col>
</Col>
<Col span={4}> <Col span={4}>
<Button type="link" onClick={() => navigate("/feedback")}> <Button type='link' onClick={() => navigate('/feedback')}>
Back Back
</Button> </Button>
</Col> </Col>
@ -83,39 +87,45 @@ function Detail() {
<Row gutter={16}> <Row gutter={16}>
<Col span={4}></Col> <Col span={4}></Col>
<Col span={18}> <Col span={18}>
<Card type="inner" title={<Title level={4}>Post Survey {RefNo} in {cityName}</Title>}> <Card
type='inner'
title={
<Title level={4}>
Post Survey {RefNo} in {cityName}
</Title>
}>
<Form labelCol={{ span: 5 }}> <Form labelCol={{ span: 5 }}>
<Divider orientation="left">How satisfied were you with your tour guide?</Divider> <Divider orientation='left'>How satisfied were you with your tour guide?</Divider>
{HWO_Guide.map(ele => ( {HWO_Guide.map((ele) => (
<Form.Item label={ele.Describe} key={ele.id}> <Form.Item label={ele.Describe} key={ele.id}>
<Space> <Space>
<Rate disabled value={ele.rate} /> <Rate disabled value={ele.rate} />
<span className="ant-rate-text">{desc[ele.rate]}</span> <span className='ant-rate-text'>{desc[ele.rate]}</span>
</Space> </Space>
</Form.Item> </Form.Item>
))} ))}
<Divider orientation="left">How about the Driver and Car/Van?</Divider> <Divider orientation='left'>How about the Driver and Car/Van?</Divider>
{HWO_Driver.map(ele => ( {HWO_Driver.map((ele) => (
<Form.Item label={ele.Describe} key={ele.id}> <Form.Item label={ele.Describe} key={ele.id}>
<Space> <Space>
<Rate disabled value={ele.rate} /> <Rate disabled value={ele.rate} />
<span className="ant-rate-text">{desc[ele.rate]}</span> <span className='ant-rate-text'>{desc[ele.rate]}</span>
</Space> </Space>
</Form.Item> </Form.Item>
))} ))}
<Divider orientation="left">General Experience with:</Divider> <Divider orientation='left'>General Experience with:</Divider>
{HWO_Activity.map(ele => ( {HWO_Activity.map((ele) => (
<Form.Item label={ele.Describe} key={ele.id}> <Form.Item label={ele.Describe} key={ele.id}>
<Space> <Space>
<Rate disabled value={ele.rate} /> <Rate disabled value={ele.rate} />
<span className="ant-rate-text">{desc[ele.rate]}</span> <span className='ant-rate-text'>{desc[ele.rate]}</span>
</Space> </Space>
</Form.Item> </Form.Item>
))} ))}
<Divider orientation="left">Would you like to give us permission to use the photos taken by the tour guide(s) during your trip which contain your portrait?</Divider> <Divider orientation='left'>Would you like to give us permission to use the photos taken by the tour guide(s) during your trip which contain your portrait?</Divider>
<Paragraph> <Paragraph>
{PhotoPermission ? ( {PhotoPermission ? (
<> <>
@ -129,21 +139,20 @@ function Detail() {
</> </>
)} )}
</Paragraph> </Paragraph>
<Divider orientation="left">Other thoughts you want to share with us:</Divider> <Divider orientation='left'>Other thoughts you want to share with us:</Divider>
<Text>{OtherThoughts}</Text> <Text>{OtherThoughts}</Text>
<Divider orientation="left">Signature:</Divider> <Divider orientation='left'>Signature:</Divider>
{signatureData ? <img id="signature-img" alt="customer signature" title="customer signature" src={signatureData} /> : null} {signatureData ? <img id='signature-img' alt='customer signature' title='customer signature' src={signatureData} /> : null}
</Form>
</Form></Card> </Card>
</Col> </Col>
<Col span={4}></Col> <Col span={4}></Col>
</Row> </Row>
<Row gutter={16}> <Row gutter={16}>
<Col span={4}></Col> <Col span={4}></Col>
<Col span={18}> <Col span={18}>
<Card type="inner" title={<Title level={4}>External Reviews</Title>}> <Card type='inner' title={<Title level={4}>External Reviews</Title>}>
<Text>{ECI_Content}</Text> <Text>{ECI_Content}</Text>
</Card> </Card>
</Col> </Col>
@ -153,16 +162,16 @@ function Detail() {
<Row gutter={16}> <Row gutter={16}>
<Col span={4}></Col> <Col span={4}></Col>
<Col span={18}> <Col span={18}>
<Card type="inner" title={<Title level={4}>Feedback from local agent</Title>}> <Card type='inner' title={<Title level={4}>Feedback from local agent</Title>}>
<Form name="feedback_detail_from" onFinish={onFinish} labelCol={{ span: 5 }} form={form}> <Form name='feedback_detail_from' onFinish={onFinish} labelCol={{ span: 5 }} form={form}>
<Form.Item> <Form.Item>
<Upload <Upload
name="ghhfile" name='ghhfile'
// accept="image/*" // accept="image/*"
multiple={true} multiple={true}
action={config.HT_HOST + `/service-fileServer/FileUpload?GRI_SN=${GRI_SN}&VEI_SN=${authStore.login.travelAgencyId}&token=${authStore.login.token}`} action={config.HT_HOST + `/service-fileServer/FileUpload?GRI_SN=${GRI_SN}&VEI_SN=${travelAgencyId}&token=${token}`}
fileList={fileList} fileList={fileList}
listType="picture-card" listType='picture-card'
onChange={handleChange} onChange={handleChange}
onRemove={handRemove}> onRemove={handRemove}>
<div> <div>
@ -172,17 +181,17 @@ function Detail() {
</Upload> </Upload>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="info_content" name='info_content'
rules={[ rules={[
{ {
required: true, required: true,
message: "Please input your messages!", message: 'Please input your messages!',
}, },
]}> ]}>
<Input.TextArea rows={6} placeholder="Any feedback for this group you would like to send to Asia Highlights"></Input.TextArea> <Input.TextArea rows={6} placeholder='Any feedback for this group you would like to send to Asia Highlights'></Input.TextArea>
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit"> <Button type='primary' htmlType='submit'>
Submit Submit
</Button> </Button>
</Form.Item> </Form.Item>
@ -195,4 +204,4 @@ function Detail() {
); );
} }
export default observer(Detail); export default Detail;

@ -1,9 +1,9 @@
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Row, Col, Space, Table, App } from 'antd'; import { Row, Col, Space, Table, App } from 'antd';
import { useStore } from '@/stores/StoreContext.js';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import SearchForm from '@/components/SearchForm'; import SearchForm from '@/components/SearchForm';
import useAuthStore from '@/stores/Auth';
import useFeedbackStore from '@/stores/Feedback'; import useFeedbackStore from '@/stores/Feedback';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -47,9 +47,9 @@ const feedbackListColumns = [
function Index() { function Index() {
const { t } = useTranslation(); const { t } = useTranslation();
const { notification } = App.useApp(); const { notification } = App.useApp();
const { feedbackStore, authStore } = useStore();
const [feedbackList, fetchFeedbackList] = useFeedbackStore((state) => [state.feedbackList, state.fetchFeedbackList]); const travelAgencyId = useAuthStore((state) => state.loginUser.travelAgencyId)
const [loading, feedbackList, fetchFeedbackList] = useFeedbackStore((state) => [state.loading, state.feedbackList, state.fetchFeedbackList]);
const showTotal = (total) => `Total ${total} items`; const showTotal = (total) => `Total ${total} items`;
@ -78,12 +78,12 @@ function Index() {
}, },
}} }}
onSubmit={(err, formVal, filedsVal) => { onSubmit={(err, formVal, filedsVal) => {
fetchFeedbackList(authStore.login.travelAgencyId, formVal.referenceNo, formVal.startdate, formVal.endtime); fetchFeedbackList(travelAgencyId, formVal.referenceNo, formVal.startdate, formVal.endtime);
}} }}
/> />
<Row> <Row>
<Col md={24} lg={24} xxl={24}> <Col md={24} lg={24} xxl={24}>
<Table bordered={true} columns={feedbackListColumns} dataSource={feedbackList} pagination={{ defaultPageSize: 20, showTotal: showTotal }} /> <Table bordered={true} columns={feedbackListColumns} dataSource={feedbackList} pagination={{ defaultPageSize: 20, showTotal: showTotal }} loading={loading} />
</Col> </Col>
<Col md={24} lg={24} xxl={24}></Col> <Col md={24} lg={24} xxl={24}></Col>
</Row> </Row>

Loading…
Cancel
Save