mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-11-15 01:05:42 +00:00
Sort lists by ressources creation date. CSS and HTML cleanup
This commit is contained in:
parent
03a57fd7ee
commit
89f518fe38
@ -31,68 +31,10 @@
|
||||
height: 30px !important;
|
||||
}
|
||||
|
||||
/* flat-interaction addition to show background color */
|
||||
|
||||
.flat-interaction {
|
||||
background-color: #DCDCDC;
|
||||
width: 100%;
|
||||
margin-bottom: 3px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.flat-interaction:hover {
|
||||
background-color: #E6E6FA !important;
|
||||
}
|
||||
|
||||
/* CSS for clickable th elements in tables. Needed for sortable table data with
|
||||
list js. On click on th header elements will be sorted accordingly. Also a caret
|
||||
indicator will show up how the column is sorted right now.; */
|
||||
.sort {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sort:after {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: 5px solid transparent;
|
||||
content:"";
|
||||
position: relative;
|
||||
top:-10px;
|
||||
right:-5px;
|
||||
}
|
||||
.sort.asc:after {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid #000000;
|
||||
content:"";
|
||||
position: relative;
|
||||
top:13px;
|
||||
right:-5px;
|
||||
}
|
||||
.sort.desc:after {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: 5px solid #000000;
|
||||
content:"";
|
||||
position: relative;
|
||||
top:-10px;
|
||||
right:-5px;
|
||||
}
|
||||
|
||||
.show-if-only-child:not(:only-child) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* class for expert view */
|
||||
.expert-view {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-scale-x2 {
|
||||
transform: scale(2);
|
||||
}
|
||||
|
@ -13,7 +13,25 @@ class CorpusFileList extends RessourceList {
|
||||
</td>
|
||||
</tr>
|
||||
`.trim(),
|
||||
valueNames: [{data: ['id']}, 'author', 'filename', 'publishing_year', 'title']
|
||||
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'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@ -115,15 +133,4 @@ class CorpusFileList extends RessourceList {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preprocessRessource(corpusFile) {
|
||||
return {
|
||||
id: corpusFile.id,
|
||||
author: corpusFile.author,
|
||||
creationDate: corpusFile.creation_date,
|
||||
filename: corpusFile.filename,
|
||||
publishing_year: corpusFile.publishing_year,
|
||||
title: corpusFile.title
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,23 @@ class CorpusList extends RessourceList {
|
||||
</td>
|
||||
</tr>
|
||||
`.trim(),
|
||||
valueNames: [{data: ['id']}, {name: 'status', attr: 'data-status'}, 'description', 'title']
|
||||
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'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@ -107,14 +123,4 @@ class CorpusList extends RessourceList {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preprocessRessource(corpus) {
|
||||
return {
|
||||
id: corpus.id,
|
||||
creationDate: corpus.creation_date,
|
||||
description: corpus.description,
|
||||
status: corpus.status,
|
||||
title: corpus.title
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,15 @@ class JobInputList extends RessourceList {
|
||||
</td>
|
||||
</tr>
|
||||
`.trim(),
|
||||
valueNames: [{data: ['id']}, 'filename']
|
||||
ressourceMapper: jobInput => {
|
||||
return {
|
||||
id: jobInput.id,
|
||||
creationDate: jobInput.creation_date,
|
||||
filename: jobInput.filename
|
||||
};
|
||||
},
|
||||
sortValueName: 'creationDate',
|
||||
valueNames: [{data: ['id']}, {data: ['creationDate']}, 'filename']
|
||||
};
|
||||
|
||||
|
||||
@ -43,12 +51,4 @@ class JobInputList extends RessourceList {
|
||||
}
|
||||
|
||||
usersPatchHandler(patch) {return;}
|
||||
|
||||
preprocessRessource(jobInput) {
|
||||
return {
|
||||
id: jobInput.id,
|
||||
creationDate: jobInput.creation_date,
|
||||
filename: jobInput.filename
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,39 @@
|
||||
class JobList extends RessourceList {
|
||||
static options = {
|
||||
item: `
|
||||
<tr>
|
||||
<td><a class="btn-floating disabled"><i class="nopaque-icons service service-color darken service-icon"></i></a></td>
|
||||
<tr class="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 tooltipped waves-effect waves-light" data-action="view" data-position="top" data-tooltip="View"><i class="material-icons">send</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(),
|
||||
valueNames: [{data: ['id']}, {name: 'service', attr: 'data-service'}, {name: 'status', attr: 'data-status'}, 'description', 'title']
|
||||
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'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@ -109,15 +131,4 @@ class JobList extends RessourceList {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preprocessRessource(job) {
|
||||
return {
|
||||
id: job.id,
|
||||
creationDate: job.creation_date,
|
||||
description: job.description,
|
||||
service: job.service,
|
||||
status: job.status,
|
||||
title: job.title
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,36 @@ class JobResultList extends RessourceList {
|
||||
</td>
|
||||
</tr>
|
||||
`.trim(),
|
||||
valueNames: [{data: ['id']}, 'description', 'filename']
|
||||
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'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@ -63,28 +92,4 @@ class JobResultList extends RessourceList {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preprocessRessource(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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,25 @@ class QueryResultList extends RessourceList {
|
||||
</td>
|
||||
</tr>
|
||||
`.trim(),
|
||||
valueNames: [{data: ['id']}, 'corpus_title', 'description', 'query', 'title']
|
||||
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'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@ -108,15 +126,4 @@ class QueryResultList extends RessourceList {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preprocessRessource(queryResult) {
|
||||
return {
|
||||
id: queryResult.id,
|
||||
corpus_title: queryResult.corpus_title,
|
||||
creationDate: queryResult.creation_date,
|
||||
description: queryResult.description,
|
||||
query: queryResult.query,
|
||||
title: queryResult.title
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class RessourceList {
|
||||
}
|
||||
}
|
||||
}
|
||||
static options = {page: 5, pagination: [{innerWindow: 4, outerWindow: 1}]};
|
||||
static options = {page: 5, pagination: {innerWindow: 4, outerWindow: 1}};
|
||||
|
||||
|
||||
constructor(listElement, options = {}) {
|
||||
@ -48,6 +48,18 @@ class RessourceList {
|
||||
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>
|
||||
@ -107,11 +119,16 @@ class RessourceList {
|
||||
|
||||
preprocessRessource() {throw 'Not implemented'}
|
||||
|
||||
add(values) {
|
||||
let ressources = Array.isArray(values) ? values : [values];
|
||||
ressources = ressources.map(ressource => this.preprocessRessource(ressource));
|
||||
this.listjs.add(ressources, () => {
|
||||
this.listjs.sort('id', {order: 'desc'});
|
||||
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'});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ class UserList extends RessourceList {
|
||||
static options = {
|
||||
item: `
|
||||
<tr>
|
||||
<td><span class="id_"></span></td>
|
||||
<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>
|
||||
@ -14,7 +14,26 @@ class UserList extends RessourceList {
|
||||
</td>
|
||||
</tr>
|
||||
`.trim(),
|
||||
valueNames: [{data: ['id']}, 'id_', 'username', 'email', 'last_seen', 'role']
|
||||
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'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@ -78,15 +97,4 @@ class UserList extends RessourceList {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
preprocessRessource(user) {
|
||||
return {
|
||||
id: user.id,
|
||||
id_: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
last_seen: new Date(user.last_seen).toLocaleString("en-US"),
|
||||
role: user.role.name
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -37,58 +37,54 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 l6 nopaque-ressource-list" data-ressource-type="Job" data-user-id="{{ user.hashid }}">
|
||||
<h3>Corpora</h3>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
<input id="search-corpus" class="search" type="search"></input>
|
||||
<label for="search-corpus">Search corpus</label>
|
||||
<div class="col s12 l6 nopaque-ressource-list" data-ressource-type="Corpus" data-user-id="{{ user.hashid }}">
|
||||
<h3>Corpora</h3>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
<input id="search-corpus" class="search" type="search"></input>
|
||||
<label for="search-corpus">Search corpus</label>
|
||||
</div>
|
||||
<table class="highlight ressource-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Title and Description</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list"></tbody>
|
||||
</table>
|
||||
<ul class="pagination"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<table class="highlight ressource-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
<span class="sort" data-sort="title">Title</span>
|
||||
<span class="sort" data-sort="description">Description</span>
|
||||
</th>
|
||||
<th><span class="sort" data-sort="status">Status</span></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list"></tbody>
|
||||
</table>
|
||||
<ul class="pagination"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 l6 nopaque-ressource-list" data-ressource-type="Job" data-user-id="{{ user.hashid }}">
|
||||
<h3>Jobs</h3>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
<input id="search-job" class="search" type="search"></input>
|
||||
<label for="search-job">Search job</label>
|
||||
<div class="col s12 l6 nopaque-ressource-list" data-ressource-type="Job" data-user-id="{{ user.hashid }}">
|
||||
<h3>Jobs</h3>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
<input id="search-job" class="search" type="search"></input>
|
||||
<label for="search-job">Search job</label>
|
||||
</div>
|
||||
<table class="highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Service</th>
|
||||
<th>Title and Description</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list"></tbody>
|
||||
</table>
|
||||
<ul class="pagination"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<table class="highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="sort" data-sort="service">Service</span></th>
|
||||
<th>
|
||||
<span class="sort" data-sort="title">Title</span>
|
||||
<span class="sort" data-sort="description">Description</span>
|
||||
</th>
|
||||
<th><span class="sort" data-sort="status">Status</span></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list"></tbody>
|
||||
</table>
|
||||
<ul class="pagination"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,11 +19,11 @@
|
||||
<table class="highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort" data-sort="id">Id</th>
|
||||
<th class="sort" data-sort="username">Username</th>
|
||||
<th class="sort" data-sort="email">Email</th>
|
||||
<th class="sort" data-sort="last_seen">Last seen</th>
|
||||
<th class="sort" data-sort="role">Role</th>
|
||||
<th>Id</th>
|
||||
<th>Username</th>
|
||||
<th>Email</th>
|
||||
<th>Last seen</th>
|
||||
<th>Role</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -84,10 +84,10 @@
|
||||
<table class="highlight responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort" data-sort="filename">Filename</th>
|
||||
<th class="sort" data-sort="author">Author</th>
|
||||
<th class="sort" data-sort="title">Title</th>
|
||||
<th class="sort" data-sort="publishing-year">Publishing year</th>
|
||||
<th>Filename</th>
|
||||
<th>Author</th>
|
||||
<th>Title</th>
|
||||
<th>Publishing year</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -99,7 +99,7 @@
|
||||
<table class="highlight responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort" data-sort="filename">Filename</th>
|
||||
<th>Filename</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -30,11 +30,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
<span class="sort" data-sort="title">Title</span>
|
||||
<span class="sort" data-sort="description">Description</span>
|
||||
</th>
|
||||
<th><span class="sort" data-sort="status">Status</span></th>
|
||||
<th>Title and Description</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -59,25 +56,12 @@
|
||||
<table class="highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<span class="sort" data-sort="title">Title</span> and<br>
|
||||
<span class="sort" data-sort="description">Description</span>
|
||||
</th>
|
||||
<th>
|
||||
<span class="sort" data-sort="corpus">Corpus</span> and<br>
|
||||
<span class="sort" data-sort="query">Query</span>
|
||||
</th>
|
||||
<th>Title and Description</th>
|
||||
<th>Corpus and Query</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
<tr class="show-if-only-child">
|
||||
<td colspan="5">
|
||||
<span class="card-title"><i class="material-icons left">folder</i>Nothing here...</span>
|
||||
<p>No query results yet imported.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody class="list"></tbody>
|
||||
</table>
|
||||
<ul class="pagination"></ul>
|
||||
</div>
|
||||
@ -102,12 +86,9 @@
|
||||
<table class="highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span class="sort" data-sort="service">Service</span></th>
|
||||
<th>
|
||||
<span class="sort" data-sort="title">Title</span>
|
||||
<span class="sort" data-sort="description">Description</span>
|
||||
</th>
|
||||
<th><span class="sort" data-sort="status">Status</span></th>
|
||||
<th>Service</th>
|
||||
<th>Title and Description</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
Loading…
Reference in New Issue
Block a user