Some cleanup (css, html, js)

This commit is contained in:
Patrick Jentsch
2021-12-09 12:50:14 +01:00
parent 067273df6d
commit 40bd169101
45 changed files with 36 additions and 6478 deletions

@ -0,0 +1,136 @@
class CorpusFileList extends RessourceList {
static options = {
item: `
<tr class="hoverable">
<td><span class="filename"></span></td>
<td><span class="author"></span></td>
<td><span class="title"></span></td>
<td><span class="publishing_year"></span></td>
<td class="right-align">
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
<a class="action-button btn-floating service-color darken tooltipped waves-effect waves-light" data-action="download" data-position="top" data-service="corpus-analysis" data-tooltip="View"><i class="material-icons">file_download</i></a>
<a class="action-button btn-floating service-color darken tooltipped waves-effect waves-light" data-action="view" data-position="top" data-service="corpus-analysis" data-tooltip="View"><i class="material-icons">send</i></a>
</td>
</tr>
`.trim(),
ressourceMapper: corpusFile => {
return {
id: corpusFile.id,
author: corpusFile.author,
creationDate: corpusFile.creation_date,
filename: corpusFile.filename,
publishingYear: corpusFile.publishing_year,
title: corpusFile.title
};
},
sortValueName: 'creationDate',
valueNames: [
{data: ['id']},
{data: ['creationDate']},
'author',
'filename',
'publishingYear',
'title'
]
};
constructor(listElement, options = {}) {
super(listElement, {...CorpusFileList.options, ...options});
this.corpusId = listElement.dataset.corpusId;
}
init(user) {
this._init(user.corpora[this.corpusId].files);
}
onclick(event) {
let action;
let actionButtonElement;
let corpusFileElement;
let corpusFileId;
let deleteModal;
let deleteModalElement;
let tmp;
corpusFileElement = event.target.closest('tr[data-id]');
if (corpusFileElement === null) {return;}
corpusFileId = corpusFileElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) {
case 'delete':
tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal">
<div class="modal-content">
<h4>Confirm corpus deletion</h4>
<p>Do you really want to delete the corpus file <b>${app.users[this.userId].corpora[this.corpusId].files[corpusFileId].filename}</b>? It will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="/corpora/${this.corpusId}/files/${corpusFileId}/delete"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
`.trim();
deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModal = M.Modal.init(
deleteModalElement,
{
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open();
break;
case 'download':
window.location.href = `/corpora/${this.corpusId}/files/${corpusFileId}/download`;
break;
case 'view':
window.location.href = `/corpora/${this.corpusId}/files/${corpusFileId}`;
break;
default:
break;
}
}
usersPatchHandler(patch) {
let corpusFileId;
let filteredPatch;
let match;
let operation;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) {
case 'add':
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
this.add(operation.value);
}
break;
case 'remove':
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
[match, corpusFileId] = operation.path.match(re);
this.remove(corpusFileId);
}
break;
case 'replace':
re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/files/([A-Za-z0-9]*)/(author|filename|publishing_year|title)$`);
if (re.test(operation.path)) {
[match, corpusFileId, valueName] = operation.path.match(re);
this.replace(corpusFileId, valueName, operation.value);
}
break;
default:
break;
}
}
}
}

@ -0,0 +1,126 @@
class CorpusList extends RessourceList {
static options = {
item: `
<tr class="hoverable">
<td><a class="btn-floating disabled"><i class="material-icons service-color darken" data-service="corpus-analysis">book</i></a></td>
<td><b class="title"></b><br><i class="description"></i></td>
<td><span class="badge new status status-color status-text" data-badge-caption=""></span></td>
<td class="right-align">
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
<a class="action-button btn-floating service-color darken tooltipped waves-effect waves-light" data-action="view" data-position="top" data-service="corpus-analysis" data-tooltip="View"><i class="material-icons">send</i></a>
</td>
</tr>
`.trim(),
ressourceMapper: corpus => {
return {
id: corpus.id,
creationDate: corpus.creation_date,
description: corpus.description,
status: corpus.status,
title: corpus.title
};
},
sortValueName: 'creationDate',
valueNames: [
{data: ['id']},
{data: ['creationDate']},
{name: 'status', attr: 'data-status'},
'description',
'title'
]
};
constructor(listElement, options = {}) {
super(listElement, {...CorpusList.options, ...options});
}
init(user) {
super._init(user.corpora);
}
onclick(event) {
let action;
let actionButtonElement;
let corpusElement;
let corpusId;
let deleteModal;
let deleteModalElement;
let tmp;
corpusElement = event.target.closest('tr[data-id]');
if (corpusElement === null) {return;}
corpusId = corpusElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) {
case 'delete':
tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal">
<div class="modal-content">
<h4>Confirm corpus deletion</h4>
<p>Do you really want to delete the corpus <b>${app.users[this.userId].corpora[corpusId].title}</b>? All files will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="/corpora/${corpusId}/delete"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
`.trim();
deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModal = M.Modal.init(
deleteModalElement,
{
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open();
break;
case 'view':
window.location.href = `/corpora/${corpusId}`;
break;
default:
break;
}
}
usersPatchHandler(patch) {
let corpusId;
let filteredPatch;
let match;
let operation;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) {
case 'add':
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {this.add(operation.value);}
break;
case 'remove':
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
[match, corpusId] = operation.path.match(re);
this.remove(corpusId);
}
break;
case 'replace':
re = new RegExp(`^/users/${this.userId}/corpora/([A-Za-z0-9]*)/(status|description|title)$`);
if (re.test(operation.path)) {
[match, corpusId, valueName] = operation.path.match(re);
this.replace(corpusId, valueName, operation.value);
}
break;
default:
break;
}
}
}
}

@ -0,0 +1,54 @@
class JobInputList extends RessourceList {
static options = {
item: `
<tr class="hoverable">
<td><span class="filename"></span></td>
<td class="right-align">
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
</td>
</tr>
`.trim(),
ressourceMapper: jobInput => {
return {
id: jobInput.id,
creationDate: jobInput.creation_date,
filename: jobInput.filename
};
},
sortValueName: 'creationDate',
valueNames: [{data: ['id']}, {data: ['creationDate']}, 'filename']
};
constructor(listElement, options = {}) {
super(listElement, {...JobInputList.options, ...options});
this.jobId = listElement.dataset.jobId;
}
init(user) {
this._init(user.jobs[this.jobId].inputs);
}
onclick(event) {
let jobInputElement;
let jobInputId;
let action;
let actionButtonElement;
jobInputElement = event.target.closest('tr[data-id]');
if (jobInputElement === null) {return;}
jobInputId = jobInputElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
if (actionButtonElement === null) {return;}
action = actionButtonElement.dataset.action;
switch (action) {
case 'download':
window.location.href = `/jobs/${this.jobId}/inputs/${jobInputId}/download`;
break;
default:
break;
}
}
usersPatchHandler(patch) {return;}
}

@ -0,0 +1,134 @@
class JobList extends RessourceList {
static options = {
item: `
<tr class="hoverable service-color lighten">
<td><a class="btn-floating disabled"><i class="nopaque-icons service-color darken serviceDuplicate1 service-icon"></i></a></td>
<td><b class="title"></b><br><i class="description"></i></td>
<td><span class="badge new status status-color status-text" data-badge-caption=""></span></td>
<td class="right-align">
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
<a class="action-button btn-floating serviceDuplicate2 service-color darken tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
</td>
</tr>
`.trim(),
ressourceMapper: job => {
return {
id: job.id,
creationDate: job.creation_date,
description: job.description,
service: job.service,
serviceDuplicate1: job.service,
serviceDuplicate2: job.service,
status: job.status,
title: job.title
};
},
sortValueName: 'creationDate',
valueNames: [
{data: ['id']},
{data: ['creationDate']},
{data: ['service']},
{name: 'serviceDuplicate1', attr: 'data-service'},
{name: 'serviceDuplicate2', attr: 'data-service'},
{name: 'status', attr: 'data-status'},
'description',
'title'
]
};
constructor(listElement, options = {}) {
super(listElement, {...JobList.options, ...options});
}
init(user) {
this._init(user.jobs);
}
onclick(event) {
let action;
let actionButtonElement;
let deleteModal;
let deleteModalElement;
let jobElement;
let jobId;
let tmp;
jobElement = event.target.closest('tr[data-id]');
if (jobElement === null) {return;}
jobId = jobElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) {
case 'delete':
tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal">
<div class="modal-content">
<h4>Confirm job deletion</h4>
<p>Do you really want to delete the job <b>${app.users[this.userId].jobs[jobId].title}</b>? All files will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="/jobs/${jobId}/delete"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
`.trim();
deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModal = M.Modal.init(
deleteModalElement,
{
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open();
break;
case 'view':
window.location.href = `/jobs/${jobId}`;
break;
default:
break;
}
}
usersPatchHandler(patch) {
let filteredPatch;
let jobId;
let match;
let operation;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) {
case 'add':
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
this.add(operation.value);
}
break;
case 'remove':
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
[match, jobId] = operation.path.match(re);
this.remove(jobId);
}
break;
case 'replace':
re = new RegExp(`^/users/${this.userId}/jobs/([A-Za-z0-9]*)/(service|status|description|title)$`);
if (re.test(operation.path)) {
[match, jobId, valueName] = operation.path.match(re);
this.replace(jobId, valueName, operation.value);
}
break;
default:
break;
}
}
}
}

@ -0,0 +1,95 @@
class JobResultList extends RessourceList {
static options = {
item: `
<tr class="hoverable">
<td><span class="description"></span></td>
<td><span class="filename"></span></td>
<td class="right-align">
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="download" data-position="top" data-tooltip="View"><i class="material-icons">file_download</i></a>
</td>
</tr>
`.trim(),
ressourceMapper: jobResult => {
let description;
if (jobResult.filename.endsWith('.pdf.zip')) {
description = 'PDF files with text layer';
} else if (jobResult.filename.endsWith('.txt.zip')) {
description = 'Raw text files';
} else if (jobResult.filename.endsWith('.vrt.zip')) {
description = 'VRT compliant files including the NLP data';
} else if (jobResult.filename.endsWith('.xml.zip')) {
description = 'TEI compliant files';
} else if (jobResult.filename.endsWith('.poco.zip')) {
description = 'HOCR and image files for post correction (PoCo)';
} else {
description = 'All result files created during this job';
}
return {
id: jobResult.id,
creationDate: jobResult.creation_date,
description: description,
filename: jobResult.filename
};
},
sortValueName: 'creationDate',
valueNames: [
{data: ['id']},
{data: ['creationDate']},
'description',
'filename'
]
};
constructor(listElement, options = {}) {
super(listElement, {...JobResultList.options, ...options});
this.jobId = listElement.dataset.jobId;
}
init(user) {
super._init(user.jobs[this.jobId].results);
}
onclick(event) {
let action;
let actionButtonElement;
let jobResultElement;
let jobResultId;
jobResultElement = event.target.closest('tr[data-id]');
if (jobResultElement === null) {return;}
jobResultId = jobResultElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
if (actionButtonElement === null) {return;}
action = actionButtonElement.dataset.action;
switch (action) {
case 'download':
window.location.href = `/jobs/${this.jobId}/results/${jobResultId}/download`;
break;
default:
break;
}
}
usersPatchHandler(patch) {
let filteredPatch;
let operation;
let re;
re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) {
case 'add':
re = new RegExp(`^/users/${this.userId}/jobs/${this.jobId}/results/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
this.add(operation.value);
}
break;
default:
break;
}
}
}
}

@ -0,0 +1,129 @@
class QueryResultList extends RessourceList {
static options = {
item: `
<tr class="hoverable">
<td><b class="title"></b><br><i class="description"></i><br></td>
<td><span class="corpus_title"></span><br><span class="query"></span></td>
<td class="right-align">
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
</td>
</tr>
`.trim(),
ressourceMapper: queryResult => {
return {
id: queryResult.id,
corpusTitle: queryResult.corpus_title,
creationDate: queryResult.creation_date,
description: queryResult.description,
query: queryResult.query,
title: queryResult.title
};
},
sortValueName: 'creationDate',
valueNames: [
{data: ['id']},
{data: ['creationDate']},
'corpusTitle',
'description',
'query',
'title'
]
};
constructor(listElement, options = {}) {
super(listElement, {...QueryResultList.options, ...options});
}
init(user) {
super._init(user.query_results);
}
onclick(event) {
let action;
let actionButtonElement;
let deleteModal;
let deleteModalElement;
let queryResultElement;
let queryResultId;
let tmp;
queryResultElement = event.target.closest('tr[data-id]');
if (queryResultElement === null) {return;}
queryResultId = queryResultElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action;
switch (action) {
case 'delete':
tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal">
<div class="modal-content">
<h4>Confirm query result deletion</h4>
<p>Do you really want to delete the query result <b>${app.users[this.userId].query_results[queryResultId].title}</b>? It will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="/query_results/${queryResultId}/delete"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
`.trim();
deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModal = M.Modal.init(
deleteModalElement,
{
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open();
break;
case 'view':
window.location.href = `/query_results/${queryResultId}`;
break;
default:
break;
}
}
usersPatchHandler(patch) {
let filteredPatch;
let match;
let operation;
let queryResultId;
let re;
let valueName;
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)`);
filteredPatch = patch.filter(operation => re.test(operation.path));
for (operation of filteredPatch) {
switch(operation.op) {
case 'add':
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
this.add(operation.value);
}
break;
case 'remove':
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)$`);
if (re.test(operation.path)) {
[match, queryResultId] = operation.path.match(re);
this.remove(queryResultId);
}
break;
case 'replace':
re = new RegExp(`^/users/${this.userId}/query_results/([A-Za-z0-9]*)/(corpus_title|description|query|title)$`);
if (re.test(operation.path)) {
[match, queryResultId, valueName] = operation.path.match(re);
this.replace(queryResultId, valueName, operation.value);
}
break;
default:
break;
}
}
}
}

@ -0,0 +1,142 @@
class RessourceList {
/* A wrapper class for the list.js list.
* This class is not meant to be used directly, instead it should be used as
* a base class for concrete ressource list implementations.
*/
static autoInit() {
const nopaqueRessourceListElements = document.querySelectorAll('.nopaque-ressource-list[data-ressource-type]:not(.no-autoinit)');
let nopaqueRessourceListElement;
for (nopaqueRessourceListElement of nopaqueRessourceListElements) {
switch (nopaqueRessourceListElement.dataset.ressourceType) {
case 'Corpus':
new CorpusList(nopaqueRessourceListElement);
break;
case 'CorpusFile':
new CorpusFileList(nopaqueRessourceListElement);
break;
case 'Job':
new JobList(nopaqueRessourceListElement);
break;
case 'JobInput':
new JobInputList(nopaqueRessourceListElement);
break;
case 'JobResult':
new JobResultList(nopaqueRessourceListElement);
break;
case 'QueryResult':
new QueryResultList(nopaqueRessourceListElement);
break;
case 'User':
new UserList(nopaqueRessourceListElement);
break;
default:
break;
}
}
}
static options = {page: 5, pagination: {innerWindow: 4, outerWindow: 1}};
constructor(listElement, options = {}) {
let i;
if (!(listElement.hasAttribute('id'))) {
for (i = 0; true; i++) {
if (document.querySelector(`ressource-list-${i}`)) {continue;}
listElement.id = `ressource-list-${i}`;
break;
}
}
options = {
...{pagination: {item: `<li><a class="page" href="#${listElement.id}"></a></li>`}},
...options
}
if ('ressourceMapper' in options) {
this.ressourceMapper = options.ressourceMapper;
delete options.ressourceMapper;
}
if ('sortValueName' in options) {
this.sortValueName = options.sortValueName;
delete options.sortValueName;
}
this.listjs = new List(listElement, {...RessourceList.options, ...options});
this.listjs.list.innerHTML = `
<tr>
<td class="row" colspan="100%">
<div class="col s12">&nbsp;</div>
<div class="col s3 m2 xl1">
<div class="preloader-wrapper active">
<div class="spinner-layer spinner-green-only">
<div class="circle-clipper left">
<div class="circle"></div>
</div>
<div class="gap-patch">
<div class="circle"></div>
</div>
<div class="circle-clipper right">
<div class="circle"></div>
</div>
</div>
</div>
</div>
<div class="col s9 m6 xl5">
<span class="card-title">Waiting for data...</span>
<p>This list is not initialized yet.</p>
</div>
</td>
</tr>
`.trim();
this.listjs.list.style.cursor = 'pointer';
this.userId = this.listjs.listContainer.dataset.userId;
this.listjs.list.addEventListener('click', event => this.onclick(event));
if (this.userId) {
app.addEventListener('users.patch', patch => this.usersPatchHandler(patch));
app.getUserById(this.userId).then(
user => this.init(user),
error => {throw JSON.stringify(error);}
);
}
}
_init(ressources) {
this.listjs.clear();
this.add(Object.values(ressources));
let emptyListElementHTML = `
<tr class="show-if-only-child">
<td colspan="100%">
<span class="card-title"><i class="left material-icons" style="font-size: inherit;">file_download</i>Nothing here...</span>
<p>No ressource available.</p>
</td>
</tr>
`.trim();
this.listjs.list.insertAdjacentHTML('afterbegin', emptyListElementHTML);
}
init(user) {throw 'Not implemented';}
onclick(event) {throw 'Not implemented';}
usersPatchHandler(patch) {throw 'Not implemented';}
add(ressources) {
let values = Array.isArray(ressources) ? ressources : [ressources];
if ('ressourceMapper' in this) {
values = values.map(value => this.ressourceMapper(value));
}
this.listjs.add(values, () => {
if ('sortValueName' in this) {
this.listjs.sort(this.sortValueName, {order: 'desc'});
}
});
}
remove(id) {
this.listjs.remove('id', id);
}
replace(id, valueName, newValue) {
this.listjs.get('id', id)[0].values({[valueName]: newValue});
}
}

@ -0,0 +1,100 @@
class UserList extends RessourceList {
static options = {
item: `
<tr class="hoverable">
<td><span class="idDuplicate"></span></td>
<td><span class="username"></span></td>
<td><span class="email"></span></td>
<td><span class="last_seen"></span></td>
<td><span class="role"></span></td>
<td class="right-align">
<a class="action-button btn-floating red tooltipped waves-effect waves-light" data-action="delete" data-position="top" data-tooltip="Delete"><i class="material-icons">delete</i></a>
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="edit" data-position="top" data-tooltip="Edit"><i class="material-icons">edit</i></a>
<a class="action-button btn-floating tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</i></a>
</td>
</tr>
`.trim(),
ressourceMapper: user => {
return {
id: user.id,
idDuplicate: user.id,
username: user.username,
email: user.email,
last_seen: new Date(user.last_seen).toLocaleString("en-US"),
role: user.role.name
};
},
sortValueName: 'memberSince',
valueNames: [
{data: ['id']},
{data: ['memberSince']},
'email',
'idDuplicate',
'last_seen',
'role',
'username'
]
};
constructor(listElement, options = {}) {
super(listElement, {...UserList.options, ...options});
}
init(users) {
super._init(Object.values(users));
}
onclick(event) {
let action;
let actionButtonElement;
let deleteModal;
let deleteModalElement;
let tmp;
let userElement;
let userId;
userElement = event.target.closest('tr[data-id]');
if (userElement === null) {return;}
userId = userElement.dataset.id;
actionButtonElement = event.target.closest('.action-button[data-action]');
action = (actionButtonElement === null) ? 'view' : actionButtonElement.dataset.action;
switch (action) {
case 'delete':
tmp = document.createElement('div');
tmp.innerHTML = `
<div class="modal">
<div class="modal-content">
<h4>Confirm user deletion</h4>
<p>Do you really want to delete user <b>${userId}</b>? All files will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
<a class="btn modal-close red waves-effect waves-light" href="/admin/users/${userId}/delete"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
`.trim();
deleteModalElement = document.querySelector('#modals').appendChild(tmp.firstChild);
deleteModal = M.Modal.init(
deleteModalElement,
{
onCloseEnd: () => {
deleteModal.destroy();
deleteModalElement.remove();
}
}
);
deleteModal.open();
break;
case 'edit':
window.location.href = `/admin/users/${userId}/edit`;
break;
case 'view':
window.location.href = `/admin/users/${userId}`;
break;
default:
break;
}
}
}