Fix wrong i18n key

Fix wrong i18n key

<template>

          1

          1

</template>

<template>

          1

          1

</template>


Fix channel set thumbnail display.

Fix channel set thumbnail display.

<template>

          {{ channel.name }}

          {{ channel.description }}

          ×

          {{ $tr('selectButtonLabel') }}

        {{ $tr("versionText", {'version': channel.version}) }}

</template>


Fix coach icon placement on LessonSummaryPage

<template>

  <core-table>

            {{ $tr('resourceReorderColumnHeaderForTable') }}

            {{ $tr('resourceTypeColumnHeaderForTable') }}

          {{ $tr('nameColumnHeaderForTable') }}

          {{ $tr('resourceProgressColumnHeaderForTable') }}

            {{ $tr('resourceRemovalColumnHeaderForTable') }}

    <transition-group
      name="resource-reorder"
      slot="tbody"
      tag="tbody"
    >

          <ui-icon-button
            type="flat"
            icon="keyboard_arrow_up"
            :ariaLabel="$tr('moveResourceUpButtonDescription')"
            :disabled="index === 0"
            @click="moveUpOne(index)"
            class="position-adjustment-button"
          />
          <ui-icon-button
            type="flat"
            icon="keyboard_arrow_down"
            :ariaLabel="$tr('moveResourceDownButtonDescription')"
            :disabled="index === (workingResources.length - 1)"
            @click="moveDownOne(index)"
            class="position-adjustment-button"
          />

          <content-icon :kind="resourceKind(resourceId)" />

            <k-router-link
              :to="resourceUserSummaryLink(resourceId)"
              :text="resourceTitle(resourceId)"
            />

              <dfn class="visuallyhidden"> {{ $tr('parentChannelLabel') }} </dfn>
              {{ resourceChannelTitle(resourceId) }}

          <coach-content-label
            class="coach-content-label"
            :value="getCachedResource(resourceId).num_coach_contents"
            :isTopic="false"
          />

          <progress-bar
            v-if="resourceProgress(resourceId)!==null"
            class="resource-progress-bar"
            :progress="resourceProgress(resourceId)"
            :showPercentage="false"
          />

            {{ resourceProgressMessage(resourceId) }}

          <k-button
            :text="$tr('resourceRemovalButtonLabel')"
            @click="removeResource(resourceId)"
            appearance="flat-button"
          />

    </transition-group>
  </core-table>

</template>


Added success and failure toast notifications for adding, deleteing and removing tags in the sidebar

<template>

    <sidebar-header title="Stars">
      <refresh-button
        :active="refreshingStars"
        @click.native="refreshStars"/>
    </sidebar-header>

      <sidebar-item
        :class="{ 'selected': noFiltersApplied }"
        class="all-stars"
        title="All Stars"
        icon="InboxIcon"
        icon-size="16"
        @click.native="resetFilters"
      />
      <sidebar-item
        :class="{ 'selected': viewingUntagged }"
        class="untagged-stars"
        title="Untagged Stars"
        icon="StarIcon"
        icon-size="16"
        @click.native="setViewingUntagged(true)"
      />

    <sidebar-header title="Tags">
      <tag-sorter/>
    </sidebar-header>
    <new-tag-form @submit="doAddTag"/>

      <sidebar-tag
        v-for="tag in tags"
        :tag="tag"
        :key="tag.id"
        :is-selected="currentTag.id == tag.id"
        :data-id="tag.id"
        @click.native="doSetCurrentTag(tag, $event)"
        @starsDropped="tagStarWithData"
        @deleteTag="doDeleteTag"
        @renameTag="doRenameTag"
      />

    <sidebar-header title="Languages"/>

      <sidebar-item
        v-for="(value, key) in languages"
        :key="key"
        :badge="value"
        :title="key"
        :class="{ 'selected': currentLanguage == key }"
        class="language rounded"
        @click.native="setCurrentLanguage(key)"
      />

</template>

<template>

    <sidebar-header title="Stars">
      <refresh-button
        :active="refreshingStars"
        @click.native="refreshStars"/>
    </sidebar-header>

      <sidebar-item
        :class="{ 'selected': noFiltersApplied }"
        class="all-stars"
        title="All Stars"
        icon="InboxIcon"
        icon-size="16"
        @click.native="resetFilters"
      />
      <sidebar-item
        :class="{ 'selected': viewingUntagged }"
        class="untagged-stars"
        title="Untagged Stars"
        icon="StarIcon"
        icon-size="16"
        @click.native="setViewingUntagged(true)"
      />

    <sidebar-header title="Tags">
      <tag-sorter/>
    </sidebar-header>
    <new-tag-form @submit="doAddTag"/>

      <sidebar-tag
        v-for="tag in tags"
        :tag="tag"
        :key="tag.id"
        :is-selected="currentTag.id == tag.id"
        :data-id="tag.id"
        @click.native="doSetCurrentTag(tag, $event)"
        @starsDropped="tagStarWithData"
        @deleteTag="doDeleteTag"
        @renameTag="renameTag"
      />

    <sidebar-header title="Languages"/>

      <sidebar-item
        v-for="(value, key) in languages"
        :key="key"
        :badge="value"
        :title="key"
        :class="{ 'selected': currentLanguage == key }"
        class="language rounded"
        @click.native="setCurrentLanguage(key)"
      />

</template>


docs(VDatePicker): null date on clear in format example

resolves #9711

<template>
  <v-container>
    <v-row>
      <v-col cols="12" lg="6">
        <v-menu
          v-model="menu1"
          :close-on-content-click="false"
          max-width="290"
        >
          <template v-slot:activator="{ on }">
            <v-text-field
              :value="computedDateFormattedMomentjs"
              clearable
              label="Formatted with Moment.js"
              readonly
              v-on="on"
              @click:clear="date = null"
            ></v-text-field>
          </template>
          <v-date-picker
            v-model="date"
            @change="menu1 = false"
          ></v-date-picker>
        </v-menu>
      </v-col>

      <v-col cols="12" lg="6">
        <v-menu
          v-model="menu2"
          :close-on-content-click="false"
          max-width="290"
        >
          <template v-slot:activator="{ on }">
            <v-text-field
              :value="computedDateFormattedDatefns"
              clearable
              label="Formatted with datefns"
              readonly
              v-on="on"
              @click:clear="date = null"
            ></v-text-field>
          </template>
          <v-date-picker
            v-model="date"
            @change="menu2 = false"
          ></v-date-picker>
        </v-menu>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
  import moment from 'moment'
  import format from 'date-fns/format'

  export default {
    data: () => ({
      date: new Date().toISOString().substr(0, 10),
      menu1: false,
      menu2: false,
    }),

    computed: {
      computedDateFormattedMomentjs () {
        return this.date ? moment(this.date).format('dddd, MMMM Do YYYY') : ''
      },
      computedDateFormattedDatefns () {
        return this.date ? format(this.date, 'dddd, MMMM Do YYYY') : ''
      },
    },
  }
</script>

<codepen-resources lang="json">
  {
    "js": [
      "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js",
      "https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.29.0/date_fns.min.js"
    ]
  }
</codepen-resources>

<template>
  <v-container>
    <v-row>
      <v-col cols="12" lg="6">
        <v-menu
          v-model="menu1"
          :close-on-content-click="false"
          max-width="290"
        >
          <template v-slot:activator="{ on }">
            <v-text-field
              :value="computedDateFormattedMomentjs"
              clearable
              label="Formatted with Moment.js"
              readonly
              v-on="on"
            ></v-text-field>
          </template>
          <v-date-picker
            v-model="date"
            @change="menu1 = false"
          ></v-date-picker>
        </v-menu>
      </v-col>

      <v-col cols="12" lg="6">
        <v-menu
          v-model="menu2"
          :close-on-content-click="false"
          max-width="290"
        >
          <template v-slot:activator="{ on }">
            <v-text-field
              :value="computedDateFormattedDatefns"
              clearable
              label="Formatted with datefns"
              readonly
              v-on="on"
            ></v-text-field>
          </template>
          <v-date-picker
            v-model="date"
            @change="menu2 = false"
          ></v-date-picker>
        </v-menu>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
  import moment from 'moment'
  import format from 'date-fns/format'

  export default {
    data: () => ({
      date: new Date().toISOString().substr(0, 10),
      menu1: false,
      menu2: false,
    }),

    computed: {
      computedDateFormattedMomentjs () {
        return this.date ? moment(this.date).format('dddd, MMMM Do YYYY') : ''
      },
      computedDateFormattedDatefns () {
        return this.date ? format(this.date, 'dddd, MMMM Do YYYY') : ''
      },
    },
  }
</script>

<codepen-resources lang="json">
  {
    "js": [
      "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js",
      "https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.29.0/date_fns.min.js"
    ]
  }
</codepen-resources>


Bugfix: Using mdi check icon.

Bugfix: Using mdi check icon.

<template>

                {{item.description}}

            <v-spacer></v-spacer>
            <v-icon class="item-icon" v-show="item.isSelected">mdi-check</v-icon>

 0">
            <select-element :item="subItem" v-for="subItem in item.subItems"
                            :key="subItem.id" :is-root="false" :select-multiple="selectMultiple"
                            :dis-select-parent-items="disSelectParentItems" :disabled="disabled" :min-items="minItems"
                            :number-of-selected-items="numberOfSelectedItems"
                            @select-changed="childSelectChanged">
            </select-element>

</template>

<template>

                {{item.description}}

            <v-spacer></v-spacer>
            <v-icon class="item-icon" v-show="item.isSelected">done</v-icon>

 0">
            <select-element :item="subItem" v-for="subItem in item.subItems"
                            :key="subItem.id" :is-root="false" :select-multiple="selectMultiple"
                            :dis-select-parent-items="disSelectParentItems" :disabled="disabled" :min-items="minItems"
                            :number-of-selected-items="numberOfSelectedItems"
                            @select-changed="childSelectChanged">
            </select-element>

</template>


static

static

<template>

    <common-header></common-header>

      <navigator module="information"></navigator>

          您的当前位置 : 基本信息

              公司名称： {{company.name}}

              联系人： {{company.username}}

              联系电话： {{company.userMobile}}

              所在省份： {{company.province}}

              所在城市： {{company.city}}

              公司地址： {{company.address}}

</template>


chore: 增加第三方菜单选项

chore: 增加第三方菜单选项

<template>

          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            切换导航条

          </button>

用户

            <router-link to="/users" tag="li" active-class="active" exact>
              用户管理
            </router-link>
            <router-link to="/users/register" tag="li" active-class="active" exact>
              注册设置
            </router-link>
            <router-link to="/users/setting" tag="li" active-class="active">
              基础设置
            </router-link>
            <router-link to="/users/roles" tag="li" active-class="active">
              角色管理
            </router-link>
            <router-link to="/users/abilities" tag="li" active-class="active">
              权限管理
            </router-link>
            <router-link to="/users/recommends" tag="li" active-class="active">
              推荐用户
            </router-link>

                第三方

QQ 登录

微信登录

微博登录

环信即时聊天

    <router-view></router-view>

</template>

<template>

          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            切换导航条

          </button>

用户

            <router-link to="/users" tag="li" active-class="active" exact>
              用户管理
            </router-link>
            <router-link to="/users/register" tag="li" active-class="active" exact>
              注册设置
            </router-link>
            <router-link to="/users/setting" tag="li" active-class="active">
              基础设置
            </router-link>
            <router-link to="/users/roles" tag="li" active-class="active">
              角色管理
            </router-link>
            <router-link to="/users/abilities" tag="li" active-class="active">
              权限管理
            </router-link>
            <router-link to="/users/recommends" tag="li" active-class="active">
              推荐用户
            </router-link>

    <router-view></router-view>

</template>


Avoid jumpy numbers - https://twitter.com/JonasBadalic/status/926796340008505344

<template>

        {{ device.name }}
        {{ device.ip }}
        {{ creationTime }}
        {{ relativeTime }}

</template>


add more pH error handling

add more pH error handling

<template>

Fertilizer Calc

{{error}}

{{message}}

      <form class="form-inline row" v-on:submit.prevent="">

          NPK Analysis: <input type="text" class="form-control ml-2 mb-2">

      </form>
      <form class="form-inline row" v-on:submit.prevent="onAdd">

          Product
          <select v-model="chosenProduct" class="form-control" id="product-input">
            <option :value="null" disabled>Product</option>
            <option v-for="name in Object.keys(recipes)" :key="name">{{name}}</option>
          </select>

          Percent
          <input v-model="chosenPercent" class="form-control" type="number" min="1" max="100" step="0.01" placeholder="Percent" id="percent-input"/>

            %

          <button class="btn btn-primary text-white" v-on:click="onAdd">Add</button>

      </form>

 0">

Recipe

            Product
            Percent

                    ×

                  {{component[0]}}

              {{(component[1]*100).toFixed(2)}}%

Result

            Ingredient
            CAS Number
            Percent

                  {{ingredient}}

              {{ingredientCasNumbers[ingredient]}}
              {{(percent * 100).toFixed(2)}}%

              Total

              {{totalResultPercent.mul(100).toFixed(2)}}%

Nutrient Concentrations

            Nutrient
            Percent

              {{nutrient}}

                {{concentrations[nutrient].mul(100).toFixed(2)}}%

          Density: {{densityLowerRange.toFixed(2)}} lbs/gal

          Density: {{densityLowerRange.toFixed(2)}}-{{densityUpperRange.toFixed(2)}} lbs/gal

          pH: {{phLowerRange.toFixed(2)}}

          pH: {{phLowerRange.toFixed(2)}}-{{phUpperRange.toFixed(2)}}

Note: pH calculation assumes:

Fertilizer products used to make the blends are both weak acids

Concentration of protons is equal to concentration of the acid

Components are simple liquids which are not buffered

Ionization constants are equal

There is no neutralization reaction or formation of a salt

        Load data: <input type="file" id="file" ref="file" v-on:change="handleDataUpload()"/>

</template>


Removing redundant submission check.

Removing redundant submission check.

<template>

  <core-modal
    :title="modalTexts.title"
    @cancel="closeModal()"
    width="400px"
  >
    <ui-alert
      v-if="showServerError"
      type="error"
      :dismissible="false"
    >
      {{ $tr('submitLessonError') }}
    </ui-alert>

    <form @submit.prevent="submitLessonData">
      <k-textbox
        @blur="titleIsVisited = true"
        ref="titleField"
        :label="$tr('title')"
        :maxlength="50"
        :autofocus="true"
        :invalid="titleIsInvalid"
        :invalidText="titleIsInvalidText"
        v-model="title"
        :disabled="formIsSubmitted"
      />
      <k-textbox
        :label="$tr('description')"
        :maxlength="200"
        :textArea="true"
        v-model="description"
        :disabled="formIsSubmitted"
      />

      <fieldset>
        <legend>{{ $tr('visibleTo') }}</legend>
        <recipient-selector
          v-model="selectedCollectionIds"
          :groups="groups"
          :classId="classId"
          :disabled="formIsSubmitted"
        />
      </fieldset>

      <div class="core-modal-buttons">
        <k-button
          :text="$tr('cancel')"
          appearance="flat-button"
          @click="closeModal()"
        />
        <k-button
          :text="modalTexts.submitButton"
          type="submit"
          :primary="true"
        />
      </div>
    </form>
  </core-modal>

</template>

<script>

  import xor from 'lodash/xor';
  import coreModal from 'kolibri.coreVue.components.coreModal';
  import kButton from 'kolibri.coreVue.components.kButton';
  import kTextbox from 'kolibri.coreVue.components.kTextbox';
  import RecipientSelector from './RecipientSelector';
  import UiAlert from 'keen-ui/src/UiAlert';
  import { LessonResource } from 'kolibri.resources';
  import { createSnackbar } from 'kolibri.coreVue.vuex.actions';
  import { lessonSummaryLink } from '../lessonsRouterUtils';
  import { LessonsPageNames } from '../../../lessonsConstants';
  import { refreshLessonReport } from '../../../state/actions/lessonReportsActions';

  export default {
    name: 'lessonDetailsModal',
    components: {
      coreModal,
      kButton,
      kTextbox,
      RecipientSelector,
      UiAlert,
    },
    data() {
      return {
        // set default values
        title: this.currentLesson.title,
        description: this.currentLesson.description,
        selectedCollectionIds: this.currentLessonAssignedCollectionIds,
        titleIsVisited: false,
        formIsSubmitted: false,
        showServerError: false,
      };
    },
    computed: {
      formData() {
        return {
          title: this.title,
          description: this.description,
          lesson_assignments: this.selectedCollectionIds.map(groupId => ({ collection: groupId })),
        };
      },
      modalTexts() {
        if (this.isInEditMode) {
          // For existing Lesson
          return {
            title: this.$tr('editingLessonDetails'),
            submitButton: this.$tr('save'),
            snackbarText: this.$tr('changesToLessonSaved'),
          };
        }
        // For new Lesson
        return {
          title: this.$tr('newLesson'),
          submitButton: this.$tr('continue'),
          snackbarText: this.$tr('newLessonCreated'),
        };
      },
      titleIsInvalidText() {
        // submission is handled because "blur" event happens on submit
        if (this.titleIsVisited) {
          if (this.title === '') {
            return this.$tr('required');
          }
        }
        return '';
      },
      titleIsInvalid() {
        return Boolean(this.titleIsInvalidText);
      },
      formIsValid() {
        return !this.titleIsInvalid;
      },
      groupsHaveChanged() {
        const unsharedIds = xor(
          this.selectedCollectionIds,
          this.currentLessonAssignedCollectionIds
        );
        return unsharedIds.length > 0;
      },
      lessonDetailsHaveChanged() {
        return (
          this.currentLesson.title !== this.title ||
          this.currentLesson.description !== this.description ||
          this.groupsHaveChanged
        );
      },
    },
    methods: {
      showSuccessSnackbar() {
        this.createSnackbar({
          text: this.modalTexts.snackbarText,
          autoDismiss: true,
        });
      },
      handleSubmitSuccess() {
        this.closeModal();
        this.showSuccessSnackbar();
      },
      handleSubmitFailure() {
        this.formIsSubmitted = false;
        this.showServerError = true;
      },
      submitLessonData() {
        this.showServerError = false;
        // Return immediately if "submit" has already been clicked
        if (this.formIsSubmitted) {
          // IDEA a loading indictor or something would probably be handy
          return;
        }

        if (this.formIsValid) {
          this.formIsSubmitted = true;

          return this.isInEditMode ? this.updateLesson() : this.createLesson();
        } else {
          // shouldn't ever be true, but being safe
          this.formIsSubmitted = false;
          this.$refs.titleField.focus();
        }
      },
      // probably better suited for actions
      createLesson() {
        return LessonResource.createModel({
          ...this.formData,
          resources: [],
          collection: this.classId,
        })
          .save()
          .then(newLesson => {
            this.handleSubmitSuccess();
            return this.$router.push(
              lessonSummaryLink({ classId: this.classId, lessonId: newLesson.id })
            );
          })
          .catch(() => {
            this.handleSubmitFailure();
          });
      },
      updateLesson() {
        if (!this.lessonDetailsHaveChanged) {
          this.closeModal();
          return Promise.resolve();
        }
        return LessonResource.getModel(this.currentLesson.id)
          .save({ ...this.formData })
          .then(updatedLesson => {
            this.handleSubmitSuccess();
            return this.updateCurrentLesson(updatedLesson);
          })
          .catch(() => {
            this.handleSubmitFailure();
          });
      },
      closeModal() {
        this.$emit('cancel');
      },
    },
    vuex: {
      getters: {
        classId: state => state.classId,
        currentLesson: state => {
          const newLesson = {
            title: '',
            description: '',
          };
          return state.pageState.currentLesson || newLesson;
        },
        groups: state => state.pageState.learnerGroups,
        // If the page name is (Lesson) SUMMARY, then should be in edit mode
        isInEditMode: state => state.pageName === LessonsPageNames.SUMMARY,
        // This only exists in edit mode
        currentLessonAssignedCollectionIds: state => {
          if (state.pageState.currentLesson) {
            return state.pageState.currentLesson.lesson_assignments.map(a => a.collection);
          }
          return [state.classId];
        },
      },
      actions: {
        createSnackbar,
        updateCurrentLesson(store, lesson) {
          store.dispatch('SET_CURRENT_LESSON', lesson);
          return refreshLessonReport(store, lesson.id);
        },
      },
    },
    $trs: {
      cancel: 'Cancel',
      changesToLessonSaved: 'Changes to lesson saved',
      continue: 'Continue',
      description: 'Description',
      editingLessonDetails: 'Edit lesson details',
      newLesson: 'New lesson',
      newLessonCreated: 'New lesson created',
      required: 'This is required',
      save: 'Save',
      submitLessonError: 'There was a problem saving this lesson',
      title: 'Title',
      visibleTo: 'Visible to',
    },
  };

</script>

<style lang="stylus" scoped>

  fieldset
    border: none
    margin: 0
    padding: 0

  legend
    padding-top: 16px
    padding-bottom: 8px

</style>

<template>

  <core-modal
    :title="modalTexts.title"
    @cancel="closeModal()"
    width="400px"
  >
    <ui-alert
      v-if="showServerError"
      type="error"
      :dismissible="false"
    >
      {{ $tr('submitLessonError') }}
    </ui-alert>

    <form @submit.prevent="submitLessonData">
      <k-textbox
        @blur="titleIsVisited = true"
        ref="titleField"
        :label="$tr('title')"
        :maxlength="50"
        :autofocus="true"
        :invalid="titleIsInvalid"
        :invalidText="titleIsInvalidText"
        v-model="title"
        :disabled="formIsSubmitted"
      />
      <k-textbox
        :label="$tr('description')"
        :maxlength="200"
        :textArea="true"
        v-model="description"
        :disabled="formIsSubmitted"
      />

      <fieldset>
        <legend>{{ $tr('visibleTo') }}</legend>
        <recipient-selector
          v-model="selectedCollectionIds"
          :groups="groups"
          :classId="classId"
          :disabled="formIsSubmitted"
        />
      </fieldset>

      <div class="core-modal-buttons">
        <k-button
          :text="$tr('cancel')"
          appearance="flat-button"
          @click="closeModal()"
        />
        <k-button
          :text="modalTexts.submitButton"
          type="submit"
          :primary="true"
        />
      </div>
    </form>
  </core-modal>

</template>

<script>

  import xor from 'lodash/xor';
  import coreModal from 'kolibri.coreVue.components.coreModal';
  import kButton from 'kolibri.coreVue.components.kButton';
  import kTextbox from 'kolibri.coreVue.components.kTextbox';
  import RecipientSelector from './RecipientSelector';
  import UiAlert from 'keen-ui/src/UiAlert';
  import { LessonResource } from 'kolibri.resources';
  import { createSnackbar } from 'kolibri.coreVue.vuex.actions';
  import { lessonSummaryLink } from '../lessonsRouterUtils';
  import { LessonsPageNames } from '../../../lessonsConstants';
  import { refreshLessonReport } from '../../../state/actions/lessonReportsActions';

  export default {
    name: 'lessonDetailsModal',
    components: {
      coreModal,
      kButton,
      kTextbox,
      RecipientSelector,
      UiAlert,
    },
    data() {
      return {
        // set default values
        title: this.currentLesson.title,
        description: this.currentLesson.description,
        selectedCollectionIds: this.currentLessonAssignedCollectionIds,
        titleIsVisited: false,
        formIsSubmitted: false,
        showServerError: false,
      };
    },
    computed: {
      formData() {
        return {
          title: this.title,
          description: this.description,
          lesson_assignments: this.selectedCollectionIds.map(groupId => ({ collection: groupId })),
        };
      },
      modalTexts() {
        if (this.isInEditMode) {
          // For existing Lesson
          return {
            title: this.$tr('editingLessonDetails'),
            submitButton: this.$tr('save'),
            snackbarText: this.$tr('changesToLessonSaved'),
          };
        }
        // For new Lesson
        return {
          title: this.$tr('newLesson'),
          submitButton: this.$tr('continue'),
          snackbarText: this.$tr('newLessonCreated'),
        };
      },
      titleIsInvalidText() {
        if (this.titleIsVisited || this.formIsSubmitted) {
          if (this.title === '') {
            return this.$tr('required');
          }
        }
        return '';
      },
      titleIsInvalid() {
        return Boolean(this.titleIsInvalidText);
      },
      formIsValid() {
        return !this.titleIsInvalid;
      },
      groupsHaveChanged() {
        const unsharedIds = xor(
          this.selectedCollectionIds,
          this.currentLessonAssignedCollectionIds
        );
        return unsharedIds.length > 0;
      },
      lessonDetailsHaveChanged() {
        return (
          this.currentLesson.title !== this.title ||
          this.currentLesson.description !== this.description ||
          this.groupsHaveChanged
        );
      },
    },
    methods: {
      showSuccessSnackbar() {
        this.createSnackbar({
          text: this.modalTexts.snackbarText,
          autoDismiss: true,
        });
      },
      handleSubmitSuccess() {
        this.closeModal();
        this.showSuccessSnackbar();
      },
      handleSubmitFailure() {
        this.formIsSubmitted = false;
        this.showServerError = true;
      },
      submitLessonData() {
        this.showServerError = false;
        // Return immediately if "submit" has already been clicked
        if (this.formIsSubmitted) {
          // IDEA a loading indictor or something would probably be handy
          return;
        }

        if (this.formIsValid) {
          this.formIsSubmitted = true;

          return this.isInEditMode ? this.updateLesson() : this.createLesson();
        } else {
          // shouldn't ever be true, but being safe
          this.formIsSubmitted = false;
          this.$refs.titleField.focus();
        }
      },
      // probably better suited for actions
      createLesson() {
        return LessonResource.createModel({
          ...this.formData,
          resources: [],
          collection: this.classId,
        })
          .save()
          .then(newLesson => {
            this.handleSubmitSuccess();
            return this.$router.push(
              lessonSummaryLink({ classId: this.classId, lessonId: newLesson.id })
            );
          })
          .catch(() => {
            this.handleSubmitFailure();
          });
      },
      updateLesson() {
        if (!this.lessonDetailsHaveChanged) {
          this.closeModal();
          return Promise.resolve();
        }
        return LessonResource.getModel(this.currentLesson.id)
          .save({ ...this.formData })
          .then(updatedLesson => {
            this.handleSubmitSuccess();
            return this.updateCurrentLesson(updatedLesson);
          })
          .catch(() => {
            this.handleSubmitFailure();
          });
      },
      closeModal() {
        this.$emit('cancel');
      },
    },
    vuex: {
      getters: {
        classId: state => state.classId,
        currentLesson: state => {
          const newLesson = {
            title: '',
            description: '',
          };
          return state.pageState.currentLesson || newLesson;
        },
        groups: state => state.pageState.learnerGroups,
        // If the page name is (Lesson) SUMMARY, then should be in edit mode
        isInEditMode: state => state.pageName === LessonsPageNames.SUMMARY,
        // This only exists in edit mode
        currentLessonAssignedCollectionIds: state => {
          if (state.pageState.currentLesson) {
            return state.pageState.currentLesson.lesson_assignments.map(a => a.collection);
          }
          return [state.classId];
        },
      },
      actions: {
        createSnackbar,
        updateCurrentLesson(store, lesson) {
          store.dispatch('SET_CURRENT_LESSON', lesson);
          return refreshLessonReport(store, lesson.id);
        },
      },
    },
    $trs: {
      cancel: 'Cancel',
      changesToLessonSaved: 'Changes to lesson saved',
      continue: 'Continue',
      description: 'Description',
      editingLessonDetails: 'Edit lesson details',
      newLesson: 'New lesson',
      newLessonCreated: 'New lesson created',
      required: 'This is required',
      save: 'Save',
      submitLessonError: 'There was a problem saving this lesson',
      title: 'Title',
      visibleTo: 'Visible to',
    },
  };

</script>

<style lang="stylus" scoped>

  fieldset
    border: none
    margin: 0
    padding: 0

  legend
    padding-top: 16px
    padding-bottom: 8px

</style>


+ send hide_deleted when retrieving a list of consumer messages for viewing functional group details

<template>

            <page-side-nav/>
            <page-user-nav/>

                    <template v-if="environment == 'STAGING' && id != null">
                        Promote to production
                        Delete
                        Restore
                    </template>

Functional Group

Name

                        <input v-model="fg.name" :disabled="id" type="email" class="form-control" id="email" required>

Description

                        <textarea v-model="fg.description" :disabled="fieldsDisabled" type="text" rows="2" class="form-control" id="description"></textarea>

User Consent Prompt

                            {{ selectPromptText.prompt }}

Make Default Functional Group

                            Always allow applications access to this functional group

RPCs

                            <rpc-item
                                v-for="(item, index) in fg.rpcs"
                                v-if="item.selected"
                                v-bind:status="fg.status"
                                v-bind:environment="environment"
                                v-bind:fieldsDisabled="fieldsDisabled"
                                v-bind:item="item"
                                v-bind:index="index"
                                v-bind:key="index">
                            </rpc-item>

                        <vue-ladda
                            type="submit"
                            class="btn btn-card"
                            data-style="zoom-in"
                            v-if="!fieldsDisabled"
                            v-on:click="saveGroup()"
                            v-bind:loading="save_button_loading"
                            v-bind:class="{ 'btn-style-green': !fg.is_deleted, 'btn-danger': fg.is_deleted }">
                            Save functional group
                        </vue-ladda>

                    <input v-model="rpc_search" placeholder="Search for an RPC" class="form-control" id="rpc-search">

                        {{ item.name }}

                        Are you sure you want to delete this Functional Group? By doing so, the Functional Group will be immediately removed from the staging policy table, and will be removed from the production policy table upon the next promotion to production.

                    <vue-ladda
                        type="button"
                        class="btn btn-card btn-danger"
                        data-style="zoom-in"
                        v-on:click="deleteGroup()"
                        v-bind:loading="delete_button_loading">
                        Yes, delete this functional group
                    </vue-ladda>

                        Are you sure you want to restore this Functional Group? By doing so, the Functional Group will be immediately restored on the staging policy table, and will be restored on the production policy table upon the next promotion to production.

                    <vue-ladda
                        type="button"
                        class="btn btn-card btn-success"
                        data-style="zoom-in"
                        v-on:click="undeleteGroup()"
                        v-bind:loading="undelete_button_loading">
                        Yes, restore this functional group
                    </vue-ladda>

                        Copying this group will copy the contents and configuration of the group into the creation process for a new Functional Group, giving you an easier starting place.

                    <vue-ladda
                        type="button"
                        class="btn btn-card btn-style-green"
                        data-style="zoom-in"
                        v-on:click="copyGroup()"
                        v-bind:loading="copy_button_loading">
                        Copy as Editable Template
                    </vue-ladda>

                        Promoting this functional group will change its status to production. The functional group will no longer be editable, and it will take precedence over all previous versions of this functional group.

                    <vue-ladda
                        type="button"
                        class="btn btn-card btn-style-green"
                        data-style="zoom-in"
                        v-on:click="promoteGroup()"
                        v-bind:loading="promote_button_loading">
                        Promote to Production
                    </vue-ladda>

</template>


#29 signOut && closeMenu

#29 signOut && closeMenu

<template>

      <jgm-logo/>

      <makeries-menu/>

      <blog-menu :one-at-a-time="true" :acc-data="categories"/>

      <v-touch @tap="closeMobileMenu" v-for="(link, index) in mainMenu" :key="link.id" v-if="index > 0">
        <router-link class="mobile-nav-item" :to="link.url">{{link.title}}</router-link>
      </v-touch>
      <v-touch @tap="closeAndSignOut" v-if="authenticated">
        logout
      </v-touch>

</template>

<template>

      <jgm-logo/>

      <makeries-menu/>

      <blog-menu :one-at-a-time="true" :acc-data="categories"/>

      <v-touch @tap="closeMobileMenu" v-for="(link, index) in mainMenu" :key="link.id" v-if="index > 0">
        <router-link class="mobile-nav-item" :to="link.url">{{link.title}}</router-link>
      </v-touch>
      <v-touch @tap="closeMobileMenu" v-if="authenticated">
        logout
      </v-touch>

</template>


doc: 修改dropdownmenu的文档样式

doc: 修改dropdownmenu的文档样式

<template>

    <DropdownMenu @onclick="trigger" :datas="param" class-name="header-info-dropdown" :show-count="true" :width="150">
      个人信息
    </DropdownMenu>

</template>


link user account details' account row to details page of account

<template>

			{{ $t('adminUserAccountsDetail.title') }}
			{{ email }}

						{{ $t('adminUserAccountsDetail.fields.email') }}
						{{ userAccount.email }}

						{{ $t('adminUserAccountsDetail.fields.userRole') }}

							<button class="btn btn-sm btn-primary translucent" @click="switchRole" v-if="userAccount.userRole !== 'ROLE_ADMIN' && !userIsViewingHimself">ADMIN</button>
							<button class="btn btn-sm btn-primary no-pointer" v-if="userAccount.userRole === 'ROLE_ADMIN'">ADMIN</button>
							<button class="btn btn-sm btn-primary translucent" @click="switchRole" v-if="userAccount.userRole !== 'ROLE_USER' && !userIsViewingHimself">USER</button>
							<button class="btn btn-sm btn-primary no-pointer" v-if="userAccount.userRole === 'ROLE_USER'">USER</button>

						{{ $t('adminUserAccountsDetail.fields.creationDate') }}
						{{ userAccount.creationDate | moment(dateFormat) }}

						{{ $t('adminUserAccountsDetail.fields.balance') }}
						{{ userAccount.balance }}

						{{ $t('adminUserAccountsDetail.fields.deleted') }}

							<button v-if="!userAccount.deleted && !userIsViewingHimself" class="btn btn-danger btn-sm" @click="deleteUser">

								{{ $t('adminUserAccountsDetail.deleteButton') }}
							</button>
							<button v-if="userAccount.deleted && !userIsViewingHimself" class="btn btn-success btn-sm" @click="undeleteUser">

								{{ $t('adminUserAccountsDetail.undeleteButton') }}
							</button>

						{{ $t('adminUserAccountsDetail.fields.account') }}

								<router-link :to="{ name: 'admin-accounts-detail', params: { publicKeyClient: userAccount.accountPublicKeyClient } }">
									{{ userAccount.accountPublicKeyClient }}
								</router-link>

				{{ $t('adminUserAccountsDetail.noUserFoundTitle') }}
				{{ $t('adminUserAccountsDetail.noUserFound') }}

</template>

<template>

			{{ $t('adminUserAccountsDetail.title') }}
			{{ email }}

						{{ $t('adminUserAccountsDetail.fields.email') }}
						{{ userAccount.email }}

						{{ $t('adminUserAccountsDetail.fields.userRole') }}

							<button class="btn btn-sm btn-primary translucent" @click="switchRole" v-if="userAccount.userRole !== 'ROLE_ADMIN' && !userIsViewingHimself">ADMIN</button>
							<button class="btn btn-sm btn-primary no-pointer" v-if="userAccount.userRole === 'ROLE_ADMIN'">ADMIN</button>
							<button class="btn btn-sm btn-primary translucent" @click="switchRole" v-if="userAccount.userRole !== 'ROLE_USER' && !userIsViewingHimself">USER</button>
							<button class="btn btn-sm btn-primary no-pointer" v-if="userAccount.userRole === 'ROLE_USER'">USER</button>

						{{ $t('adminUserAccountsDetail.fields.creationDate') }}
						{{ userAccount.creationDate | moment(dateFormat) }}

						{{ $t('adminUserAccountsDetail.fields.balance') }}
						{{ userAccount.balance }}

						{{ $t('adminUserAccountsDetail.fields.deleted') }}

							<button v-if="!userAccount.deleted && !userIsViewingHimself" class="btn btn-danger btn-sm" @click="deleteUser">

								{{ $t('adminUserAccountsDetail.deleteButton') }}
							</button>
							<button v-if="userAccount.deleted && !userIsViewingHimself" class="btn btn-success btn-sm" @click="undeleteUser">

								{{ $t('adminUserAccountsDetail.undeleteButton') }}
							</button>

						{{ $t('adminUserAccountsDetail.fields.account') }}

								{{ userAccount.accountPublicKeyClient }}

				{{ $t('adminUserAccountsDetail.noUserFoundTitle') }}
				{{ $t('adminUserAccountsDetail.noUserFound') }}

</template>


fix: do not allow the last profile to be removed (#1215)

<template>

{{ $t('PAGES.PROFILE_ALL.HEADER') }} ({{ totalBalances.join(', ') }})

      <RouterLink
        :to="{ name: 'profile-new' }"
        class="ProfileAll__grid__profile flex flex-row w-full"
      >
        <div
          :style="`backgroundImage: url('${assets_loadImage(addProfileImagePath)}')`"
          :title="$t('PAGES.PROFILE_ALL.ADD_PROFILE')"
          class="ProfileAvatar__image profile-avatar-xl background-image flex"
        />

          {{ $t('PAGES.PROFILE_ALL.ADD_PROFILE') }}

      </RouterLink>

        <ProfileAvatar
          :profile="profile"
          letter-size="2xl"
          @click="selectProfile(profile.id)"
        />

              {{ profile.name | truncate(12) }}

              {{ profileBalance(profile) }}

            <RouterLink
              :to="{ name: 'profile-edition', params: { profileId: profile.id } }"
              class="ProfileAll__grid__profile__edition-link font-semibold flex text-xs mt-2 mb-1"
            >
              {{ $t('PAGES.PROFILE_ALL.EDIT_PROFILE') }}
            </RouterLink>

            <button
              v-if="profiles.length > 1"
              class="ProfileAll__grid__profile__delete font-semibold flex text-xs cursor-pointer text-theme-page-text-light hover:underline hover:text-red"
              @click="openRemovalConfirmation(profile)"
            >
              {{ $t('PAGES.PROFILE_ALL.REMOVE_PROFILE') }}
            </button>

            {{ $t('PAGES.PROFILE_ALL.SELECT_PROFILE') }}

    <ProfileRemovalConfirmation
      v-if="profileToRemove"
      :profile="profileToRemove"
      @cancel="hideRemovalConfirmation"
      @removed="onRemoval"
    />

</template>

<template>

{{ $t('PAGES.PROFILE_ALL.HEADER') }} ({{ totalBalances.join(', ') }})

      <RouterLink
        :to="{ name: 'profile-new' }"
        class="ProfileAll__grid__profile flex flex-row w-full"
      >
        <div
          :style="`backgroundImage: url('${assets_loadImage(addProfileImagePath)}')`"
          :title="$t('PAGES.PROFILE_ALL.ADD_PROFILE')"
          class="ProfileAvatar__image profile-avatar-xl background-image flex"
        />

          {{ $t('PAGES.PROFILE_ALL.ADD_PROFILE') }}

      </RouterLink>

        <ProfileAvatar
          :profile="profile"
          letter-size="2xl"
          @click="selectProfile(profile.id)"
        />

              {{ profile.name | truncate(12) }}

              {{ profileBalance(profile) }}

            <RouterLink
              :to="{ name: 'profile-edition', params: { profileId: profile.id } }"
              class="ProfileAll__grid__profile__edition-link font-semibold flex text-xs mt-2 mb-1"
            >
              {{ $t('PAGES.PROFILE_ALL.EDIT_PROFILE') }}
            </RouterLink>

            <button
              class="ProfileAll__grid__profile__delete font-semibold flex text-xs cursor-pointer text-theme-page-text-light hover:underline hover:text-red"
              @click="openRemovalConfirmation(profile)"
            >
              {{ $t('PAGES.PROFILE_ALL.REMOVE_PROFILE') }}
            </button>

            {{ $t('PAGES.PROFILE_ALL.SELECT_PROFILE') }}

    <ProfileRemovalConfirmation
      v-if="profileToRemove"
      :profile="profileToRemove"
      @cancel="hideRemovalConfirmation"
      @removed="onRemoval"
    />

</template>


delete style

delete style

<template>

        <Row><Col style="height:20px;"></Col></Row>

        <Row type="flex" justify="center" class="code-row-bg">
            <Col span=16 style="text-align:center">
Achilles
</Col>

        </Row>

            <cpu-module
                v-for="(item, index) in profilerComputed"
                :singleProfiler="item"
                :key="item.uniqueKey"
                :done="item.done"
                :loadingMsg="item.loadingMsg"
                :error="error">
            </cpu-module>

            DO MEM

        <navigation :allProfiler="profilerComputed">
        </navigation>

</template>


完成提交UI和ux

完成提交UI和ux

<template>

    <button type="button" class="btn btn-default" @click="goBack">返回</button>

            <input type="checkbox" v-model="checkBoxSelectAll" />

          节点名称
          显示名称
          描述

            <input type="checkbox" :value="perm.id" v-model="seleced" />

          {{ perm.name }}
          {{ perm.display_name }}
          {{ perm.description }}

    <button v-if="submit" type="button" class="btn btn-primary" disabled="disabled">

    </button>
    <button v-else type="button" class="btn btn-primary">提交</button>

</template>

<template>

    <button type="button" class="btn btn-default" @click="goBack">返回</button>

            <input type="checkbox" v-model="checkBoxSelectAll" />

          节点名称
          显示名称
          描述

            <input type="checkbox" :value="perm.id" v-model="seleced" />

          {{ perm.name }}
          {{ perm.display_name }}
          {{ perm.description }}

</template>


feat : 简单调整样式

feat : 简单调整样式

<template>

{{post.title}}

                发布于 <time>{{post.date}}</time>
                分类

                        {{tag}}

</template>


hide indicators if too many

hide indicators if too many

<template>

{{ title }}

{{ currentSlide }} of {{ totalSlides }}

        <template v-for="n in 3">
          <slot :slidesScope="slidesScope"></slot>
        </template>

        <button aria-controls="carousel-slides" title="Previous slide" class="carousel__arrow carousel__arrow--left" @click="slideToPrevious()">

        </button>
        <button aria-controls="carousel-slides" title="Next slide" class="carousel__arrow carousel__arrow--right" @click="slideToNext()">

        </button>

      <template v-if="showIndicators">
        <button
          v-for="slide in totalSlides"
          :title="`Move to slide ${slide}`"
          aria-controls="carousel-slides"
          :aria-pressed="isCurrentSlide(slide)"
          :class="['carousel__indicator', selectedSlideClass(slide)]"
          @click="changeSlide(slide)"></button>
      </template>

      <button :title="pauseTitle" v-if="this.slideIntervalLength" class="carousel__pause" @click="toggleSlideInterval">

      </button>

</template>

<template>

{{ title }}

{{ currentSlide }} of {{ totalSlides }}

        <template v-for="n in 3">
          <slot :slidesScope="slidesScope"></slot>
        </template>

        <button aria-controls="carousel-slides" title="Previous slide" class="carousel__arrow carousel__arrow--left" @click="slideToPrevious()">

        </button>
        <button aria-controls="carousel-slides" title="Next slide" class="carousel__arrow carousel__arrow--right" @click="slideToNext()">

        </button>

      <button
        v-for="slide in totalSlides"
        :title="`Move to slide ${slide}`"
        aria-controls="carousel-slides"
        :aria-pressed="isCurrentSlide(slide)"
        :class="['carousel__indicator', selectedSlideClass(slide)]"
        @click="changeSlide(slide)"></button>

      <button :title="pauseTitle" v-if="this.slideIntervalLength" class="carousel__pause" @click="toggleSlideInterval">

      </button>

</template>


check popper exist before invoke scrollIntoView

<template>

      <transition-group @after-leave="resetInputHeight">
        <el-tag
          v-for="item in selected"
          :key="getValueKey(item)"
          closable
          :hit="item.hitState"
          type="primary"
          @close="deleteTag($event, item)"
          close-transition>
          {{ item.currentLabel }}
        </el-tag>
      </transition-group>

      <input
        type="text"
        class="el-select__input"
        :class="`is-${ size }`"
        @focus="visible = true"
        :disabled="disabled"
        @keyup="managePlaceholder"
        @keydown="resetInputState"
        @keydown.down.prevent="navigateOptions('next')"
        @keydown.up.prevent="navigateOptions('prev')"
        @keydown.enter.prevent="selectOption"
        @keydown.esc.stop.prevent="visible = false"
        @keydown.delete="deletePrevTag"
        v-model="query"
        :debounce="remote ? 300 : 0"
        v-if="filterable"
        :style="{ width: inputLength + 'px', 'max-width': inputWidth - 42 + 'px' }"
        ref="input">

    <el-input
      ref="reference"
      v-model="selectedLabel"
      type="text"
      :placeholder="currentPlaceholder"
      :name="name"
      :size="size"
      :disabled="disabled"
      :readonly="!filterable || multiple"
      :validate-event="false"
      @focus="handleFocus"
      @click="handleIconClick"
      @mousedown.native="handleMouseDown"
      @keyup.native="debouncedOnInputChange"
      @keydown.native.down.prevent="navigateOptions('next')"
      @keydown.native.up.prevent="navigateOptions('prev')"
      @keydown.native.enter.prevent="selectOption"
      @keydown.native.esc.stop.prevent="visible = false"
      @keydown.native.tab="visible = false"
      @paste.native="debouncedOnInputChange"
      @mouseenter.native="inputHovering = true"
      @mouseleave.native="inputHovering = false"
      :icon="iconClass">
    </el-input>
    <transition
      name="el-zoom-in-top"
      @before-enter="handleMenuEnter"
      @after-leave="doDestroy">
      <el-select-menu
        ref="popper"
        v-show="visible && emptyText !== false">
        <el-scrollbar
          tag="ul"
          wrap-class="el-select-dropdown__wrap"
          view-class="el-select-dropdown__list"
          :class="{ 'is-empty': !allowCreate && filteredOptionsCount === 0 }"
          v-show="options.length > 0 && !loading">
          <el-option
            :value="query"
            created
            v-if="showNewOption">
          </el-option>
          <slot></slot>
        </el-scrollbar>

{{ emptyText }}

      </el-select-menu>
    </transition>

</template>


set focus to another element when focused element is removed or re-rendered

<!--

    Copyright (C) 2021 con terra GmbH (info@conterra.de)

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

            http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<template>
    <v-scroll-y-transition hide-on-leave>
        <v-card :id="getId"
            v-if="!fieldQuery.disableNot || !fieldQuery.disableField || !fieldQuery.disableRelationalOperator || !fieldQuery.disableValue"
            raised
            class="mb-2"
        >
            <v-container
                v-if="fieldQuery.not"
                fluid
                grid-list-md
                class="pa-0 pl-2"
            >
                <v-chip
                    label
                    color="red"
                >
                    <v-icon left>
                        warning
                    </v-icon>
                    {{ i18n.negated }}
                </v-chip>
            </v-container>
            <v-container
                fluid
                grid-list-md
                class="pa-1 pl-2"
            >
                <v-layout
                    row
                    wrap
                    align-center
                >
                    <v-flex
                        v-if="allowNegation"
                        xs2
                        md1
                    >
                        <v-switch
                            :id = "'negator'+index"
                            v-model="fieldQuery.not"
                            :value="fieldQuery.not"
                            :disabled="fieldQuery.disableNot"
                            class="pa-0 ma-0"
                            color="red"
                            hide-details
                            :aria-label="i18n.aria.negate"
                        />
                    </v-flex>
                    <v-flex
                        :class="{ xs7: !allowNegation, md4: !allowNegation }"
                        xs5
                        md3
                    >
                        <v-select
                            :id="'firstSelect'+index"
                            ref="selectedFieldIdSelect"
                            v-model="fieldQuery.selectedFieldId"
                            :items="fieldQuery.fields"
                            :disabled="fieldQuery.disableField"
                            class="pa-0 ma-0"
                            item-value="id"
                            single-line
                            hide-details
                            @change="fieldChanged($event, fieldQuery)"
                        />
                    </v-flex>
                    <v-flex
                        xs5
                        md3
                    >
                        <v-select
                            ref="relationalOperatorSelect"
                            v-model="fieldQuery.relationalOperator"
                            :items="getRelationalOperators(selectedField)"
                            :disabled="fieldQuery.disableRelationalOperator"
                            class="pa-0 ma-0"
                            single-line
                            hide-details
                            @change="relationalOperatorChanged($event, fieldQuery)"
                        />
                    </v-flex>
                    <v-flex
                        :class="{ xs12: $root.editable, md5: $root.editable }"
                        xs8
                        md3
                    >
                        <v-menu
                            v-if="selectedField && selectedField.type === 'date' && fieldQuery.relationalOperator !== '$exists'"
                            ref="dateMenu"
                            :close-on-content-click="false"
                            transition="scale-transition"
                            lazy
                            offset-y
                            full-width
                            max-width="290px"
                            min-width="290px"
                        >
                            <template v-slot:activator="{ on }">
                                <v-text-field
                                    v-model="dateString"
                                    :disabled="fieldQuery.disableValue"
                                    :placeholder="i18n.enterValue"
                                    class="pa-0 ma-0"
                                    required
                                    hide-details
                                    readonly
                                    v-on="on"
                                />
                            </template>
                            <v-date-picker
                                v-model="dateString"
                                :locale="locale"
                                full-width
                                scrollable
                            />
                        </v-menu>
                        <v-select
                            v-else-if="selectedField && selectedField.type === 'boolean' || fieldQuery.relationalOperator === '$exists'"
                            ref="valueBooleanSelect"
                            v-model="fieldQuery.value"
                            :items="getBooleanItems()"
                            :disabled="fieldQuery.disableValue"
                            class="pa-0 ma-0"
                            single-line
                            hide-details
                        />
                        <v-select
                            v-else-if="selectedField && selectedField.codedValues.length > 0"
                            ref="valueCodedValueSelect"
                            v-model="fieldQuery.value"
                            :items="selectedField && selectedField.codedValues"
                            :disabled="fieldQuery.disableValue"
                            class="pa-0 ma-0"
                            item-value="code"
                            item-text="name"
                            single-line
                            hide-details
                            :multiple="fieldQuery.relationalOperator==='$in'"
                        />
                        <v-combobox
                            v-else-if="selectedField && enableDistinctValues && selectedField.type === 'number'"
                            v-model="fieldQuery.value"
                            ref="valueNumberDistinctValuesCombobox"
                            :items="selectedField.distinctValues"
                            :disabled="fieldQuery.disableValue"
                            :rules="[rules.required, rules.number]"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            class="pa-0 ma-0"
                            required
                            single-line
                            hide-details
                            hide-no-data
                            clearable
                            :search-input.sync="search"
                            :multiple="fieldQuery.relationalOperator==='$in'"
                        />
                        <v-combobox
                            v-else-if="selectedField && enableDistinctValues && selectedField.type === 'string'"
                            v-model="fieldQuery.value"
                            ref="valueStringDistinctValuesCombobox"
                            :items="selectedField.distinctValues"
                            :disabled="fieldQuery.disableValue"
                            :rules="[rules.required]"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            class="pa-0 ma-0"
                            required
                            single-line
                            hide-details
                            hide-no-data
                            clearable
                            :search-input.sync="search"
                            :multiple="fieldQuery.relationalOperator==='$in'"
                        />
                        <v-text-field
                            v-else-if="selectedField && selectedField.type === 'number'"
                            v-model="fieldQuery.value"
                            :disabled="fieldQuery.disableValue"
                            :rules="[rules.required]"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            type="number"
                            class="pa-0 ma-0"
                            required
                            hide-details
                            clearable
                        />
                        <v-text-field
                            v-else
                            v-model="fieldQuery.value"
                            :disabled="fieldQuery.disableValue"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            :rules="[rules.required]"
                            class="pa-0 ma-0"
                            required
                            hide-details
                            clearable
                        />
                    </v-flex>
                    <v-flex
                        v-if="!$root.editable"
                        xs2
                        md1
                    >
                        <v-fab-transition>
                            <v-btn
                                v-if="$root.fieldQueries.length > 1"
                                :disabled="$root.editable"
                                :aria-label="i18n.aria.remove"
                                class="ma-0"
                                icon
                                small
                                @click="emitEventsForRemove"
                            >
                                <v-icon>delete</v-icon>
                            </v-btn>
                        </v-fab-transition>
                    </v-flex>
                    <v-flex
                        v-if="!$root.editable"
                        xs2
                        md1
                    >
                        <v-fab-transition>
                            <v-btn
                                v-if="$root.fieldQueries.length === index + 1"
                                :disabled="$root.editable"
                                :aria-label="i18n.aria.add"
                                class="ma-0"
                                icon
                                small
                                @click="emitEventsForAdd"
                            >
                                <v-icon>add</v-icon>
                            </v-btn>
                        </v-fab-transition>
                    </v-flex>
                </v-layout>
            </v-container>
        </v-card>
    </v-scroll-y-transition>
</template>
<script>
    export default {
        props: {
            i18n: {
                type: Object,
                default: () => {
                    return {};
                }
            },
            locale: {
                type: String,
                default: "en"
            },
            fieldQuery: {
                type: Object,
                default: () => {
                    return {};
                }
            },
            index: {
                type: Number,
                default: 0
            },
            allowNegation: {
                type: Boolean,
                default: false
            },
            activeTool: {
                type: Boolean,
                default: false
            },
            enableDistinctValues: {
                type: Boolean,
                default: true
            }
        },
        data() {
            return {
                rules: {
                    required: (value) => !!value || this.i18n.rules.required,
                    number: (value) => (typeof Number.parseFloat(value) === "number" && !isNaN(Number.parseFloat(value))) || this.i18n.rules.number,
                    string: (value) => typeof value === "string" || this.i18n.rules.string
                },
                search: ""
            }
        },
        mounted(){
            if (this.index > 0){
                let focusId;
                if (this.allowNegation){
                    focusId = "negator" + this.index;
                } else {
                    focusId = "firstSelect" + this.index
                }
                const el = document.getElementById(focusId);
                el.focus();
            }
        },
        computed: {
            selectedField() {
                return this.fieldQuery.fields.find((field) => field.id === this.fieldQuery.selectedFieldId);
            },
            dateString: {
                get: function () {
                    let dateObj;
                    if (this.fieldQuery.value) {
                        dateObj = new Date(this.fieldQuery.value);
                    } else {
                        dateObj = new Date();
                    }
                    if (!isNaN(dateObj.getTime())) {
                        return dateObj.toISOString().substr(0, 10)
                    } else {
                        return null;
                    }
                },
                set: function (value) {
                    this.fieldQuery.value = new Date(value);
                }
            },
            getId(){
                return 'fieldQuery'+ (this.index + 1);
            }
        },
        watch: {
            search: function (value) {
                const selectedField = this.selectedField;
                this.getDistinctValues(value, selectedField);
            },
            activeTool: function (value) {
                if (!value) {
                    if (this.$refs.selectedFieldIdSelect) {
                        this.$refs.selectedFieldIdSelect.isMenuActive = false;
                    }
                    if (this.$refs.relationalOperatorSelect) {
                        this.$refs.relationalOperatorSelect.isMenuActive = false;
                    }
                    if (this.$refs.valueBooleanSelect) {
                        this.$refs.valueBooleanSelect.isMenuActive = false;
                    }
                    if (this.$refs.valueCodedValueSelect) {
                        this.$refs.valueCodedValueSelect.isMenuActive = false;
                    }
                    if (this.$refs.valueNumberDistinctValuesCombobox) {
                        this.$refs.valueNumberDistinctValuesCombobox.isMenuActive = false;
                    }
                    if (this.$refs.valueStringDistinctValuesCombobox) {
                        this.$refs.valueStringDistinctValuesCombobox.isMenuActive = false;
                    }
                }
            }
        },
        methods: {
            fieldChanged: function (selectedFieldId, fieldQuery) {
                const value = this.search;
                const selectedField = this.selectedField;
                this.getDistinctValues(value, selectedField);
                if (selectedField.type === "date") {
                    fieldQuery.value = null;
                    fieldQuery.relationalOperator = "$lte";
                } else {
                    fieldQuery.value = (selectedField.codedValues[0]
                        && selectedField.codedValues[0].code) || selectedField.distinctValues[0] || "";
                    fieldQuery.relationalOperator = "$eq";
                }
                if (fieldQuery.relationalOperator === "$exists") {
                    fieldQuery.value = true;
                }
            },
            relationalOperatorChanged: function (relationalOperator, fieldQuery) {
                const selectedField = this.selectedField;
                if (fieldQuery.value === null || fieldQuery.value === ""){ /* only if no value was selected*/
                    if (relationalOperator === "$exists") {
                        fieldQuery.value = true;
                    } else if (relationalOperator === "$in") {
                        fieldQuery.value = [];
                    } else {
                        if (selectedField.type === "date") {
                            fieldQuery.value = "";
                        } else {
                            fieldQuery.value = (selectedField.codedValues[0]
                                && selectedField.codedValues[0].code) || selectedField.distinctValues[0] || "";
                        }
                    }
                }
            },
            getBooleanItems: function () {
                return [
                    {value: true, text: this.i18n.yes},
                    {value: false, text: this.i18n.no}
                ]
            },
            getRelationalOperators: function (field) {
                if (!field) {
                    return [];
                }
                const type = field.type;
                switch (type) {
                    case "codedvalue":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$gt", text: this.i18n.relationalOperators.is_greater_than},
                            {value: "$gte", text: this.i18n.relationalOperators.is_greater_or_equal},
                            {value: "$lt", text: this.i18n.relationalOperators.is_less_than},
                            {value: "$lte", text: this.i18n.relationalOperators.is_less_or_equal},
                            {value: "$exists", text: this.i18n.relationalOperators.exists}
                        ];
                    case "boolean":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$exists", text: this.i18n.relationalOperators.exists}
                        ];
                    case "string":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$eqw", text: this.i18n.relationalOperators.eqw},
                            {value: "$suggest", text: this.i18n.relationalOperators.suggest},
                            {value: "$exists", text: this.i18n.relationalOperators.exists},
                            {value: "$in", text: this.i18n.relationalOperators.in}
                        ];
                    case "number":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$gt", text: this.i18n.relationalOperators.is_greater_than},
                            {value: "$gte", text: this.i18n.relationalOperators.is_greater_or_equal},
                            {value: "$lt", text: this.i18n.relationalOperators.is_less_than},
                            {value: "$lte", text: this.i18n.relationalOperators.is_less_or_equal},
                            {value: "$in", text: this.i18n.relationalOperators.in}
                        ];
                    case "date":
                        return [
                            {value: "$lte", text: this.i18n.relationalOperators.before},
                            {value: "$gte", text: this.i18n.relationalOperators.after},
                            {value: "$exists", text: this.i18n.relationalOperators.exists}
                        ];
                }
            },
            getDistinctValues(value, fieldQuery) {
                this.$root.$emit("getDistinctValues", {value, fieldQuery});
            },
            emitEventsForAdd(){
                this.$root.$emit('add', {});
                this.$emit("add-event");
            },
            emitEventsForRemove(){
                this.$root.$emit('remove', this.fieldQuery);
                this.$emit("remove-event");
                let index = this.index;
                const idSubstr = this.allowNegation ? "negator" : "firstSelect";
                const nextElement = document.getElementById(idSubstr + (index + 1));
                this.$nextTick(()=>{
                    let focusIndex;
                    if (nextElement){
                        focusIndex = index;
                    } else {
                        focusIndex = index - 1;
                    }
                    const focusEl = document.getElementById(idSubstr + focusIndex);
                    focusEl.focus();
                });
            }
        }
    }
</script>

<!--

    Copyright (C) 2021 con terra GmbH (info@conterra.de)

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

            http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<template>
    <v-scroll-y-transition hide-on-leave>
        <v-card
            v-if="!fieldQuery.disableNot || !fieldQuery.disableField || !fieldQuery.disableRelationalOperator || !fieldQuery.disableValue"
            raised
            class="mb-2"
        >
            <v-container
                v-if="fieldQuery.not"
                fluid
                grid-list-md
                class="pa-0 pl-2"
            >
                <v-chip
                    label
                    color="red"
                >
                    <v-icon left>
                        warning
                    </v-icon>
                    {{ i18n.negated }}
                </v-chip>
            </v-container>
            <v-container
                fluid
                grid-list-md
                class="pa-1 pl-2"
            >
                <v-layout
                    row
                    wrap
                    align-center
                >
                    <v-flex
                        v-if="allowNegation"
                        xs2
                        md1
                    >
                        <v-switch
                            v-model="fieldQuery.not"
                            :value="fieldQuery.not"
                            :disabled="fieldQuery.disableNot"
                            class="pa-0 ma-0"
                            color="red"
                            hide-details
                            :aria-label="i18n.aria.negate"
                        />
                    </v-flex>
                    <v-flex
                        :class="{ xs7: !allowNegation, md4: !allowNegation }"
                        xs5
                        md3
                    >
                        <v-select
                            ref="selectedFieldIdSelect"
                            v-model="fieldQuery.selectedFieldId"
                            :items="fieldQuery.fields"
                            :disabled="fieldQuery.disableField"
                            class="pa-0 ma-0"
                            item-value="id"
                            single-line
                            hide-details
                            @change="fieldChanged($event, fieldQuery)"
                        />
                    </v-flex>
                    <v-flex
                        xs5
                        md3
                    >
                        <v-select
                            ref="relationalOperatorSelect"
                            v-model="fieldQuery.relationalOperator"
                            :items="getRelationalOperators(selectedField)"
                            :disabled="fieldQuery.disableRelationalOperator"
                            class="pa-0 ma-0"
                            single-line
                            hide-details
                            @change="relationalOperatorChanged($event, fieldQuery)"
                        />
                    </v-flex>
                    <v-flex
                        :class="{ xs12: $root.editable, md5: $root.editable }"
                        xs8
                        md3
                    >
                        <v-menu
                            v-if="selectedField && selectedField.type === 'date' && fieldQuery.relationalOperator !== '$exists'"
                            ref="dateMenu"
                            :close-on-content-click="false"
                            transition="scale-transition"
                            lazy
                            offset-y
                            full-width
                            max-width="290px"
                            min-width="290px"
                        >
                            <template v-slot:activator="{ on }">
                                <v-text-field
                                    v-model="dateString"
                                    :disabled="fieldQuery.disableValue"
                                    :placeholder="i18n.enterValue"
                                    class="pa-0 ma-0"
                                    required
                                    hide-details
                                    readonly
                                    v-on="on"
                                />
                            </template>
                            <v-date-picker
                                v-model="dateString"
                                :locale="locale"
                                full-width
                                scrollable
                            />
                        </v-menu>
                        <v-select
                            v-else-if="selectedField && selectedField.type === 'boolean' || fieldQuery.relationalOperator === '$exists'"
                            ref="valueBooleanSelect"
                            v-model="fieldQuery.value"
                            :items="getBooleanItems()"
                            :disabled="fieldQuery.disableValue"
                            class="pa-0 ma-0"
                            single-line
                            hide-details
                        />
                        <v-select
                            v-else-if="selectedField && selectedField.codedValues.length > 0"
                            ref="valueCodedValueSelect"
                            v-model="fieldQuery.value"
                            :items="selectedField && selectedField.codedValues"
                            :disabled="fieldQuery.disableValue"
                            class="pa-0 ma-0"
                            item-value="code"
                            item-text="name"
                            single-line
                            hide-details
                            :multiple="fieldQuery.relationalOperator==='$in'"
                        />
                        <v-combobox
                            v-else-if="selectedField && enableDistinctValues && selectedField.type === 'number'"
                            v-model="fieldQuery.value"
                            ref="valueNumberDistinctValuesCombobox"
                            :items="selectedField.distinctValues"
                            :disabled="fieldQuery.disableValue"
                            :rules="[rules.required, rules.number]"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            class="pa-0 ma-0"
                            required
                            single-line
                            hide-details
                            hide-no-data
                            clearable
                            :search-input.sync="search"
                            :multiple="fieldQuery.relationalOperator==='$in'"
                        />
                        <v-combobox
                            v-else-if="selectedField && enableDistinctValues && selectedField.type === 'string'"
                            v-model="fieldQuery.value"
                            ref="valueStringDistinctValuesCombobox"
                            :items="selectedField.distinctValues"
                            :disabled="fieldQuery.disableValue"
                            :rules="[rules.required]"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            class="pa-0 ma-0"
                            required
                            single-line
                            hide-details
                            hide-no-data
                            clearable
                            :search-input.sync="search"
                            :multiple="fieldQuery.relationalOperator==='$in'"
                        />
                        <v-text-field
                            v-else-if="selectedField && selectedField.type === 'number'"
                            v-model="fieldQuery.value"
                            :disabled="fieldQuery.disableValue"
                            :rules="[rules.required]"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            type="number"
                            class="pa-0 ma-0"
                            required
                            hide-details
                            clearable
                        />
                        <v-text-field
                            v-else
                            v-model="fieldQuery.value"
                            :disabled="fieldQuery.disableValue"
                            :loading="selectedField.loading"
                            :placeholder="i18n.enterValue"
                            :rules="[rules.required]"
                            class="pa-0 ma-0"
                            required
                            hide-details
                            clearable
                        />
                    </v-flex>
                    <v-flex
                        v-if="!$root.editable"
                        xs2
                        md1
                    >
                        <v-fab-transition>
                            <v-btn
                                v-if="$root.fieldQueries.length > 1"
                                :disabled="$root.editable"
                                :aria-label="i18n.aria.remove"
                                class="ma-0"
                                icon
                                small
                                @click="emitEventsForRemove"
                            >
                                <v-icon>delete</v-icon>
                            </v-btn>
                        </v-fab-transition>
                    </v-flex>
                    <v-flex
                        v-if="!$root.editable"
                        xs2
                        md1
                    >
                        <v-fab-transition>
                            <v-btn
                                v-if="$root.fieldQueries.length === index + 1"
                                :disabled="$root.editable"
                                :aria-label="i18n.aria.add"
                                class="ma-0"
                                icon
                                small
                                @click="emitEventsForAdd"
                            >
                                <v-icon>add</v-icon>
                            </v-btn>
                        </v-fab-transition>
                    </v-flex>
                </v-layout>
            </v-container>
        </v-card>
    </v-scroll-y-transition>
</template>
<script>
    export default {
        props: {
            i18n: {
                type: Object,
                default: () => {
                    return {};
                }
            },
            locale: {
                type: String,
                default: "en"
            },
            fieldQuery: {
                type: Object,
                default: () => {
                    return {};
                }
            },
            index: {
                type: Number,
                default: 0
            },
            allowNegation: {
                type: Boolean,
                default: false
            },
            activeTool: {
                type: Boolean,
                default: false
            },
            enableDistinctValues: {
                type: Boolean,
                default: true
            }
        },
        data() {
            return {
                rules: {
                    required: (value) => !!value || this.i18n.rules.required,
                    number: (value) => (typeof Number.parseFloat(value) === "number" && !isNaN(Number.parseFloat(value))) || this.i18n.rules.number,
                    string: (value) => typeof value === "string" || this.i18n.rules.string
                },
                search: ""
            }
        },
        computed: {
            selectedField() {
                return this.fieldQuery.fields.find((field) => field.id === this.fieldQuery.selectedFieldId);
            },
            dateString: {
                get: function () {
                    let dateObj;
                    if (this.fieldQuery.value) {
                        dateObj = new Date(this.fieldQuery.value);
                    } else {
                        dateObj = new Date();
                    }
                    if (!isNaN(dateObj.getTime())) {
                        return dateObj.toISOString().substr(0, 10)
                    } else {
                        return null;
                    }
                },
                set: function (value) {
                    this.fieldQuery.value = new Date(value);
                }
            }
        },
        watch: {
            search: function (value) {
                const selectedField = this.selectedField;
                this.getDistinctValues(value, selectedField);
            },
            activeTool: function (value) {
                if (!value) {
                    if (this.$refs.selectedFieldIdSelect) {
                        this.$refs.selectedFieldIdSelect.isMenuActive = false;
                    }
                    if (this.$refs.relationalOperatorSelect) {
                        this.$refs.relationalOperatorSelect.isMenuActive = false;
                    }
                    if (this.$refs.valueBooleanSelect) {
                        this.$refs.valueBooleanSelect.isMenuActive = false;
                    }
                    if (this.$refs.valueCodedValueSelect) {
                        this.$refs.valueCodedValueSelect.isMenuActive = false;
                    }
                    if (this.$refs.valueNumberDistinctValuesCombobox) {
                        this.$refs.valueNumberDistinctValuesCombobox.isMenuActive = false;
                    }
                    if (this.$refs.valueStringDistinctValuesCombobox) {
                        this.$refs.valueStringDistinctValuesCombobox.isMenuActive = false;
                    }
                }
            }
        },
        methods: {
            fieldChanged: function (selectedFieldId, fieldQuery) {
                const value = this.search;
                const selectedField = this.selectedField;
                this.getDistinctValues(value, selectedField);
                if (selectedField.type === "date") {
                    fieldQuery.value = null;
                    fieldQuery.relationalOperator = "$lte";
                } else {
                    fieldQuery.value = (selectedField.codedValues[0]
                        && selectedField.codedValues[0].code) || selectedField.distinctValues[0] || "";
                    fieldQuery.relationalOperator = "$eq";
                }
                if (fieldQuery.relationalOperator === "$exists") {
                    fieldQuery.value = true;
                }
            },
            relationalOperatorChanged: function (relationalOperator, fieldQuery) {
                const selectedField = this.selectedField;
                if (fieldQuery.value === null || fieldQuery.value === ""){ /* only if no value was selected*/
                    if (relationalOperator === "$exists") {
                        fieldQuery.value = true;
                    } else if (relationalOperator === "$in") {
                        fieldQuery.value = [];
                    } else {
                        if (selectedField.type === "date") {
                            fieldQuery.value = "";
                        } else {
                            fieldQuery.value = (selectedField.codedValues[0]
                                && selectedField.codedValues[0].code) || selectedField.distinctValues[0] || "";
                        }
                    }
                }
            },
            getBooleanItems: function () {
                return [
                    {value: true, text: this.i18n.yes},
                    {value: false, text: this.i18n.no}
                ]
            },
            getRelationalOperators: function (field) {
                if (!field) {
                    return [];
                }
                const type = field.type;
                switch (type) {
                    case "codedvalue":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$gt", text: this.i18n.relationalOperators.is_greater_than},
                            {value: "$gte", text: this.i18n.relationalOperators.is_greater_or_equal},
                            {value: "$lt", text: this.i18n.relationalOperators.is_less_than},
                            {value: "$lte", text: this.i18n.relationalOperators.is_less_or_equal},
                            {value: "$exists", text: this.i18n.relationalOperators.exists}
                        ];
                    case "boolean":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$exists", text: this.i18n.relationalOperators.exists}
                        ];
                    case "string":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$eqw", text: this.i18n.relationalOperators.eqw},
                            {value: "$suggest", text: this.i18n.relationalOperators.suggest},
                            {value: "$exists", text: this.i18n.relationalOperators.exists},
                            {value: "$in", text: this.i18n.relationalOperators.in}
                        ];
                    case "number":
                        return [
                            {value: "$eq", text: this.i18n.relationalOperators.is},
                            {value: "$gt", text: this.i18n.relationalOperators.is_greater_than},
                            {value: "$gte", text: this.i18n.relationalOperators.is_greater_or_equal},
                            {value: "$lt", text: this.i18n.relationalOperators.is_less_than},
                            {value: "$lte", text: this.i18n.relationalOperators.is_less_or_equal},
                            {value: "$in", text: this.i18n.relationalOperators.in}
                        ];
                    case "date":
                        return [
                            {value: "$lte", text: this.i18n.relationalOperators.before},
                            {value: "$gte", text: this.i18n.relationalOperators.after},
                            {value: "$exists", text: this.i18n.relationalOperators.exists}
                        ];
                }
            },
            getDistinctValues(value, fieldQuery) {
                this.$root.$emit("getDistinctValues", {value, fieldQuery});
            },
            emitEventsForAdd(){
                this.$root.$emit('add', {});
                this.$emit("add-event");
            },
            emitEventsForRemove(){
                this.$root.$emit('remove', this.fieldQuery);
                this.$emit("remove-event");
            }
        }
    }
</script>


fix: always show root timetap page in ModalTimetapSchedule.vue

<template>
  <base-modal :class="[ !show ? 'hide' : undefined, 'timetap-modal']">
    <template slot="header">
      {{ $t('parents_landing_2.book_your_class') }}

    </template>

    <template slot="body">

    </template>
  </base-modal>
</template>