539 lines
16 KiB
Python
539 lines
16 KiB
Python
import React, { createRef, Fragment, useEffect, useState } from 'react';
|
|
import {
|
|
Breadcrumb,
|
|
BreadcrumbItem,
|
|
Card,
|
|
CardBody,
|
|
Col,
|
|
DropdownItem,
|
|
DropdownMenu,
|
|
DropdownToggle,
|
|
Form,
|
|
FormGroup,
|
|
Input,
|
|
Label,
|
|
Media,
|
|
Row,
|
|
UncontrolledDropdown,
|
|
Spinner,
|
|
CustomInput,
|
|
Button,
|
|
Modal,
|
|
ModalHeader,
|
|
ModalBody,
|
|
ModalFooter,
|
|
} from 'reactstrap';
|
|
import Cascader from 'rc-cascader';
|
|
import loadable from '@loadable/component';
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
import { Link } from 'react-router-dom';
|
|
import Flex from '../../common/Flex';
|
|
import { getCookieValue, createCookie } from '../../../helpers/utils';
|
|
import withRedirect from '../../../hoc/withRedirect';
|
|
import { withTranslation } from 'react-i18next';
|
|
import { toast } from 'react-toastify';
|
|
import ButtonIcon from '../../common/ButtonIcon';
|
|
import { APIBaseURL } from '../../../config';
|
|
import { Steps, Timeline, Collapse, message } from 'antd';
|
|
|
|
const { Step } = Steps;
|
|
const { Panel } = Collapse;
|
|
|
|
const TicketList = ({ setRedirect, setRedirectUrl, t }) => {
|
|
useEffect(() => {
|
|
let is_logged_in = getCookieValue('is_logged_in');
|
|
let user_name = getCookieValue('user_name');
|
|
let user_display_name = getCookieValue('user_display_name');
|
|
let user_uuid = getCookieValue('user_uuid');
|
|
let token = getCookieValue('token');
|
|
if (is_logged_in === null || !is_logged_in) {
|
|
setRedirectUrl(`/authentication/basic/login`);
|
|
setRedirect(true);
|
|
} else {
|
|
//update expires time of cookies
|
|
createCookie('is_logged_in', true, 1000 * 60 * 60 * 8);
|
|
createCookie('user_name', user_name, 1000 * 60 * 60 * 8);
|
|
createCookie('user_display_name', user_display_name, 1000 * 60 * 60 * 8);
|
|
createCookie('user_uuid', user_uuid, 1000 * 60 * 60 * 8);
|
|
createCookie('token', token, 1000 * 60 * 60 * 8);
|
|
}
|
|
});
|
|
let table = createRef();
|
|
|
|
// State
|
|
const [ticketTypes, setTicketTypes] = useState([]);
|
|
const [ticketList, setTicketList] = useState([]);
|
|
const [ticketApplicationfields, setTicketApplicationfields] = useState({});
|
|
const [ticketListAll, setTicketListAll] = useState([]);
|
|
|
|
// Select Button
|
|
const [selectedTicketType, setSelectedTicketType] = useState(1);
|
|
const [selectedTicketTypeName, setSelectedTicketTypeName] = useState('');
|
|
|
|
// View Button
|
|
const [modalIsShown, setModalIsShown] = useState(false);
|
|
const [openedModalName, setOpenedModalName] = useState('');
|
|
const [ticketStatus, setTicketStatus] = useState({});
|
|
|
|
useEffect(() => {
|
|
// Get Ticket Type
|
|
getTicketTypes();
|
|
getTicketList();
|
|
}, []);
|
|
|
|
const getTicketTypes = () => {
|
|
let isResponseOK = false;
|
|
fetch(APIBaseURL + '/ticket/types', {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-type': 'application/json'
|
|
},
|
|
body: null
|
|
})
|
|
.then(response => {
|
|
console.log(response);
|
|
if (response.ok) {
|
|
isResponseOK = true;
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(json => {
|
|
if (isResponseOK) {
|
|
setTicketTypes(json);
|
|
setSelectedTicketType(json[0].id);
|
|
setSelectedTicketTypeName(json[0].name);
|
|
} else {
|
|
toast.error(json.description);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.log(err);
|
|
});
|
|
};
|
|
const getTicketList = () => {
|
|
let isResponseOK = false;
|
|
fetch(APIBaseURL + '/ticket/list/completed' + '?username=admin', {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-type': 'application/json'
|
|
},
|
|
body: null
|
|
})
|
|
.then(response => {
|
|
console.log(response);
|
|
if (response.ok) {
|
|
isResponseOK = true;
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(json => {
|
|
if (isResponseOK) {
|
|
console.log('/ticket/list', json);
|
|
setTicketList(json);
|
|
setTicketListAll(json);
|
|
} else {
|
|
toast.error(json.description);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.log(err);
|
|
});
|
|
};
|
|
const DetailedDataTable = loadable(() => import('../common/DetailedDataTable'));
|
|
|
|
const actionFormatter = (dataField, { id }) => (
|
|
// Control your row with this id
|
|
<UncontrolledDropdown>
|
|
<DropdownToggle color="link" size="sm" className="text-600 btn-reveal mr-3">
|
|
<FontAwesomeIcon icon="ellipsis-h" className="fs--1" />
|
|
</DropdownToggle>
|
|
<DropdownMenu right className="border py-2">
|
|
<DropdownItem onClick={viewTicketStatusModal.bind(this, id)}>{t('View State')}</DropdownItem>
|
|
<DropdownItem onClick={viewTicketRecordModal.bind(this, id)}>{t('View Record')}</DropdownItem>
|
|
<DropdownItem onClick={viewTicketWorkflowModal.bind(this, id)}>{t('View Workflow')}</DropdownItem>
|
|
</DropdownMenu>
|
|
</UncontrolledDropdown>
|
|
);
|
|
let hiddenModal = () => {
|
|
setModalIsShown(false);
|
|
};
|
|
|
|
let viewTicketStatusModal = (id, e) => {
|
|
console.log('View Ticket Status', id);
|
|
// Get Ticket Status
|
|
let isResponseOK = false;
|
|
fetch(APIBaseURL + '/ticket/status/' + id + '?username=admin', {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-type': 'application/json'
|
|
},
|
|
body: null
|
|
})
|
|
.then(response => {
|
|
console.log(response);
|
|
if (response.ok) {
|
|
isResponseOK = true;
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(json => {
|
|
if (isResponseOK) {
|
|
let temp = json.data;
|
|
let current_id;
|
|
temp.value.map((item, index) => {
|
|
console.log(
|
|
'/ticket/status ---',
|
|
item.state_id,
|
|
temp.current_state_id,
|
|
item.state_id == temp.current_state_id
|
|
);
|
|
if (item.state_id == temp.current_state_id) {
|
|
current_id = index;
|
|
}
|
|
});
|
|
console.log('temp', temp);
|
|
setTicketStatus({ ...temp, current_id: current_id });
|
|
setModalIsShown(true);
|
|
setOpenedModalName('Status');
|
|
} else {
|
|
toast.error(json.description);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.log(err);
|
|
});
|
|
};
|
|
|
|
let viewTicketRecordModal = (id, e) => {
|
|
console.log('View Ticket Record', id);
|
|
// Get Ticket Status
|
|
let isResponseOK = false;
|
|
fetch(APIBaseURL + '/ticket/status/' + id + '?username=admin', {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-type': 'application/json'
|
|
},
|
|
body: null
|
|
})
|
|
.then(response => {
|
|
console.log(response);
|
|
if (response.ok) {
|
|
isResponseOK = true;
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(json => {
|
|
if (isResponseOK) {
|
|
let temp = json.data;
|
|
setTicketStatus({ ...temp });
|
|
setModalIsShown(true);
|
|
setOpenedModalName('Record');
|
|
} else {
|
|
toast.error(json.description);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.log(err);
|
|
});
|
|
};
|
|
|
|
let viewTicketWorkflowModal = (id, e) => {
|
|
console.log('View Ticket Workflow', id);
|
|
setModalIsShown(true);
|
|
setOpenedModalName('Workflow');
|
|
};
|
|
|
|
// Get Modal
|
|
let getModalHeader = () => {
|
|
switch (openedModalName) {
|
|
case 'Status':
|
|
console.log('switch status');
|
|
return <ModalHeader>{t('Ticket Status')}</ModalHeader>;
|
|
|
|
case 'Record':
|
|
console.log('switch record');
|
|
return <ModalHeader>{t('Ticket Record')}</ModalHeader>;
|
|
|
|
case 'Workflow':
|
|
console.log('switch workflow');
|
|
return <ModalHeader>{t('Ticket Workflow')}</ModalHeader>;
|
|
|
|
default:
|
|
console.log('switch none');
|
|
return <ModalHeader>{t('Ticket None')}</ModalHeader>;
|
|
}
|
|
};
|
|
|
|
let getModalBody = () => {
|
|
switch (openedModalName) {
|
|
case 'Status':
|
|
console.log('switch status');
|
|
return (
|
|
<ModalBody>
|
|
{ticketStatus && (
|
|
<Steps current={ticketStatus.current_id}>
|
|
{ticketStatus.value.map((item, index) => {
|
|
let description = '';
|
|
if (index < ticketStatus.current_id) {
|
|
let description_user = item.state_flow_log_list[0].participant_info.participant_alias;
|
|
let description_operation = item.state_flow_log_list[0].gmt_created;
|
|
let description_time = item.state_flow_log_list[0].transition.transition_name;
|
|
description = description_user + description_operation + '@' + description_time;
|
|
}
|
|
return <Step key={item.state_id} title={item.state_name} description="" />;
|
|
})}
|
|
</Steps>
|
|
)}
|
|
</ModalBody>
|
|
);
|
|
|
|
case 'Record':
|
|
console.log('switch Record');
|
|
return (
|
|
<ModalBody>
|
|
{ticketStatus && (
|
|
<Timeline mode="left">
|
|
<Timeline.Item key={100} label={''} />
|
|
{ticketStatus.value
|
|
.filter(item => item.state_flow_log_list.length > 0)
|
|
.map((item, index) => {
|
|
let description_user = item.state_flow_log_list[0].participant_info.participant_alias;
|
|
let description_time = item.state_flow_log_list[0].gmt_created;
|
|
let description_operation = item.state_flow_log_list[0].transition.transition_name;
|
|
let description = "用户'" + description_user + "'发起了'" + description_operation + "'操作";
|
|
return (
|
|
<Timeline.Item key={index} label={description_time}>
|
|
{description}
|
|
</Timeline.Item>
|
|
);
|
|
})}
|
|
<Timeline.Item key={999} label={''} />
|
|
</Timeline>
|
|
)}
|
|
</ModalBody>
|
|
);
|
|
|
|
case 'Workflow':
|
|
console.log('switch workflow');
|
|
return <ModalBody>{t('Ticket Workflow')}</ModalBody>;
|
|
|
|
default:
|
|
console.log('switch none');
|
|
return <ModalBody>{}</ModalBody>;
|
|
}
|
|
};
|
|
|
|
let getModalFooter = () => {
|
|
switch (openedModalName) {
|
|
case 'Status':
|
|
return (
|
|
<ModalHeader>
|
|
<Button onClick={hiddenModal}>{t('Close')}</Button>
|
|
</ModalHeader>
|
|
);
|
|
|
|
case 'Record':
|
|
return (
|
|
<ModalHeader>
|
|
<Button onClick={hiddenModal}>{t('Close')}</Button>
|
|
</ModalHeader>
|
|
);
|
|
|
|
case 'Workflow':
|
|
return (
|
|
<ModalHeader>
|
|
<Button onClick={hiddenModal}>{t('Close')}</Button>
|
|
</ModalHeader>
|
|
);
|
|
|
|
default:
|
|
return (
|
|
<ModalHeader>
|
|
<Button onClick={hiddenModal}>{t('Close')}</Button>
|
|
</ModalHeader>
|
|
);
|
|
}
|
|
};
|
|
|
|
const columns = [
|
|
{
|
|
key: 'a00',
|
|
dataField: 'id',
|
|
headerClasses: 'border-0',
|
|
text: t('ID'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
{
|
|
key: 'a0',
|
|
dataField: 'title',
|
|
headerClasses: 'border-0',
|
|
text: t('Title'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
{
|
|
key: 'a1',
|
|
dataField: 'ticket_type',
|
|
headerClasses: 'border-0',
|
|
text: t('Ticket Type'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
{
|
|
key: 'a2',
|
|
dataField: 'sn',
|
|
headerClasses: 'border-0',
|
|
text: t('Serial Number'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
{
|
|
key: 'a3',
|
|
dataField: 'state',
|
|
headerClasses: 'border-0',
|
|
text: t('Ticket State'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
{
|
|
key: 'a4',
|
|
dataField: 'creator',
|
|
headerClasses: 'border-0',
|
|
text: t('Ticket Creator'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
{
|
|
key: 'a5',
|
|
dataField: 'gmt_created',
|
|
headerClasses: 'border-0',
|
|
text: t('Created Time'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
{
|
|
key: 'a6',
|
|
dataField: 'gmt_modified',
|
|
headerClasses: 'border-0',
|
|
text: t('Modified Time'),
|
|
classes: 'border-0 py-2 align-middle',
|
|
sort: true
|
|
},
|
|
|
|
{
|
|
key: 'a000',
|
|
dataField: '',
|
|
headerClasses: 'border-0',
|
|
text: '',
|
|
classes: 'border-0 py-2 align-middle',
|
|
formatter: actionFormatter,
|
|
align: 'right'
|
|
}
|
|
];
|
|
|
|
const labelClasses = 'ls text-uppercase text-600 font-weight-semi-bold mb-0';
|
|
|
|
let onWorkflowTypeChange = ({ target }) => {
|
|
setSelectedTicketType(target.value);
|
|
ticketTypes.map(ticketType => {
|
|
if (ticketType.id == target.value) {
|
|
setSelectedTicketTypeName(ticketType.name);
|
|
console.log('onWorkflowTypeChange', ticketType.name, ticketType.id, target.value);
|
|
}
|
|
});
|
|
};
|
|
|
|
let searchTicket = () => {
|
|
setTicketList(ticketListAll.filter(item => item.ticket_type === selectedTicketTypeName));
|
|
};
|
|
|
|
useEffect(() => {
|
|
console.log('ticketApplicationfields', ticketApplicationfields);
|
|
}, [ticketApplicationfields]);
|
|
|
|
useEffect(() => {
|
|
console.log('ticketList', ticketList);
|
|
}, [ticketList]);
|
|
|
|
useEffect(() => {
|
|
console.log('ticketListAll', ticketListAll);
|
|
}, [ticketListAll]);
|
|
|
|
useEffect(() => {
|
|
console.log('ticketTypes', ticketTypes);
|
|
}, [ticketTypes]);
|
|
|
|
useEffect(() => {
|
|
console.log('selectedTicketType', selectedTicketType);
|
|
}, [selectedTicketType]);
|
|
|
|
useEffect(() => {
|
|
console.log('selectedTicketTypeName', selectedTicketTypeName);
|
|
}, [selectedTicketTypeName]);
|
|
|
|
useEffect(() => {
|
|
console.log('ticketStatus', ticketStatus);
|
|
}, [ticketStatus]);
|
|
|
|
useEffect(() => {
|
|
console.log('modalIsShown', modalIsShown);
|
|
}, [modalIsShown]);
|
|
|
|
useEffect(() => {
|
|
console.log('openedModalName', openedModalName);
|
|
}, [openedModalName]);
|
|
|
|
return (
|
|
<Fragment>
|
|
<div>
|
|
<Breadcrumb>
|
|
<BreadcrumbItem>{t('Ticket')}</BreadcrumbItem>
|
|
<BreadcrumbItem active>{t('Ticket List')}</BreadcrumbItem>
|
|
</Breadcrumb>
|
|
</div>
|
|
<Card className="bg-light mb-3">
|
|
<CardBody className="p-3">
|
|
<Form>
|
|
<Row form>
|
|
<Col xs="auto">
|
|
<FormGroup>
|
|
<Label className={labelClasses} for="workflowType">
|
|
{t('Ticket Type')}
|
|
</Label>
|
|
<CustomInput type="select" id="workflowType" name="workflowType" onChange={onWorkflowTypeChange}>
|
|
{ticketTypes.map((ticketType, index) => (
|
|
<option value={ticketType.id} key={ticketType.id}>
|
|
{t(ticketType.name)}
|
|
</option>
|
|
))}
|
|
</CustomInput>
|
|
</FormGroup>
|
|
</Col>
|
|
<Col xs="auto">
|
|
<br />
|
|
<ButtonIcon
|
|
icon="external-link-alt"
|
|
transform="shrink-3 down-2"
|
|
color="falcon-default"
|
|
onClick={searchTicket}
|
|
>
|
|
{t('Apply')}
|
|
</ButtonIcon>
|
|
</Col>
|
|
</Row>
|
|
</Form>
|
|
</CardBody>
|
|
</Card>
|
|
<Modal size="xl" isOpen={modalIsShown}>
|
|
{getModalHeader()}
|
|
{getModalBody()}
|
|
{getModalFooter()}
|
|
</Modal>
|
|
<DetailedDataTable data={ticketList} title={t('Ticket List')} columns={columns} pagesize={10} />
|
|
</Fragment>
|
|
);
|
|
};
|
|
|
|
export default withTranslation()(withRedirect(TicketList));
|