unclean SPARC
This commit is contained in:
@@ -0,0 +1,358 @@
|
||||
class SupportView extends EICDomContent {
|
||||
|
||||
TICKET_STATUSES = [
|
||||
{id: 0, label: '', severity: ''},
|
||||
{id: 1, label: 'new', severity: 'info'},
|
||||
{id: 2, label: 'opened', severity: 'warning'},
|
||||
{id: 3, label: 'opened', severity: 'warning'},
|
||||
{id: 4, label: 'closed', severity: 'success'},
|
||||
{id: 5, label: '(state5)', severity: 'danger'},
|
||||
{id: 6, label: '(state6)', severity: 'danger'},
|
||||
{id: 7, label: 'opened', severity: 'warning'},
|
||||
]
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
Object.assign(this, app.helpers.basicDialogs)
|
||||
}
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.components = ui.eicfy(this.el)
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.gridTickets = new DataGrid(this.find('.support-tickets .list [eicdatagrid]'), {
|
||||
headers: [
|
||||
{ label: 'Ticket', filter: 'text', sortable: true},
|
||||
{ label: 'Modified', type: 'dateTime', sortable: true},
|
||||
{ label: 'Status', type: 'markup', filter: 'list', sortable: true},
|
||||
]
|
||||
});
|
||||
this.btTicketsRefresh = new Button(null, {icon: 'icon-refresh', severity: 'primary', rounded: true, size: 'xsmall', hint: 'Refresh the list'})
|
||||
this.btTicketsRefresh.addEventListener('click', this.refreshTicketsList.bind(this));
|
||||
this.gridTickets.el.querySelector('header .cell.actions').append(this.btTicketsRefresh.el);
|
||||
|
||||
this.gridTickets.onRowClick = this.onTicketSelect.bind(this);
|
||||
|
||||
this.gridConsole = new DataGrid(this.find('.support-console [eicdatagrid]'), {
|
||||
headers: [
|
||||
{ label: 'Time', filter: 'text', sortable: true},
|
||||
{ label: 'Level', filter: 'list', sortable: true},
|
||||
{ label: 'Error', filter: 'text', sortable: true},
|
||||
{ label: 'Url', filter: 'text', sortable: true},
|
||||
],
|
||||
actions: [],
|
||||
height: '30vh'
|
||||
});
|
||||
this.gridConsole.onRowClick = this.showErrDetails.bind(this)
|
||||
|
||||
this.ticketDetail = this.find('.support-tickets .inspector');
|
||||
ui.hide(this.ticketDetail);
|
||||
|
||||
this.btReply = this.find('.support-tickets .inspector footer .cta button');
|
||||
this.btReply.addEventListener('click', this.onTicketReplyOpen.bind(this))
|
||||
|
||||
this.attachments = this.components.find(item => item.el.name == 'attachments')
|
||||
this.attachments.activeEditor = BinaryFileContentSelector;
|
||||
|
||||
this.replyForm = new Form(this.find('.support-tickets .inspector footer .reply-form'));
|
||||
ui.hide(this.replyForm.el);
|
||||
|
||||
this.replyForm.el.querySelector('button.send').addEventListener('click', this.onTicketReply.bind(this))
|
||||
this.replyForm.el.querySelector('button.cancel').addEventListener('click', this.onTicketReplyCancel.bind(this))
|
||||
|
||||
this.btIssueNew = this.components.find(item => item.el.classList.contains('create'));
|
||||
this.btIssueNew.addEventListener('click', this.onTicketCreate.bind(this));
|
||||
|
||||
this.refreshConsole();
|
||||
|
||||
this.refreshTicketsList()
|
||||
}
|
||||
|
||||
DOMContentFocused() {
|
||||
if(!this.refreshTo) this.refreshTo = setTimeout(this.checkUp2Date.bind(this),500)
|
||||
this.refreshConsole();
|
||||
}
|
||||
|
||||
DOMContentBlured() {
|
||||
if(this.refreshTo) {
|
||||
clearTimeout(this.refreshTo)
|
||||
this.refreshTo = null
|
||||
}
|
||||
}
|
||||
|
||||
refreshTicketsList(event=null){
|
||||
if(event){
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
this.gridTickets.loading = true;
|
||||
this.tickets.list()
|
||||
.then(this.refreshTickets.bind(this));
|
||||
}
|
||||
|
||||
refreshTickets(payload) {
|
||||
|
||||
this.gridTickets.loading = false;
|
||||
this.gridTickets.clear();
|
||||
|
||||
let tickets = payload.reverse();
|
||||
|
||||
if(tickets.length > 0) {
|
||||
for(let ticket of tickets) {
|
||||
|
||||
let status = this.TICKET_STATUSES.find(item => item.id == (ticket.state.id || 0))
|
||||
this.gridTickets.addRow(ticket.id, [
|
||||
`${ticket.title} (#${ticket.number})`,
|
||||
ticket.state.lastActivity,
|
||||
`<span eicchip xsmall ${status.severity}>${status.label}</span>`,
|
||||
], true);
|
||||
}
|
||||
this.gridTickets.filter()
|
||||
this.tickets.get(tickets[0].id)
|
||||
.then(this.refreshInspector.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
clearInspector() {
|
||||
ui.show(this.ticketDetail, 'grid');
|
||||
this.ticketDetail.classList.remove('loading');
|
||||
this.ticketDetail.querySelector('header h1').innerHTML = ''
|
||||
this.ticketDetail.querySelector('header h2').innerHTML = ''
|
||||
this.ticketDetail.querySelector('section').innerHTML = '';
|
||||
this.replyForm.el.querySelector('textarea').innerHTML = '';
|
||||
this.attachments.clear();
|
||||
}
|
||||
|
||||
freezeInspector() {
|
||||
ui.show(this.ticketDetail, 'grid');
|
||||
this.ticketDetail.classList.add('loading');
|
||||
}
|
||||
|
||||
refreshInspector(payload) {
|
||||
let ticket = payload.ticket;
|
||||
let articles = payload.articles.reverse();
|
||||
|
||||
this.clearInspector();
|
||||
|
||||
let content = this.ticketDetail.querySelector('section');
|
||||
this.ticketDetail.querySelector('header h1').innerHTML = `${ticket.title} (#${ticket.number})`;
|
||||
this.ticketDetail.querySelector('header h2').innerHTML = ticket.typeofissue || 'General issue';
|
||||
this.replyForm.el.querySelector('button.send').dataset.id = ticket.id;
|
||||
|
||||
let lastDate = null;
|
||||
|
||||
for(let article of articles) {
|
||||
let message = article.body
|
||||
.split("\n").join('<br/>')
|
||||
.split(' -> ').join(' → ')
|
||||
let currentDate = ui.format.date( article.created )
|
||||
if(lastDate != currentDate ) {
|
||||
content.append(ui.create(`<div class="center" small>${currentDate}</div>`))
|
||||
lastDate = currentDate;
|
||||
}
|
||||
switch(article.sender) {
|
||||
case 'Agent':
|
||||
case 'System':
|
||||
content.append(ui.create(`<div class="cols-1 left sender"><span eicchip primary xsmall><i class="icon-user"></i><span>EIC support wrote</span></span></div>`))
|
||||
break;
|
||||
case 'Customer':
|
||||
content.append(ui.create(`<div class="cols-1 right sender"><span eicchip success xsmall><i class="icon-user"></i><span>You wrote</span></span></div>`))
|
||||
break;
|
||||
}
|
||||
|
||||
let thread = ui.create(`<div class="content"><div class="subject">${article.subject || ''}</div><div class="message">${message}</div><div class="attachments"></div></div>`)
|
||||
content.append(thread)
|
||||
|
||||
for(let file of article.attachments) {
|
||||
let button = new Button(null, {
|
||||
label: file.filename,
|
||||
severity: 'info',
|
||||
size: 'xsmall',
|
||||
rounded: true,
|
||||
icon: 'icon-attachment'
|
||||
});
|
||||
|
||||
button.el.dataset.ticket = ticket.id;
|
||||
button.el.dataset.article = article.id;
|
||||
button.el.dataset.attachment = file.id;
|
||||
button.el.addEventListener('click', this.onFileOpen.bind(this));
|
||||
|
||||
thread.querySelector('.attachments').append(button.el)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshConsole() {
|
||||
this.gridConsole.clear();
|
||||
for(let err of app.latestErrors) {
|
||||
this.gridConsole.addRow(err.errId,
|
||||
[ err.timestamp,
|
||||
`<span class="${err.level.toLowerCase()}">${err.level}</span>`,
|
||||
err.message,
|
||||
err.url,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
checkUp2Date() {
|
||||
let mustRefresh = false
|
||||
if(app.latestErrors.lengh != this.gridConsole.length) mustRefresh=true
|
||||
else {
|
||||
for(let err of app.latestErrors) {
|
||||
if(!this.gridConsole.getRowById(err.errId)) { // err not in grid (the opposite should not happen)
|
||||
mustRefresh = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if(mustRefresh) this.refreshConsole()
|
||||
this.refreshTo = setTimeout(this.checkUp2Date.bind(this),500)
|
||||
}
|
||||
|
||||
onFileOpen(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let ticketId = event.currentTarget.dataset.ticket;
|
||||
let articleId = event.currentTarget.dataset.article;
|
||||
let attachmentId = event.currentTarget.dataset.attachment;
|
||||
|
||||
this.tickets.download(ticketId, articleId, attachmentId);
|
||||
}
|
||||
|
||||
onTicketSelect(target) {
|
||||
|
||||
this.closeReplyForm();
|
||||
this.freezeInspector();
|
||||
this.tickets.get(target.dataset.id)
|
||||
.then(this.refreshInspector.bind(this));
|
||||
}
|
||||
|
||||
async onTicketCreate(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let ticket = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'common/support/dialogs/SupportIssueFormDialog',
|
||||
{ title: 'Report an issue' },
|
||||
{
|
||||
mode: 'create',
|
||||
models: { tickets: this.tickets }
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(ticket) {
|
||||
this.tickets.list()
|
||||
.then(this.refreshTickets.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
closeReplyForm() {
|
||||
ui.show(this.ticketDetail.querySelector('footer .cta'), 'grid');
|
||||
ui.hide(this.ticketDetail.querySelector('footer .reply-form'));
|
||||
}
|
||||
|
||||
openReplyForm() {
|
||||
ui.hide(this.ticketDetail.querySelector('footer .cta'));
|
||||
ui.show(this.ticketDetail.querySelector('footer .reply-form'), 'grid');
|
||||
}
|
||||
|
||||
onTicketReplyOpen(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.openReplyForm();
|
||||
}
|
||||
|
||||
async onTicketReply(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let ticketId = event.currentTarget.dataset.id;
|
||||
|
||||
if(this.replyForm.validate()) {
|
||||
let payload = this.replyForm.value;
|
||||
payload.attachments = this.attachments.selectedItems.map(item => { return { filename: item.label, "mime-type": item.type, data: item.content} })
|
||||
|
||||
let size = JSON.stringify(payload).length
|
||||
let limit = 1024 * 1024 * 5;
|
||||
if(size < limit) {
|
||||
this.tickets.update(ticketId, payload)
|
||||
.then( this.onReplySuccess.bind(this, ticketId))
|
||||
} else {
|
||||
ui.growl.append('Your message is exceeding the allowed 5Mb size limit. Please consider removing files or reduce file size', 'danger')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onReplySuccess(id, payload) {
|
||||
|
||||
this.replyForm.el.querySelector('textarea').value = '';
|
||||
this.attachments.clear();
|
||||
|
||||
this.closeReplyForm();
|
||||
|
||||
this.tickets.list().then( this.refreshTickets.bind(this));
|
||||
this.tickets.get(id).then(this.refreshInspector.bind(this));
|
||||
}
|
||||
|
||||
onTicketReplyCancel(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.closeReplyForm();
|
||||
}
|
||||
|
||||
async showErrDetails(row, event) {
|
||||
let err = app.latestErrors.find(err=>err.errId==row.dataset['id'])
|
||||
if(!err) return
|
||||
let trace = '<div class="trace">'
|
||||
for(let step of err.stacktrace) {
|
||||
if(step[2].length<2) continue
|
||||
trace += `<div class="traceline">${step[0]} @ ${step[1]} ${step[2].join(':')}</div>`
|
||||
}
|
||||
trace += '</div>'
|
||||
|
||||
let messageData = ''
|
||||
if(Array.isArray(err.messageData)) {
|
||||
messageData = '<div class="msgdata">'+err.messageData.map(x=>JSON.stringify(x)).join('</div><div class="msgdata">')+'</div>'
|
||||
} else if(err.messageData) messageData = '<div class="msgdata">'+err.messageData+'</div>'
|
||||
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Error Details',
|
||||
message: `<div eicdatagrid class="console-error-detail" footer="hidden" header="hidden"></div>`,
|
||||
cancelLabel: 'Close',
|
||||
okLabel: '',
|
||||
severity: 'primary',
|
||||
muted: true,
|
||||
okPromise:() => { },
|
||||
contentLoaded: (el => {
|
||||
el.style['max-width'] = '80vw'
|
||||
el.style['max-height'] = '50vw'
|
||||
this.gridEntry = new DataGrid(el.querySelector('.support-entryGrid'), {
|
||||
headers: [
|
||||
{ label: ''},
|
||||
{ label: ''}
|
||||
],
|
||||
height: 'auto'
|
||||
|
||||
});
|
||||
this.gridEntry.addRow('time', ['Time', err.timestamp])
|
||||
this.gridEntry.addRow('level', ['Level', `<span class="${err.level.toLowerCase()}">${err.level}</span>`])
|
||||
this.gridEntry.addRow('message', ['Message', `<span class="${err.level.toLowerCase()}">${err.message}</span><br>${messageData}`])
|
||||
this.gridEntry.addRow('user', ['User UID', err.user.identity.uuid])
|
||||
this.gridEntry.addRow('user', ['User Roles', err.user.roles.join(', ')])
|
||||
this.gridEntry.addRow('url', ['Url', err.url])
|
||||
this.gridEntry.addRow('stacktrace', ['Trace', trace])
|
||||
}).bind(this),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SupportView',SupportView);
|
||||
|
||||
Reference in New Issue
Block a user