feat: criterias component
This commit is contained in:
		@@ -34,37 +34,46 @@
 | 
			
		||||
      v-tab-item(key='rights', :transition='false', :reverse-transition='false')
 | 
			
		||||
        v-card
 | 
			
		||||
          v-card-title.pb-0
 | 
			
		||||
            v-btn(color='primary')
 | 
			
		||||
              v-icon(left) add
 | 
			
		||||
              | Add Rule
 | 
			
		||||
            v-subheader
 | 
			
		||||
              v-icon.mr-2 border_color
 | 
			
		||||
              .subheading Read and Write
 | 
			
		||||
            v-spacer
 | 
			
		||||
            v-btn(flat)
 | 
			
		||||
            v-btn(flat, outline)
 | 
			
		||||
              v-icon(left) arrow_drop_down
 | 
			
		||||
              | Load Preset
 | 
			
		||||
            v-btn(flat, outline)
 | 
			
		||||
              v-icon(left) vertical_align_bottom
 | 
			
		||||
              | Import Rules
 | 
			
		||||
          v-list(dense, two-line)
 | 
			
		||||
            v-list-tile.grey.lighten-5.px-2
 | 
			
		||||
              v-list-tile-avatar(color='red'): v-icon(color='white') remove_circle
 | 
			
		||||
              v-list-tile-content
 | 
			
		||||
                v-list-tile-title /javascript/*
 | 
			
		||||
                v-list-tile-sub-title.caption #[strong WRITE]
 | 
			
		||||
              v-list-tile-action
 | 
			
		||||
                v-btn(icon): v-icon(color='grey') delete
 | 
			
		||||
            v-divider(inset).my-0
 | 
			
		||||
            v-list-tile.grey.lighten-5.px-2
 | 
			
		||||
              v-list-tile-avatar(color='green'): v-icon(color='white') check
 | 
			
		||||
              v-list-tile-content
 | 
			
		||||
                v-list-tile-title /javascript/*
 | 
			
		||||
                v-list-tile-sub-title.caption #[strong WRITE]
 | 
			
		||||
              v-list-tile-action
 | 
			
		||||
                v-btn(icon): v-icon(color='grey') delete
 | 
			
		||||
            v-divider(inset).my-0
 | 
			
		||||
            v-list-tile.grey.lighten-5.px-2
 | 
			
		||||
              v-list-tile-avatar(color='green'): v-icon(color='white') check
 | 
			
		||||
              v-list-tile-content
 | 
			
		||||
                v-list-tile-title /javascript/*
 | 
			
		||||
                v-list-tile-sub-title.caption #[strong READ]
 | 
			
		||||
              v-list-tile-action
 | 
			
		||||
                v-btn(icon): v-icon(color='grey') delete
 | 
			
		||||
          .pa-3.pl-4
 | 
			
		||||
            criterias
 | 
			
		||||
          v-divider.my-0
 | 
			
		||||
          v-card-title.pb-0
 | 
			
		||||
            v-subheader
 | 
			
		||||
              v-icon.mr-2 pageview
 | 
			
		||||
              .subheading Read Only
 | 
			
		||||
            v-spacer
 | 
			
		||||
            v-btn(flat, outline)
 | 
			
		||||
              v-icon(left) arrow_drop_down
 | 
			
		||||
              | Load Preset
 | 
			
		||||
            v-btn(flat, outline)
 | 
			
		||||
              v-icon(left) vertical_align_bottom
 | 
			
		||||
              | Import Rules
 | 
			
		||||
          .pa-3.pl-4
 | 
			
		||||
            criterias
 | 
			
		||||
          v-divider.my-0
 | 
			
		||||
          v-card-title.pb-0
 | 
			
		||||
            v-subheader Legend
 | 
			
		||||
          .px-4.pb-4
 | 
			
		||||
            .body-1.px-1.py-2 Any number of rules can be used at the same time. However, some rules requires more processing time than others. Rule types are color-coded as followed:
 | 
			
		||||
            .caption
 | 
			
		||||
              v-icon(color='blue') stop
 | 
			
		||||
              span Fast rules. None or insignificant latency introduced to all page loads.
 | 
			
		||||
            .caption
 | 
			
		||||
              v-icon(color='orange') stop
 | 
			
		||||
              span Medium rules. Some latency added to all page loads.
 | 
			
		||||
            .caption
 | 
			
		||||
              v-icon(color='red') stop
 | 
			
		||||
              span Slow rules. May adds noticeable latency to all page loads. Avoid using in multiple rules.
 | 
			
		||||
 | 
			
		||||
      v-tab-item(key='users', :transition='false', :reverse-transition='false')
 | 
			
		||||
        v-card
 | 
			
		||||
@@ -100,10 +109,15 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import Criterias from '../common/criterias.vue'
 | 
			
		||||
 | 
			
		||||
import groupQuery from 'gql/admin-groups-query-single.gql'
 | 
			
		||||
import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  components: {
 | 
			
		||||
    Criterias
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      group: {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								client/components/common/criterias-item.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								client/components/common/criterias-item.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
<template lang="pug">
 | 
			
		||||
  .criterias-item
 | 
			
		||||
    //- Type
 | 
			
		||||
    v-select(solo, :items='filteredCriteriaTypes', v-model='item.type', placeholder='Rule Type', ref='typeSelect')
 | 
			
		||||
      template(slot='item', slot-scope='data')
 | 
			
		||||
        v-list-tile-avatar
 | 
			
		||||
          v-avatar(:color='data.item.color', size='40', tile): v-icon(color='white') {{ data.item.icon }}
 | 
			
		||||
        v-list-tile-content
 | 
			
		||||
          v-list-tile-title(v-html='data.item.text')
 | 
			
		||||
          v-list-tile-sub-title.caption(v-html='data.item.description')
 | 
			
		||||
 | 
			
		||||
    //- Operator
 | 
			
		||||
    v-select(solo, :items='filteredCriteriaOperators', v-model='item.operator', placeholder='Operator', :disabled='!item.type', :class='!item.type ? "blue-grey lighten-4" : ""')
 | 
			
		||||
      template(slot='item', slot-scope='data')
 | 
			
		||||
        v-list-tile-avatar
 | 
			
		||||
          v-avatar.white--text(color='blue', size='30', tile) {{ data.item.icon }}
 | 
			
		||||
        v-list-tile-content
 | 
			
		||||
          v-list-tile-title(v-html='data.item.text')
 | 
			
		||||
 | 
			
		||||
    //- Value
 | 
			
		||||
    v-select(v-if='item.type === "country"', solo, :items='countries', v-model='item.value', placeholder='Countries...', multiple, item-text='name', item-value='code')
 | 
			
		||||
    v-text-field(v-else-if='item.type === "path"', solo, v-model='item.value', label='Path (e.g. /section)')
 | 
			
		||||
    v-text-field(v-else-if='item.type === "date"', solo, @click.native.stop='dateActivator = true', v-model='item.value', label='YYYY-MM-DD', readonly)
 | 
			
		||||
    v-text-field.blue-grey.lighten-4(v-else, solo, disabled)
 | 
			
		||||
 | 
			
		||||
    v-dialog(lazy, v-model='dateActivator', width='290px', ref='dateDialog')
 | 
			
		||||
      v-date-picker(v-model='item.value', scrollable, color='primary')
 | 
			
		||||
        v-btn(flat, color='primary' @click='$refs.dateDialog.save(date)', block) ok
 | 
			
		||||
 | 
			
		||||
    v-btn(icon, @click='remove'): v-icon(color='blue-grey') clear
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import _ from 'lodash'
 | 
			
		||||
 | 
			
		||||
// import countriesQuery from 'gql/upsells-query-countries.gql'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  inject: ['allowedCriteriaTypes'],
 | 
			
		||||
  props: {
 | 
			
		||||
    value: {
 | 
			
		||||
      type: Object,
 | 
			
		||||
      default() { return {} }
 | 
			
		||||
    },
 | 
			
		||||
    groupIndex: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      default() { return 0 }
 | 
			
		||||
    },
 | 
			
		||||
    itemIndex: {
 | 
			
		||||
      type: Number,
 | 
			
		||||
      default() { return 0 }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      item: {
 | 
			
		||||
        operator: '',
 | 
			
		||||
        type: '',
 | 
			
		||||
        value: ''
 | 
			
		||||
      },
 | 
			
		||||
      dateActivator: false,
 | 
			
		||||
      dateDialog: false,
 | 
			
		||||
      countries: [],
 | 
			
		||||
      criteriaTypes: [
 | 
			
		||||
        { text: 'Path', value: 'path', icon: 'space_bar', color: 'blue', description: 'Match the path of the document being viewed.' },
 | 
			
		||||
        { text: 'Date', value: 'date', icon: 'date_range', color: 'blue', description: 'Match the current calendar day.' },
 | 
			
		||||
        { text: 'Time', value: 'time', icon: 'access_time', color: 'blue', description: 'Match the current time of day.' },
 | 
			
		||||
        { text: 'User Country', value: 'country', icon: 'public', color: 'red', description: `Match the user's country.` },
 | 
			
		||||
        { text: 'User Group', value: 'group', icon: 'group', color: 'orange', description: 'Match the user group assignments.' }
 | 
			
		||||
      ],
 | 
			
		||||
      criteriaOperators: {
 | 
			
		||||
        country: [
 | 
			
		||||
          { text: 'In', value: 'in', icon: '[...]' },
 | 
			
		||||
          { text: 'Not In', value: 'notIn', icon: '[ x ]' }
 | 
			
		||||
        ],
 | 
			
		||||
        path: [
 | 
			
		||||
          { text: 'Matches Exactly', value: 'eq', icon: '=' },
 | 
			
		||||
          { text: 'NOT Matches Exactly', value: 'ne', icon: '!=' },
 | 
			
		||||
          { text: 'Starts With', value: 'sw', icon: 'x...' },
 | 
			
		||||
          { text: 'NOT Starts With', value: 'nsw', icon: '!x...' },
 | 
			
		||||
          { text: 'Ends With', value: 'ew', icon: '...x' },
 | 
			
		||||
          { text: 'NOT Ends With', value: 'new', icon: '!...x' },
 | 
			
		||||
          { text: 'Matches Regex', value: 'regexp', icon: '^x$' }
 | 
			
		||||
        ],
 | 
			
		||||
        date: [
 | 
			
		||||
          { text: 'On or After', value: 'gte', icon: '>=' },
 | 
			
		||||
          { text: 'On or Before', value: 'lte', icon: '<=' }
 | 
			
		||||
        ],
 | 
			
		||||
        time: [
 | 
			
		||||
          { text: 'At or Later Than', value: 'gte', icon: '>=' },
 | 
			
		||||
          { text: 'At or Before', value: 'lte', icon: '<=' }
 | 
			
		||||
        ],
 | 
			
		||||
        group: [
 | 
			
		||||
          { text: 'Is Part Of', value: 'in', icon: '[...]' },
 | 
			
		||||
          { text: 'Is Not Part Of', value: 'notIn', icon: '[ x ]' }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    filteredCriteriaOperators() {
 | 
			
		||||
      return _.get(this.criteriaOperators, this.item.type, [])
 | 
			
		||||
    },
 | 
			
		||||
    filteredCriteriaTypes() {
 | 
			
		||||
      console.info(this.allowedCriteriaTypes)
 | 
			
		||||
      return _.filter(this.criteriaTypes, c => _.includes(this.allowedCriteriaTypes, c.value))
 | 
			
		||||
    },
 | 
			
		||||
    itemType() {
 | 
			
		||||
      return this.item.type
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    itemType(newValue, oldValue) {
 | 
			
		||||
      this.item.operator = _.head(this.criteriaOperators[newValue]).value
 | 
			
		||||
      this.item.value = ''
 | 
			
		||||
    },
 | 
			
		||||
    item: {
 | 
			
		||||
      handler(newValue, oldValue) {
 | 
			
		||||
        this.$emit('update', this.groupIndex, this.itemIndex, this.item)
 | 
			
		||||
      },
 | 
			
		||||
      deep: true
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    if (!this.item.type) {
 | 
			
		||||
      this.$refs.typeSelect.showMenu()
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    remove() {
 | 
			
		||||
      this.$emit('remove', this.groupIndex, this.itemIndex)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // apollo: {
 | 
			
		||||
  //   countries: {
 | 
			
		||||
  //     query: countriesQuery,
 | 
			
		||||
  //     update: (data) => data.location.countries
 | 
			
		||||
  //   }
 | 
			
		||||
  // }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										173
									
								
								client/components/common/criterias.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								client/components/common/criterias.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,173 @@
 | 
			
		||||
<template lang="pug">
 | 
			
		||||
  .criterias
 | 
			
		||||
    transition-group(name='criterias-group', tag='div')
 | 
			
		||||
      .criterias-group(v-for='(group, g) in groups', :key='g')
 | 
			
		||||
        transition-group(name='criterias-item', tag='div')
 | 
			
		||||
          criterias-item(v-for='(item, i) in group', :key='i', :item='item', :group-index='g', :item-index='i', @update='updateItem', @remove='removeItem')
 | 
			
		||||
        .criterias-item-more
 | 
			
		||||
          v-btn.ml-0(@click='addItem(group)', small, color='blue-grey lighten-2', dark, depressed)
 | 
			
		||||
            v-icon(color='white', left) add
 | 
			
		||||
            | Add condition
 | 
			
		||||
    .criterias-group-more
 | 
			
		||||
      v-btn(@click='addGroup', small, color='blue-grey lighten-1', dark, depressed)
 | 
			
		||||
        v-icon(color='white', left) add
 | 
			
		||||
        | Add condition group
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import CriteriasItem from './criterias-item.vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  components: {
 | 
			
		||||
    CriteriasItem
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    value: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      default() { return [] }
 | 
			
		||||
    },
 | 
			
		||||
    types: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      default() {
 | 
			
		||||
        return ['country', 'path', 'date', 'time', 'group']
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  provide () {
 | 
			
		||||
    return {
 | 
			
		||||
      allowedCriteriaTypes: this.types
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      dataGroups: this.value || []
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    groups: {
 | 
			
		||||
      get() { return this.dataGroups },
 | 
			
		||||
      set(grp) {
 | 
			
		||||
        this.dataGroups = grp
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    dataGroups(newValue, oldValue) {
 | 
			
		||||
      if (newValue !== oldValue) {
 | 
			
		||||
        this.$emit('input', newValue)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    addGroup() {
 | 
			
		||||
      this.dataGroups.push([{}])
 | 
			
		||||
    },
 | 
			
		||||
    addItem(group) {
 | 
			
		||||
      group.push({})
 | 
			
		||||
    },
 | 
			
		||||
    updateItem(groupIndex, itemIndex, item) {
 | 
			
		||||
      console.info(item)
 | 
			
		||||
      this.$set(this.dataGroups[groupIndex], itemIndex, item)
 | 
			
		||||
    },
 | 
			
		||||
    removeItem(groupIndex, itemIndex) {
 | 
			
		||||
      this.dataGroups[groupIndex].splice(itemIndex, 1)
 | 
			
		||||
      if (this.dataGroups[groupIndex].length < 1) {
 | 
			
		||||
        this.dataGroups.splice(groupIndex, 1)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
.criterias {
 | 
			
		||||
  &-group {
 | 
			
		||||
    background-color: mc('blue-grey', '100');
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 1rem;
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    &-enter-active, &-leave-active {
 | 
			
		||||
      transition: all .5s ease;
 | 
			
		||||
    }
 | 
			
		||||
    &-enter, &-leave-to {
 | 
			
		||||
      opacity: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    & + .criterias-group {
 | 
			
		||||
      margin-top: 1rem;
 | 
			
		||||
 | 
			
		||||
      &::before {
 | 
			
		||||
        content: 'OR';
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        display: inline-flex;
 | 
			
		||||
        padding: 0 2rem;
 | 
			
		||||
        top: -1.25rem;
 | 
			
		||||
        left: 2rem;
 | 
			
		||||
        background-color: mc('blue-grey', '100');
 | 
			
		||||
        color: mc('blue-grey', '700');
 | 
			
		||||
        font-weight: 600;
 | 
			
		||||
        font-size: .9rem;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &-more {
 | 
			
		||||
      margin: .5rem 0 0 .4rem;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &-item {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    background-color: mc('blue-grey', '200');
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: .5rem;
 | 
			
		||||
 | 
			
		||||
    &-enter-active, &-leave-active {
 | 
			
		||||
      transition: all .5s ease;
 | 
			
		||||
    }
 | 
			
		||||
    &-enter, &-leave-to {
 | 
			
		||||
      opacity: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    & + .criterias-item {
 | 
			
		||||
      margin-top: .5rem;
 | 
			
		||||
      position: relative;
 | 
			
		||||
 | 
			
		||||
      &::before {
 | 
			
		||||
        content: 'AND';
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        width: 2rem;
 | 
			
		||||
        height: 2rem;
 | 
			
		||||
        border-radius: 50%;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        font-weight: 600;
 | 
			
		||||
        color: mc('blue-grey', '700');
 | 
			
		||||
        font-size: .7rem;
 | 
			
		||||
        background-color: mc('blue-grey', '100');
 | 
			
		||||
        left: -2rem;
 | 
			
		||||
        top: -1.3rem;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .input-group {
 | 
			
		||||
      &:nth-child(1) {
 | 
			
		||||
        flex: 0 1 350px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:nth-child(2) {
 | 
			
		||||
        flex: 0 1 250px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      & + * {
 | 
			
		||||
        margin-left: .5rem;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &-more {
 | 
			
		||||
      margin-top: .15rem;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
		Reference in New Issue
	
	Block a user