291 lines
12 KiB
JavaScript
291 lines
12 KiB
JavaScript
class MailingDashboardView extends EICDomContent {
|
|
|
|
constructor() {
|
|
super()
|
|
Object.assign(this, app.helpers.activeAttributes)
|
|
this.selectedMailingId = null
|
|
this.tileMarkup = app.Assets.Store.html['/app/assets/html/mailing/tile.html']
|
|
}
|
|
|
|
DOMContentLoaded(options) {
|
|
for(let model in options.models) this[model] = options.models[model]
|
|
const components = ui.eicfy(this.el)
|
|
this.setupTriggers(components)
|
|
this.setupRefs(components)
|
|
this.setupStatusButtons()
|
|
|
|
this.initializeData()
|
|
}
|
|
|
|
DOMContentFocused(options) {
|
|
// Avoid 2nd refesh on DomContentLoaded
|
|
if(this.wasBlured){ this.refreshSearch() }
|
|
this.wasBlured = false
|
|
}
|
|
|
|
DOMContentBlured(options) { this.wasBlured = true }
|
|
|
|
async initializeData() {
|
|
const result = await this.mailings.getReadableFolders()
|
|
this.authorizedPaths = result.authorizedPaths || []
|
|
|
|
if (this.authorizedPaths.length === 0) {
|
|
this.output('searchResults', `<div style="padding: 2rem; text-align: center;">
|
|
<h2 style="color:#b00020;">Access Denied</h2>
|
|
<p>You're not authorized to access to mailing dashboard</p>
|
|
</div>`)
|
|
return
|
|
}
|
|
|
|
this.components.searchCriteria.value = [app.User.identity.uuid]
|
|
}
|
|
|
|
setupStatusButtons(){
|
|
this.statusButtons = {}
|
|
this.statusCounters = {}
|
|
for(let status in this.mailings.getStatusLabel()){
|
|
this.statusButtons[status] = new Button(null, { rounded: true, size: 'xsmall', severity: 'info', label: `<span>${this.mailings.getStatusLabel()[status]}</span>`})
|
|
this.statusButtons[status].click = this.onStatusChange.bind(this, status)
|
|
this.statusCounters[status] = new Badge(null, {size: 'xxsmall'})
|
|
this.statusButtons[status].el.append(this.statusCounters[status].el)
|
|
this.find('.filters').append(this.statusButtons[status].el)
|
|
}
|
|
}
|
|
|
|
onStatusChange(status, event){
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
this.statusButtons[status].severity = (this.statusButtons[status].severity == 'secondary') ? 'info' : 'secondary'
|
|
if(this.getSelectedStatus().length == 0) { // turning off last status resets them all (no-status is meaningless)
|
|
for(let status in this.statusButtons) this.statusButtons[status].severity = 'info'
|
|
}
|
|
this.refreshSearch()
|
|
}
|
|
|
|
onOneStatus(event){
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
for(let status in this.statusButtons){
|
|
this.statusButtons[status].severity = (status == event.target.dataset.status) ? 'info' : 'secondary'
|
|
}
|
|
this.refreshSearch()
|
|
}
|
|
|
|
onSearchChange(component, event){
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
this.refreshSearch()
|
|
}
|
|
|
|
async refreshSearch(){
|
|
this.output('searchResults','')
|
|
this.setAsyncLoading(true)
|
|
const mailings = await this.mailings.search(this.components.searchCriteria.value, this.getSelectedStatus())
|
|
this.setAsyncLoading(false)
|
|
this.fillResults(mailings)
|
|
}
|
|
|
|
getSelectedStatus(){
|
|
return(
|
|
Object.keys(this.mailings.getStatusLabel()).reduce( (acc, status) => {
|
|
if(!this.statusButtons[status].el.hasAttribute('secondary')) acc.push(status)
|
|
return(acc)
|
|
}, [] )
|
|
)
|
|
}
|
|
|
|
updateStatusCounts(counts){
|
|
for(let status in this.mailings.getStatusLabel()){
|
|
this.statusCounters[status].value = (status in counts) ? counts[status] : 0
|
|
}
|
|
}
|
|
|
|
fillResults(mailings){
|
|
this.output('searchResults', '')
|
|
|
|
if (!this.authorizedPaths || this.authorizedPaths.length === 0) {
|
|
this.output('searchResults', `<div style="padding: 2rem; text-align: center;">
|
|
<h2 style="color:#b00020;">Access Denied</h2>
|
|
<p>You're not authorized to access to view this mailing</p>
|
|
</div>`)
|
|
return
|
|
}
|
|
|
|
mailings = mailings.filter(mailing =>
|
|
this.authorizedPaths.some(path => mailing.path.startsWith(path))
|
|
)
|
|
|
|
let statusCounts = {}
|
|
if (mailings.length === 0) {
|
|
this.output('searchResults', `<div class="empty-message">No mailings found for your search and access rights.</div>`)
|
|
this.updateStatusCounts(statusCounts)
|
|
return
|
|
}
|
|
|
|
for(let mailing of mailings){
|
|
if(mailing.status in statusCounts) statusCounts[mailing.status]++
|
|
else statusCounts[mailing.status] = 1
|
|
const lastStatus = this.mailings.getLatestStatus(mailing)
|
|
const dirs = mailing.path.split('/').filter(item=>item)
|
|
const pathChips = dirs.map((dir, idx) => {
|
|
const path = dirs.slice(0, idx+1).join('/')
|
|
return(`<span eicchip xxsmall info data-path="/${path}">${dir}</span>`)
|
|
}).join('')
|
|
const tile = ui.create(Controller.processTemplate('resultTile',
|
|
this.tileMarkup,
|
|
{ mid: mailing.id,
|
|
name: mailing.name ? mailing.name : 'Untitled',
|
|
status: mailing.status,
|
|
statusLabel: this.mailings.getStatusLabel()[mailing.status],
|
|
path: mailing.path,
|
|
pathChips: pathChips,
|
|
lastUpdate: (new Intl.DateTimeFormat("fr-FR", {timeZone: "Europe/Paris", dateStyle:'medium', timeStyle:'medium'})).format(new Date(lastStatus.dateTime)), //'23 Aug 2024 16:48:01',
|
|
lastContributor: `${lastStatus.changedBy.firstname} ${lastStatus.changedBy.lastname}`,
|
|
kpis: {
|
|
nbRecipients: mailing.nbRecipients || 0,
|
|
nbPending: (mailing.status=='sent') ? mailing.nbRecipients - mailing.kpis.bouncesCount -mailing.kpis.readCount : 'N/A',
|
|
nbBounced: (mailing.status=='sent') ? mailing.kpis.bouncesCount : 'N/A',
|
|
nbReached: (mailing.status=='sent') ? mailing.kpis.readCount : 'N/A',
|
|
},
|
|
}
|
|
))
|
|
if(!mailing.nbRecipients || (mailing.nbRecipients.length==0)) {
|
|
tile.querySelector('[data-trigger="onRecipientsList"]').closest('li[menuitem]').setAttribute('disabled','')
|
|
}
|
|
if(!mailing.kpis.bouncesCount || (mailing.kpis.bouncesCount.length==0)) {
|
|
tile.querySelector('[data-trigger="onBouncesList"]').closest('li[menuitem]').setAttribute('disabled','')
|
|
}
|
|
tile.querySelectorAll('[eicdropdown]').forEach(el => {
|
|
const dropDown = new DropDown(el)
|
|
dropDown.menu.el.querySelectorAll('[data-trigger]').forEach(el => {
|
|
el.addEventListener('click', this[el.dataset.trigger].bind(this, dropDown))
|
|
})
|
|
})
|
|
tile.querySelectorAll('[data-path]').forEach(el => el.addEventListener('click', this.onPathClicked.bind(this)))
|
|
tile.querySelectorAll('button[data-trigger]').forEach(el => {
|
|
const btn = new Button(el)
|
|
btn.click = this[el.dataset.trigger].bind(this, btn)
|
|
})
|
|
tile.querySelector('[data-trigger="onOneStatus"]').addEventListener('click', this.onOneStatus.bind(this))
|
|
|
|
//temporary until we have stats & unschedule screens
|
|
if(['sent'].includes(mailing.status)) ui.hide(tile.querySelector('[data-trigger="onMailingSelect"]'))
|
|
this.outputs.searchResults.append(tile)
|
|
if(mailing.status=='sent'){
|
|
new PieChart(tile.querySelector('[eicpiechart]'), {
|
|
title: '',
|
|
height: parseInt(getComputedStyle(document.querySelector('.mailings')).getPropertyValue('--chart-height'))+10,
|
|
data: [
|
|
{ severity: 'danger', value: mailing.kpis.bouncesCount },
|
|
{ severity: 'success', value: mailing.kpis.readCount },
|
|
{ severity: 'secondary', value: mailing.nbRecipients-mailing.kpis.bouncesCount-mailing.kpis.readCount }
|
|
]
|
|
})
|
|
} else {
|
|
tile.querySelector('[eicpiechart]').innerHTML = ''
|
|
}
|
|
}
|
|
this.updateStatusCounts(statusCounts)
|
|
}
|
|
|
|
onPathClicked(event){
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
const criteria = this.components.searchCriteria.value.filter(item => (!item.startsWith('/'))) // Don't filter straight in the component, (dbl-trigger)
|
|
this.components.searchCriteria.value = [...criteria, event.target.dataset.path] // push not foreseen in component
|
|
}
|
|
|
|
async onPathBrowse() {
|
|
const button = this.find('[data-trigger="onPathBrowse"]')
|
|
this.showLoading(button)
|
|
let result = await this.mailings.getReadableFolders()
|
|
let { mailings, authorizedPaths } = result
|
|
|
|
if (!Array.isArray(authorizedPaths)) {
|
|
authorizedPaths = []
|
|
}
|
|
|
|
if (!authorizedPaths.includes('/mailing')) authorizedPaths.unshift('/mailing')
|
|
this.mailings.ffs.authorizedPaths = authorizedPaths
|
|
|
|
if (!mailings.mailings) {
|
|
mailings = { mailings }
|
|
}
|
|
|
|
this.mailings.ffs.loadStructure(mailings || {}, [])
|
|
this.mailings.ffs.currentPath = '/mailing'
|
|
this.mailings.ffs.authorizedPaths = authorizedPaths
|
|
|
|
let dialogResult = await this.openDialog(
|
|
await this.loadContent(
|
|
'templates/Ffs/dialogs/FileBrowserDialog',
|
|
{ title: `Select a path...` },
|
|
{
|
|
ffs: this.mailings.ffs,
|
|
mailings: this.mailings.mailings,
|
|
editable: false,
|
|
canManage: false,
|
|
startPath: '/mailing',
|
|
}
|
|
)
|
|
)
|
|
|
|
if (dialogResult) {
|
|
const criteria = this.components.searchCriteria.value.filter(item => (!item.startsWith('/')))
|
|
this.components.searchCriteria.value = [...criteria, dialogResult]
|
|
this.hideLoading(button)
|
|
}
|
|
this.hideLoading(button)
|
|
}
|
|
|
|
onMailingSelect(component, event){
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
app.Router.route(`/mailings/${component.el.dataset.id}`)
|
|
}
|
|
|
|
async onRecipientsList(component, event){
|
|
const mid = event.target.dataset.id
|
|
component.button.loading = true
|
|
let recipients = await this.mailings.getRecipients(mid)
|
|
component.button.loading = false
|
|
this.toCSV( // Kick id & meta
|
|
recipients.map(row => { const { id, meta , ...filtered } = row; return(filtered) } ),
|
|
{ headers: ['Email', 'Source name', 'Source type' ],
|
|
filename: `Recipient`
|
|
}
|
|
)
|
|
}
|
|
|
|
async onBouncesList(component, event){
|
|
const mid = event.target.dataset.id
|
|
component.button.loading = true
|
|
const bounces = await this.mailings.getBounces(mid)
|
|
component.button.loading = false
|
|
this.toCSV( // Kick id & meta
|
|
bounces.map(row => { const { id, ...filtered } = row; return(filtered) } ),
|
|
{ headers: ['Email', 'Date bounced' ],
|
|
filename: `Bounces`
|
|
}
|
|
)
|
|
}
|
|
|
|
async onMailingCreate(component, event){
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
component.loading = true
|
|
const path = this.authorizedPaths?.[0]
|
|
|
|
const response = await this.mailings.save({
|
|
status: "created",
|
|
path: path
|
|
})
|
|
component.loading = false
|
|
if(response && response.id) {
|
|
app.Router.route(`/mailings/${response.id}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
app.registerClass('MailingDashboardView', MailingDashboardView)
|