mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
refactored genericAPI as ApiMixin
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<div id="app" style="margin-bottom: 4vh">
|
||||
<generic-modal-form
|
||||
<!-- v-if prevents component from loading before this_model has been assigned -->
|
||||
<generic-modal-form v-if="this_model"
|
||||
:model="this_model"
|
||||
:action="this_action"
|
||||
:item1="this_item"
|
||||
:item2="this_target"
|
||||
:show="show_modal"
|
||||
@finish-action="finishAction"/>
|
||||
<generic-split-lists
|
||||
<generic-split-lists v-if="this_model"
|
||||
:list_name="this_model.name"
|
||||
@reset="resetList"
|
||||
@get-list="getItems"
|
||||
@@ -60,8 +61,7 @@ import {BootstrapVue} from 'bootstrap-vue'
|
||||
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
|
||||
import {CardMixin, ToastMixin, genericAPI} from "@/utils/utils";
|
||||
import {Models, Actions} from "@/utils/models";
|
||||
import {CardMixin, ToastMixin, ApiMixin} from "@/utils/utils";
|
||||
import {StandardToasts} from "@/utils/utils";
|
||||
|
||||
import GenericSplitLists from "@/components/GenericSplitLists";
|
||||
@@ -72,23 +72,27 @@ Vue.use(BootstrapVue)
|
||||
|
||||
export default {
|
||||
name: 'FoodListView', // TODO: make generic name
|
||||
mixins: [CardMixin, ToastMixin],
|
||||
mixins: [CardMixin, ToastMixin, ApiMixin],
|
||||
components: {GenericHorizontalCard, GenericSplitLists, GenericModalForm},
|
||||
data() {
|
||||
return {
|
||||
// this.Models and this.Actions inherited from ApiMixin
|
||||
items_left: [],
|
||||
items_right: [],
|
||||
load_more_left: true,
|
||||
load_more_right: true,
|
||||
this_model: Models.FOOD, //TODO: mounted method to calcuate
|
||||
this_model: undefined,
|
||||
this_action: undefined,
|
||||
this_item: {},
|
||||
this_target: {},
|
||||
models: Models,
|
||||
show_modal:false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.this_model = this.Models.FOOD //TODO: mounted method to calcuate
|
||||
},
|
||||
methods: {
|
||||
// this.genericAPI inherited from ApiMixin
|
||||
resetList: function(e) {
|
||||
if (e.column === 'left') {
|
||||
this.items_left = []
|
||||
@@ -104,22 +108,22 @@ export default {
|
||||
|
||||
switch (e.action) {
|
||||
case 'delete':
|
||||
this.this_action = Actions.DELETE
|
||||
this.this_action = this.Actions.DELETE
|
||||
this.show_modal = true
|
||||
break;
|
||||
case 'new':
|
||||
this.this_action = Actions.CREATE
|
||||
this.this_action = this.Actions.CREATE
|
||||
this.show_modal = true
|
||||
break;
|
||||
case 'edit':
|
||||
this.this_item = e.source
|
||||
this.this_action = Actions.UPDATE
|
||||
this.this_action = this.Actions.UPDATE
|
||||
this.show_modal = true
|
||||
break;
|
||||
case 'move':
|
||||
if (target == null) {
|
||||
this.this_item = e.source
|
||||
this.this_action = Actions.MOVE
|
||||
this.this_action = this.Actions.MOVE
|
||||
this.show_modal = true
|
||||
} else {
|
||||
this.moveThis(source.id, target.id)
|
||||
@@ -128,7 +132,7 @@ export default {
|
||||
case 'merge':
|
||||
if (target == null) {
|
||||
this.this_item = e.source
|
||||
this.this_action = Actions.MERGE
|
||||
this.this_action = this.Actions.MERGE
|
||||
this.show_modal = true
|
||||
} else {
|
||||
this.mergeThis(e.source.id, e.target.id)
|
||||
@@ -154,21 +158,21 @@ export default {
|
||||
let update = undefined
|
||||
if (e !== 'cancel') {
|
||||
switch(this.this_action) {
|
||||
case Actions.DELETE:
|
||||
case this.Actions.DELETE:
|
||||
this.deleteThis(this.this_item.id)
|
||||
break;
|
||||
case Actions.CREATE:
|
||||
case this.Actions.CREATE:
|
||||
this.saveThis(e.form_data)
|
||||
break;
|
||||
case Actions.UPDATE:
|
||||
case this.Actions.UPDATE:
|
||||
update = e.form_data
|
||||
update.id = this.this_item.id
|
||||
this.saveThis(update)
|
||||
break;
|
||||
case Actions.MERGE:
|
||||
case this.Actions.MERGE:
|
||||
this.mergeThis(this.this_item.id, e.form_data.target)
|
||||
break;
|
||||
case Actions.MOVE:
|
||||
case this.Actions.MOVE:
|
||||
this.moveThis(this.this_item.id, e.form_data.target)
|
||||
break;
|
||||
}
|
||||
@@ -178,7 +182,7 @@ export default {
|
||||
getItems: function(params, callback) {
|
||||
let column = params?.column ?? 'left'
|
||||
// TODO: does this need to be a callback?
|
||||
genericAPI(this.this_model, Actions.LIST, params).then((result) => {
|
||||
this.genericAPI(this.this_model, this.Actions.LIST, params).then((result) => {
|
||||
if (result.data.results.length){
|
||||
if (column ==='left') {
|
||||
// if paginated results are in result.data.results otherwise just result.data
|
||||
@@ -187,6 +191,7 @@ export default {
|
||||
this.items_right = this.items_right.concat(result.data?.results ?? result.data)
|
||||
}
|
||||
// are the total elements less than the length of the array? if so, stop loading
|
||||
// TODO: generalize this to handle results in result.data
|
||||
callback(result.data.count > (column==="left" ? this.items_left.length : this.items_right.length))
|
||||
} else {
|
||||
callback(false) // stop loading
|
||||
@@ -202,11 +207,11 @@ export default {
|
||||
})
|
||||
},
|
||||
getThis: function(id, callback){
|
||||
return genericAPI(this.this_model, Actions.FETCH, {'id': id})
|
||||
return this.genericAPI(this.this_model, this.Actions.FETCH, {'id': id})
|
||||
},
|
||||
saveThis: function (thisItem) {
|
||||
if (!thisItem?.id) { // if there is no item id assume it's a new item
|
||||
genericAPI(this.this_model, Actions.CREATE, thisItem).then((result) => {
|
||||
this.genericAPI(this.this_model, this.Actions.CREATE, thisItem).then((result) => {
|
||||
// place all new items at the top of the list - could sort instead
|
||||
this.items_left = [result.data].concat(this.items_left)
|
||||
// this creates a deep copy to make sure that columns stay independent
|
||||
@@ -217,7 +222,7 @@ export default {
|
||||
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
|
||||
})
|
||||
} else {
|
||||
genericAPI(this.this_model, Actions.UPDATE, thisItem).then((result) => {
|
||||
this.genericAPI(this.this_model, this.Actions.UPDATE, thisItem).then((result) => {
|
||||
this.refreshThis(thisItem.id)
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)
|
||||
}).catch((err) => {
|
||||
@@ -232,12 +237,12 @@ export default {
|
||||
this.clearState()
|
||||
return
|
||||
}
|
||||
if (!source_id || !target_id) {
|
||||
if (source_id === undefined || target_id === undefined) {
|
||||
this.makeToast(this.$t('Warning'), this.$t('Nothing to do'), 'warning')
|
||||
this.clearState()
|
||||
return
|
||||
}
|
||||
genericAPI(this.this_model, Actions.MOVE, {'source': source_id, 'target': target_id}).then((result) => {
|
||||
this.genericAPI(this.this_model, this.Actions.MOVE, {'source': source_id, 'target': target_id}).then((result) => {
|
||||
if (target_id === 0) {
|
||||
let item = this.findCard(source_id, this.items_left) || this.findCard(source_id, this.items_right)
|
||||
this.items_left = [item].concat(this.destroyCard(source_id, this.items_left)) // order matters, destroy old card before adding it back in at root
|
||||
@@ -268,7 +273,7 @@ export default {
|
||||
this.clearState()
|
||||
return
|
||||
}
|
||||
genericAPI(this.this_model, Actions.MERGE, {'source': source_id, 'target': target_id}).then((result) => {
|
||||
this.genericAPI(this.this_model, this.Actions.MERGE, {'source': source_id, 'target': target_id}).then((result) => {
|
||||
this.items_left = this.destroyCard(source_id, this.items_left)
|
||||
this.items_right = this.destroyCard(source_id, this.items_right)
|
||||
this.refreshThis(target_id)
|
||||
@@ -287,7 +292,7 @@ export default {
|
||||
'root': item.id,
|
||||
'pageSize': 200
|
||||
}
|
||||
genericAPI(this.this_model, Actions.LIST, options).then((result) => {
|
||||
this.genericAPI(this.this_model, this.Actions.LIST, options).then((result) => {
|
||||
parent = this.findCard(item.id, col === 'left' ? this.items_left : this.items_right)
|
||||
if (parent) {
|
||||
Vue.set(parent, 'children', result.data.results)
|
||||
@@ -306,7 +311,7 @@ export default {
|
||||
'foods': food.id,
|
||||
'pageSize': 200
|
||||
}
|
||||
genericAPI(Models.RECIPE, Actions.LIST, options).then((result) => {
|
||||
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, options).then((result) => {
|
||||
parent = this.findCard(food.id, col === 'left' ? this.items_left : this.items_right)
|
||||
if (parent) {
|
||||
Vue.set(parent, 'recipes', result.data.results)
|
||||
@@ -326,7 +331,7 @@ export default {
|
||||
})
|
||||
},
|
||||
deleteThis: function(id) {
|
||||
genericAPI(this.this_model, Actions.DELETE, {'id': id}).then((result) => {
|
||||
this.genericAPI(this.this_model, this.Actions.DELETE, {'id': id}).then((result) => {
|
||||
this.items_left = this.destroyCard(id, this.items_left)
|
||||
this.items_right = this.destroyCard(id, this.items_right)
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE)
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
<div class="row" :class="{'overflow-hidden' : show_split}" style="margin-top: 2vh">
|
||||
<div class="col col-md" :class="{'mh-100 overflow-auto' : show_split}">
|
||||
<generic-horizontal-card v-for="kw in keywords" v-bind:key="kw.id"
|
||||
:model=kw
|
||||
model_name="Keyword"
|
||||
:item=kw
|
||||
item_type="Keyword"
|
||||
:draggable="true"
|
||||
:merge="true"
|
||||
:move="true"
|
||||
@@ -86,12 +86,12 @@
|
||||
<!-- right side keyword cards -->
|
||||
<div class="col col-md mh-100 overflow-auto " v-if="show_split">
|
||||
<generic-horizontal-card v-for="kw in keywords2" v-bind:key="kw.id"
|
||||
:model=kw
|
||||
model_name="Keyword"
|
||||
:item=kw
|
||||
item_type="Keyword"
|
||||
:draggable="true"
|
||||
:merge="true"
|
||||
:move="true"
|
||||
@item-action="startAction($event, 'left')"
|
||||
@item-action="startAction($event, 'right')"
|
||||
/>
|
||||
<infinite-loading
|
||||
:identifier='right'
|
||||
@@ -147,26 +147,25 @@
|
||||
{{this.$t("delete_confimation", {'kw': this_item.name})}} {{this_item.name}}
|
||||
</b-modal>
|
||||
<!-- move modal -->
|
||||
<b-modal class="modal"
|
||||
<b-modal class="modal" v-if="models"
|
||||
:id="'id_modal_keyword_move'"
|
||||
:title="this.$t('Move_Keyword')"
|
||||
:ok-title="this.$t('Move')"
|
||||
:cancel-title="this.$t('Cancel')"
|
||||
@ok="moveKeyword(this_item.id, this_item.target.id)">
|
||||
{{ this.$t("move_selection", {'child': this_item.name}) }}
|
||||
<generic-multiselect
|
||||
<generic-multiselect
|
||||
@change="this_item.target=$event.val"
|
||||
label="name"
|
||||
:models="models.KEYWORD"
|
||||
:model="models.KEYWORD"
|
||||
:multiple="false"
|
||||
:sticky_options="[{'id': 0,'name': $t('Root')}]"
|
||||
:tree_api="true"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:placeholder="this.$t('Search')">
|
||||
</generic-multiselect>
|
||||
</b-modal>
|
||||
<!-- merge modal -->
|
||||
<b-modal class="modal"
|
||||
<b-modal class="modal" v-if="models"
|
||||
:id="'id_modal_keyword_merge'"
|
||||
:title="this.$t('Merge_Keyword')"
|
||||
:ok-title="this.$t('Merge')"
|
||||
@@ -177,7 +176,6 @@
|
||||
@change="this_item.target=$event.val"
|
||||
:model="models.KEYWORD"
|
||||
:multiple="false"
|
||||
:tree_api="true"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:placeholder="this.$t('Search')">
|
||||
</generic-multiselect>
|
||||
@@ -444,7 +442,6 @@ export default {
|
||||
let parent = {}
|
||||
let pageSize = 200
|
||||
let keyword = String(kw.id)
|
||||
console.log(apiClient.listRecipes)
|
||||
|
||||
apiClient.listRecipes(
|
||||
undefined, keyword, undefined, undefined, undefined, undefined,
|
||||
@@ -500,25 +497,25 @@ export default {
|
||||
|
||||
})
|
||||
},
|
||||
findKeyword: function(kw_list, id){
|
||||
if (kw_list.length == 0) {
|
||||
return false
|
||||
}
|
||||
let keyword = kw_list.filter(kw => kw.id == id)
|
||||
if (keyword.length == 1) {
|
||||
return keyword[0]
|
||||
} else if (keyword.length == 0) {
|
||||
for (const k of kw_list.filter(kw => kw.show_children == true)) {
|
||||
keyword = this.findKeyword(k.children, id)
|
||||
if (keyword) {
|
||||
return keyword
|
||||
findKeyword: function(card_list, id){
|
||||
let card_length = card_list?.length ?? 0
|
||||
if (card_length == 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('something terrible happened')
|
||||
}
|
||||
},
|
||||
let cards = card_list.filter(obj => obj.id == id)
|
||||
if (cards.length == 1) {
|
||||
return cards[0]
|
||||
} else if (cards.length == 0) {
|
||||
for (const c of card_list.filter(x => x.show_children == true)) {
|
||||
cards = this.findKeyword(c.children, id)
|
||||
if (cards) {
|
||||
return cards
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('something terrible happened')
|
||||
}
|
||||
},
|
||||
// this would move with modals with mixin?
|
||||
prepareEmoji: function() {
|
||||
this.$refs._edit.addText(this.this_item.icon || '');
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<b-input-group class="mt-2">
|
||||
<b-input-group class="mt-2" v-if="models">
|
||||
<generic-multiselect @change="genericSelectChanged" parent_variable="search_books"
|
||||
:initial_selection="settings.search_books"
|
||||
:model="models.RECIPE_BOOK"
|
||||
@@ -355,7 +355,7 @@ export default {
|
||||
|
||||
this.loadMealPlan()
|
||||
// this.loadRecentlyViewed()
|
||||
// this.refreshData(false) // this gets triggered whenthe cookies get loaded
|
||||
// this.refreshData(false) // this gets triggered when the cookies get loaded
|
||||
})
|
||||
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
@@ -414,7 +414,6 @@ export default {
|
||||
this.pagination_count = result.data.count
|
||||
this.recipes = this.removeDuplicates(result.data.results, recipe => recipe.id)
|
||||
this.facets = result.data.facets
|
||||
console.log(this.recipes)
|
||||
})
|
||||
},
|
||||
openRandom: function () {
|
||||
@@ -427,7 +426,6 @@ export default {
|
||||
},
|
||||
loadMealPlan: function () {
|
||||
let apiClient = new ApiApiFactory()
|
||||
// TODO setting to change days to look for meal plans
|
||||
if (this.settings.show_meal_plan) {
|
||||
apiClient.listMealPlans({
|
||||
query: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<b-dropdown variant="link" toggle-class="text-decoration-none" no-caret>
|
||||
<b-dropdown variant="link" toggle-class="text-decoration-none" no-caret style="boundary:window">
|
||||
<template #button-content>
|
||||
<i class="fas fa-ellipsis-v" ></i>
|
||||
</template>
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
<script>
|
||||
|
||||
import Multiselect from 'vue-multiselect'
|
||||
import {genericAPI} from "@/utils/utils";
|
||||
import {Actions} from "@/utils/models";
|
||||
import {ApiMixin} from "@/utils/utils";
|
||||
|
||||
export default {
|
||||
name: "GenericMultiselect",
|
||||
components: {Multiselect},
|
||||
mixins: [ApiMixin],
|
||||
data() {
|
||||
return {
|
||||
// this.Models and this.Actions inherited from ApiMixin
|
||||
loading: false,
|
||||
objects: [],
|
||||
selected_objects: [],
|
||||
@@ -65,13 +66,14 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// this.genericAPI inherited from ApiMixin
|
||||
search: function (query) {
|
||||
let options = {
|
||||
'page': 1,
|
||||
'pageSize': 10,
|
||||
'query': query
|
||||
}
|
||||
genericAPI(this.model, Actions.LIST, options).then((result) => {
|
||||
this.genericAPI(this.model, this.Actions.LIST, options).then((result) => {
|
||||
this.objects = this.sticky_options.concat(result.data?.results ?? result.data)
|
||||
})
|
||||
},
|
||||
|
||||
@@ -146,20 +146,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
resetSearch: function () {
|
||||
if (this.search_right !== '') {
|
||||
this.search_right = ''
|
||||
} else {
|
||||
this.left_page = 1
|
||||
this.$emit('reset', {'column':'left'})
|
||||
this.left += 1
|
||||
}
|
||||
if (this.search_left !== '') {
|
||||
this.search_left = ''
|
||||
} else {
|
||||
this.right_page = 1
|
||||
this.$emit('reset', {'column':'right'})
|
||||
this.right += 1
|
||||
}
|
||||
this.search_right = ''
|
||||
this.search_left = ''
|
||||
},
|
||||
infiniteHandler: function($state, col) {
|
||||
let params = {
|
||||
@@ -169,7 +157,7 @@ export default {
|
||||
}
|
||||
// TODO: change this to be an emit and watch a prop to determine if loaded or complete
|
||||
new Promise((callback) => this.$emit('get-list', params, callback)).then((result) => {
|
||||
this[col+'_page']+=1
|
||||
this[col+'_page'] += 1
|
||||
$state.loaded();
|
||||
if (!result) { // callback needs to return true if handler should continue loading more data
|
||||
$state.complete();
|
||||
@@ -178,7 +166,6 @@ export default {
|
||||
$state.complete();
|
||||
})
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,12 +46,11 @@ export default {
|
||||
name: 'GenericModalForm',
|
||||
components: {CheckboxInput, LookupInput, TextInput},
|
||||
props: {
|
||||
model: {type: Object, default: function() {}},
|
||||
action: {type: Object, default: function() {}},
|
||||
model: {required: true, type: Object, default: function() {}},
|
||||
action: {required: true, type: Object, default: function() {}},
|
||||
item1: {type: Object, default: function() {}},
|
||||
item2: {type: Object, default: function() {}},
|
||||
// action: {type: String, default: ''},
|
||||
show: {type: Boolean, default: false},
|
||||
show: {required: true, type: Boolean, default: false},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import GenericMultiselect from "@/components/GenericMultiselect";
|
||||
|
||||
export default {
|
||||
|
||||
@@ -161,35 +161,47 @@ import {ApiApiFactory} from "@/utils/openapi/api.ts"; // TODO: is it possible t
|
||||
import axios from "axios";
|
||||
axios.defaults.xsrfCookieName = 'csrftoken'
|
||||
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
|
||||
export function genericAPI(model, action, options) {
|
||||
let setup = getConfig(model, action)
|
||||
let func = setup.function
|
||||
let config = setup?.config ?? {}
|
||||
let params = setup?.params ?? []
|
||||
let parameters = []
|
||||
let this_value = undefined
|
||||
params.forEach(function (item, index) {
|
||||
if (Array.isArray(item)) {
|
||||
this_value = {}
|
||||
// if the value is an array, convert it to a dictionary of key:value
|
||||
// filtered based on OPTIONS passed
|
||||
// maybe map/reduce is better?
|
||||
for (const [k, v] of Object.entries(options)) {
|
||||
if (item.includes(k)) {
|
||||
this_value[k] = formatParam(config?.[k], v)
|
||||
import { Actions, Models } from './models';
|
||||
|
||||
export const ApiMixin = {
|
||||
data() {
|
||||
return {
|
||||
Models: Models,
|
||||
Actions: Actions
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
genericAPI: function(model, action, options) {
|
||||
let setup = getConfig(model, action)
|
||||
let func = setup.function
|
||||
let config = setup?.config ?? {}
|
||||
let params = setup?.params ?? []
|
||||
let parameters = []
|
||||
let this_value = undefined
|
||||
params.forEach(function (item, index) {
|
||||
if (Array.isArray(item)) {
|
||||
this_value = {}
|
||||
// if the value is an array, convert it to a dictionary of key:value
|
||||
// filtered based on OPTIONS passed
|
||||
// maybe map/reduce is better?
|
||||
for (const [k, v] of Object.entries(options)) {
|
||||
if (item.includes(k)) {
|
||||
this_value[k] = formatParam(config?.[k], v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this_value = formatParam(config?.[item], options?.[item] ?? undefined)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this_value = formatParam(config?.[item], options?.[item] ?? undefined)
|
||||
// if no value is found so far, get the default if it exists
|
||||
if (this_value === undefined) {
|
||||
this_value = getDefault(config?.[item], options)
|
||||
}
|
||||
parameters.push(this_value)
|
||||
});
|
||||
let apiClient = new ApiApiFactory()
|
||||
return apiClient[func](...parameters)
|
||||
}
|
||||
// if no value is found so far, get the default if it exists
|
||||
if (!this_value) {
|
||||
this_value = getDefault(config?.[item], options)
|
||||
}
|
||||
parameters.push(this_value)
|
||||
});
|
||||
let apiClient = new ApiApiFactory()
|
||||
return apiClient[func](...parameters)
|
||||
}
|
||||
}
|
||||
|
||||
// /*
|
||||
@@ -202,7 +214,9 @@ function formatParam(config, value) {
|
||||
case 'type':
|
||||
switch(v) {
|
||||
case 'string':
|
||||
value = String(value)
|
||||
if (value !== undefined){
|
||||
value = String(value)
|
||||
}
|
||||
break;
|
||||
case 'integer':
|
||||
value = parseInt(value)
|
||||
@@ -328,7 +342,6 @@ function formTranslate(translate, model, item1, item2) {
|
||||
// * Utility functions to use manipulate nested components
|
||||
// * */
|
||||
import Vue from 'vue'
|
||||
import { Actions } from './models';
|
||||
export const CardMixin = {
|
||||
methods: {
|
||||
findCard: function(id, card_list){
|
||||
|
||||
Reference in New Issue
Block a user