Discover: Fix SidebarFilter Funnel.

This commit is contained in:
chiayin
2023-04-24 17:48:29 +08:00
parent 0cff786e9a
commit 280da0731d
10 changed files with 159 additions and 162 deletions

View File

@@ -0,0 +1,120 @@
<template>
<div class=" w-full h-full">
<div class="h-[calc(100%_-_58px)] border-b border-neutral-400 mb-2 overflow-y-auto overflow-x-auto scrollbar">
<div v-if="this.temporaryData.length === 0" class="h-full flex justify-center items-center">
<span class="text-neutral-500">No Filter.</span>
</div>
<div v-else>
<Timeline :value="ruleData">
<template #content="rule">
<div class="border-b border-neutral-300 flex justify-between items-center space-x-2">
<!-- content -->
<div class="pl-2 mb-2">
<p class="text-sm font-medium leading-5">{{ rule.item.type }}:&nbsp;<span class="text-neutral-500">{{ rule.item.label }}</span></p>
</div>
<!-- button -->
<div class="min-w-fit">
<InputSwitch v-model="rule.item.toggle" @input="isRule($event, rule.index)"/>
<button type="button" class="m-2 focus:ring focus:ring-danger/20 text-neutral-500 hover:text-danger" @click.stop="deleteRule(rule.index)">
<span class="material-symbols-outlined">delete</span>
</button>
</div>
</div>
</template>
</Timeline>
</div>
</div>
<!-- Button -->
<div class="">
<div class="float-right space-x-4 px-4 py-2">
<button type="button" class="btn btn-sm " :class="[ temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']" :disabled="temporaryData.length === 0" @click="deleteRule('all')">Delete All</button>
<button type="button" class="btn btn-sm" :class="[ temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']" :disabled="temporaryData.length === 0" @click="submitAll">Apply All</button>
</div>
</div>
</div>
</template>
<script>
import { storeToRefs } from 'pinia';
import LoadingStore from '@/stores/loading.js';
import AllMapDataStore from '@/stores/allMapData.js';
export default {
setup() {
const loadingStore = LoadingStore();
const allMapDataStore = AllMapDataStore();
const { isLoading } = storeToRefs(loadingStore);
const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData} = storeToRefs(allMapDataStore);
return { isLoading, hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, allMapDataStore }
},
methods: {
/**
* @param {boolean} e ture | false
* @param {numble} index rule's index
*/
isRule(e, index){
let rule = this.isRuleData[index];
// 先取得 rule object
// 為了讓 data 順序不亂掉,將值指向 0submitAll 時再刪掉
if(!e) this.temporaryData[index] = 0;
else this.temporaryData[index] = rule;
},
// header:Funnel 刪除全部的 Funnel
deleteRule(index) {
if(index === 'all') {
this.temporaryData = [];
this.isRuleData = [];
this.$toast.success('All deleted.');
}else{
this.$toast.success(`Delete ${this.ruleData[index].label}.`);
this.temporaryData.splice(index, 1);
this.isRuleData.splice(index, 1);
this.ruleData.splice(index, 1);
}
},
// header:Funnel 發送暫存的選取資料
async submitAll() {
this.postRuleData = this.temporaryData.filter(item => item !== 0); // 取得 submit 的資料,有 toggle button 的話,找出並刪除陣列中為 0 的項目
await this.allMapDataStore.checkHasResult();
if(this.hasResultRule === null) return;
else if(this.hasResultRule) {
this.isLoading = true;
await this.allMapDataStore.addTempFilterId();
await this.allMapDataStore.getAllMapData();
await this.$emit('submit-all');
this.isLoading = false;
this.$toast.success('Filter Success. View the Map.');
}else {
this.isLoading = true;
await new Promise(resolve => setTimeout(resolve, 1000));
this.isLoading = false;
this.$toast.warning('No Data.');
};
},
},
}
</script>
<style scoped>
/* TimeLine */
:deep(.p-timeline) {
@apply leading-none my-4
}
:deep(.p-timeline-event-opposite) {
@apply hidden
}
:deep(.p-timeline-event-separator) {
@apply mx-4
}
:deep(.p-timeline-event-marker) {
@apply !bg-primary !border-primary !w-2 !h-2
}
:deep(.p-timeline-event-connector) {
@apply !bg-primary my-2 !w-[1px]
}
:deep(.p-timeline-event-content) {
@apply !px-0
}
</style>

View File

@@ -60,11 +60,9 @@
</div>
</div>
</div>
<!-- title: Activity Select -->
<div class="space-y-2 w-[calc(100%_-_240px)] h-[calc(100%_-_106px)]">
<p class="h2 ml-1">Activity Select</p>
<!-- Filter task Data-->
<ActOccCase v-if="selectValue[0] === 'Sequence' && selectValue[1] === 'Have activity(s)'" :tableTitle="'Activity List'" :tableData="filterTaskData" :tableSelect="selectFilterTask" :progressWidth ="progressWidth" @on-row-select="onRowAct"></ActOccCase>
<!-- Filter Start Data -->
@@ -80,7 +78,6 @@
<div v-if="selectValue[0] === 'Sequence' && selectValue[1] === 'Sequence'" class="flex justify-between items-center w-full h-full space-x-4">
<ActAndSeq :filterTaskData="filterTaskData" :progressWidth ="progressWidth" :listSeq="listSeq" @update:listSeq="onUpdateListSeq"></ActAndSeq>
</div>
<!-- Button -->
<div class="float-right space-x-4 px-4 py-2">
<button type="button" class="btn btn-sm btn-neutral" @click="reset">Clear</button>
@@ -88,44 +85,8 @@
</div>
</div>
</div>
<!-- header: funnel -->
<div v-if="tab === 'funnel'" class=" w-full h-full">
<div class="h-[calc(100%_-_58px)] border-b border-neutral-400 mb-2 overflow-y-auto overflow-x-auto scrollbar">
<div v-if="this.temporaryData.length === 0" class="h-full flex justify-center items-center">
<span class="text-neutral-500">No Filter.</span>
</div>
<div v-else>
<Timeline :value="ruleData">
<template #content="rule">
<div class="border-b border-neutral-300 flex justify-between items-center space-x-2">
<!-- content -->
<div class="pl-2 mb-2">
<p class="text-sm font-medium leading-5">{{ rule.item.type }}:&nbsp;<span class="text-neutral-500">{{ rule.item.label }}</span></p>
<!-- <p class="text-sm font-medium mb-5">{{ rule.item.type }}</p>
<p class="text-sm text-neutral-500 mb-2">{{ rule.item.label }}</p> -->
</div>
<!-- button -->
<div class="min-w-fit">
<InputSwitch v-model="rule.item.toggle" @input="isRule($event, rule.index)"/>
<button type="button" class="m-2 focus:ring focus:ring-danger/20 text-neutral-500 hover:text-danger" @click.stop="deleteRule(rule.index)">
<span class="material-symbols-outlined">delete</span>
</button>
</div>
</div>
</template>
</Timeline>
</div>
</div>
<!-- Button -->
<div class="">
<div class="float-right space-x-4 px-4 py-2">
<button type="button" class="btn btn-sm " :class="[ temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']" :disabled="temporaryData.length === 0" @click="deleteRule('all')">Delete All</button>
<button type="button" class="btn btn-sm" :class="[ temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']" :disabled="temporaryData.length === 0" @click="submitAll">Apply All</button>
</div>
</div>
</div>
<Funnel v-if="tab === 'funnel'" @submit-all="sumbitAll"></Funnel>
</Sidebar>
</template>
@@ -133,44 +94,20 @@
import { storeToRefs } from 'pinia';
import LoadingStore from '@/stores/loading.js';
import AllMapDataStore from '@/stores/allMapData.js';
import ActOccCase from '@/components/Discover/table/actOccCase.vue';
import ActOcc from '@/components/Discover/table/actOcc.vue';
import ActAndSeq from '@/components/Discover/table/actAndSeq.vue';
import ActOccCase from '@/components/Discover/Filter/ActOccCase.vue';
import ActOcc from '@/components/Discover/Filter/ActOcc.vue';
import ActAndSeq from '@/components/Discover/Filter/ActAndSeq.vue';
import Funnel from '@/components/Discover/Filter/Funnel.vue';
export default {
props: {
sidebarFilter: {
type: Boolean,
require: true,
},
filterTasks: {
type: Array,
require: true,
},
filterStartToEnd: {
type: Array,
require: true,
},
filterEndToStart: {
type: Array,
require: true,
},
filterTimeframe: {
type: Object,
require: true,
},
filterTrace: {
type: Array,
require: true,
},
},
props: ['sidebarFilter', 'filterTasks', 'filterStartToEnd', 'filterEndToStart', 'filterTimeframe', 'filterTrace'],
setup() {
const loadingStore = LoadingStore();
const allMapDataStore = AllMapDataStore();
const { isLoading } = storeToRefs(loadingStore);
const { hasResultRule, temporaryData, postRuleData} = storeToRefs(allMapDataStore);
const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData} = storeToRefs(allMapDataStore);
return { isLoading, hasResultRule, temporaryData, postRuleData, allMapDataStore }
return { isLoading, hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, allMapDataStore }
},
data() {
return {
@@ -204,14 +141,13 @@ export default {
isEndSelected: null,
isActAllTask: true,
rowData: [],
ruleData: [],
isRuleData: [], // toggle button data
}
},
components: {
ActOccCase,
ActOcc,
ActAndSeq,
Funnel,
},
computed: {
// All Task
@@ -233,25 +169,12 @@ export default {
}
},
methods: {
/**
* @param {string} switch Summary or Insight
*/
switchTab(tab) {
this.tab = tab;
},
/**
* @param {boolean} e ture | false
* @param {numble} index rule's index
*/
isRule(e, index){
let rule = this.isRuleData[index];
// rule object
// data 0submitAll
if(!e) this.temporaryData[index] = 0;
else this.temporaryData[index] = rule;
},
/**
* Number to percentage
* @param {number} val
@@ -372,9 +295,8 @@ export default {
* @param {object} e task's object
*/
setRule(e) {
let label = "";
let type = "";
let includeStr = e.is_exclude?" Exclude ":" Include ";
let label, type;
const includeStr = e.is_exclude?" Exclude ":" Include ";
switch(e.type){
case "contains-task":
@@ -412,17 +334,15 @@ export default {
label = `from ${e.lower} to ${e.upper} ${includeStr}`
type = "Trace"
break
default:
break
}
};
return {
type:type,
label:label,
type,
label,
toggle:true,
}
};
},
/**
* 清空選項
* @param {boolean} massage true | false 清空選項
*/
reset(massage) {
this.selectFilterTask = null;
@@ -446,7 +366,7 @@ export default {
//
if(sele[0] === 'Sequence'){
if(sele[1] === 'Have activity(s)'){ // Activity Sequence Have activity(s)
if(this.selectFilterTask === null || this.selectFilterTask.length === 0) return this.$toast.error('Not selected');
if(!this.selectFilterTask?.length) return this.$toast.error('Not selected');
else {
// task obj
data = this.selectFilterTask.map(task => {
@@ -457,7 +377,7 @@ export default {
}
})
};
}else if(sele[1] === 'Start activity / end activity') { // Activity Sequence Start activity & end activity
}else if(sele[1] === 'Start activity / end activity') { // Activity Sequence Start activity / end activity
if(sele[2] === 'Start') {
if(this.selectFilterStart === null || this.selectFilterStart.length === 0) return this.$toast.error('Not selected');
else {
@@ -502,16 +422,20 @@ export default {
const postData = Array.isArray(data) ? data : [data];
// filter
// this.logId = this.$route.params.logId;
this.postRuleData = postData;
await this.allMapDataStore.checkHasResult();
// Data Funnel Data Funnel
if(this.hasResultRule === null) return;
else if(this.hasResultRule) {
if(!this.temporaryData?.length){
this.temporaryData.push(...postData);
this.isRuleData = Array.from(this.temporaryData);
this.ruleData = this.temporaryData.map(e => this.setRule(e));
this.ruleData = this.isRuleData.map(e => this.setRule(e));
}else {
this.isRuleData.push(...postData);
this.ruleData = this.isRuleData.map(e => this.setRule(e));
}
this.reset(false);
this.isLoading = true;
await new Promise(resolve => setTimeout(resolve, 1000));
@@ -525,67 +449,18 @@ export default {
this.$toast.warning('No Data.');
};
},
// header:Funnel Funnel
deleteRule(index) {
if(index === 'all') {
this.temporaryData = [];
this.isRuleData = [];
this.$toast.success('All deleted.');
}else{
this.$toast.success(`Delete ${this.ruleData[index].label}.`);
this.temporaryData.splice(index, 1);
this.isRuleData.splice(index, 1);
this.ruleData.splice(index, 1);
/**
* create map
*/
sumbitAll() {
this.$emit('submit-all');
}
},
// header:Funnel
async submitAll() {
this.postRuleData = this.temporaryData; // submit
this.postRuleData = this.postRuleData.filter(item => item !== 0); // toggle button 0
await this.allMapDataStore.checkHasResult();
if(this.hasResultRule === null) return;
else if(this.hasResultRule) {
this.isLoading = true;
await this.allMapDataStore.addTempFilterId();
await this.allMapDataStore.getAllMapData();
await this.$emit('submit-all');
this.isLoading = false;
this.$toast.success('Filter Success. View the Map.');
}else {
this.isLoading = true;
await new Promise(resolve => setTimeout(resolve, 1000));
this.isLoading = false;
this.$toast.warning('No Data.');
};
},
},
}
</script>
<style scoped>
#searchFiles::-webkit-search-cancel-button{
appearance: none;
}
/* TimeLine */
:deep(.p-timeline) {
@apply leading-none my-4
}
:deep(.p-timeline-event-opposite) {
@apply hidden
}
:deep(.p-timeline-event-separator) {
@apply mx-4
}
:deep(.p-timeline-event-marker) {
@apply !bg-primary !border-primary !w-2 !h-2
}
:deep(.p-timeline-event-connector) {
@apply !bg-primary my-2 !w-[1px]
}
:deep(.p-timeline-event-content) {
@apply !px-0
}
</style>

View File

@@ -31,6 +31,8 @@ export default defineStore('allMapDataStore', {
hasResultRule: null, // click Apply 後檢查是否有 Data
temporaryData: [], // 沒被 apply 的 Data
postRuleData: [], // has-result API & temp-filters API 的 Data
ruleData: [], // Funnle view's data
isRuleData: [], // toggle button data
}),
getters: {
processMap: state => {

View File

@@ -58,10 +58,10 @@ import { storeToRefs } from 'pinia';
import LoadingStore from '@/stores/loading.js';
import AllMapDataStore from '@/stores/allMapData.js';
import cytoscapeMap from '@/module/cytoscapeMap.js';
import SidebarView from '@/components/Discover/sidebarView.vue';
import SidebarState from '@/components/Discover/sidebarState.vue';
import SidebarTraces from '@/components/Discover/sidebarTraces.vue';
import SidebarFilter from '@/components/Discover/sidebarFilter.vue';
import SidebarView from '@/components/Discover/SidebarView.vue';
import SidebarState from '@/components/Discover/SidebarState.vue';
import SidebarTraces from '@/components/Discover/SidebarTraces.vue';
import SidebarFilter from '@/components/Discover/SidebarFilter.vue';
export default {
setup() {