|
|
|
|
@ -1,13 +1,28 @@
|
|
|
|
|
import { useState, useEffect } from "react";
|
|
|
|
|
import { Row, Col, Space, Button, Form, Table, Typography, Input, DatePicker, Drawer, Select, App } from "antd";
|
|
|
|
|
import {
|
|
|
|
|
Row,
|
|
|
|
|
Col,
|
|
|
|
|
Space,
|
|
|
|
|
Button,
|
|
|
|
|
Form,
|
|
|
|
|
Table,
|
|
|
|
|
Typography,
|
|
|
|
|
Input,
|
|
|
|
|
DatePicker,
|
|
|
|
|
Drawer,
|
|
|
|
|
Select,
|
|
|
|
|
App,
|
|
|
|
|
} from "antd";
|
|
|
|
|
import dayjs from "dayjs";
|
|
|
|
|
import { FileAddOutlined, EditOutlined } from "@ant-design/icons";
|
|
|
|
|
import { groupBy, isEmpty } from '@/utils/commons'
|
|
|
|
|
import { groupBy, isEmpty } from "@/utils/commons";
|
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
|
import useFormStore from "@/stores/Form";
|
|
|
|
|
import { usingStorage } from '@/hooks/usingStorage'
|
|
|
|
|
import useExternalReviewStore, { fetchRecentGroup } from "@/stores/ExternalReview";
|
|
|
|
|
import useReservationStore, {fetchCityList} from '@/stores/Reservation'
|
|
|
|
|
import { usingStorage } from "@/hooks/usingStorage";
|
|
|
|
|
import useExternalReviewStore, {
|
|
|
|
|
fetchRecentGroup,
|
|
|
|
|
} from "@/stores/ExternalReview";
|
|
|
|
|
import useReservationStore, { fetchCityList } from "@/stores/Reservation";
|
|
|
|
|
import SearchForm from "@/components/SearchForm";
|
|
|
|
|
|
|
|
|
|
function ReviewList() {
|
|
|
|
|
@ -81,9 +96,13 @@ function ReviewList() {
|
|
|
|
|
title: t("review.Action"),
|
|
|
|
|
width: "120px",
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
render: () => {
|
|
|
|
|
render: (_, review) => {
|
|
|
|
|
return (
|
|
|
|
|
<Button type="link" icon={<EditOutlined />} onClick={() => setOpenReview(true)}>
|
|
|
|
|
<Button
|
|
|
|
|
type="link"
|
|
|
|
|
icon={<EditOutlined />}
|
|
|
|
|
onClick={() => onReviewSeleted(review)}
|
|
|
|
|
>
|
|
|
|
|
Edit
|
|
|
|
|
</Button>
|
|
|
|
|
);
|
|
|
|
|
@ -91,45 +110,49 @@ function ReviewList() {
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const [formReview] = Form.useForm()
|
|
|
|
|
const [openReview, setOpenReview] = useState(false)
|
|
|
|
|
const [dataLoading, setDataLoading] = useState(false)
|
|
|
|
|
const [guideSelectOptions, setGuideSelectOptions] = useState([])
|
|
|
|
|
const [groupListOptions, setGroupListOptions] = useState([])
|
|
|
|
|
const [cityListOptions, setCityListOptions] = useState([])
|
|
|
|
|
const [formReview] = Form.useForm();
|
|
|
|
|
const [openReview, setOpenReview] = useState(false);
|
|
|
|
|
const [dataLoading, setDataLoading] = useState(false);
|
|
|
|
|
const [guideSelectOptions, setGuideSelectOptions] = useState([]);
|
|
|
|
|
const [groupListOptions, setGroupListOptions] = useState([]);
|
|
|
|
|
const [cityListOptions, setCityListOptions] = useState([]);
|
|
|
|
|
|
|
|
|
|
const formValuesToSub = useFormStore(s => s.formValuesToSub);
|
|
|
|
|
const { travelAgencyId } = usingStorage()
|
|
|
|
|
const [fetchAgencyGuideList] = useReservationStore(s => [
|
|
|
|
|
s.fetchAgencyGuideList
|
|
|
|
|
])
|
|
|
|
|
const [fetchReviewList, reviewList] = useExternalReviewStore((s) => [
|
|
|
|
|
s.fetchReviewList,
|
|
|
|
|
s.reviewList,
|
|
|
|
|
const formValuesToSub = useFormStore((s) => s.formValuesToSub);
|
|
|
|
|
const { travelAgencyId } = usingStorage();
|
|
|
|
|
const [fetchAgencyGuideList] = useReservationStore((s) => [
|
|
|
|
|
s.fetchAgencyGuideList,
|
|
|
|
|
]);
|
|
|
|
|
const [fetchReviewList, reviewList, postReview] = useExternalReviewStore(
|
|
|
|
|
(s) => [s.fetchReviewList, s.reviewList, s.postReview]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const { notification } = App.useApp()
|
|
|
|
|
const { notification } = App.useApp();
|
|
|
|
|
|
|
|
|
|
useEffect (() => {
|
|
|
|
|
initAgencyGuideList(travelAgencyId)
|
|
|
|
|
}, [travelAgencyId])
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
initAgentData(travelAgencyId);
|
|
|
|
|
}, [travelAgencyId]);
|
|
|
|
|
|
|
|
|
|
const initAgencyGuideList = async (travelAgencyId) => {
|
|
|
|
|
fetchAgencyGuideList(travelAgencyId)
|
|
|
|
|
.then((guideList) => {
|
|
|
|
|
const guideCity = (groupBy(guideList, 'cityName'));
|
|
|
|
|
const guideOptions = Object.keys(guideCity).map(city => ({
|
|
|
|
|
label: isEmpty(city) ? '-' : city, options: guideCity[city].map(guide => ({ value: guide.guideId, label: guide.guideName + '(' + guide.mobileNo + ')', })),
|
|
|
|
|
}));
|
|
|
|
|
setGuideSelectOptions(guideOptions)
|
|
|
|
|
})
|
|
|
|
|
const initAgentData = async (travelAgencyId) => {
|
|
|
|
|
fetchAgencyGuideList(travelAgencyId).then((guideList) => {
|
|
|
|
|
const guideCity = groupBy(guideList, "cityName");
|
|
|
|
|
const guideOptions = Object.keys(guideCity).map((city) => ({
|
|
|
|
|
label: isEmpty(city) ? "-" : city,
|
|
|
|
|
options: guideCity[city].map((guide) => ({
|
|
|
|
|
value: guide.guideId,
|
|
|
|
|
label: guide.guideName + "(" + guide.mobileNo + ")",
|
|
|
|
|
})),
|
|
|
|
|
}));
|
|
|
|
|
setGuideSelectOptions(guideOptions);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const recentGroupList = await fetchRecentGroup(travelAgencyId)
|
|
|
|
|
setGroupListOptions(recentGroupList.map(group => ({ value: group.id, label: group.number })))
|
|
|
|
|
}
|
|
|
|
|
const searchReview = (submitValues, current = 1) => {
|
|
|
|
|
const recentGroupList = await fetchRecentGroup(travelAgencyId);
|
|
|
|
|
setGroupListOptions(
|
|
|
|
|
recentGroupList.map((group) => ({ value: group.id, label: group.number }))
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
const searchReview = (submitValues) => {
|
|
|
|
|
setDataLoading(true);
|
|
|
|
|
fetchReviewList(submitValues, current)
|
|
|
|
|
fetchReviewList(submitValues, travelAgencyId)
|
|
|
|
|
.catch((ex) => {
|
|
|
|
|
notification.error({
|
|
|
|
|
message: `Notification`,
|
|
|
|
|
@ -143,77 +166,148 @@ function ReviewList() {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onReviewSeleted = async (review) => {
|
|
|
|
|
// 转换为 Form.Item DatePicker 格式
|
|
|
|
|
review.datePosted = dayjs(review.datePosted);
|
|
|
|
|
formReview.setFieldsValue(review);
|
|
|
|
|
setOpenReview(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const onReviewFinish = (values) => {
|
|
|
|
|
postReview(values, travelAgencyId)
|
|
|
|
|
.then(() => {
|
|
|
|
|
notification.info({
|
|
|
|
|
message: "Notification",
|
|
|
|
|
description: "Success",
|
|
|
|
|
placement: "top",
|
|
|
|
|
});
|
|
|
|
|
setOpenReview(false);
|
|
|
|
|
searchReview(formValuesToSub);
|
|
|
|
|
})
|
|
|
|
|
.catch((ex) => {
|
|
|
|
|
notification.error({
|
|
|
|
|
message: "Notification",
|
|
|
|
|
description: ex.message,
|
|
|
|
|
placement: "top",
|
|
|
|
|
duration: 4,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<Drawer title='External Reviews' closable={{ 'aria-label': 'Close Button' }} size={'large'} onClose={() => setOpenReview(false)} open={openReview}>
|
|
|
|
|
<Drawer
|
|
|
|
|
title="External Reviews"
|
|
|
|
|
closable={{ "aria-label": "Close Button" }}
|
|
|
|
|
size={"large"}
|
|
|
|
|
open={openReview}
|
|
|
|
|
onClose={() => {
|
|
|
|
|
setOpenReview(false);
|
|
|
|
|
formReview.resetFields();
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Form
|
|
|
|
|
layout={'vertical'}
|
|
|
|
|
layout={"vertical"}
|
|
|
|
|
form={formReview}
|
|
|
|
|
scrollToFirstError
|
|
|
|
|
onFinish={(values) => {
|
|
|
|
|
}}>
|
|
|
|
|
<Form.Item name='reviewId' className='hidden' ><Input /></Form.Item>
|
|
|
|
|
<Form.Item name='reviewLink' label='Review Link' rules={[{ required: true, message: 'Please input Review Link' }]}>
|
|
|
|
|
onReviewFinish(values);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Form.Item name="reviewId" className="hidden">
|
|
|
|
|
<Input />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item name='datePosted' label='Date Posted' rules={[{ required: true, message: 'Please input Date Posted' }]}>
|
|
|
|
|
<DatePicker allowClear={false} className="w-full" />
|
|
|
|
|
<Form.Item
|
|
|
|
|
name="reviewLink"
|
|
|
|
|
label="Review Link"
|
|
|
|
|
rules={[{ required: true, message: "Please input Review Link" }]}
|
|
|
|
|
>
|
|
|
|
|
<Input />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item name='customerId' label='Customer ID'>
|
|
|
|
|
<Form.Item
|
|
|
|
|
name="datePosted"
|
|
|
|
|
label="Date Posted"
|
|
|
|
|
rules={[{ required: true, message: "Please input Date Posted" }]}
|
|
|
|
|
>
|
|
|
|
|
<DatePicker
|
|
|
|
|
maxDate={dayjs()}
|
|
|
|
|
allowClear={false}
|
|
|
|
|
className="w-full"
|
|
|
|
|
/>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item name="customerId" label="Customer ID">
|
|
|
|
|
<Input />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item name='referenceNumber' label='Reference Number' rules={[{ required: true, message: 'Please input Reference Number' }]}>
|
|
|
|
|
<Form.Item
|
|
|
|
|
name="referenceId"
|
|
|
|
|
label="Reference Number"
|
|
|
|
|
rules={[
|
|
|
|
|
{ required: true, message: "Please input Reference Number" },
|
|
|
|
|
]}
|
|
|
|
|
>
|
|
|
|
|
<Select
|
|
|
|
|
showSearch
|
|
|
|
|
placeholder='Select a Reference Number'
|
|
|
|
|
optionFilterProp='children'
|
|
|
|
|
placeholder="Select a Reference Number"
|
|
|
|
|
optionFilterProp="children"
|
|
|
|
|
onChange={async (groupId) => {
|
|
|
|
|
const cityList = await fetchCityList(travelAgencyId, groupId)
|
|
|
|
|
setCityListOptions(cityList.map(city => {
|
|
|
|
|
return {
|
|
|
|
|
value: city.CII_SN,
|
|
|
|
|
label: city.CityName
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
const cityList = await fetchCityList(travelAgencyId, groupId);
|
|
|
|
|
setCityListOptions(
|
|
|
|
|
cityList.map((city) => {
|
|
|
|
|
return {
|
|
|
|
|
value: city.CII_SN,
|
|
|
|
|
label: city.CityName,
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
filterOption={(input, option) =>
|
|
|
|
|
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
|
|
|
|
(option?.label ?? "")
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.includes(input.toLowerCase())
|
|
|
|
|
}
|
|
|
|
|
options={groupListOptions}
|
|
|
|
|
/>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item name='guide' label='Guide' rules={[{ required: true, message: 'Please input Guide' }]}>
|
|
|
|
|
<Form.Item
|
|
|
|
|
name="guideId"
|
|
|
|
|
label="Guide"
|
|
|
|
|
rules={[{ required: true, message: "Please input Guide" }]}
|
|
|
|
|
>
|
|
|
|
|
<Select
|
|
|
|
|
showSearch
|
|
|
|
|
placeholder='Select a guide'
|
|
|
|
|
optionFilterProp='children'
|
|
|
|
|
placeholder="Select a guide"
|
|
|
|
|
optionFilterProp="children"
|
|
|
|
|
filterOption={(input, option) =>
|
|
|
|
|
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
|
|
|
|
(option?.label ?? "")
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.includes(input.toLowerCase())
|
|
|
|
|
}
|
|
|
|
|
options={guideSelectOptions}
|
|
|
|
|
/>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item name='cityOfService' label='City of Service'>
|
|
|
|
|
<Form.Item name="cityId" label="City of Service">
|
|
|
|
|
<Select
|
|
|
|
|
showSearch
|
|
|
|
|
allowClear
|
|
|
|
|
placeholder='Select a city'
|
|
|
|
|
optionFilterProp='children'
|
|
|
|
|
placeholder="Select a city"
|
|
|
|
|
optionFilterProp="children"
|
|
|
|
|
filterOption={(input, option) =>
|
|
|
|
|
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
|
|
|
|
(option?.label ?? "")
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.includes(input.toLowerCase())
|
|
|
|
|
}
|
|
|
|
|
options={cityListOptions}
|
|
|
|
|
/>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item>
|
|
|
|
|
|
|
|
|
|
<Space className="w-full">
|
|
|
|
|
<Button type='primary' htmlType='submit' block>
|
|
|
|
|
Submit
|
|
|
|
|
</Button>
|
|
|
|
|
<Button block>
|
|
|
|
|
Cancel
|
|
|
|
|
</Button>
|
|
|
|
|
<Space className="w-full">
|
|
|
|
|
<Button type="primary" htmlType="submit" block>
|
|
|
|
|
Submit
|
|
|
|
|
</Button>
|
|
|
|
|
<Button block onClick={() => {
|
|
|
|
|
setOpenReview(false)
|
|
|
|
|
formReview.resetFields()
|
|
|
|
|
}}>Cancel</Button>
|
|
|
|
|
</Space>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
</Form>
|
|
|
|
|
@ -234,7 +328,12 @@ function ReviewList() {
|
|
|
|
|
/>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col flex="200px" className="flex justify-center items-center">
|
|
|
|
|
<Button color="cyan" variant="solid" icon={<FileAddOutlined />} onClick={() => setOpenReview(true)}>
|
|
|
|
|
<Button
|
|
|
|
|
color="cyan"
|
|
|
|
|
variant="solid"
|
|
|
|
|
icon={<FileAddOutlined />}
|
|
|
|
|
onClick={() => setOpenReview(true)}
|
|
|
|
|
>
|
|
|
|
|
Add Review
|
|
|
|
|
</Button>
|
|
|
|
|
</Col>
|
|
|
|
|
|