Merge branch 'feature/price_manager' of github.com:hainatravel/GHHub into feature/price_manager

# Conflicts:
#	src/views/App.jsx
feature/price_manager
Jimmy Liow 1 year ago
commit 5c236defd1

@ -21,6 +21,13 @@
"preview": "Preview",
"Total": "Total",
"Action": "Action",
"Import": "Import",
"Export": "Export",
"Copy": "Copy",
"Table": {
"Total": "Total {{total}} items"
},
"Login": "Login",
"Username": "Username",

@ -7,20 +7,49 @@
"Attractions": "Attractions",
"Meals": "Meals",
"Extras": "Extras",
"Overtravel": "超公里",
"Special": "Special"
},
"auditState": {
"New": "New",
"Pending": "Pending",
"Approve": "Approve",
"Approved": "Approved",
"Rejected": "Rejected",
"Published": "Published"
},
"auditStateAction": {
"New": "New",
"Pending": "Pending",
"Approved": "Approve",
"Rejected": "Reject",
"Published": "Publish"
},
"Status": "Status",
"State": "State",
"Title": "Title",
"Vendor": "Vendor",
"AuState": "Audit State",
"CreatedBy": "Created By",
"CreateDate": "Create Date",
"AuditedBy": "Audited By",
"AuditDate": "Audit Date"
"AuditDate": "Audit Date",
"Quotation": "Quotation",
"Offer": "Offer",
"Unit": "Unit",
"GroupSize": "Group Size",
"UseDates": "Use Dates",
"Weekdays": "Weekdays",
"UseYear": "Use Year",
"AgeType": {
"Type": "Age Type",
"Adult": "Adult",
"Child": "Child"
},
"#": "#"
}

@ -21,6 +21,13 @@
"preview": "预览",
"Total": "总数",
"Action": "操作",
"Import": "导入",
"Export": "导出",
"Copy": "复制",
"Table": {
"Total": "共 {{total}} 条"
},
"Login": "登录",
"Username": "账号",

@ -6,21 +6,49 @@
"Package": "包价线路",
"Attractions": "景点",
"Meals": "餐费",
"Extras": "附加",
"Extras": "附加项目",
"Overtravel": "超公里",
"Special": "特殊项目"
},
"auditState": {
"New": "新增",
"Pending": "待审核",
"Approve": "已通过",
"Approved": "已通过",
"Rejected": "已拒绝",
"Published": "已发布"
},
"auditStateAction": {
"New": "新增",
"Pending": "待审核",
"Approved": "审核通过",
"Rejected": "审核拒绝",
"Published": "审核发布"
},
"Status": "状态",
"State": "状态",
"Title": "名称",
"Vendor": "供应商",
"AuState": "审核状态",
"CreatedBy": "提交人员",
"CreateDate": "提交时间",
"Auditors": "审核人员",
"AuditDate": "审核时间"
"AuditedBy": "审核人员",
"AuditDate": "审核时间",
"Quotation": "报价",
"Offer": "报价",
"Unit": "单位",
"GroupSize": "人等",
"UseDates": "使用日期",
"Weekdays": "有效日/周X",
"UseYear": "年份",
"AgeType": {
"Type": "人群",
"Adult": "成人",
"Child": "儿童"
},
"#": "#"
}

@ -0,0 +1,12 @@
import { Select } from 'antd';
import { useProductsAuditStates } from '@/hooks/useProductsSets';
const AuditStateSelector = ({ ...props }) => {
const states = useProductsAuditStates();
return (
<>
<Select labelInValue allowClear options={states} {...props}/>
</>
);
};
export default AuditStateSelector;

@ -3,24 +3,27 @@ import { Form, Input, Row, Col, Select, DatePicker, Space, Button } from 'antd';
import { objectMapper, at } from '@/utils/commons';
import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config';
import useFormStore from '@/stores/Form';
import usePresets from '@/hooks/usePresets';
import useDatePresets from '@/hooks/useDatePresets';
import { useTranslation } from 'react-i18next';
import SearchInput from './SearchInput';
import { fetchJSON } from '@/utils/request';
import { HT_HOST } from '@/config';
import SearchInput from './SearchInput';
import AuditStateSelector from './AuditStateSelector';
//
export const fetchVendorList = async () => {
const { errcode, Result } = await fetchJSON(`${HT_HOST}/service-cusservice/PTGetHWVendorList`)
return errcode !== 0 ? [] : Result
export const fetchVendorList = async (q) => {
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/VendorList`, { q })
return errcode !== 0 ? [] : result
}
const { RangePicker } = DatePicker;
const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => {
const { t } = useTranslation();
const presets = usePresets();
const presets = useDatePresets();
const [formValues, setFormValues] = useFormStore((state) => [state.formValues, state.setFormValues]);
const [formValuesToSub, setFormValuesToSub] = useFormStore((state) => [state.formValuesToSub, state.setFormValuesToSub]);
const [form] = Form.useForm();
@ -47,13 +50,16 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => {
{ key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') },
],
'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' },
'auditState': { key: 'auditState', transform: (value) => value?.value || value?.key || '', default: '' },
'audit_state': { key: 'audit_state', transform: (value) => value?.value || value?.key || '', default: '' },
'agency': {
key: 'agency',
transform: (value) => {
return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : '';
},
},
'year': [
{ key: 'year', transform: (arrVal) => (arrVal ? arrVal.format('YYYY') : '') },
],
};
let dest = {};
const { dates, ...omittedValue } = values;
@ -162,15 +168,15 @@ function getFields(props) {
'referenceNo',
99,
<Form.Item name='referenceNo' label={t('group:RefNo')} {...fieldProps.referenceNo}>
<Input placeholder={t('group:RefNo')} allowClear />
<Input placeholder={t('group:RefNo')} allowClear />
</Form.Item>,
fieldProps?.referenceNo?.col || 6
),
item(
'PNR',
99,
<Form.Item name='PNR' label="PNR">
<Input placeholder={t('group:PNR')} allowClear />
<Form.Item name='PNR' label='PNR'>
<Input placeholder={t('group:PNR')} allowClear />
</Form.Item>,
fieldProps?.PNR?.col || 4
),
@ -221,34 +227,37 @@ function getFields(props) {
/**
*
*/
item(
'year',
99,
<Form.Item name={'year'} label={t('products:UseYear')} {...fieldProps.year} initialValue={at(props, 'initialValue.year')[0]}>
<DatePicker picker='year' allowClear {...fieldComProps.year} />
</Form.Item>,
fieldProps?.year?.col || 3
),
item(
'agency',
99,
<Form.Item name='agency' label={t('products:Vendor')} {...fieldProps.agency} initialValue={at(props, 'initialValue.agency')[0]}>
<SearchInput placeholder={t('products:Vendor')} mode={'multiple'} maxTagCount={0} {...fieldComProps.agency} fetchOptions={fetchVendorList} map={{VEI2_CompanyBN: 'label', VEI_SN: 'value'}} />
<SearchInput
placeholder={t('products:Vendor')}
mode={'multiple'}
maxTagCount={0}
{...fieldComProps.agency}
fetchOptions={fetchVendorList}
map={{ travel_agency_name: 'label', travel_agency_id: 'value' }}
/>
</Form.Item>,
fieldProps?.agency?.col || 6
),
item(
'auditState',
'audit_state',
99,
<Form.Item name={`auditState`} initialValue={at(props, 'initialValue.auditState')[0] || { value: '0', label: 'Status' }}>
<Select
style={{ width: '100%' }}
labelInValue
allowClear
options={[ // todo:
{ value: '0', label: 'New' },
{ value: '1', label: 'Pending' },
{ value: '2', label: 'Approve' },
// { value: '3', label: 'Rejected' },
{ value: '4', label: 'Published' },
]}
/>
<Form.Item name={`audit_state`} initialValue={at(props, 'initialValue.audit_state')[0] || { value: '', label: 'Status' }}>
<AuditStateSelector />
</Form.Item>,
fieldProps?.auditState?.col || 3
fieldProps?.audit_state?.col || 3
),
];
baseChildren = baseChildren
.map((x) => {

@ -31,6 +31,7 @@ function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) {
showSearch
allowClear
maxTagCount={1}
dropdownStyle={{width: '16rem'}}
{...props}
onSearch={debounceFetcher}
notFoundContent={fetching ? <Spin size='small' /> : null}

@ -14,7 +14,7 @@ const HeaderWrapper = ({ children, header, ...props }) => {
<Header className='header px-6 h-10 ' style={{ background: 'white' }}>
<Flex justify={'space-between'} align={'center'} className='h-full'>
{/* {header} */}
<div className="flex-auto">{header}</div>
<div className="grow h-full">{header}</div>
<BackBtn />
</Flex>
</Header>

@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import dayjs from "dayjs";
import { useTranslation } from 'react-i18next';
const usePresets = () => {
const useDatePresets = () => {
const [presets, setPresets] = useState([]);
const { t, i18n } = useTranslation();
@ -39,4 +39,4 @@ const usePresets = () => {
return presets;
}
export default usePresets;
export default useDatePresets;

@ -0,0 +1,14 @@
export const useLanguageSets = () => {
const newData = [
{ key: '1', value: '1', label: 'English' },
{ key: '2', value: '2', label: 'Chinese (中文)' },
{ key: '3', value: '3', label: 'Japanese (日本語)' },
{ key: '4', value: '4', label: 'German (Deutsch)' },
{ key: '5', value: '5', label: 'French (Français)' },
{ key: '6', value: '6', label: 'Spanish (Español)' },
{ key: '7', value: '7', label: 'Russian (Русский)' },
{ key: '8', value: '8', label: 'Italian (Italiano)' },
];
return newData;
};

@ -3,6 +3,21 @@ import { useTranslation } from 'react-i18next';
/**
* 产品管理 相关的预设数据
* 项目类型
1 酒店预定
2 火车
3 飞机票务
4 游船
5 快巴
6 旅行社(综费) -
7 景点 -
8 特殊项目 -
9 其他
A 酒店
B 超公里 -
C 餐费 -
D 包价包
X
*/
export const useProductsTypes = () => {
@ -11,14 +26,15 @@ export const useProductsTypes = () => {
useEffect(() => {
const newData = [
{ label: t('products:type.Experience'), value: 'Experience', key: 'Experience' },
{ label: t('products:type.Experience'), value: '6', key: '6' },
{ label: t('products:type.Overtravel'), value: 'B', key: 'B' },
{ label: t('products:type.Car'), value: 'Car', key: 'Car' },
{ label: t('products:type.Guide'), value: 'Guide', key: 'Guide' },
{ label: t('products:type.Package'), value: 'Package', key: 'Package' },
{ label: t('products:type.Attractions'), value: 'Attractions', key: 'Attractions' },
{ label: t('products:type.Meals'), value: 'Meals', key: 'Meals' },
{ label: t('products:type.Extras'), value: 'Extras', key: 'Extras' },
{ label: t('products:type.Special'), value: 'Special', key: 'Special' },
{ label: t('products:type.Package'), value: 'D', key: 'D' },
{ label: t('products:type.Attractions'), value: '7', key: '7' },
{ label: t('products:type.Meals'), value: 'C', key: 'C' },
{ label: t('products:type.Extras'), value: '8', key: '8' },
// { label: t('products:type.Special'), value: 'Special', key: 'Special' },
];
setTypes(newData);
}, [i18n.language]);
@ -27,20 +43,26 @@ export const useProductsTypes = () => {
};
export const useProductsAuditStatus = () => {
export const useProductsAuditStates = () => {
const [types, setTypes] = useState([]);
const { t, i18n } = useTranslation();
useEffect(() => {
const newData = [
{ value: '0', label: 'New' },
{ value: '1', label: 'Pending' },
{ value: '2', label: 'Approve' },
{ value: '3', label: 'Rejected' },
{ value: '4', label: 'Published' },
{ key: '-1', value: '-1', label: t('products:auditState.New') },
{ key: '0', value: '0', label: t('products:auditState.Pending') },
{ key: '2', value: '2', label: t('products:auditState.Approved') },
{ key: '3', value: '3', label: t('products:auditState.Rejected') },
{ key: '1', value: '1', label: t('products:auditState.Published') },
];
setTypes(newData);
}, [i18n.language]);
return types;
};
export const useProductsAuditStatesMapVal = (value) => {
const stateSets = useProductsAuditStates();
const stateMapVal = stateSets.reduce((r, c) => ({ ...r, [`${c.value}`]: c }), {});
return stateMapVal;
};

@ -1,6 +1,12 @@
import React, { useState } from 'react';
import { Dropdown, Menu } from 'antd';
import { useState, useEffect } from 'react';
import { Dropdown } from 'antd';
import { useTranslation } from 'react-i18next';
import { appendRequestParams } from '@/utils/request';
const i18n_to_htcode = {
'zh': 2,
'en': 1,
};
/**
* 语言选择组件
@ -8,6 +14,13 @@ import { useTranslation } from 'react-i18next';
const Language = () => {
const { t, i18n } = useTranslation();
const [selectedKeys, setSelectedKeys] = useState([i18n.language]);
useEffect(() => {
appendRequestParams('lgc', i18n_to_htcode[i18n.language]);
return () => {};
}, [i18n.language]);
//
const handleChangeLanguage = ({ key }) => {
setSelectedKeys([key]);

@ -76,8 +76,8 @@ const router = createBrowserRouter([
{ path: "airticket",element: <RequireAuth subject={PERM_AIR_TICKET} result={true}><Airticket /></RequireAuth>},
{ path: "airticket/plan/:coli_sn",element:<AirticketPlan />},
{ path: "products",element:<ProductsIndex />},
{ path: "products/:travel_agency_id/audit",element:<ProductsAudit />},
{ path: "products/:travel_agency_id",element:<ProductsDetail />},
{ path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:<ProductsAudit />},
{ path: "products/:travel_agency_id/:use_year/:audit_state/edit",element:<ProductsDetail />},
{ path: "gysgl",element:<Gysgl />},
]
},

@ -88,6 +88,7 @@ const useAuthStore = create((set, get) => ({
setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN)
setStorage(KEY_USER_DETAIL, {username: userDetail.LoginName, travelAgencyName: userDetail.VName})
appendRequestParams('token', loginToken)
appendRequestParams('wu_id', userDetail.LMI_SN)
// loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`)
startTokenInterval()
},

@ -1,23 +1,57 @@
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { fetchJSON } from '@/utils/request';
import { fetchJSON, postForm } from '@/utils/request';
import { HT_HOST } from '@/config';
import { groupBy } from '@/utils/commons';
export const searchAgencyAction = async (param) => {
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param);
return errcode !== 0 ? [] : result;
};
export const getAgencyProductsAction = async (param) => {
const _param = { ...param, use_year: (param.use_year || '').replace('all', ''), audit_state: (param.audit_state || '').replace('all', '') };
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, _param);
return errcode !== 0 ? { agency: {}, products: [] } : result;
};
export const postProductsQuoteAudit = async (auditState, quoteRow) => {
const postbody = {
audit_state: auditState,
id: quoteRow.id,
travel_agency_id: quoteRow.travel_agency_id,
};
const formData = new FormData();
Object.keys(postbody).forEach((key) => {
formData.append(key, postbody[key]);
});
const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/quotation_audit`, formData);
return json;
// return errcode !== 0 ? {} : result;
};
export const postProductsAudit = async (auditState, infoRow) => {
const postbody = {
audit_state: auditState,
id: infoRow.id,
travel_agency_id: infoRow.travel_agency_id,
};
const formData = new FormData();
Object.keys(postbody).forEach((key) => {
formData.append(key, postbody[key]);
});
const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products-audit`, formData);
return json;
// const { errcode, result } = json;
// return errcode !== 0 ? {} : result;
};
const initialState = {
loading: false,
productsList: [
{
'audit_date': '2001-03-03',
'travel_agency_name': '新油低外',
'travel_agency_id': '650000200301029585',
'created_by': '冯丽',
'create_date': '1989-06-20',
'lastedit_memo': 'nostrud ad eu',
'audited_by': '黎静',
'audit_state': '1',
},
],
searchValues: {},
agencyList: [],
activeAgency: {},
agencyProducts: {},
};
export const useProductsStore = create(
devtools((set, get) => ({
@ -25,11 +59,33 @@ export const useProductsStore = create(
...initialState,
// state actions
setProductsList: (productsList) => set({ productsList }),
setLoading: loading => set({ loading }),
setSearchValues: searchValues => set({ searchValues }),
setAgencyList: (agencyList) => set({ agencyList }),
setActiveAgency: activeAgency => set({ activeAgency }),
setAgencyProducts: agencyProducts => set({ agencyProducts }),
reset: () => set(initialState),
// side effects
searchAgency: async (param) => {
const { setLoading, setAgencyList } = get();
setLoading(true);
const res = await searchAgencyAction(param);
setAgencyList(res);
setLoading(false);
},
getAgencyProducts: async (param) => {
const { setLoading, setActiveAgency, setAgencyProducts } = get();
setLoading(true);
const res = await getAgencyProductsAction(param);
const productsData = groupBy(res.products, row => row.info.product_type_id);
setAgencyProducts(productsData);
setActiveAgency(res.agency);
setLoading(false);
},
}))
);
export default useProductsStore;

@ -28,7 +28,7 @@ function App() {
const [validateUserPassword, tokenTimeout] = useAuthStore(
(state) => [state.validateUserPassword, state.tokenTimeout])
const { loginToken, userDetail } = usingStorage()
const { loginToken, userDetail, userId } = usingStorage()
const noticeUnRead = useNoticeStore((state) => state.noticeUnRead)
const href = useHref()
@ -40,6 +40,7 @@ function App() {
if (!needToLogin) {
appendRequestParams('token', loginToken)
appendRequestParams('wu_id', userId)
}
useEffect(() => {
@ -154,7 +155,6 @@ function App() {
{ label: <Link to='/account/role-list'>{t('account:management.roleList')}</Link>, key: '4' },
{ type: 'divider' },
{ label: <Link to='/logout'>{t('Logout')}</Link>, key: '99' },
{ label: <Link to='/account/change-vendor'>{t('ChangeVendor')}</Link>, key: 'change-vendor' },
],
{ type: 'divider' },
{ label: <>v{BUILD_VERSION}</>, key: 'BUILD_VERSION' },

@ -1,30 +1,182 @@
import { createContext, useContext, useEffect, useState } from 'react';
import { Button, Table, Tabs } from 'antd';
import { useProductsTypes } from '@/hooks/useProductsSets';
import { useEffect, useState } from 'react';
import { useParams, } from 'react-router-dom';
import { App, Button, Collapse, Table, Space, } from 'antd';
import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets';
import SecondHeaderWrapper from '@/components/SecondHeaderWrapper';
import { useTranslation } from 'react-i18next';
import useProductsStore, { postProductsQuoteAudit } from '@/stores/Products/Index';
import { isEmpty } from '@/utils/commons';
const Header = () => {
const Header = ({ title, agency, refresh, ...props}) => {
const { t } = useTranslation();
const [activeAgency, ] = useProductsStore((state) => [state.activeAgency, ]);
const { message, notification } = App.useApp();
const handleAuditItem = (state, row) => {
postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id})
.then((json) => {
if (json.errcode === 0) {
message.success(json.errmsg);
if (typeof refresh === 'function') {
refresh();
}
}
})
.catch((ex) => {
notification.error({
message: 'Notification',
description: ex.message,
placement: 'top',
duration: 4,
});
});
};
return (
<div className='flex justify-end gap-4 '>
<div className='flex-auto'></div>
<Button size='small'>导入合同</Button>
<Button size='small'>导出PDF</Button>
<div className='flex justify-end items-center gap-4 h-full'>
<div className='grow'>
<h2 className='m-0 leading-tight'>{title}</h2>
</div>
{/* <Button size='small'>{t('Copy')}</Button> */}
{/* <Button size='small'>{t('Import')}</Button> */}
<Button size='small' type={'primary'} onClick={() => handleAuditItem('2', agency)}>
{t('products:auditStateAction.Published')}
</Button>
{/* <Button size='small' type={'primary'} ghost onClick={() => handleAuditItem('2', agency)}>
{t('products:auditStateAction.Approved')}
</Button> */}
<Button size='small' type={'primary'} danger ghost onClick={() => handleAuditItem('3', agency)}>
{t('products:auditStateAction.Rejected')}
</Button>
{/* todo: export */}
<Button size='small'>{t('Export')} PDF</Button>
</div>
);
};
const TypesTabs = () => {
const PriceTable = ({dataSource,refresh}) => {
const { t } = useTranslation('products');
const [loading, activeAgency, ] = useProductsStore((state) => [state.loading, state.activeAgency, ]);
const { message, notification } = App.useApp();
const stateMapVal = useProductsAuditStatesMapVal();
const handleAuditPriceItem = (state, row) => {
postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id})
.then((json) => {
if (json.errcode === 0) {
message.success(json.errmsg);
if (typeof refresh === 'function') {
refresh();
}
}
})
.catch((ex) => {
notification.error({
message: 'Notification',
description: ex.message,
placement: 'top',
duration: 4,
});
});
};
const columns = [
{ key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) },
{ key: 'adult', title: t('AgeType.Adult'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` },
{ key: 'child', title: t('AgeType.Child'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` },
// {key: 'price', title: t('Currency'), },
// {key: 'currency', title: t('Currency'), },
// {key: 'unit', title: t('Unit'), },
{
key: 'groupSize',
dataIndex: ['group_size_min'],
title: t('GroupSize'),
render: (_, { group_size_min, group_size_max }) => `${group_size_min} - ${group_size_max}`,
},
{
key: 'useDates',
dataIndex: ['use_dates_start'],
title: t('UseDates'),
render: (_, { use_dates_start, use_dates_end }) => `${use_dates_start} ~ ${use_dates_end}`,
},
{ key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays') },
{
key: 'state',
title: t('State'),
render: (_, r) => {
return stateMapVal[`${r.audit_state_id}`]?.label;
},
},
{
title: '价格审核',
key: 'action',
render: (_, r) => r.audit_state_id <= 0 ?(
<Space>
<Button onClick={() => handleAuditPriceItem('2', r)}></Button>
<Button onClick={() => handleAuditPriceItem('3', r)}></Button>
</Space>
) : null,
},
];
return <Table pagination={false} {...{ loading, columns, dataSource }} rowKey={(r) => r.id} />;
}
/**
*
*/
const TypesPanels = (props) => {
const [loading, agencyProducts, ] = useProductsStore((state) => [state.loading, state.agencyProducts]);
// console.log(agencyProducts);
const productsTypes = useProductsTypes();
const [activeKey, setActiveKey] = useState([]);
const [showTypes, setShowTypes] = useState([]);
useEffect(() => {
// ; ,
const hasDataTypes = Object.keys(agencyProducts);
const _show = productsTypes
.filter((kk) => hasDataTypes.includes(kk.value))
.map((ele) => ({
...ele,
children: (
<PriceTable
// loading={loading}
dataSource={agencyProducts[ele.value].reduce((r, c) => r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i === 0 ? c.quotation.length : 0 }))), [])}
refresh={props.refresh}
/>
),
}));
setShowTypes(_show);
setActiveKey(isEmpty(_show) ? [] : [_show[0].key]);
return () => {};
}, [productsTypes, agencyProducts]);
const onCollapseChange = (_activeKey) => {
setActiveKey(_activeKey)
}
return (
<Tabs items={productsTypes} type='card' />
<Collapse items={showTypes} activeKey={activeKey} onChange={onCollapseChange} />
)
}
const Audit = ({ ...props }) => {
const { travel_agency_id, use_year, audit_state } = useParams();
const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]);
const handleGetAgencyProducts = () => {
getAgencyProducts({ travel_agency_id, use_year, audit_state });
}
useEffect(() => {
handleGetAgencyProducts();
return () => {};
}, [travel_agency_id])
return (
<>
<SecondHeaderWrapper header={<Header />}>
<TypesTabs />
<SecondHeaderWrapper header={<Header title={activeAgency.travel_agency_name} agency={activeAgency} refresh={handleGetAgencyProducts} />}>
<hr />
<TypesPanels refresh={handleGetAgencyProducts} />
</SecondHeaderWrapper>
</>
);

@ -1,22 +1,32 @@
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Row, Col, Space, Typography, Table, Button } from 'antd';
import useProductsStore from '@/stores/Products/Index';
import { usingStorage } from '@/hooks/usingStorage';
import { Row, Col, Space, Table } from 'antd';
import SearchForm from '@/components/SearchForm';
import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next';
import { useProductsTypes } from '@/hooks/useProductsSets';
import useProductsStore from '@/stores/Products/Index';
import useFormStore from '@/stores/Form';
import { objectMapper } from '@/utils/commons';
function Index() {
const { t } = useTranslation();
const { userId } = usingStorage();
const [loading, productsList] = useProductsStore((state) => [state.loading, state.productsList]);
const [loading, agencyList, searchAgency] = useProductsStore((state) => [state.loading, state.agencyList, state.searchAgency]);
const [searchValues, setSearchValues] = useProductsStore((state) => [state.searchValues, state.setSearchValues]);
const formValuesToSub = useFormStore(state => state.formValuesToSub);
const handleSearchAgency = (formVal = undefined) => {
const { starttime, endtime, ...param } = formVal || formValuesToSub;
const searchParam = objectMapper(param, { agency: 'travel_agency_ids', startdate: 'edit_date1', enddate: 'edit_date2' });
setSearchValues(searchParam);
searchAgency(searchParam);
}
const [noticeList, setNoticeList] = useState([]);
useEffect(() => {}, []);
useEffect(() => {
// handleSearchAgency();
}, []);
const showTotal = (total) => t('Table.Total', { total });
const showTotal = (total) => `Total ${total} items`;
const columns = [
{ title: t('products:Vendor'), key: 'vendor', dataIndex: 'travel_agency_name' },
{ title: t('products:CreatedBy'), key: 'created_by', dataIndex: 'created_by' },
@ -29,8 +39,8 @@ function Index() {
key: 'action',
render: (_, r) => (
<Space size={'large'}>
<Link to={`/products/${r.travel_agency_id}`}>{t('Edit')}</Link>
<Link to={`/products/${r.travel_agency_id}/audit`}>{t('Audit')}</Link>
<Link to={`/products/${r.travel_agency_id}/${searchValues.year || 'all'}/${searchValues.audit_state || 'all'}/edit`}>{t('Edit')}</Link>
<Link to={`/products/${r.travel_agency_id}/${searchValues.year || 'all'}/${searchValues.audit_state || 'all'}/audit`}>{t('Audit')}</Link>
</Space>
),
},
@ -38,23 +48,37 @@ function Index() {
return (
<Space direction='vertical' style={{ width: '100%' }}>
<SearchForm
initialValue={{
dates: [dayjs().subtract(2, 'M').startOf('M'), dayjs().endOf('M')],
}}
fieldsConfig={{
shows: ['agency', 'auditState', 'dates', 'keyword'], // todo:
shows: ['agency', 'audit_state', 'dates', 'year', 'keyword'], // todo: agency
fieldProps: {
agency: { col: 4 },
dates: { label: t('products:CreateDate') },
keyword: { label: t('products:Title')},
keyword: { label: t('products:Title'), col: 4 },
},
fieldComProps: {
agency: { mode: null }, // todo:
},
sort: { agency: 1, auditState: 2, keyword: 100},
sort: { agency: 1, audit_state: 2, keyword: 100 },
}}
initialValue={{
dates: [dayjs().subtract(2, 'M').startOf('M'), dayjs().endOf('M')],
audit_state: { value: '', label: t('products:State') },
year: dayjs().add(1, 'year'),
}}
onSubmit={(err, formVal, filedsVal) => {
handleSearchAgency(formVal);
}}
/>
<Row>
<Col md={24} lg={24} xxl={24}>
<Table bordered={true} columns={columns} dataSource={productsList} pagination={{ defaultPageSize: 20, showTotal: showTotal }} loading={loading} />
<Table
bordered={true}
columns={columns}
dataSource={agencyList}
pagination={{ defaultPageSize: 20, showTotal: showTotal }}
loading={loading}
rowKey={'travel_agency_id'}
/>
</Col>
<Col md={24} lg={24} xxl={24}></Col>
</Row>

Loading…
Cancel
Save