parent
							
								
									a1bcb12f20
								
							
						
					
					
						commit
						053bcad80a
					
				@ -0,0 +1,78 @@ | 
				
			||||
<template> | 
				
			||||
        <v-app id="inspire"> | 
				
			||||
            <snackbar></snackbar> | 
				
			||||
            <v-navigation-drawer v-model="drawer" app> | 
				
			||||
                <v-list dense> | 
				
			||||
                    <v-list-item> | 
				
			||||
                        <v-list-item-action> | 
				
			||||
                            <v-icon>mdi-home</v-icon> | 
				
			||||
                        </v-list-item-action> | 
				
			||||
                        <v-list-item-content> | 
				
			||||
                            <v-list-item-title>Home</v-list-item-title> | 
				
			||||
                        </v-list-item-content> | 
				
			||||
                    </v-list-item> | 
				
			||||
                    <v-list-item> | 
				
			||||
                        <v-list-item-action> | 
				
			||||
                            <v-icon>mdi-book</v-icon> | 
				
			||||
                        </v-list-item-action> | 
				
			||||
                        <v-list-item-content> | 
				
			||||
                            <v-list-item-title> | 
				
			||||
                                <router-link :to="{ name: 'CourseList' }">Courses</router-link> | 
				
			||||
                            </v-list-item-title> | 
				
			||||
                        </v-list-item-content> | 
				
			||||
                    </v-list-item> | 
				
			||||
                    <v-list-item> | 
				
			||||
                        <v-list-item-action> | 
				
			||||
                            <v-icon>mdi-book</v-icon> | 
				
			||||
                        </v-list-item-action> | 
				
			||||
                        <v-list-item-content> | 
				
			||||
                            <v-list-item-title> | 
				
			||||
                                <router-link :to="{ name: 'CourseCategoryList' }">Courses category</router-link> | 
				
			||||
                            </v-list-item-title> | 
				
			||||
                        </v-list-item-content> | 
				
			||||
                    </v-list-item> | 
				
			||||
 | 
				
			||||
                    <v-list-item> | 
				
			||||
                        <v-list-item-action> | 
				
			||||
                            <v-icon>mdi-comment-quote</v-icon> | 
				
			||||
                        </v-list-item-action> | 
				
			||||
                        <v-list-item-content> | 
				
			||||
                            <v-list-item-title> | 
				
			||||
                                <router-link :to="{ name: 'ReviewList' }">Reviews</router-link> | 
				
			||||
                            </v-list-item-title> | 
				
			||||
                        </v-list-item-content> | 
				
			||||
                    </v-list-item> | 
				
			||||
                </v-list> | 
				
			||||
            </v-navigation-drawer> | 
				
			||||
            <v-app-bar app color="indigo" dark> | 
				
			||||
                <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon> | 
				
			||||
                <v-toolbar-title>Application</v-toolbar-title> | 
				
			||||
            </v-app-bar> | 
				
			||||
 | 
				
			||||
            <v-content> | 
				
			||||
                <Breadcrumb layout-class="pl-3 py-3" /> | 
				
			||||
                <router-view></router-view> | 
				
			||||
            </v-content> | 
				
			||||
            <v-footer color="indigo" app> | 
				
			||||
                <span class="white--text">© 2019</span> | 
				
			||||
            </v-footer> | 
				
			||||
        </v-app> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
    import Breadcrumb from './components/Breadcrumb'; | 
				
			||||
    import Snackbar from './components/Snackbar'; | 
				
			||||
 | 
				
			||||
    export default { | 
				
			||||
        name: "App", | 
				
			||||
        components: { | 
				
			||||
            Breadcrumb, | 
				
			||||
            Snackbar | 
				
			||||
        }, | 
				
			||||
        data: () => ({ | 
				
			||||
            drawer: null | 
				
			||||
        }), | 
				
			||||
        beforeMount() { | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
 export const VueConfig = { | 
				
			||||
   delimiters: ['[[', ']]'] | 
				
			||||
 }; | 
				
			||||
@ -0,0 +1,45 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <v-row justify="space-around"> | 
				
			||||
      <v-icon v-if="handleShow" small class="mr-2" @click="handleShow">mdi-eye</v-icon> | 
				
			||||
      <v-icon v-if="handleEdit" small class="mr-2" @click="handleEdit">mdi-pencil</v-icon> | 
				
			||||
      <v-icon v-if="handleDelete" small @click="confirmDelete = true">mdi-delete</v-icon> | 
				
			||||
    </v-row> | 
				
			||||
    <ConfirmDelete | 
				
			||||
      v-if="handleDelete" | 
				
			||||
      :visible="confirmDelete" | 
				
			||||
      :handle-delete="handleDelete" | 
				
			||||
      @close="confirmDelete = false" | 
				
			||||
    /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import ConfirmDelete from './ConfirmDelete'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'ActionCell', | 
				
			||||
  components: { | 
				
			||||
    ConfirmDelete | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      confirmDelete: false | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  props: { | 
				
			||||
    handleShow: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    handleEdit: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    handleDelete: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,40 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <v-breadcrumbs :items="items" divider="/" :class="layoutClass" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
export default { | 
				
			||||
  name: 'Breadcrumb', | 
				
			||||
  props: ['layoutClass'], | 
				
			||||
  data() { | 
				
			||||
    return {}; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    items() { | 
				
			||||
      const { path, matched } = this.$route; | 
				
			||||
      const items = [ | 
				
			||||
        { | 
				
			||||
          text: 'Home', | 
				
			||||
          href: '/' | 
				
			||||
        } | 
				
			||||
      ]; | 
				
			||||
      const lastItem = matched[matched.length - 1]; | 
				
			||||
      for (let i = 0, len = matched.length; i < len; i += 1) { | 
				
			||||
        const route = matched[i]; | 
				
			||||
 | 
				
			||||
        if (route.path) { | 
				
			||||
          items.push({ | 
				
			||||
            text: route.name, | 
				
			||||
            disabled: route.path === path || lastItem.path === route.path, | 
				
			||||
            href: route.path | 
				
			||||
          }); | 
				
			||||
        } | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      return items; | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,45 @@ | 
				
			||||
<template> | 
				
			||||
  <v-dialog v-model="show" persistent width="300"> | 
				
			||||
    <v-card> | 
				
			||||
      <v-card-text>{{ $t('Are you sure you want to delete this item?') }}</v-card-text> | 
				
			||||
      <v-card-actions> | 
				
			||||
        <v-spacer></v-spacer> | 
				
			||||
        <v-btn color="error darken-1" @click="handleDelete"> | 
				
			||||
          {{ $t('Delete') }} | 
				
			||||
        </v-btn> | 
				
			||||
        <v-btn color="secondary darken-1" text @click.stop="show = false"> | 
				
			||||
          {{ $t('Cancel') }} | 
				
			||||
        </v-btn> | 
				
			||||
      </v-card-actions> | 
				
			||||
    </v-card> | 
				
			||||
  </v-dialog> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
export default { | 
				
			||||
  name: 'ConfirmDelete', | 
				
			||||
  props: { | 
				
			||||
    visible: { | 
				
			||||
      type: Boolean, | 
				
			||||
      required: true, | 
				
			||||
      default: () => false | 
				
			||||
    }, | 
				
			||||
    handleDelete: { | 
				
			||||
      type: Function, | 
				
			||||
      required: true | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    show: { | 
				
			||||
      get() { | 
				
			||||
        return this.visible; | 
				
			||||
      }, | 
				
			||||
      set(value) { | 
				
			||||
        if (!value) { | 
				
			||||
          this.$emit('close'); | 
				
			||||
        } | 
				
			||||
      } | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,40 @@ | 
				
			||||
<template> | 
				
			||||
  <v-expansion-panels v-model="filtersExpanded"> | 
				
			||||
    <v-expansion-panel> | 
				
			||||
      <v-expansion-panel-header> | 
				
			||||
        {{ $t('Filters') }} | 
				
			||||
 | 
				
			||||
        <template slot="actions"> | 
				
			||||
          <v-icon large>mdi-filter-variant</v-icon> | 
				
			||||
        </template> | 
				
			||||
      </v-expansion-panel-header> | 
				
			||||
      <v-expansion-panel-content> | 
				
			||||
        <slot name="filter"></slot> | 
				
			||||
 | 
				
			||||
        <v-btn color="primary" @click="handleFilter">{{ $t('Filter')}}</v-btn> | 
				
			||||
        <v-btn color="primary" class="ml-2" text @click="handleReset">{{ $t('Reset') }}</v-btn> | 
				
			||||
      </v-expansion-panel-content> | 
				
			||||
    </v-expansion-panel> | 
				
			||||
  </v-expansion-panels> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
export default { | 
				
			||||
  name: 'DataFilter', | 
				
			||||
  props: { | 
				
			||||
    handleReset: { | 
				
			||||
      type: Function, | 
				
			||||
      required: true | 
				
			||||
    }, | 
				
			||||
    handleFilter: { | 
				
			||||
      type: Function, | 
				
			||||
      required: true | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      filtersExpanded: false | 
				
			||||
    }; | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,52 @@ | 
				
			||||
<template> | 
				
			||||
  <v-menu | 
				
			||||
    v-model="showMenu" | 
				
			||||
    :close-on-content-click="false" | 
				
			||||
    :nudge-right="40" | 
				
			||||
    transition="scale-transition" | 
				
			||||
    offset-y | 
				
			||||
    min-width="290px" | 
				
			||||
  > | 
				
			||||
    <template v-slot:activator="{ on }"> | 
				
			||||
      <v-text-field | 
				
			||||
        v-model="date" | 
				
			||||
        :label="label" | 
				
			||||
        prepend-icon="mdi-calendar" | 
				
			||||
        readonly | 
				
			||||
        v-on="on" | 
				
			||||
      ></v-text-field> | 
				
			||||
    </template> | 
				
			||||
    <v-date-picker v-model="date" @input="handleInput"></v-date-picker> | 
				
			||||
  </v-menu> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { formatDateTime } from '../utils/dates'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  props: { | 
				
			||||
    label: { | 
				
			||||
      type: String, | 
				
			||||
      required: false, | 
				
			||||
      default: () => '' | 
				
			||||
    }, | 
				
			||||
    value: String | 
				
			||||
  }, | 
				
			||||
  created() { | 
				
			||||
    this.date = this.value ? this.value : this.date; | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      date: this.value ? this.value : new Date().toISOString().substr(0, 10), | 
				
			||||
      showMenu: false | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    formatDateTime, | 
				
			||||
    handleInput() { | 
				
			||||
      this.showMenu = false; | 
				
			||||
      this.$emit('input', this.date); | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,18 @@ | 
				
			||||
<template> | 
				
			||||
  <div class="text-center"> | 
				
			||||
    <v-overlay :value="visible"> | 
				
			||||
      <v-progress-circular indeterminate size="64"></v-progress-circular> | 
				
			||||
    </v-overlay> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
export default { | 
				
			||||
  props: { | 
				
			||||
    visible: { | 
				
			||||
      type: Boolean, | 
				
			||||
      required: true | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,32 @@ | 
				
			||||
<template> | 
				
			||||
  <v-snackbar | 
				
			||||
    v-model="show" | 
				
			||||
    :color="color" | 
				
			||||
    :multi-line="true" | 
				
			||||
    :timeout="timeout" | 
				
			||||
    right | 
				
			||||
    top | 
				
			||||
  > | 
				
			||||
    {{ text }} | 
				
			||||
    <template v-if="subText"> | 
				
			||||
      <p>{{ subText }}</p> | 
				
			||||
    </template> | 
				
			||||
    <v-btn dark text @click.native="close">{{ $t('Close') }}</v-btn> | 
				
			||||
  </v-snackbar> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('notifications', ['color', 'show', 'subText', 'text', 'timeout']) | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  methods: { | 
				
			||||
    close() { | 
				
			||||
      this.show = false; | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,137 @@ | 
				
			||||
<template> | 
				
			||||
  <v-toolbar class="my-md-auto" elevation="0"> | 
				
			||||
    <slot name="left"></slot> | 
				
			||||
    <v-spacer /> | 
				
			||||
    <div> | 
				
			||||
      <v-btn | 
				
			||||
              v-if="handleList" | 
				
			||||
              :loading="isLoading" | 
				
			||||
              color="primary" | 
				
			||||
              @click="listItem" | 
				
			||||
      > | 
				
			||||
        {{ $t('List') }} | 
				
			||||
      </v-btn> | 
				
			||||
      <v-btn | 
				
			||||
        v-if="handleEdit" | 
				
			||||
        :loading="isLoading" | 
				
			||||
        color="primary" | 
				
			||||
        @click="editItem" | 
				
			||||
      > | 
				
			||||
        {{ $t('Edit') }} | 
				
			||||
      </v-btn> | 
				
			||||
      <v-btn | 
				
			||||
        v-if="handleSubmit" | 
				
			||||
        :loading="isLoading" | 
				
			||||
        color="primary" | 
				
			||||
        @click="submitItem" | 
				
			||||
      > | 
				
			||||
        <v-icon left>mdi-content-save</v-icon> | 
				
			||||
        {{ $t('Submit') }} | 
				
			||||
      </v-btn> | 
				
			||||
      <v-btn | 
				
			||||
        v-if="handleReset" | 
				
			||||
        color="primary" | 
				
			||||
        class="ml-sm-2" | 
				
			||||
        @click="resetItem" | 
				
			||||
      > | 
				
			||||
        {{ $t('Reset') }} | 
				
			||||
      </v-btn> | 
				
			||||
      <v-btn | 
				
			||||
        v-if="handleDelete" | 
				
			||||
        color="error" | 
				
			||||
        class="ml-sm-2" | 
				
			||||
        @click="confirmDelete = true" | 
				
			||||
      > | 
				
			||||
        {{ $t('Delete') }} | 
				
			||||
      </v-btn> | 
				
			||||
 | 
				
			||||
      <v-btn v-if="handleAdd" color="primary" rounded @click="addItem"> | 
				
			||||
        <v-icon>mdi-plus-circle</v-icon> | 
				
			||||
      </v-btn> | 
				
			||||
    </div> | 
				
			||||
    <ConfirmDelete | 
				
			||||
      v-if="handleDelete" | 
				
			||||
      :visible="confirmDelete" | 
				
			||||
      :handle-delete="handleDelete" | 
				
			||||
      @close="confirmDelete = false" | 
				
			||||
    /> | 
				
			||||
  </v-toolbar> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import ConfirmDelete from './ConfirmDelete'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'Toolbar', | 
				
			||||
  components: { | 
				
			||||
    ConfirmDelete | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      confirmDelete: false | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  props: { | 
				
			||||
    handleList: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    handleEdit: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    handleSubmit: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    handleReset: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    handleDelete: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    handleAdd: { | 
				
			||||
      type: Function, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    title: { | 
				
			||||
      type: String, | 
				
			||||
      required: false | 
				
			||||
    }, | 
				
			||||
    isLoading: { | 
				
			||||
      type: Boolean, | 
				
			||||
      required: false, | 
				
			||||
      default: () => false | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    listItem() { | 
				
			||||
      if (this.handleList) { | 
				
			||||
        this.handleList(); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    addItem() { | 
				
			||||
      if (this.handleAdd) { | 
				
			||||
        this.handleAdd(); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    editItem() { | 
				
			||||
      if (this.handleEdit) { | 
				
			||||
        this.handleEdit(); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    submitItem() { | 
				
			||||
      if (this.handleSubmit) { | 
				
			||||
        this.handleSubmit(); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    resetItem() { | 
				
			||||
      if (this.handleReset) { | 
				
			||||
        this.handleReset(); | 
				
			||||
      } | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,76 @@ | 
				
			||||
<template> | 
				
			||||
  <v-container fluid> | 
				
			||||
    <v-row> | 
				
			||||
      <v-col cols="12" sm="6" md="6"> | 
				
			||||
        <v-text-field | 
				
			||||
          v-model="item.title" | 
				
			||||
          :label="$t('title')" | 
				
			||||
          type="text" | 
				
			||||
        /> | 
				
			||||
      </v-col> | 
				
			||||
 | 
				
			||||
      <v-col cols="12" sm="6" md="6"> | 
				
			||||
        <v-text-field | 
				
			||||
          v-model="item.code" | 
				
			||||
          :label="$t('code')" | 
				
			||||
          type="text" | 
				
			||||
        /> | 
				
			||||
      </v-col> | 
				
			||||
    </v-row> | 
				
			||||
 | 
				
			||||
    <v-row> | 
				
			||||
      <v-col cols="12" sm="6" md="6"> | 
				
			||||
        <v-combobox | 
				
			||||
              v-model="item.category" | 
				
			||||
              :items="categorySelectItems" | 
				
			||||
              :no-data-text="$t('No results')" | 
				
			||||
              :label="$t('category')" | 
				
			||||
              item-text="name" | 
				
			||||
              item-value="@id" | 
				
			||||
          chips | 
				
			||||
        /> | 
				
			||||
      </v-col> | 
				
			||||
     | 
				
			||||
      <v-row cols="12"></v-row> | 
				
			||||
    </v-row> | 
				
			||||
 | 
				
			||||
  </v-container> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
 | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseFilter', | 
				
			||||
  props: { | 
				
			||||
    values: { | 
				
			||||
      type: Object, | 
				
			||||
      required: true | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return {}; | 
				
			||||
  }, | 
				
			||||
  mounted() { | 
				
			||||
    this.categoryGetSelectItems(); | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions({ | 
				
			||||
      categoryGetSelectItems: 'coursecategory/fetchSelectItems' | 
				
			||||
    }), | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('coursecategory', { | 
				
			||||
      categorySelectItems: 'selectItems' | 
				
			||||
    }), | 
				
			||||
 | 
				
			||||
    // eslint-disable-next-line | 
				
			||||
    item() { | 
				
			||||
      return this.initialValues || this.values; | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,173 @@ | 
				
			||||
<template> | 
				
			||||
  <v-form> | 
				
			||||
    <v-container fluid> | 
				
			||||
      <v-row> | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model="item.title" | 
				
			||||
                  :error-messages="titleErrors" | 
				
			||||
                  :label="$t('title')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.title.$touch()" | 
				
			||||
                  @blur="$v.item.title.$touch()" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
 | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model="item.code" | 
				
			||||
                  :error-messages="codeErrors" | 
				
			||||
                  :label="$t('code')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.code.$touch()" | 
				
			||||
                  @blur="$v.item.code.$touch()" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
      </v-row> | 
				
			||||
 | 
				
			||||
      <v-row> | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-combobox | 
				
			||||
                  v-model="item.category" | 
				
			||||
                  :items="categorySelectItems" | 
				
			||||
                  :error-messages="categoryErrors" | 
				
			||||
                  :no-data-text="$t('No results')" | 
				
			||||
                  :label="$t('category')" | 
				
			||||
                  item-text="name" | 
				
			||||
                  item-value="@id" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
 | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model.number="item.visibility" | 
				
			||||
                  :error-messages="visibilityErrors" | 
				
			||||
                  :label="$t('visibility')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.visibility.$touch()" | 
				
			||||
                  @blur="$v.item.visibility.$touch()" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
      </v-row> | 
				
			||||
 | 
				
			||||
    </v-container> | 
				
			||||
  </v-form> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  import has from 'lodash/has'; | 
				
			||||
  import { validationMixin } from 'vuelidate'; | 
				
			||||
  import { required } from 'vuelidate/lib/validators'; | 
				
			||||
  import { mapActions } from 'vuex'; | 
				
			||||
  import { mapFields } from 'vuex-map-fields'; | 
				
			||||
 | 
				
			||||
  export default { | 
				
			||||
    name: 'CourseForm', | 
				
			||||
    mixins: [validationMixin], | 
				
			||||
    props: { | 
				
			||||
      values: { | 
				
			||||
        type: Object, | 
				
			||||
        required: true | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      errors: { | 
				
			||||
        type: Object, | 
				
			||||
        default: () => {} | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      initialValues: { | 
				
			||||
        type: Object, | 
				
			||||
        default: () => {} | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    data() { | 
				
			||||
      return { | 
				
			||||
        title: null, | 
				
			||||
        code: null, | 
				
			||||
        category: null, | 
				
			||||
        visibility: null, | 
				
			||||
      }; | 
				
			||||
    }, | 
				
			||||
    computed: { | 
				
			||||
      ...mapFields('coursecategory', { | 
				
			||||
        categorySelectItems: 'selectItems' | 
				
			||||
      }), | 
				
			||||
 | 
				
			||||
      // eslint-disable-next-line | 
				
			||||
      item() { | 
				
			||||
        return this.initialValues || this.values; | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      titleErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.title.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'title') && errors.push(this.violations.title); | 
				
			||||
 | 
				
			||||
        !this.$v.item.title.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
      codeErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.code.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'code') && errors.push(this.violations.code); | 
				
			||||
 | 
				
			||||
        !this.$v.item.code.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
      categoryErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.category.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'category') && errors.push(this.violations.category); | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
      visibilityErrors() { | 
				
			||||
        const errors = []; | 
				
			||||
 | 
				
			||||
        if (!this.$v.item.visibility.$dirty) return errors; | 
				
			||||
 | 
				
			||||
        has(this.violations, 'visibility') && errors.push(this.violations.visibility); | 
				
			||||
 | 
				
			||||
        !this.$v.item.visibility.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
        return errors; | 
				
			||||
      }, | 
				
			||||
 | 
				
			||||
      violations() { | 
				
			||||
        return this.errors || {}; | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    mounted() { | 
				
			||||
      this.categoryGetSelectItems(); | 
				
			||||
    }, | 
				
			||||
    methods: { | 
				
			||||
      ...mapActions({ | 
				
			||||
        categoryGetSelectItems: 'coursecategory/fetchSelectItems' | 
				
			||||
      }), | 
				
			||||
    }, | 
				
			||||
    validations: { | 
				
			||||
      item: { | 
				
			||||
        title: { | 
				
			||||
          required, | 
				
			||||
        }, | 
				
			||||
        code: { | 
				
			||||
          required, | 
				
			||||
        }, | 
				
			||||
        category: { | 
				
			||||
        }, | 
				
			||||
        visibility: { | 
				
			||||
          required, | 
				
			||||
        }, | 
				
			||||
      } | 
				
			||||
    } | 
				
			||||
  }; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,9 @@ | 
				
			||||
<template> | 
				
			||||
  <router-view></router-view> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  export default { | 
				
			||||
      name: 'CourseLayout' | 
				
			||||
  } | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,40 @@ | 
				
			||||
<template> | 
				
			||||
  <v-container fluid> | 
				
			||||
    <v-row> | 
				
			||||
      <v-col cols="12" sm="6" md="6"> | 
				
			||||
        <v-text-field | 
				
			||||
                v-model="item.name" | 
				
			||||
                :label="$t('name')" | 
				
			||||
                type="text" | 
				
			||||
        /> | 
				
			||||
      </v-col> | 
				
			||||
    </v-row> | 
				
			||||
  </v-container> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseCategoryFilter', | 
				
			||||
  props: { | 
				
			||||
    values: { | 
				
			||||
      type: Object, | 
				
			||||
      required: true | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return {}; | 
				
			||||
  }, | 
				
			||||
  mounted() { | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  computed: { | 
				
			||||
    // eslint-disable-next-line | 
				
			||||
    item() { | 
				
			||||
      return this.initialValues || this.values; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,113 @@ | 
				
			||||
<template> | 
				
			||||
  <v-form> | 
				
			||||
    <v-container fluid> | 
				
			||||
      <v-row> | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model="item.name" | 
				
			||||
                  :error-messages="nameErrors" | 
				
			||||
                  :label="$t('name')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.name.$touch()" | 
				
			||||
                  @blur="$v.item.name.$touch()" | 
				
			||||
          /> | 
				
			||||
          </v-col> | 
				
			||||
 | 
				
			||||
        <v-col cols="12" sm="6" md="6"> | 
				
			||||
          <v-text-field | 
				
			||||
                  v-model="item.code" | 
				
			||||
                  :error-messages="codeErrors" | 
				
			||||
                  :label="$t('code')" | 
				
			||||
                  required | 
				
			||||
                  @input="$v.item.code.$touch()" | 
				
			||||
                  @blur="$v.item.code.$touch()" | 
				
			||||
          /> | 
				
			||||
        </v-col> | 
				
			||||
 | 
				
			||||
      </v-row> | 
				
			||||
 | 
				
			||||
    </v-container> | 
				
			||||
  </v-form> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import has from 'lodash/has'; | 
				
			||||
import { validationMixin } from 'vuelidate'; | 
				
			||||
import { required } from 'vuelidate/lib/validators'; | 
				
			||||
import { mapActions } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseCategoryForm', | 
				
			||||
  mixins: [validationMixin], | 
				
			||||
  props: { | 
				
			||||
    values: { | 
				
			||||
      type: Object, | 
				
			||||
      required: true | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    errors: { | 
				
			||||
      type: Object, | 
				
			||||
      default: () => {} | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    initialValues: { | 
				
			||||
      type: Object, | 
				
			||||
      default: () => {} | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  mounted() { | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
 | 
				
			||||
    // eslint-disable-next-line | 
				
			||||
    item() { | 
				
			||||
      return this.initialValues || this.values; | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    nameErrors() { | 
				
			||||
      const errors = []; | 
				
			||||
 | 
				
			||||
      if (!this.$v.item.name.$dirty) return errors; | 
				
			||||
 | 
				
			||||
      has(this.violations, 'name') && errors.push(this.violations.name); | 
				
			||||
 | 
				
			||||
      !this.$v.item.name.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
      return errors; | 
				
			||||
    }, | 
				
			||||
    codeErrors() { | 
				
			||||
      const errors = []; | 
				
			||||
 | 
				
			||||
      if (!this.$v.item.code.$dirty) return errors; | 
				
			||||
 | 
				
			||||
      has(this.violations, 'code') && errors.push(this.violations.code); | 
				
			||||
 | 
				
			||||
      !this.$v.item.code.required && errors.push(this.$t('Field is required')); | 
				
			||||
 | 
				
			||||
      return errors; | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    violations() { | 
				
			||||
      return this.errors || {}; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
  }, | 
				
			||||
  validations: { | 
				
			||||
    item: { | 
				
			||||
       name: { | 
				
			||||
          required, | 
				
			||||
        }, | 
				
			||||
      code: { | 
				
			||||
        required, | 
				
			||||
      }, | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,9 @@ | 
				
			||||
<template> | 
				
			||||
  <router-view></router-view> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
  export default { | 
				
			||||
      name: 'CourseCategoryLayout' | 
				
			||||
  } | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,10 @@ | 
				
			||||
export default class SubmissionError extends Error { | 
				
			||||
  constructor (errors) { | 
				
			||||
    super('Submit Validation Failed'); | 
				
			||||
    this.errors = errors; | 
				
			||||
    //Error.captureStackTrace(this, this.constructor);
 | 
				
			||||
    this.name = this.constructor.name; | 
				
			||||
 | 
				
			||||
    return this; | 
				
			||||
  } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,13 @@ | 
				
			||||
import Vue from 'vue'; | 
				
			||||
import VueI18n from 'vue-i18n'; | 
				
			||||
import messages from './locales/en'; | 
				
			||||
 | 
				
			||||
Vue.use(VueI18n); | 
				
			||||
 | 
				
			||||
export default new VueI18n({ | 
				
			||||
    locale: process.env.VUE_APP_I18N_LOCALE || 'en', | 
				
			||||
    fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', | 
				
			||||
    messages: { | 
				
			||||
        en: messages | 
				
			||||
    } | 
				
			||||
}); | 
				
			||||
@ -0,0 +1,22 @@ | 
				
			||||
export default { | 
				
			||||
  'Submit': 'Submit', | 
				
			||||
  'Reset': 'Reset', | 
				
			||||
  'Delete': 'Delete', | 
				
			||||
  'Edit': 'Edit', | 
				
			||||
  'Are you sure you want to delete this item?': 'Are you sure you want to delete this item?', | 
				
			||||
  'No results': 'No results', | 
				
			||||
  'Close': 'Close', | 
				
			||||
  'Cancel': 'Cancel', | 
				
			||||
  'Updated': 'Updated', | 
				
			||||
  'Field': 'Field', | 
				
			||||
  'Value': 'Value', | 
				
			||||
  'Filters': 'Filters', | 
				
			||||
  'Filter': 'Filter', | 
				
			||||
  'Data unavailable': 'Data unavailable', | 
				
			||||
  'Loading...': 'Loading...', | 
				
			||||
  'Deleted': 'Deleted', | 
				
			||||
  'Please, insert a value bigger than zero!': 'Please, insert a value bigger than zero!', | 
				
			||||
  'Please type something': 'Please type something', | 
				
			||||
  'Field is required': 'Field is required', | 
				
			||||
  'Records per page:': 'Records per page:', | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,67 @@ | 
				
			||||
import Vue from "vue"; | 
				
			||||
import App from "./App"; | 
				
			||||
import router from "./router"; | 
				
			||||
import store from "./store"; | 
				
			||||
import courseCategoryService from './services/coursecategory'; | 
				
			||||
import courseService from './services/course'; | 
				
			||||
import makeCrudModule from './store/modules/crud'; | 
				
			||||
 | 
				
			||||
// import '@mdi/font/css/materialdesignicons.css'
 | 
				
			||||
 | 
				
			||||
/*router.beforeEach((to, from, next) => { | 
				
			||||
    // hack to allow for forward slashes in path ids
 | 
				
			||||
    if (to.fullPath.includes('%2F')) { | 
				
			||||
        next(to.fullPath.replace('%2F', '/')); | 
				
			||||
    } | 
				
			||||
    next(); | 
				
			||||
});*/ | 
				
			||||
 | 
				
			||||
import vuetify from './plugins/vuetify' // path to vuetify export
 | 
				
			||||
 | 
				
			||||
import ApolloClient from 'apollo-boost' | 
				
			||||
const apolloClient = new ApolloClient({ | 
				
			||||
    // You should use an absolute URL here
 | 
				
			||||
    uri: '/api/graphql/' | 
				
			||||
}) | 
				
			||||
 | 
				
			||||
import VueApollo from 'vue-apollo'; | 
				
			||||
Vue.use(VueApollo); | 
				
			||||
 | 
				
			||||
import Vuelidate from 'vuelidate'; | 
				
			||||
import i18n from './i18n'; | 
				
			||||
Vue.config.productionTip = false; | 
				
			||||
Vue.use(Vuelidate); | 
				
			||||
 | 
				
			||||
const apolloProvider = new VueApollo({ | 
				
			||||
    defaultClient: apolloClient, | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
//import './quasar'
 | 
				
			||||
 | 
				
			||||
store.registerModule( | 
				
			||||
    'course', | 
				
			||||
    makeCrudModule({ | 
				
			||||
        service: courseService | 
				
			||||
    }) | 
				
			||||
); | 
				
			||||
 | 
				
			||||
store.registerModule( | 
				
			||||
    'coursecategory', | 
				
			||||
    makeCrudModule({ | 
				
			||||
        service: courseCategoryService | 
				
			||||
    }) | 
				
			||||
); | 
				
			||||
 | 
				
			||||
Vue.config.productionTip = false; | 
				
			||||
 | 
				
			||||
new Vue({ | 
				
			||||
    vuetify, | 
				
			||||
    i18n, | 
				
			||||
    components: {App}, | 
				
			||||
    apolloProvider, | 
				
			||||
    data: {}, | 
				
			||||
    store, | 
				
			||||
    router, | 
				
			||||
    render: h => h(App) | 
				
			||||
}). | 
				
			||||
$mount("#app"); | 
				
			||||
@ -0,0 +1,41 @@ | 
				
			||||
import NotificationMixin from './NotificationMixin'; | 
				
			||||
import { formatDateTime } from '../utils/dates'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  mixins: [NotificationMixin], | 
				
			||||
  methods: { | 
				
			||||
    formatDateTime, | 
				
			||||
    onCreated(item) { | 
				
			||||
      this.showMessage(`${item['@id']} created`); | 
				
			||||
 | 
				
			||||
      this.$router.push({ | 
				
			||||
        name: `${this.$options.servicePrefix}Update`, | 
				
			||||
        params: { id: item['@id'] } | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
    onSendForm() { | 
				
			||||
      const createForm = this.$refs.createForm; | 
				
			||||
      createForm.$v.$touch(); | 
				
			||||
      if (!createForm.$v.$invalid) { | 
				
			||||
        this.create(createForm.$v.item.$model); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    resetForm() { | 
				
			||||
      this.$refs.createForm.$v.$reset(); | 
				
			||||
      this.item = {}; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  watch: { | 
				
			||||
    created(created) { | 
				
			||||
      if (!created) { | 
				
			||||
        return; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      this.onCreated(created); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    error(message) { | 
				
			||||
      message && this.showError(message); | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,88 @@ | 
				
			||||
import isEmpty from 'lodash/isEmpty'; | 
				
			||||
import { formatDateTime } from '../utils/dates'; | 
				
			||||
import NotificationMixin from './NotificationMixin'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  mixins: [NotificationMixin], | 
				
			||||
 | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      options: { | 
				
			||||
        sortBy: [], | 
				
			||||
        descending: false, | 
				
			||||
        page: 1, | 
				
			||||
        itemsPerPage: 15 | 
				
			||||
      }, | 
				
			||||
      filters: {} | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  watch: { | 
				
			||||
    deletedItem(item) { | 
				
			||||
      this.showMessage(`${item['@id']} deleted.`); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    error(message) { | 
				
			||||
      message && this.showError(message); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    items() { | 
				
			||||
      this.options.totalItems = this.totalItems; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  methods: { | 
				
			||||
    onUpdateOptions(props) { | 
				
			||||
      const { page, itemsPerPage, sortBy, descending, totalItems } = props; | 
				
			||||
      let params = { | 
				
			||||
        ...this.filters | 
				
			||||
      }; | 
				
			||||
      if (itemsPerPage > 0) { | 
				
			||||
        params = { ...params, itemsPerPage, page }; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      if (!isEmpty(sortBy)) { | 
				
			||||
        params[`order[${sortBy}]`] = descending ? 'desc' : 'asc'; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      this.getPage(params).then(() => { | 
				
			||||
        this.options.sortBy = sortBy; | 
				
			||||
        this.options.descending = descending; | 
				
			||||
        this.options.itemsPerPage = itemsPerPage; | 
				
			||||
        this.options.totalItems = totalItems; | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    onSendFilter() { | 
				
			||||
      this.resetList = true; | 
				
			||||
      this.onUpdateOptions(this.options); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    resetFilter() { | 
				
			||||
      this.filters = {}; | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    addHandler() { | 
				
			||||
      this.$router.push({ name: `${this.$options.servicePrefix}Create` }); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    showHandler(item) { | 
				
			||||
      this.$router.push({ | 
				
			||||
        name: `${this.$options.servicePrefix}Show`, | 
				
			||||
        params: { id: item['@id'] } | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    editHandler(item) { | 
				
			||||
      this.$router.push({ | 
				
			||||
        name: `${this.$options.servicePrefix}Update`, | 
				
			||||
        params: { id: item['@id'] } | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    deleteHandler(item) { | 
				
			||||
      this.deleteItem(item).then(() => this.onUpdateOptions(this.options)); | 
				
			||||
    }, | 
				
			||||
    formatDateTime | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,37 @@ | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('notifications', ['color', 'show', 'subText', 'text', 'timeout']) | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  methods: { | 
				
			||||
    cleanState() { | 
				
			||||
      setTimeout(() => { | 
				
			||||
        this.show = false; | 
				
			||||
      }, this.timeout); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    showError(error) { | 
				
			||||
      this.showMessage(error, 'danger'); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    showMessage(message, color = 'success') { | 
				
			||||
      this.show = true; | 
				
			||||
      this.color = color; | 
				
			||||
 | 
				
			||||
      if (typeof message === 'string') { | 
				
			||||
        this.text = message; | 
				
			||||
        this.cleanState(); | 
				
			||||
 | 
				
			||||
        return; | 
				
			||||
      } | 
				
			||||
 | 
				
			||||
      this.text = message.message; | 
				
			||||
 | 
				
			||||
      if (message.response) this.subText = message.response.data.message; | 
				
			||||
 | 
				
			||||
      this.cleanState(); | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,47 @@ | 
				
			||||
import NotificationMixin from './NotificationMixin'; | 
				
			||||
import { formatDateTime } from '../utils/dates'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  mixins: [NotificationMixin], | 
				
			||||
  created() { | 
				
			||||
    this.retrieve(decodeURIComponent(this.$route.params.id)); | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    item() { | 
				
			||||
      return this.find(decodeURIComponent(this.$route.params.id)); | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    list() { | 
				
			||||
      this.$router | 
				
			||||
          .push({ name: `${this.$options.servicePrefix}List` }) | 
				
			||||
          .catch(() => {}); | 
				
			||||
    }, | 
				
			||||
    del() { | 
				
			||||
      this.deleteItem(this.item).then(() => { | 
				
			||||
        this.showMessage(`${this.item['@id']} deleted.`); | 
				
			||||
        this.$router | 
				
			||||
          .push({ name: `${this.$options.servicePrefix}List` }) | 
				
			||||
          .catch(() => {}); | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
    formatDateTime, | 
				
			||||
    editHandler() { | 
				
			||||
      this.$router.push({ | 
				
			||||
        name: `${this.$options.servicePrefix}Update`, | 
				
			||||
        params: { id: this.item['@id'] } | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  watch: { | 
				
			||||
    error(message) { | 
				
			||||
      message && this.showError(message); | 
				
			||||
    }, | 
				
			||||
    deleteError(message) { | 
				
			||||
      message && this.showError(message); | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  beforeDestroy() { | 
				
			||||
    this.reset(); | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,79 @@ | 
				
			||||
import NotificationMixin from './NotificationMixin'; | 
				
			||||
import { formatDateTime } from '../utils/dates'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  mixins: [NotificationMixin], | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      item: {} | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  created() { | 
				
			||||
    this.retrieve(decodeURIComponent(this.$route.params.id)); | 
				
			||||
  }, | 
				
			||||
  beforeDestroy() { | 
				
			||||
    this.reset(); | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    retrieved() { | 
				
			||||
      return this.find(decodeURIComponent(this.$route.params.id)); | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    del() { | 
				
			||||
      this.deleteItem(this.retrieved).then(() => { | 
				
			||||
        this.showMessage(`${this.item['@id']} deleted.`); | 
				
			||||
        this.$router | 
				
			||||
          .push({ name: `${this.$options.servicePrefix}List` }) | 
				
			||||
          .catch(() => {}); | 
				
			||||
      }); | 
				
			||||
    }, | 
				
			||||
    formatDateTime, | 
				
			||||
    reset() { | 
				
			||||
      this.$refs.updateForm.$v.$reset(); | 
				
			||||
      this.updateReset(); | 
				
			||||
      this.delReset(); | 
				
			||||
      this.createReset(); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    onSendForm() { | 
				
			||||
      const updateForm = this.$refs.updateForm; | 
				
			||||
      updateForm.$v.$touch(); | 
				
			||||
 | 
				
			||||
      if (!updateForm.$v.$invalid) { | 
				
			||||
        this.update(updateForm.$v.item.$model); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    resetForm() { | 
				
			||||
      this.$refs.updateForm.$v.$reset(); | 
				
			||||
      this.item = { ...this.retrieved }; | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  watch: { | 
				
			||||
    deleted(deleted) { | 
				
			||||
      if (!deleted) { | 
				
			||||
        return; | 
				
			||||
      } | 
				
			||||
      this.$router | 
				
			||||
        .push({ name: `${this.$options.servicePrefix}List` }) | 
				
			||||
        .catch(() => {}); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    error(message) { | 
				
			||||
      message && this.showError(message); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    deleteError(message) { | 
				
			||||
      message && this.showError(message); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    updated(val) { | 
				
			||||
      this.showMessage(`${val['@id']} updated.`); | 
				
			||||
    }, | 
				
			||||
 | 
				
			||||
    retrieved(val) { | 
				
			||||
      this.item = { ...val }; | 
				
			||||
    } | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,15 @@ | 
				
			||||
// src/plugins/vuetify.js
 | 
				
			||||
 | 
				
			||||
import Vue from 'vue' | 
				
			||||
import Vuetify from 'vuetify' | 
				
			||||
import 'vuetify/dist/vuetify.min.css' | 
				
			||||
 | 
				
			||||
Vue.use(Vuetify) | 
				
			||||
 | 
				
			||||
const opts = { | 
				
			||||
    icons: { | 
				
			||||
        iconfont: 'mdi' | 
				
			||||
    } | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
export default new Vuetify(opts) | 
				
			||||
@ -0,0 +1,5 @@ | 
				
			||||
import Vue from 'vue' | 
				
			||||
 | 
				
			||||
// import './styles/quasar.sass'
 | 
				
			||||
import '@quasar/extras/material-icons/material-icons.css' | 
				
			||||
import Quasar from 'quasar/dist/quasar.umd.js' | 
				
			||||
@ -0,0 +1,28 @@ | 
				
			||||
export default { | 
				
			||||
  path: '/courses', | 
				
			||||
  name: 'courses', | 
				
			||||
  component: () => import('../components/course/Layout'), | 
				
			||||
  redirect: { name: 'CourseList' }, | 
				
			||||
  children: [ | 
				
			||||
    { | 
				
			||||
      name: 'CourseList', | 
				
			||||
      path: '', | 
				
			||||
      component: () => import('../views/course/List') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'CourseCreate', | 
				
			||||
      path: 'new', | 
				
			||||
      component: () => import('../views/course/Create') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'CourseUpdate', | 
				
			||||
      path: ':id/edit', | 
				
			||||
      component: () => import('../views/course/Update') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'CourseShow', | 
				
			||||
      path: ':id', | 
				
			||||
      component: () => import('../views/course/Show') | 
				
			||||
    } | 
				
			||||
  ] | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,28 @@ | 
				
			||||
export default { | 
				
			||||
  path: '/course_categories', | 
				
			||||
  name: 'course_categories', | 
				
			||||
  component: () => import('../components/coursecategory/Layout'), | 
				
			||||
  redirect: { name: 'CourseCategoryList' }, | 
				
			||||
  children: [ | 
				
			||||
    { | 
				
			||||
      name: 'CourseCategoryList', | 
				
			||||
      path: '', | 
				
			||||
      component: () => import('../views/coursecategory/List') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'CourseCategoryCreate', | 
				
			||||
      path: 'new', | 
				
			||||
      component: () => import('../views/coursecategory/Create') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'CourseCategoryUpdate', | 
				
			||||
      path: ':id/edit', | 
				
			||||
      component: () => import('../views/coursecategory/Update') | 
				
			||||
    }, | 
				
			||||
    { | 
				
			||||
      name: 'CourseCategoryShow', | 
				
			||||
      path: ':id', | 
				
			||||
      component: () => import('../views/coursecategory/Show') | 
				
			||||
    } | 
				
			||||
  ] | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,18 @@ | 
				
			||||
import Vue from "vue"; | 
				
			||||
import VueRouter from "vue-router"; | 
				
			||||
 | 
				
			||||
Vue.use(VueRouter); | 
				
			||||
 | 
				
			||||
import courseRoutes from './course'; | 
				
			||||
import coursecategoryRoutes from './coursecategory'; | 
				
			||||
import sessionRoutes from './../../quasar/router/session'; | 
				
			||||
 | 
				
			||||
export default new VueRouter({ | 
				
			||||
    mode: "history", | 
				
			||||
    routes: [ | 
				
			||||
        courseRoutes, | 
				
			||||
        ...sessionRoutes, | 
				
			||||
        coursecategoryRoutes, | 
				
			||||
        // { path: "*", redirect: "/home" }
 | 
				
			||||
    ] | 
				
			||||
}); | 
				
			||||
@ -0,0 +1,24 @@ | 
				
			||||
import fetch from '../utils/fetch'; | 
				
			||||
 | 
				
			||||
export default function makeService(endpoint) { | 
				
			||||
  return { | 
				
			||||
    find(id) { | 
				
			||||
      return fetch(`${id}`); | 
				
			||||
    }, | 
				
			||||
    findAll(params) { | 
				
			||||
      return fetch(endpoint, params); | 
				
			||||
    }, | 
				
			||||
    create(payload) { | 
				
			||||
      return fetch(endpoint, { method: 'POST', body: JSON.stringify(payload) }); | 
				
			||||
    }, | 
				
			||||
    del(item) { | 
				
			||||
      return fetch(item['@id'], { method: 'DELETE' }); | 
				
			||||
    }, | 
				
			||||
    update(payload) { | 
				
			||||
      return fetch(payload['@id'], { | 
				
			||||
        method: 'PUT', | 
				
			||||
        body: JSON.stringify(payload) | 
				
			||||
      }); | 
				
			||||
    } | 
				
			||||
  }; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
import makeService from './api'; | 
				
			||||
 | 
				
			||||
export default makeService('courses'); | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
import makeService from './api'; | 
				
			||||
 | 
				
			||||
export default makeService('course_categories'); | 
				
			||||
@ -0,0 +1,14 @@ | 
				
			||||
import Vue from "vue"; | 
				
			||||
import Vuex from "vuex"; | 
				
			||||
 | 
				
			||||
import notifications from './modules/notifications'; | 
				
			||||
//import session from './../../quasar/store/modules/session/';
 | 
				
			||||
 | 
				
			||||
Vue.use(Vuex); | 
				
			||||
 | 
				
			||||
export default new Vuex.Store({ | 
				
			||||
    modules: { | 
				
			||||
        notifications, | 
				
			||||
        //session,
 | 
				
			||||
    } | 
				
			||||
}); | 
				
			||||
@ -0,0 +1,273 @@ | 
				
			||||
import Vue from 'vue'; | 
				
			||||
import { getField, updateField } from 'vuex-map-fields'; | 
				
			||||
import remove from 'lodash/remove'; | 
				
			||||
import SubmissionError from '../../error/SubmissionError'; | 
				
			||||
 | 
				
			||||
const initialState = () => ({ | 
				
			||||
  allIds: [], | 
				
			||||
  byId: {}, | 
				
			||||
  created: null, | 
				
			||||
  deleted: null, | 
				
			||||
  error: "", | 
				
			||||
  isLoading: false, | 
				
			||||
  resetList: false, | 
				
			||||
  selectItems: null, | 
				
			||||
  totalItems: 0, | 
				
			||||
  updated: null, | 
				
			||||
  view: null, | 
				
			||||
  violations: null | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
const handleError = (commit, e) => { | 
				
			||||
  commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
 | 
				
			||||
  if (e instanceof SubmissionError) { | 
				
			||||
    commit(ACTIONS.SET_VIOLATIONS, e.errors); | 
				
			||||
    // eslint-disable-next-line
 | 
				
			||||
    commit(ACTIONS.SET_ERROR, e.errors._error); | 
				
			||||
 | 
				
			||||
    return Promise.reject(e); | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  // eslint-disable-next-line
 | 
				
			||||
  commit(ACTIONS.SET_ERROR, e.message); | 
				
			||||
 | 
				
			||||
  return Promise.reject(e); | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
export const ACTIONS = { | 
				
			||||
  ADD: 'ADD', | 
				
			||||
  RESET_CREATE: 'RESET_CREATE', | 
				
			||||
  RESET_DELETE: 'RESET_DELETE', | 
				
			||||
  RESET_LIST: 'RESET_LIST', | 
				
			||||
  RESET_SHOW: 'RESET_SHOW', | 
				
			||||
  RESET_UPDATE: 'RESET_UPDATE', | 
				
			||||
  SET_CREATED: 'SET_CREATED', | 
				
			||||
  SET_DELETED: 'SET_DELETED', | 
				
			||||
  SET_ERROR: 'SET_ERROR', | 
				
			||||
  SET_SELECT_ITEMS: 'SET_SELECT_ITEMS', | 
				
			||||
  SET_TOTAL_ITEMS: 'SET_TOTAL_ITEMS', | 
				
			||||
  SET_UPDATED: 'SET_UPDATED', | 
				
			||||
  SET_VIEW: 'SET_VIEW', | 
				
			||||
  SET_VIOLATIONS: 'SET_VIOLATIONS', | 
				
			||||
  TOGGLE_LOADING: 'TOGGLE_LOADING' | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
export default function makeCrudModule({ | 
				
			||||
  normalizeRelations = x => x, | 
				
			||||
  resolveRelations = x => x, | 
				
			||||
  service | 
				
			||||
} = {}) { | 
				
			||||
  return { | 
				
			||||
    actions: { | 
				
			||||
      create: ({ commit }, values) => { | 
				
			||||
        commit(ACTIONS.SET_ERROR, ''); | 
				
			||||
        commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
 | 
				
			||||
        service | 
				
			||||
          .create(values) | 
				
			||||
          .then(response => response.json()) | 
				
			||||
          .then(data => { | 
				
			||||
            commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
            commit(ACTIONS.ADD, data); | 
				
			||||
            commit(ACTIONS.SET_CREATED, data); | 
				
			||||
          }) | 
				
			||||
          .catch(e => handleError(commit, e)); | 
				
			||||
      }, | 
				
			||||
      del: ({ commit }, item) => { | 
				
			||||
        commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
 | 
				
			||||
        service | 
				
			||||
          .del(item) | 
				
			||||
          .then(() => { | 
				
			||||
            commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
            commit(ACTIONS.SET_DELETED, item); | 
				
			||||
          }) | 
				
			||||
          .catch(e => handleError(commit, e)); | 
				
			||||
      }, | 
				
			||||
      fetchAll: ({ commit, state }, params) => { | 
				
			||||
        if (!service) throw new Error('No service specified!'); | 
				
			||||
 | 
				
			||||
        commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
 | 
				
			||||
        service | 
				
			||||
          .findAll({ params }) | 
				
			||||
          .then(response => response.json()) | 
				
			||||
          .then(retrieved => { | 
				
			||||
            commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
 | 
				
			||||
            commit( | 
				
			||||
              ACTIONS.SET_TOTAL_ITEMS, | 
				
			||||
              retrieved['hydra:totalItems'] | 
				
			||||
            ); | 
				
			||||
            commit(ACTIONS.SET_VIEW, retrieved['hydra:view']); | 
				
			||||
 | 
				
			||||
            if (true === state.resetList) { | 
				
			||||
              commit(ACTIONS.RESET_LIST); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            retrieved['hydra:member'].forEach(item => { | 
				
			||||
              commit(ACTIONS.ADD, normalizeRelations(item)); | 
				
			||||
            }); | 
				
			||||
          }) | 
				
			||||
          .catch(e => handleError(commit, e)); | 
				
			||||
      }, | 
				
			||||
      fetchSelectItems: ( | 
				
			||||
        { commit }, | 
				
			||||
        { params = { properties: ['@id', 'name'] } } = {} | 
				
			||||
      ) => { | 
				
			||||
        commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
 | 
				
			||||
        if (!service) throw new Error('No service specified!'); | 
				
			||||
 | 
				
			||||
        service | 
				
			||||
          .findAll({ params }) | 
				
			||||
          .then(response => response.json()) | 
				
			||||
          .then(retrieved => { | 
				
			||||
            commit( | 
				
			||||
              ACTIONS.SET_SELECT_ITEMS, | 
				
			||||
              retrieved['hydra:member'] | 
				
			||||
            ); | 
				
			||||
          }) | 
				
			||||
          .catch(e => handleError(commit, e)); | 
				
			||||
      }, | 
				
			||||
      load: ({ commit }, id) => { | 
				
			||||
        if (!service) throw new Error('No service specified!'); | 
				
			||||
 | 
				
			||||
        commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
        service | 
				
			||||
          .find(id) | 
				
			||||
          .then(response => response.json()) | 
				
			||||
          .then(item => { | 
				
			||||
            commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
            commit(ACTIONS.ADD, normalizeRelations(item)); | 
				
			||||
          }) | 
				
			||||
          .catch(e => handleError(commit, e)); | 
				
			||||
      }, | 
				
			||||
      resetCreate: ({ commit }) => { | 
				
			||||
        commit(ACTIONS.RESET_CREATE); | 
				
			||||
      }, | 
				
			||||
      resetDelete: ({ commit }) => { | 
				
			||||
        commit(ACTIONS.RESET_DELETE); | 
				
			||||
      }, | 
				
			||||
      resetShow: ({ commit }) => { | 
				
			||||
        commit(ACTIONS.RESET_SHOW); | 
				
			||||
      }, | 
				
			||||
      resetUpdate: ({ commit }) => { | 
				
			||||
        commit(ACTIONS.RESET_UPDATE); | 
				
			||||
      }, | 
				
			||||
      update: ({ commit }, item) => { | 
				
			||||
        commit(ACTIONS.SET_ERROR, ''); | 
				
			||||
        commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
 | 
				
			||||
        service | 
				
			||||
          .update(item) | 
				
			||||
          .then(response => response.json()) | 
				
			||||
          .then(data => { | 
				
			||||
            commit(ACTIONS.TOGGLE_LOADING); | 
				
			||||
            commit(ACTIONS.SET_UPDATED, data); | 
				
			||||
          }) | 
				
			||||
          .catch(e => handleError(commit, e)); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    getters: { | 
				
			||||
      find: state => id => { | 
				
			||||
        return resolveRelations(state.byId[id]); | 
				
			||||
      }, | 
				
			||||
      getField, | 
				
			||||
      list: (state, getters) => { | 
				
			||||
        return state.allIds.map(id => getters.find(id)); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    mutations: { | 
				
			||||
      updateField, | 
				
			||||
      [ACTIONS.ADD]: (state, item) => { | 
				
			||||
        Vue.set(state.byId, item['@id'], item); | 
				
			||||
        Vue.set(state, 'isLoading', false); | 
				
			||||
        if (state.allIds.includes(item['@id'])) return; | 
				
			||||
        state.allIds.push(item['@id']); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.RESET_CREATE]: state => { | 
				
			||||
        Object.assign(state, { | 
				
			||||
          isLoading: false, | 
				
			||||
          error: '', | 
				
			||||
          created: null, | 
				
			||||
          violations: null | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.RESET_DELETE]: state => { | 
				
			||||
        Object.assign(state, { | 
				
			||||
          isLoading: false, | 
				
			||||
          error: '', | 
				
			||||
          deleted: null | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.RESET_LIST]: state => { | 
				
			||||
        Object.assign(state, { | 
				
			||||
          allIds: [], | 
				
			||||
          byId: {}, | 
				
			||||
          error: '', | 
				
			||||
          isLoading: false, | 
				
			||||
          resetList: false | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.RESET_SHOW]: state => { | 
				
			||||
        Object.assign(state, { | 
				
			||||
          error: '', | 
				
			||||
          isLoading: false | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.RESET_UPDATE]: state => { | 
				
			||||
        Object.assign(state, { | 
				
			||||
          error: '', | 
				
			||||
          isLoading: false, | 
				
			||||
          updated: null, | 
				
			||||
          violations: null | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_CREATED]: (state, created) => { | 
				
			||||
        Object.assign(state, { created }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_DELETED]: (state, deleted) => { | 
				
			||||
        if (!state.allIds.includes(deleted['@id'])) return; | 
				
			||||
        Object.assign(state, { | 
				
			||||
          allIds: remove(state.allIds, item => item['@id'] === deleted['@id']), | 
				
			||||
          byId: remove(state.byId, id => id === deleted['@id']), | 
				
			||||
          deleted | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_ERROR]: (state, error) => { | 
				
			||||
        Object.assign(state, { error, isLoading: false }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_SELECT_ITEMS]: (state, selectItems) => { | 
				
			||||
        Object.assign(state, { | 
				
			||||
          error: '', | 
				
			||||
          isLoading: false, | 
				
			||||
          selectItems | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_TOTAL_ITEMS]: (state, totalItems) => { | 
				
			||||
        Object.assign(state, { totalItems }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_UPDATED]: (state, updated) => { | 
				
			||||
        Object.assign(state, { | 
				
			||||
          byId: { | 
				
			||||
            [updated['@id']]: updated | 
				
			||||
          }, | 
				
			||||
          updated | 
				
			||||
        }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_VIEW]: (state, view) => { | 
				
			||||
        Object.assign(state, { view }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.SET_VIOLATIONS]: (state, violations) => { | 
				
			||||
        Object.assign(state, { violations }); | 
				
			||||
      }, | 
				
			||||
      [ACTIONS.TOGGLE_LOADING]: state => { | 
				
			||||
        Object.assign(state, { error: '', isLoading: !state.isLoading }); | 
				
			||||
      } | 
				
			||||
    }, | 
				
			||||
    namespaced: true, | 
				
			||||
    state: initialState | 
				
			||||
  }; | 
				
			||||
} | 
				
			||||
@ -0,0 +1,18 @@ | 
				
			||||
import {getField, updateField} from 'vuex-map-fields'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  namespaced: true, | 
				
			||||
  state: { | 
				
			||||
    show: false, | 
				
			||||
    color: 'error', | 
				
			||||
    text: 'An error occurred', | 
				
			||||
    subText: '', | 
				
			||||
    timeout: 6000 | 
				
			||||
  }, | 
				
			||||
  getters: { | 
				
			||||
    getField | 
				
			||||
  }, | 
				
			||||
  mutations: { | 
				
			||||
    updateField | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
@ -0,0 +1,9 @@ | 
				
			||||
import moment from 'moment'; | 
				
			||||
 | 
				
			||||
const formatDateTime = function(date) { | 
				
			||||
  if (!date) return null; | 
				
			||||
 | 
				
			||||
  return moment(date).format('DD/MM/YYYY'); | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
export { formatDateTime }; | 
				
			||||
@ -0,0 +1,71 @@ | 
				
			||||
import isObject from 'lodash/isObject'; | 
				
			||||
import { ENTRYPOINT } from '../config/entrypoint'; | 
				
			||||
import SubmissionError from '../error/SubmissionError'; | 
				
			||||
import { normalize } from './hydra'; | 
				
			||||
 | 
				
			||||
const MIME_TYPE = 'application/ld+json'; | 
				
			||||
 | 
				
			||||
const makeParamArray = (key, arr) => | 
				
			||||
  arr.map(val => `${key}[]=${val}`).join('&'); | 
				
			||||
 | 
				
			||||
export default function(id, options = {}) { | 
				
			||||
  if ('undefined' === typeof options.headers) options.headers = new Headers(); | 
				
			||||
 | 
				
			||||
  if (null === options.headers.get('Accept')) | 
				
			||||
    options.headers.set('Accept', MIME_TYPE); | 
				
			||||
 | 
				
			||||
  if ( | 
				
			||||
    'undefined' !== options.body && | 
				
			||||
    !(options.body instanceof FormData) && | 
				
			||||
    null === options.headers.get('Content-Type') | 
				
			||||
  ) | 
				
			||||
    options.headers.set('Content-Type', MIME_TYPE); | 
				
			||||
 | 
				
			||||
  if (options.params) { | 
				
			||||
    const params = normalize(options.params); | 
				
			||||
    let queryString = Object.keys(params) | 
				
			||||
      .map(key => | 
				
			||||
        Array.isArray(params[key]) | 
				
			||||
          ? makeParamArray(key, params[key]) | 
				
			||||
          : `${key}=${params[key]}` | 
				
			||||
      ) | 
				
			||||
      .join('&'); | 
				
			||||
    id = `${id}?${queryString}`; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  const entryPoint = ENTRYPOINT + (ENTRYPOINT.endsWith('/') ? '' : '/'); | 
				
			||||
 | 
				
			||||
  const payload = options.body && JSON.parse(options.body); | 
				
			||||
  if (isObject(payload) && payload['@id']) | 
				
			||||
    options.body = JSON.stringify(normalize(payload)); | 
				
			||||
 | 
				
			||||
    //console.log(id); console.log(new URL(id, entryPoint));
 | 
				
			||||
 | 
				
			||||
  return global.fetch(new URL(id, entryPoint), options).then(response => { | 
				
			||||
    if (response.ok) return response; | 
				
			||||
 | 
				
			||||
    return response.json().then( | 
				
			||||
      json => { | 
				
			||||
        const error = | 
				
			||||
          json['hydra:description'] || | 
				
			||||
          json['hydra:title'] || | 
				
			||||
          'An error occurred.'; | 
				
			||||
 | 
				
			||||
        if (!json.violations) throw Error(error); | 
				
			||||
 | 
				
			||||
        let errors = { _error: error }; | 
				
			||||
        json.violations.forEach(violation => | 
				
			||||
          errors[violation.propertyPath] | 
				
			||||
            ? (errors[violation.propertyPath] += | 
				
			||||
            '\n' + errors[violation.propertyPath]) | 
				
			||||
            : (errors[violation.propertyPath] = violation.message) | 
				
			||||
        ); | 
				
			||||
 | 
				
			||||
        throw new SubmissionError(errors); | 
				
			||||
      }, | 
				
			||||
      () => { | 
				
			||||
        throw new Error(response.statusText || 'An error occurred.'); | 
				
			||||
      } | 
				
			||||
    ); | 
				
			||||
  }); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,19 @@ | 
				
			||||
import get from 'lodash/get'; | 
				
			||||
import has from 'lodash/has'; | 
				
			||||
import mapValues from 'lodash/mapValues'; | 
				
			||||
 | 
				
			||||
export function normalize(data) { | 
				
			||||
  if (has(data, 'hydra:member')) { | 
				
			||||
    // Normalize items in collections
 | 
				
			||||
    data['hydra:member'] = data['hydra:member'].map(item => normalize(item)); | 
				
			||||
 | 
				
			||||
    return data; | 
				
			||||
  } | 
				
			||||
 | 
				
			||||
  // Flatten nested documents
 | 
				
			||||
  return mapValues(data, value => | 
				
			||||
    Array.isArray(value) | 
				
			||||
      ? value.map(v => get(v, '@id', v)) | 
				
			||||
      : get(value, '@id', value) | 
				
			||||
  ); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,7 @@ | 
				
			||||
import moment from 'moment'; | 
				
			||||
 | 
				
			||||
const date = function(value) { | 
				
			||||
  return moment(value).isValid(); | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
export { date }; | 
				
			||||
@ -0,0 +1,46 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <CourseForm ref="createForm" :values="item" :errors="violations" /> | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
 | 
				
			||||
    <Toolbar :handle-submit="onSendForm" :handle-reset="resetForm"></Toolbar> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions } from 'vuex'; | 
				
			||||
import { createHelpers } from 'vuex-map-fields'; | 
				
			||||
import CourseForm from '../../components/course/Form'; | 
				
			||||
import Loading from '../../components/Loading'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
import CreateMixin from '../../mixins/CreateMixin'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'Course'; | 
				
			||||
 | 
				
			||||
const { mapFields } = createHelpers({ | 
				
			||||
  getterType: 'course/getField', | 
				
			||||
  mutationType: 'course/updateField' | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseCreate', | 
				
			||||
  servicePrefix, | 
				
			||||
  mixins: [CreateMixin], | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    CourseForm | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      item: {} | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields(['error', 'isLoading', 'created', 'violations']) | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('course', ['create', 'reset']) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,122 @@ | 
				
			||||
<template> | 
				
			||||
  <div class="course-list"> | 
				
			||||
    <Toolbar :handle-add="addHandler" /> | 
				
			||||
 | 
				
			||||
    <v-container grid-list-xl fluid> | 
				
			||||
      <v-layout row wrap> | 
				
			||||
<!--        <v-flex sm12>--> | 
				
			||||
<!--          <h1>Course List</h1>--> | 
				
			||||
<!--        </v-flex>--> | 
				
			||||
        <v-flex lg12> | 
				
			||||
          <DataFilter :handle-filter="onSendFilter" :handle-reset="resetFilter"> | 
				
			||||
            <CourseFilterForm | 
				
			||||
              ref="filterForm" | 
				
			||||
              :values="filters" | 
				
			||||
              slot="filter" | 
				
			||||
            /> | 
				
			||||
          </DataFilter> | 
				
			||||
 | 
				
			||||
          <br /> | 
				
			||||
 | 
				
			||||
          <v-data-table | 
				
			||||
            v-model="selected" | 
				
			||||
            :headers="headers" | 
				
			||||
            :items="items" | 
				
			||||
            :items-per-page.sync="options.itemsPerPage" | 
				
			||||
            :loading="isLoading" | 
				
			||||
            :loading-text="$t('Loading...')" | 
				
			||||
            :options.sync="options" | 
				
			||||
            :server-items-length="totalItems" | 
				
			||||
            class="elevation-1" | 
				
			||||
            item-key="@id" | 
				
			||||
            show-select | 
				
			||||
            @update:options="onUpdateOptions" | 
				
			||||
          > | 
				
			||||
            <template slot="item.category" slot-scope="{ item }"> | 
				
			||||
              <div v-if="item['category']"> | 
				
			||||
                {{ item['category'].name }} | 
				
			||||
              </div> | 
				
			||||
              <div v-else> | 
				
			||||
                - | 
				
			||||
              </div> | 
				
			||||
            </template> | 
				
			||||
 | 
				
			||||
            <template slot="item.visibility" slot-scope="{ item }"> | 
				
			||||
              {{ $n(item['visibility']) }} | 
				
			||||
            </template> | 
				
			||||
 | 
				
			||||
            <template slot="item.expirationDate" slot-scope="{ item }"> | 
				
			||||
              {{ formatDateTime(item['expirationDate'], 'long') }} | 
				
			||||
            </template> | 
				
			||||
 | 
				
			||||
            <ActionCell | 
				
			||||
              slot="item.action" | 
				
			||||
              slot-scope="props" | 
				
			||||
              :handle-show="() => showHandler(props.item)" | 
				
			||||
              :handle-edit="() => editHandler(props.item)" | 
				
			||||
              :handle-delete="() => deleteHandler(props.item)" | 
				
			||||
            ></ActionCell> | 
				
			||||
          </v-data-table> | 
				
			||||
        </v-flex> | 
				
			||||
      </v-layout> | 
				
			||||
    </v-container> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import ListMixin from '../../mixins/ListMixin'; | 
				
			||||
import ActionCell from '../../components/ActionCell'; | 
				
			||||
import CourseFilterForm from '../../components/course/Filter'; | 
				
			||||
import DataFilter from '../../components/DataFilter'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseList', | 
				
			||||
  servicePrefix: 'Course', | 
				
			||||
  mixins: [ListMixin], | 
				
			||||
  components: { | 
				
			||||
    Toolbar, | 
				
			||||
    ActionCell, | 
				
			||||
    CourseFilterForm, | 
				
			||||
    DataFilter | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      headers: [ | 
				
			||||
        { text: 'title', value: 'title' }, | 
				
			||||
        { text: 'code', value: 'code' }, | 
				
			||||
        { text: 'courseLanguage', value: 'Language' }, | 
				
			||||
        { text: 'category', value: 'category' }, | 
				
			||||
        { text: 'visibility', value: 'visibility' }, | 
				
			||||
        { | 
				
			||||
          text: 'Actions', | 
				
			||||
          value: 'action', | 
				
			||||
          sortable: false | 
				
			||||
        } | 
				
			||||
      ], | 
				
			||||
      selected: [] | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    ...mapGetters('course', { | 
				
			||||
      items: 'list' | 
				
			||||
    }), | 
				
			||||
    ...mapFields('course', { | 
				
			||||
      deletedItem: 'deleted', | 
				
			||||
      error: 'error', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      resetList: 'resetList', | 
				
			||||
      totalItems: 'totalItems', | 
				
			||||
      view: 'view' | 
				
			||||
    }) | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('course', { | 
				
			||||
      getPage: 'fetchAll', | 
				
			||||
      deleteItem: 'del' | 
				
			||||
    }) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,119 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <Toolbar | 
				
			||||
            :handle-delete="del" | 
				
			||||
            :handle-list="list" | 
				
			||||
    > | 
				
			||||
      <template slot="left"> | 
				
			||||
        <v-toolbar-title v-if="item">{{ | 
				
			||||
          `${$options.servicePrefix} ${item['@id']}` | 
				
			||||
        }}</v-toolbar-title> | 
				
			||||
      </template> | 
				
			||||
    </Toolbar> | 
				
			||||
    <br /> | 
				
			||||
    <div v-if="item" class="table-course-show"> | 
				
			||||
      <v-simple-table> | 
				
			||||
        <template slot="default"> | 
				
			||||
          <thead> | 
				
			||||
            <tr> | 
				
			||||
              <th>Field</th> | 
				
			||||
              <th>Value</th> | 
				
			||||
              <th>Field</th> | 
				
			||||
              <th>Value</th> | 
				
			||||
            </tr> | 
				
			||||
          </thead> | 
				
			||||
          <tbody> | 
				
			||||
            <tr> | 
				
			||||
              <td><strong>{{ $t('title') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['title'] }} | 
				
			||||
              </td> | 
				
			||||
 | 
				
			||||
              <td><strong>{{ $t('code') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['code'] }} | 
				
			||||
              </td> | 
				
			||||
            </tr> | 
				
			||||
 | 
				
			||||
            <tr> | 
				
			||||
              <td><strong>{{ $t('courseLanguage') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['courseLanguage'] }} | 
				
			||||
              </td> | 
				
			||||
 | 
				
			||||
              <td><strong>{{ $t('category') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                <div v-if="item['category']"> | 
				
			||||
                  {{ item['category'].name }} | 
				
			||||
                </div> | 
				
			||||
                <div v-else> | 
				
			||||
                  - | 
				
			||||
                </div> | 
				
			||||
 | 
				
			||||
              </td> | 
				
			||||
            </tr> | 
				
			||||
 | 
				
			||||
            <tr> | 
				
			||||
              <td><strong>{{ $t('visibility') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                {{ $n(item['visibility']) }}              </td> | 
				
			||||
 | 
				
			||||
              <td><strong>{{ $t('departmentName') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['departmentName'] }} | 
				
			||||
              </td> | 
				
			||||
            </tr> | 
				
			||||
 | 
				
			||||
            <tr> | 
				
			||||
              <td><strong>{{ $t('departmentUrl') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['departmentUrl'] }} | 
				
			||||
              </td> | 
				
			||||
 | 
				
			||||
              <td><strong>{{ $t('expirationDate') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                {{ formatDateTime(item['expirationDate'], 'long') }}              </td> | 
				
			||||
            </tr> | 
				
			||||
 | 
				
			||||
          </tbody> | 
				
			||||
        </template> | 
				
			||||
      </v-simple-table> | 
				
			||||
    </div> | 
				
			||||
 | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
 | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import Loading from '../../components/Loading'; | 
				
			||||
import ShowMixin from '../../mixins/ShowMixin'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'Course'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseShow', | 
				
			||||
  servicePrefix, | 
				
			||||
  components: { | 
				
			||||
      Loading, | 
				
			||||
      Toolbar | 
				
			||||
  }, | 
				
			||||
  mixins: [ShowMixin], | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('course', { | 
				
			||||
      isLoading: 'isLoading' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters('course', ['find']) | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('course', { | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      reset: 'resetShow', | 
				
			||||
      retrieve: 'load' | 
				
			||||
    }) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,67 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <v-card | 
				
			||||
        class="mx-auto" | 
				
			||||
    > | 
				
			||||
    <CourseForm | 
				
			||||
      ref="updateForm" | 
				
			||||
      v-if="item" | 
				
			||||
      :values="item" | 
				
			||||
      :errors="violations" | 
				
			||||
    /> | 
				
			||||
    <Loading :visible="isLoading || deleteLoading" /> | 
				
			||||
      <v-footer> | 
				
			||||
        <Toolbar | 
				
			||||
                :handle-submit="onSendForm" | 
				
			||||
                :handle-reset="resetForm" | 
				
			||||
                :handle-delete="del" | 
				
			||||
        /> | 
				
			||||
      </v-footer> | 
				
			||||
    </v-card> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import CourseForm from '../../components/course/Form.vue'; | 
				
			||||
import Loading from '../../components/Loading'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
import UpdateMixin from '../../mixins/UpdateMixin'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'Course'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseUpdate', | 
				
			||||
  servicePrefix, | 
				
			||||
  mixins: [UpdateMixin], | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    CourseForm | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('course', { | 
				
			||||
      deleteLoading: 'isLoading', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      error: 'error', | 
				
			||||
      updated: 'updated', | 
				
			||||
      violations: 'violations' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters('course', ['find']) | 
				
			||||
 | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('course', { | 
				
			||||
      createReset: 'resetCreate', | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      delReset: 'resetDelete', | 
				
			||||
      retrieve: 'load', | 
				
			||||
      update: 'update', | 
				
			||||
      updateReset: 'resetUpdate' | 
				
			||||
    }) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,45 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <Toolbar :handle-submit="onSendForm" :handle-reset="resetForm"></Toolbar> | 
				
			||||
    <CourseCategoryForm ref="createForm" :values="item" :errors="violations" /> | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions } from 'vuex'; | 
				
			||||
import { createHelpers } from 'vuex-map-fields'; | 
				
			||||
import CourseCategoryForm from '../../components/coursecategory/Form'; | 
				
			||||
import Loading from '../../components/Loading'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
import CreateMixin from '../../mixins/CreateMixin'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'CourseCategory'; | 
				
			||||
 | 
				
			||||
const { mapFields } = createHelpers({ | 
				
			||||
  getterType: 'coursecategory/getField', | 
				
			||||
  mutationType: 'coursecategory/updateField' | 
				
			||||
}); | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseCategoryCreate', | 
				
			||||
  servicePrefix, | 
				
			||||
  mixins: [CreateMixin], | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    CourseCategoryForm | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      item: {} | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields(['error', 'isLoading', 'created', 'violations']) | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('coursecategory', ['create', 'reset']) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,104 @@ | 
				
			||||
<template> | 
				
			||||
  <div class="coursecategory-list"> | 
				
			||||
    <Toolbar :handle-add="addHandler" /> | 
				
			||||
 | 
				
			||||
    <v-container grid-list-xl fluid> | 
				
			||||
      <v-layout row wrap> | 
				
			||||
        <v-flex sm12> | 
				
			||||
          <h1>CourseCategory List</h1> | 
				
			||||
        </v-flex> | 
				
			||||
        <v-flex lg12> | 
				
			||||
          <DataFilter :handle-filter="onSendFilter" :handle-reset="resetFilter"> | 
				
			||||
            <CourseCategoryFilterForm | 
				
			||||
              ref="filterForm" | 
				
			||||
              :values="filters" | 
				
			||||
              slot="filter" | 
				
			||||
            /> | 
				
			||||
          </DataFilter> | 
				
			||||
 | 
				
			||||
          <br /> | 
				
			||||
 | 
				
			||||
          <v-data-table | 
				
			||||
            v-model="selected" | 
				
			||||
            :headers="headers" | 
				
			||||
            :items="items" | 
				
			||||
            :items-per-page.sync="options.itemsPerPage" | 
				
			||||
            :loading="isLoading" | 
				
			||||
            :loading-text="$t('Loading...')" | 
				
			||||
            :options.sync="options" | 
				
			||||
            :server-items-length="totalItems" | 
				
			||||
            class="elevation-1" | 
				
			||||
            item-key="@id" | 
				
			||||
            show-select | 
				
			||||
            @update:options="onUpdateOptions" | 
				
			||||
          > | 
				
			||||
 | 
				
			||||
            <ActionCell | 
				
			||||
              slot="item.action" | 
				
			||||
              slot-scope="props" | 
				
			||||
              :handle-show="() => showHandler(props.item)" | 
				
			||||
              :handle-edit="() => editHandler(props.item)" | 
				
			||||
              :handle-delete="() => deleteHandler(props.item)" | 
				
			||||
            ></ActionCell> | 
				
			||||
          </v-data-table> | 
				
			||||
        </v-flex> | 
				
			||||
      </v-layout> | 
				
			||||
    </v-container> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import ListMixin from '../../mixins/ListMixin'; | 
				
			||||
import ActionCell from '../../components/ActionCell'; | 
				
			||||
import CourseCategoryFilterForm from '../../components/coursecategory/Filter'; | 
				
			||||
import DataFilter from '../../components/DataFilter'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseCategoryList', | 
				
			||||
  servicePrefix: 'CourseCategory', | 
				
			||||
  mixins: [ListMixin], | 
				
			||||
  components: { | 
				
			||||
    Toolbar, | 
				
			||||
    ActionCell, | 
				
			||||
    CourseCategoryFilterForm, | 
				
			||||
    DataFilter | 
				
			||||
  }, | 
				
			||||
  data() { | 
				
			||||
    return { | 
				
			||||
      headers: [ | 
				
			||||
        { text: 'name', value: 'name' }, | 
				
			||||
        { text: 'code', value: 'code' }, | 
				
			||||
        //{ text: 'description', value: 'description' }, | 
				
			||||
        { | 
				
			||||
          text: 'Actions', | 
				
			||||
          value: 'action', | 
				
			||||
          sortable: false | 
				
			||||
        } | 
				
			||||
      ], | 
				
			||||
      selected: [] | 
				
			||||
    }; | 
				
			||||
  }, | 
				
			||||
  computed: { | 
				
			||||
    ...mapGetters('coursecategory', { | 
				
			||||
      items: 'list' | 
				
			||||
    }), | 
				
			||||
    ...mapFields('coursecategory', { | 
				
			||||
      deletedItem: 'deleted', | 
				
			||||
      error: 'error', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      resetList: 'resetList', | 
				
			||||
      totalItems: 'totalItems', | 
				
			||||
      view: 'view' | 
				
			||||
    }) | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('coursecategory', { | 
				
			||||
      getPage: 'fetchAll', | 
				
			||||
      deleteItem: 'del' | 
				
			||||
    }) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,87 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <Toolbar :handle-edit="editHandler"  :handle-delete="del"> | 
				
			||||
      <template slot="left"> | 
				
			||||
        <v-toolbar-title v-if="item">{{ | 
				
			||||
          `${$options.servicePrefix} ${item['@id']}` | 
				
			||||
        }}</v-toolbar-title> | 
				
			||||
      </template> | 
				
			||||
    </Toolbar> | 
				
			||||
 | 
				
			||||
    <br /> | 
				
			||||
 | 
				
			||||
    <div v-if="item" class="table-coursecategory-show"> | 
				
			||||
      <v-simple-table> | 
				
			||||
        <template slot="default"> | 
				
			||||
          <thead> | 
				
			||||
            <tr> | 
				
			||||
              <th>Field</th> | 
				
			||||
              <th>Value</th> | 
				
			||||
 | 
				
			||||
              <th>Field</th> | 
				
			||||
              <th>Value</th> | 
				
			||||
            </tr> | 
				
			||||
          </thead> | 
				
			||||
          <tbody> | 
				
			||||
            <tr> | 
				
			||||
              <td><strong>{{ $t('name') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['name'] }} | 
				
			||||
              </td> | 
				
			||||
             | 
				
			||||
              <td><strong>{{ $t('code') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['code'] }} | 
				
			||||
              </td> | 
				
			||||
            </tr> | 
				
			||||
             | 
				
			||||
            <tr> | 
				
			||||
              <td><strong>{{ $t('description') }}</strong></td> | 
				
			||||
              <td> | 
				
			||||
                                    {{ item['description'] }} | 
				
			||||
              </td> | 
				
			||||
             | 
				
			||||
              <td></td> | 
				
			||||
            </tr> | 
				
			||||
             | 
				
			||||
          </tbody> | 
				
			||||
        </template> | 
				
			||||
      </v-simple-table> | 
				
			||||
    </div> | 
				
			||||
 | 
				
			||||
    <Loading :visible="isLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import Loading from '../../components/Loading'; | 
				
			||||
import ShowMixin from '../../mixins/ShowMixin'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'CourseCategory'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseCategoryShow', | 
				
			||||
  servicePrefix, | 
				
			||||
  components: { | 
				
			||||
      Loading, | 
				
			||||
      Toolbar | 
				
			||||
  }, | 
				
			||||
  mixins: [ShowMixin], | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('coursecategory', { | 
				
			||||
      isLoading: 'isLoading' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters('coursecategory', ['find']) | 
				
			||||
  }, | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('coursecategory', { | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      reset: 'resetShow', | 
				
			||||
      retrieve: 'load' | 
				
			||||
    }) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,61 @@ | 
				
			||||
<template> | 
				
			||||
  <div> | 
				
			||||
    <Toolbar | 
				
			||||
      :handle-submit="onSendForm" | 
				
			||||
      :handle-reset="resetForm" | 
				
			||||
      :handle-delete="del" | 
				
			||||
    /> | 
				
			||||
    <CourseCategoryForm | 
				
			||||
      ref="updateForm" | 
				
			||||
      v-if="item" | 
				
			||||
      :values="item" | 
				
			||||
      :errors="violations" | 
				
			||||
    /> | 
				
			||||
    <Loading :visible="isLoading || deleteLoading" /> | 
				
			||||
  </div> | 
				
			||||
</template> | 
				
			||||
 | 
				
			||||
<script> | 
				
			||||
import { mapActions, mapGetters } from 'vuex'; | 
				
			||||
import { mapFields } from 'vuex-map-fields'; | 
				
			||||
import CourseCategoryForm from '../../components/coursecategory/Form.vue'; | 
				
			||||
import Loading from '../../components/Loading'; | 
				
			||||
import Toolbar from '../../components/Toolbar'; | 
				
			||||
import UpdateMixin from '../../mixins/UpdateMixin'; | 
				
			||||
 | 
				
			||||
const servicePrefix = 'CourseCategory'; | 
				
			||||
 | 
				
			||||
export default { | 
				
			||||
  name: 'CourseCategoryUpdate', | 
				
			||||
  servicePrefix, | 
				
			||||
  mixins: [UpdateMixin], | 
				
			||||
  components: { | 
				
			||||
    Loading, | 
				
			||||
    Toolbar, | 
				
			||||
    CourseCategoryForm | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  computed: { | 
				
			||||
    ...mapFields('coursecategory', { | 
				
			||||
      deleteLoading: 'isLoading', | 
				
			||||
      isLoading: 'isLoading', | 
				
			||||
      error: 'error', | 
				
			||||
      updated: 'updated', | 
				
			||||
      violations: 'violations' | 
				
			||||
    }), | 
				
			||||
    ...mapGetters('coursecategory', ['find']) | 
				
			||||
 | 
				
			||||
  }, | 
				
			||||
 | 
				
			||||
  methods: { | 
				
			||||
    ...mapActions('coursecategory', { | 
				
			||||
      createReset: 'resetCreate', | 
				
			||||
      deleteItem: 'del', | 
				
			||||
      delReset: 'resetDelete', | 
				
			||||
      retrieve: 'load', | 
				
			||||
      update: 'update', | 
				
			||||
      updateReset: 'resetUpdate' | 
				
			||||
    }) | 
				
			||||
  } | 
				
			||||
}; | 
				
			||||
</script> | 
				
			||||
@ -0,0 +1,11 @@ | 
				
			||||
module.exports = { | 
				
			||||
  pluginOptions: { | 
				
			||||
    quasar: { | 
				
			||||
      importStrategy: 'manual', | 
				
			||||
      rtlSupport: false | 
				
			||||
    } | 
				
			||||
  }, | 
				
			||||
  transpileDependencies: [ | 
				
			||||
    'quasar' | 
				
			||||
  ] | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue