More adjustments on Processes Popup implementation. Creates Processes Page and listing.

This commit is contained in:
Mateus Machado Luna 2018-07-17 16:09:43 -03:00
parent a6a290f644
commit 7f384090fa
6 changed files with 537 additions and 41 deletions

View File

@ -0,0 +1,232 @@
<template>
<div
v-if="processes.length > 0 && !isLoading"
class="table-container">
<div class="selection-control">
<div class="field select-all is-pulled-left">
<span>
<b-checkbox
@click.native="selectAllOnPage()"
:value="allOnPageSelected">{{ $i18n.get('label_select_all_processes_page') }}</b-checkbox>
</span>
</div>
<div class="field is-pulled-right">
<b-dropdown
position="is-bottom-left"
:disabled="!isSelecting"
id="bulk-actions-dropdown">
<button
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_bulk_actions') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item
id="item-delete-selected-items"
@click="deleteSelected()">
{{ $i18n.get('label_delete_selected_processes') }}
</b-dropdown-item>
<b-dropdown-item disabled>{{ $i18n.get('label_edit_selected_processes') + ' (Not ready)' }}
</b-dropdown-item>
</b-dropdown>
</div>
</div>
<div class="table-wrapper">
<table class="tainacan-table">
<thead>
<tr>
<!-- Checking list -->
<th>
&nbsp;
<!-- nothing to show on header -->
</th>
<!-- Name -->
<th>
<div class="th-wrap">{{ $i18n.get('label_name') }}</div>
</th>
</tr>
</thead>
<tbody>
<tr
:class="{ 'selected-row': selected[index] }"
:key="index"
v-for="(bgProcess, index) of processes">
<!-- Checking list -->
<td
:class="{ 'is-selecting': isSelecting }"
class="checkbox-cell">
<b-checkbox
size="is-small"
v-model="selected[index]"/>
</td>
<!-- Name -->
<td
class="column-default-width column-main-content"
:label="$i18n.get('label_name')"
:aria-label="$i18n.get('label_name') + ': ' + bgProcess.name">
<p
v-tooltip="{
content: bgProcess.name,
autoHide: false,
placement: 'auto-start'
}">
{{ bgProcess.name }}</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'List',
data() {
return {
selected: [],
allOnPageSelected: false,
isSelecting: false
}
},
props: {
isLoading: false,
total: 0,
page: 1,
processesPerPage: 12,
processes: Array
},
watch: {
processes() {
this.selected = [];
for (let i = 0; i < this.processes.length; i++)
this.selected.push(false);
},
selected() {
let allSelected = true;
let isSelecting = false;
for (let i = 0; i < this.selected.length; i++) {
if (this.selected[i] == false) {
allSelected = false;
} else {
isSelecting = true;
}
}
this.allOnPageSelected = allSelected;
this.isSelecting = isSelecting;
}
},
methods: {
...mapActions('bgprocess', [
'deleteProcess'
]),
selectAllOnPage() {
for (let i = 0; i < this.selected.length; i++)
this.selected.splice(i, 1, !this.allOnPageSelected);
},
deleteOneProcess(ProcessId) {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_Process_delete'),
onConfirm: () => {
this.deleteProcess({ ProcessId: taxonomyId })
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_taxonomy_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: true
// });
for (let i = 0; i < this.selected.length; i++) {
if (this.selected[i].id === this.taxonomyId)
this.selected.splice(i, 1);
}
})
.catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_taxonomy'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: true
// });
});
}
}
});
},
deleteSelected() {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_selected_processes_delete'),
onConfirm: () => {
for (let i = 0; i < this.processes.length; i++) {
if (this.selected[i]) {
this.deleteTaxonomy({ taxonomyId: this.processes[i].id })
.then(() => {
// this.load();
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_taxonomy_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// })
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_taxonomy'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// });
});
}
}
this.allOnPageSelected = false;
}
}
});
}
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
.selection-control {
padding: 6px 0px 0px 12px;
background: white;
height: 40px;
.select-all {
color: $gray-light;
font-size: 14px;
&:hover {
color: $gray-light;
}
}
}
</style>

View File

@ -63,8 +63,8 @@
class="button is-small is-secondary level-item">
<b-icon icon="swap-vertical"/>
</button>
<processes-list
v-show="showProcesses"
<processes-popup
v-if="showProcesses"
@closeProcessesPopup="showProcesses = false"/>
<a
:style="{color: 'white'}"
@ -78,7 +78,7 @@
<script>
import AdvancedSearch from '../advanced-search/advanced-search.vue';
import ProcessesList from '../other/processes-list.vue';
import ProcessesPopup from '../other/processes-popup.vue';
import { mapActions } from 'vuex';
export default {
@ -96,7 +96,7 @@
},
components: {
AdvancedSearch,
ProcessesList
ProcessesPopup
},
methods: {
...mapActions('metadata', [

View File

@ -5,14 +5,14 @@
<a @click="showProcessesList = !showProcessesList">
<span class="icon has-text-tertiary">
<i
:class="{ 'mdi-chevron-up': showProcessesList,
'mdi-chevron-down': !showProcessesList }"
:class="{ 'mdi-menu-up': showProcessesList,
'mdi-menu-down': !showProcessesList }"
class="mdi mdi-18px"/>
</span>
</a>
<a @click="$emit('closeProcessesPopup')">
<span class="icon has-text-tertiary">
<i class="mdi mdi-close"/>
<i class="mdi mdi-close"/>
</span>
</a>
</div>
@ -20,33 +20,42 @@
v-if="showProcessesList"
class="popup-list">
<ul>
<li>
Listing 6 processes
<li class="popup-list-subheader">
{{ $i18n.get('label_last_processed_on') + ' ' + getDate(bgProcesses[0].processed_last) }}
<router-link
tag="a"
:to="$routerHelper.getProcessesPage()"
class="is-secondary">
See all
<span class="icon">
<i class="mdi mdi-open-in-new"/>
</span>
</router-link>
</li>
<li
:key="index"
v-for="(bgProcess, index) of bgProcesses.slice(0,6)">
v-for="(bgProcess, index) of bgProcesses">
<div class="process-item">
<div
@click="toggleDetails(index)"
class="process-title">
<b-icon
size="is-small"
type="is-gray"
:icon="processesColapses[index] ? 'menu-down' : 'menu-right'" />
Name of Process
<span class="icon has-text-gray">
<i
class="mdi mdi-18px"
:class="{ 'mdi-menu-down': processesColapses[index], 'mdi-menu-right': !processesColapses[index] }" />
</span>
<p>Name of Process</p>
</div>
<span
v-if="bgProcess.done <= 0"
class="icon has-text-gray"
class="icon has-text-gray action-icon"
@click="pauseProcess(index)">
<i class="mdi mdi-24px mdi-pause"/>
<i class="mdi mdi-24px mdi-pause-circle"/>
</span>
<span
v-if="bgProcess.done <= 0"
class="icon has-text-gray action-icon"
@click="pauseProcess(index)">
<i class="mdi mdi-24px mdi-close-circle-outline"/>
</span>
<span
v-if="bgProcess.done > 0"
@ -55,7 +64,7 @@
</span>
<span
v-if="bgProcess.done <= 0"
class="icon has-text-success">
class="icon has-text-success loading-icon">
<div class="control has-icons-right is-loading is-clearfix" />
</span>
</div>
@ -63,6 +72,10 @@
v-if="processesColapses[index]"
class="process-label">
{{ bgProcess.progress_label ? bgProcess.progress_label : $i18n.get('label_no_details_of_process') }}
<br>
{{ $i18n.get('label_queued_on') + ' ' + getDate(bgProcess.queued_on) }}
<br>
{{ $i18n.get('label_last_processed_on') + ' ' + getDate(bgProcess.processed_last) }}
</div>
</li>
</ul>
@ -72,7 +85,12 @@
v-if="!showProcessesList" />
<div class="popup-footer">
<span class="icon has-text-tertiary"><i class="mdi mdi-18px mdi-autorenew"/></span>
<p class="footer-title">{{ $i18n.get('info_no_process') }}</p>
<p class="footer-title">
{{ hasAnyProcessExecuting ?
(bgProcesses[0].progress_label ? bgProcesses[0].progress_label + (bgProcesses[0].progress_value ? ' - ' + bgProcesses[0].progress_value : '') : $i18n.get('label_no_details_of_process')):
$i18n.get('info_no_process')
}}
</p>
</div>
</div>
</template>
@ -81,18 +99,24 @@
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'ProcessesList',
name: 'ProcessesPopup',
data() {
return {
showProcessesList: false,
processesColapses: []
processesColapses: [],
hasAnyProcessExecuting: false
}
},
watch: {
bgProcesses() {
this.processesColapses = [];
for (let i = 0; i < this.bgProcesses.length; i++)
this.hasAnyProcessExecuting = false;
for (let i = 0; i < this.bgProcesses.length; i++) {
this.$set(this.processesColapses, i , false);
if (this.bgProcesses[i].done <= 0)
this.hasAnyProcessExecuting = true;
}
}
},
computed: {
@ -118,12 +142,21 @@ export default {
}
return nUnfinishedProcesses;
},
getDate(rawDate) {
let date = new Date(rawDate);
if (date instanceof Date && !isNaN(date))
return date.toLocaleString();
else
return this.$i18n.get('info_unknown_date');
},
pauseProcess(index) {
}
},
mounted() {
this.fetchProcesses();
this.showProcessesList = false;
}
}
</script>
@ -147,7 +180,7 @@ export default {
max-height: 0;
}
to {
max-height: 800px;
max-height: 222px;
}
}
@ -160,7 +193,7 @@ export default {
border-radius: 5px;
animation-name: appear;
animation-duration: 0.3s;
font-size: 0.875rem;
font-size: 0.75rem;
.popup-header, .popup-footer {
display: flex;
@ -173,23 +206,33 @@ export default {
.popup-header { padding: 10px 12px 2px 12px; }
.popup-footer {
font-size: 0.75rem;
padding: 4px 10px 6px 10px;
.footer-title { font-size: 0.625rem; }
}
.popup-list {
background-color: white;
color: black;
overflow: hidden;
height: auto;
overflow-y: scroll;
overflow-x: hidden;
max-height: 222px;
animation-name: expand;
animation-duration: 0.3s;
.popup-list-subheader {
padding: 6px 12px 2px 12px;
color: $gray-light;
font-size: 0.625rem;
a { float: right; }
}
.process-item {
padding: 6px 12px 2px 6px;
padding: 6px 12px 6px 6px;
display: flex;
justify-content: space-between;
cursor: pointer;
width: 100%;
.process-title {
margin-right: auto;
@ -197,9 +240,18 @@ export default {
text-overflow: ellipsis;
overflow: hidden;
max-width: calc(100% - 40px);
}
.control.is-loading::after {
p {
display: inline-block;
position: relative;
top: -2px;
}
}
.action-icon {
visibility: hidden;
opacity: 0;
}
.loading-icon .control.is-loading::after {
position: relative !important;
right: 0;
top: 0;
@ -207,21 +259,26 @@ export default {
}
.process-item:hover {
background-color: $tainacan-input-background;
.action-icon{
visibility: visible;
opacity: 1;
}
.loading-icon {
display: none;
}
}
.process-label {
padding: 2px 12px 6px 24px;
padding: 2px 12px 6px 32px;
margin-right: auto;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: calc(100% - 40px);
font-size: 0.625rem;
color: $gray-light;
animation-name: expand;
animation-duration: 0.3s;
}
.process-label {
font-size: 0.75rem;
color: $gray-light;
}
}

View File

@ -14,6 +14,7 @@ import TaxonomyPage from '../pages/singles/taxonomy-page.vue'
import EventsPage from '../pages/lists/events-page.vue'
import EventPage from '../pages/singles/event-page.vue'
import ExportPage from '../pages/singles/export-page.vue'
import ProcessesPage from '../pages/lists/processes-page.vue'
// Edition Form Components
import CollectionEditionForm from '../components/edition/collection-edition-form.vue'
@ -76,6 +77,9 @@ const routes = [
{ path: '/export/item/:itemId', name: 'ExportItem', component: ExportPage, meta: {title: i18nGet('title_export_item_page'), icon: 'export'} },
{ path: '/export', name: 'Export', component: ExportPage, meta: {title: i18nGet('title_export_page'), icon: 'export'} },
{ path: '/processes/', name: 'ProcessesPage', component: ProcessesPage, meta: { title: i18nGet('title_processes_page'), icon: 'swap-vertical'} },
{ path: '*', redirect: '/'}
];

View File

@ -0,0 +1,195 @@
<template>
<div>
<div class="primary-page page-container">
<tainacan-title />
<div class="above-subheader">
<b-loading
:active.sync="isLoading"
:can-cancel="false"/>
<div>
<processes-list
:is-loading="isLoading"
:total="total"
:page="page"
:processes-per-page="processesPerPage"
:processes="processes"/>
<!-- Empty state image -->
<div v-if="processes.length <= 0 && !isLoading">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<b-icon
icon="inbox"
size="is-large"/>
</p>
<p v-if="status == undefined || status == ''">{{ $i18n.get('info_no_process') }}</p>
</div>
</section>
</div>
<!-- Footer -->
<div
class="pagination-area"
v-if="processes.length > 0">
<div class="shown-items">
{{
$i18n.get('info_showing_processes') +
(processesPerPage * (page - 1) + 1) +
$i18n.get('info_to') +
getLastProcessesNumber() +
$i18n.get('info_of') + total + '.'
}}
</div>
<div class="items-per-page">
<b-field
horizontal
:label="$i18n.get('label_processes_per_page')">
<b-select
:value="processesPerPage"
@input="onChangePerPage"
:disabled="processes.length <= 0">
<option value="12">12</option>
<option value="24">24</option>
<option value="48">48</option>
<option value="96">96</option>
</b-select>
</b-field>
</div>
<div class="pagination">
<b-pagination
@change="onPageChange"
:total="total"
:current.sync="page"
order="is-centered"
size="is-small"
:per-page="processesPerPage"/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ProcessesList from "../../components/lists/processes-list.vue";
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'Processes',
data(){
return {
isLoading: false,
total: 0,
page: 1,
processesPerPage: 12,
}
},
components: {
ProcessesList
},
methods: {
...mapActions('bgprocess', [
'fetchProcesses',
]),
...mapGetters('bgprocess', [
'getProcesses'
]),
onChangePerPage(value) {
this.processesPerPage = value;
this.$userPrefs.set('processes_per_page', value)
.then((newValue) => {
this.processesPerPage = newValue;
})
.catch(() => {
this.$console.log("Error settings user prefs for processes per page")
});
this.load();
},
onPageChange(page) {
this.page = page;
this.load();
},
load() {
this.isLoading = true;
this.fetchProcesses({ 'page': this.page, 'processesPerPage': this.processesPerPage })
.then((res) => {
this.isLoading = false;
this.total = res.total;
})
.catch(() => {
this.isLoading = false;
});
},
getLastProcessesNumber() {
let last = (Number(this.processesPerPage * (this.page - 1)) + Number(this.processesPerPage));
return last > this.total ? this.total : last;
}
},
computed: {
processes(){
return this.getProcesses();
}
},
created() {
this.processesPerPage = this.$userPrefs.get('processes_per_page');
},
mounted(){
if (this.processesPerPage != this.$userPrefs.get('processes_per_page'))
this.processesPerPage = this.$userPrefs.get('processes_per_page');
if (!this.processesPerPage) {
this.processesPerPage = 12;
this.$userPrefs.set('processes_per_page', 12);
}
this.load();
}
}
</script>
<style lang="scss" scoped>
@import '../../scss/_variables.scss';
.sub-header {
max-height: $subheader-height;
height: $subheader-height;
margin-left: -$page-side-padding;
margin-right: -$page-side-padding;
margin-top: -$page-top-padding;
padding-top: $page-small-top-padding;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
border-bottom: 1px solid #ddd;
.header-item {
display: inline-block;
padding-right: 8em;
}
@media screen and (max-width: 769px) {
height: 60px;
margin-top: -0.5em;
padding-top: 0.9em;
.header-item {
padding-right: 0.5em;
}
}
}
.tabs {
padding-top: 20px;
margin-bottom: 20px;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
}
.above-subheader {
margin-bottom: 0;
margin-top: 0;
height: auto;
}
</style>

View File

@ -30,6 +30,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'terms' => __( 'Terms', 'tainacan' ),
'mapping' => __( 'Mapping', 'tainacan' ),
'importers' => __( 'Importers', 'tainacan' ),
'processes' => __( 'Proceses', 'tainacan' ),
// Actions
'edit' => __( 'Edit', 'tainacan' ),
@ -76,7 +77,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'title_items_page' => __( 'Items', 'tainacan' ),
'title_repository_metadata_page' => __( 'Repository Metadata', 'tainacan' ),
'title_repository_filters_page' => __( 'Repository Filters', 'tainacan' ),
'title_taxonomies_page' => __( 'Taxonomies Page', 'tainacan' ),
'title_taxonomies_page' => __( 'Taxonomies', 'tainacan' ),
'title_terms_page' => __( 'Terms', 'tainacan' ),
'title_repository_events_page' => __( 'Repository Events', 'tainacan' ),
'title_collection_page' => __( 'Collection', 'tainacan' ),
@ -101,8 +102,9 @@ return apply_filters( 'tainacan-admin-i18n', [
'title_importer_page' => __( 'Importer', 'tainacan' ),
'title_importers_page' => __( 'Importers', 'tainacan' ),
'title_export_collection_page' => __( 'Export Collection Page', 'tainacan' ),
'title_export_item_page' => __( 'Export Item Page', 'tainacan' ),
'title_export_page' => __( 'Export Page', 'tainacan' ),
'title_export_item_page' => __( 'Export Item', 'tainacan' ),
'title_export_page' => __( 'Export', 'tainacan' ),
'title_processes_page' => __( 'Processes', 'tainacan' ),
// Labels (used mainly on Aria Labels and Inputs)
'label_clean' => __( 'Clear', 'tainacan' ),
@ -140,6 +142,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_taxonomies_per_page' => __( 'Taxonomies per Page:', 'tainacan' ),
'label_events_per_page' => __( 'Events per Page:', 'tainacan' ),
'label_items_per_page' => __( 'Items per Page:', 'tainacan' ),
'label_processes_per_page' => __( 'Processes per Page:', 'tainacan' ),
'label_active_metadata' => __( 'Active Metadata', 'tainacan' ),
'label_available_metadata' => __( 'Available Metadata', 'tainacan' ),
'label_available_metadata_types' => __( 'Available Metadata Types', 'tainacan' ),
@ -216,6 +219,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_select_all_collections_page' => __( 'Select all collections on page', 'tainacan' ),
'label_select_all_items_page' => __( 'Select all items on page', 'tainacan' ),
'label_select_all_taxonomies_page' => __( 'Select all taxonomies on page', 'tainacan' ),
'label_select_all_processes_page' => __( 'Select all processes on page', 'tainacan' ),
'label_edit_attachments' => __( 'Edit attachments', 'tainacan' ),
'label_blank_collection' => __( 'Blank collection', 'tainacan' ),
'label_dublin_core' => __( 'Dublin Core', 'tainacan' ),
@ -258,6 +262,8 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_mapper_metadata' => __( 'Mapper Metadata', 'tainacan' ),
'label_add_more_mapper_metadata' => __( 'Add more mapper\'s metadata', 'tainacan' ),
'label_no_details_of_process' => __( 'There are no details about this process', 'tainacan' ),
'label_queued_on' => __( 'Queued on:', 'tainacan' ),
'label_last_processed_on' => __( 'Last processed on:', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
@ -328,6 +334,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'info_showing_collections' => __( 'Showing collections ', 'tainacan' ),
'info_showing_taxonomies' => __( 'Showing taxonomies ', 'tainacan' ),
'info_showing_events' => __( 'Showing events ', 'tainacan' ),
'info_showing_processes' => __( 'Showing processes ', 'tainacan' ),
'info_to' => __( ' to ', 'tainacan' ),
'info_of' => __( ' of ', 'tainacan' ),
'info_created_by' => __( 'Created by: ', 'tainacan' ),
@ -374,8 +381,9 @@ return apply_filters( 'tainacan-admin-i18n', [
'info_visibility_helper' => __( 'How the item will be available to visualization.', 'tainacan' ),
'info_errors_in_form' => __( 'There are errors in the form', 'tainacan' ),
'info_no_document_to_item' => __( 'No document was uploaded to this item.', 'tainacan' ),
'info_unfinished_processes' => __( 'unfinished processes.', 'tainacan' ),
'info_no_process' => __( 'There are no processes executing.', 'tainacan' ),
'info_unfinished_processes' => __( 'unfinished processes', 'tainacan' ),
'info_no_process' => __( 'There are no processes executing.', 'tainacan' ),
'info_unknown_date' => __( 'Unknown date.', 'tainacan' ),
// Tainacan Metadatum Types
'tainacan-text' => __( 'Text', 'tainacan' ),