mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
working on select components
This commit is contained in:
@@ -908,12 +908,12 @@ class CommentSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class RecipeOverviewSerializer(RecipeBaseSerializer):
|
class RecipeOverviewSerializer(RecipeBaseSerializer):
|
||||||
keywords = KeywordLabelSerializer(many=True)
|
keywords = KeywordLabelSerializer(many=True, read_only=True)
|
||||||
new = serializers.SerializerMethodField('is_recipe_new')
|
new = serializers.SerializerMethodField('is_recipe_new', read_only=True)
|
||||||
recent = serializers.ReadOnlyField()
|
recent = serializers.ReadOnlyField()
|
||||||
|
|
||||||
rating = CustomDecimalField(required=False, allow_null=True)
|
rating = CustomDecimalField(required=False, allow_null=True, read_only=True)
|
||||||
last_cooked = serializers.DateTimeField(required=False, allow_null=True)
|
last_cooked = serializers.DateTimeField(required=False, allow_null=True, read_only=True)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
pass
|
pass
|
||||||
@@ -928,7 +928,9 @@ class RecipeOverviewSerializer(RecipeBaseSerializer):
|
|||||||
'waiting_time', 'created_by', 'created_at', 'updated_at',
|
'waiting_time', 'created_by', 'created_at', 'updated_at',
|
||||||
'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'new', 'recent'
|
'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'new', 'recent'
|
||||||
)
|
)
|
||||||
read_only_fields = ['image', 'created_by', 'created_at']
|
read_only_fields = ['id', 'name', 'description', 'image', 'keywords', 'working_time',
|
||||||
|
'waiting_time', 'created_by', 'created_at', 'updated_at',
|
||||||
|
'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'new', 'recent']
|
||||||
|
|
||||||
|
|
||||||
class RecipeSerializer(RecipeBaseSerializer):
|
class RecipeSerializer(RecipeBaseSerializer):
|
||||||
|
|||||||
@@ -534,6 +534,17 @@ class SupermarketCategoryRelationViewSet(StandardFilterModelViewSet):
|
|||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
|
|
||||||
|
# TODO make TreeMixin a view type and move schema to view type
|
||||||
|
@extend_schema_view(
|
||||||
|
list=extend_schema(
|
||||||
|
parameters=[
|
||||||
|
OpenApiParameter(name='query', description='lookup if query string is contained within the name, case insensitive', type=str),
|
||||||
|
OpenApiParameter(name='updated_at', description='if model has an updated_at timestamp, filter only models updated at or after datetime', type=str), # TODO format hint
|
||||||
|
OpenApiParameter(name='limit', description='limit number of entries to return', type=str),
|
||||||
|
OpenApiParameter(name='random', description='randomly orders entries (only works together with limit)', type=str),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
class KeywordViewSet(viewsets.ModelViewSet, TreeMixin):
|
class KeywordViewSet(viewsets.ModelViewSet, TreeMixin):
|
||||||
queryset = Keyword.objects
|
queryset = Keyword.objects
|
||||||
model = Keyword
|
model = Keyword
|
||||||
@@ -542,7 +553,7 @@ class KeywordViewSet(viewsets.ModelViewSet, TreeMixin):
|
|||||||
pagination_class = DefaultPagination
|
pagination_class = DefaultPagination
|
||||||
|
|
||||||
|
|
||||||
class UnitViewSet(viewsets.ModelViewSet, MergeMixin, FuzzyFilterMixin):
|
class UnitViewSet(StandardFilterModelViewSet, MergeMixin):
|
||||||
queryset = Unit.objects
|
queryset = Unit.objects
|
||||||
model = Unit
|
model = Unit
|
||||||
serializer_class = UnitSerializer
|
serializer_class = UnitSerializer
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"mavon-editor": "^3.0.1",
|
"mavon-editor": "^3.0.1",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"vue": "^3.4.15",
|
"vue": "^3.4.15",
|
||||||
|
"vue-multiselect": "^3.0.0-beta.3",
|
||||||
"vue-router": "4",
|
"vue-router": "4",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"vuetify": "^3.5.8"
|
"vuetify": "^3.5.8"
|
||||||
|
|||||||
@@ -1,87 +1,65 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-if="allowCreate">
|
<v-input>
|
||||||
<v-combobox
|
|
||||||
label="Combobox"
|
<VueMultiselect
|
||||||
|
:id="id"
|
||||||
v-model="selected_items"
|
v-model="selected_items"
|
||||||
v-model:search="search_query"
|
:options="items"
|
||||||
@update:search="debouncedSearchFunction"
|
:close-on-select="true"
|
||||||
:items="items"
|
:clear-on-select="true"
|
||||||
:loading="search_loading"
|
:hide-selected="multiple"
|
||||||
:hide-no-data="!(allowCreate && search_query != '')"
|
:preserve-search="true"
|
||||||
|
:internal-search="false"
|
||||||
|
:limit="limit"
|
||||||
|
:placeholder="model"
|
||||||
|
:label="label"
|
||||||
|
track-by="id"
|
||||||
:multiple="multiple"
|
:multiple="multiple"
|
||||||
:clearable="clearable"
|
:taggable="allowCreate"
|
||||||
item-title="name"
|
tag-placeholder="TODO CREATE PLACEHOLDER"
|
||||||
item-value="id"
|
:loading="search_loading"
|
||||||
:chips="renderAsChips"
|
@search-change="debouncedSearchFunction"
|
||||||
:closable-chips="renderAsChips"
|
@input="selectionChanged"
|
||||||
no-filter
|
@tag="addItem"
|
||||||
|
@open="search('')"
|
||||||
|
:disabled="disabled"
|
||||||
>
|
>
|
||||||
|
</VueMultiselect>
|
||||||
|
</v-input>
|
||||||
|
|
||||||
<template #no-data v-if="allowCreate && search_query != '' && !search_loading && multiple">
|
|
||||||
<v-list-item>
|
|
||||||
<v-list-item-title>
|
|
||||||
Press enter to create "<strong>{{ search_query }}</strong>"
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item="{ item, index, props }">
|
|
||||||
<v-list-item v-bind="props">
|
|
||||||
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="renderAsChips" v-slot:chip="{ item, index, props }">
|
|
||||||
<v-chip closable>{{ item.title }}</v-chip>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</v-combobox>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
|
|
||||||
<v-autocomplete
|
|
||||||
label="Autocomplete"
|
|
||||||
:items="items"
|
|
||||||
:loading="search_loading"
|
|
||||||
:multiple="multiple"
|
|
||||||
item-title="name"
|
|
||||||
item-value="id"
|
|
||||||
chips
|
|
||||||
closable-chips
|
|
||||||
no-filter
|
|
||||||
@update:search="debouncedSearchFunction"
|
|
||||||
></v-autocomplete>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {computed, onMounted, ref, Ref, watch} from 'vue'
|
import {computed, onMounted, ref, Ref,} from 'vue'
|
||||||
import {ApiApi} from "@/openapi/index.js";
|
import {ApiApi} from "@/openapi/index.js";
|
||||||
import {useDebounceFn} from "@vueuse/core";
|
import {useDebounceFn} from "@vueuse/core";
|
||||||
|
import {GenericModel, getModelFromStr} from "@/types/Models";
|
||||||
|
import VueMultiselect from 'vue-multiselect'
|
||||||
|
|
||||||
const props = defineProps(
|
const props = defineProps(
|
||||||
{
|
{
|
||||||
search_on_load: {type: Boolean, default: false},
|
model: {type: String, required: true},
|
||||||
multiple: {type: Boolean, default: true},
|
multiple: {type: Boolean, default: true},
|
||||||
|
limit: {type: Number, default: 25},
|
||||||
allowCreate: {type: Boolean, default: false},
|
allowCreate: {type: Boolean, default: false},
|
||||||
|
|
||||||
|
id: {type: String, required: false, default: Math.random().toString()},
|
||||||
|
|
||||||
|
// not verified
|
||||||
|
search_on_load: {type: Boolean, default: false},
|
||||||
|
|
||||||
|
|
||||||
clearable: {type: Boolean, default: false,},
|
clearable: {type: Boolean, default: false,},
|
||||||
chips: {type: Boolean, default: undefined,},
|
chips: {type: Boolean, default: undefined,},
|
||||||
|
|
||||||
itemName: {type: String, default: 'name'},
|
itemName: {type: String, default: 'name'},
|
||||||
itemValue: {type: String, default: 'id'},
|
itemValue: {type: String, default: 'id'},
|
||||||
|
|
||||||
// old props
|
|
||||||
|
|
||||||
placeholder: {type: String, default: undefined},
|
placeholder: {type: String, default: undefined},
|
||||||
model: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
label: {type: String, default: "name"},
|
label: {type: String, default: "name"},
|
||||||
parent_variable: {type: String, default: undefined},
|
parent_variable: {type: String, default: undefined},
|
||||||
limit: {type: Number, default: 25},
|
|
||||||
sticky_options: {
|
sticky_options: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default() {
|
default() {
|
||||||
@@ -104,40 +82,15 @@ const props = defineProps(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const model_class = ref({} as GenericModel<any>)
|
||||||
const items: Ref<Array<any>> = ref([])
|
const items: Ref<Array<any>> = ref([])
|
||||||
const selected_items: Ref<Array<any> | any> = ref(undefined)
|
const selected_items: Ref<Array<any> | any> = ref(undefined)
|
||||||
const search_query = ref('')
|
const search_query = ref('')
|
||||||
const search_loading = ref(false)
|
const search_loading = ref(false)
|
||||||
|
|
||||||
const renderAsChips = computed(() => {
|
|
||||||
if (props.chips != undefined) {
|
|
||||||
return props.chips
|
|
||||||
}
|
|
||||||
return props.multiple
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(selected_items, (new_items, old_items) => {
|
|
||||||
if (!(new_items instanceof Array) && !(old_items instanceof Array)) {
|
|
||||||
//TODO detect creation of single selects
|
|
||||||
} else {
|
|
||||||
if (old_items == undefined && new_items instanceof Array) {
|
|
||||||
old_items = []
|
|
||||||
}
|
|
||||||
if (new_items == undefined && old_items instanceof Array) {
|
|
||||||
new_items = []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old_items.length > new_items.length) {
|
|
||||||
// item was removed
|
|
||||||
} else if (old_items.length < new_items.length) {
|
|
||||||
console.log('items created')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
model_class.value = getModelFromStr(props.model)
|
||||||
if (props.search_on_load) {
|
if (props.search_on_load) {
|
||||||
debouncedSearchFunction('')
|
debouncedSearchFunction('')
|
||||||
}
|
}
|
||||||
@@ -155,16 +108,15 @@ const debouncedSearchFunction = useDebounceFn((query: string) => {
|
|||||||
* @param query input to search for on the API
|
* @param query input to search for on the API
|
||||||
*/
|
*/
|
||||||
function search(query: string) {
|
function search(query: string) {
|
||||||
const api = new ApiApi()
|
|
||||||
search_loading.value = true
|
search_loading.value = true
|
||||||
api.apiFoodList({query: query}).then(r => {
|
model_class.value.list(query).then(r => {
|
||||||
if (r.results) {
|
items.value = r
|
||||||
items.value = r.results
|
if (props.allowCreate && search_query.value != '') {
|
||||||
if (props.allowCreate && search_query.value != '') {
|
// TODO check if search_query is already in items
|
||||||
// TODO check if search_query is already in items
|
items.value.unshift({id: null, name: `Create "${search_query.value}"`})
|
||||||
items.value.unshift({id: null, name: `Create "${search_query.value}"`})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
//useMessageStore().addMessage(MessageType.ERROR, err, 8000)
|
//useMessageStore().addMessage(MessageType.ERROR, err, 8000)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
@@ -172,9 +124,34 @@ function search(query: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addItem(item: string) {
|
||||||
|
console.log("CREATEING NEW with -> ", item)
|
||||||
|
const api = new ApiApi()
|
||||||
|
api.apiKeywordList()
|
||||||
|
|
||||||
|
model_class.value.create(item).then(createdObj => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE)
|
||||||
|
if (selected_items.value instanceof Array) {
|
||||||
|
selected_items.value.push(createdObj)
|
||||||
|
} else {
|
||||||
|
selected_items.value = createdObj
|
||||||
|
}
|
||||||
|
items.value.push(createdObj)
|
||||||
|
selectionChanged()
|
||||||
|
}).catch((err) => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE)
|
||||||
|
}).finally(() => {
|
||||||
|
search_loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectionChanged() {
|
||||||
|
//this.$emit("change", { var: this.parent_variable, val: this.selected_objects })
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
184
vue3/src/components/inputs/ModelSelectVuetify.vue
Normal file
184
vue3/src/components/inputs/ModelSelectVuetify.vue
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<!-- WIP component that does not really work, that's why vue-multiselect is temporarily used -->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="allowCreate">
|
||||||
|
<v-combobox
|
||||||
|
label="Combobox"
|
||||||
|
v-model="selected_items"
|
||||||
|
v-model:search="search_query"
|
||||||
|
@update:search="debouncedSearchFunction"
|
||||||
|
:items="items"
|
||||||
|
:loading="search_loading"
|
||||||
|
:hide-no-data="!(allowCreate && search_query != '')"
|
||||||
|
:multiple="multiple"
|
||||||
|
:clearable="clearable"
|
||||||
|
item-title="name"
|
||||||
|
item-value="id"
|
||||||
|
:chips="multiple"
|
||||||
|
:closable-chips="multiple"
|
||||||
|
no-filter
|
||||||
|
>
|
||||||
|
|
||||||
|
<template #no-data v-if="allowCreate && search_query != '' && !search_loading && multiple">
|
||||||
|
<v-list-item>
|
||||||
|
<v-list-item-title>
|
||||||
|
Press enter to create "<strong>{{ search_query }}</strong>"
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- <template v-slot:item="{ item, index, props }">-->
|
||||||
|
<!-- <v-list-item v-bind="props">-->
|
||||||
|
|
||||||
|
<!-- </v-list-item>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
|
||||||
|
<template v-if="multiple" v-slot:chip="{ item, index, props }">
|
||||||
|
<v-chip closable>{{ item.title }}</v-chip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</v-combobox>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
|
||||||
|
<v-autocomplete
|
||||||
|
label="Autocomplete"
|
||||||
|
v-model="selected_items"
|
||||||
|
v-model:search="search_query"
|
||||||
|
@update:search="debouncedSearchFunction"
|
||||||
|
:items="items"
|
||||||
|
:loading="search_loading"
|
||||||
|
:hide-no-data="!(allowCreate && search_query != '')"
|
||||||
|
:multiple="multiple"
|
||||||
|
:clearable="clearable"
|
||||||
|
item-title="name"
|
||||||
|
item-value="id"
|
||||||
|
:chips="multiple"
|
||||||
|
:closable-chips="multiple"
|
||||||
|
no-filter
|
||||||
|
></v-autocomplete>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {computed, onMounted, PropType, ref, Ref, watch} from 'vue'
|
||||||
|
import {ApiApi} from "@/openapi/index.js";
|
||||||
|
import {useDebounceFn} from "@vueuse/core";
|
||||||
|
import {Models} from "@/types/Models";
|
||||||
|
import {VAutocomplete, VCombobox} from "vuetify/components";
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps(
|
||||||
|
{
|
||||||
|
search_on_load: {type: Boolean, default: false},
|
||||||
|
multiple: {type: Boolean, default: true},
|
||||||
|
allowCreate: {type: Boolean, default: false},
|
||||||
|
clearable: {type: Boolean, default: false,},
|
||||||
|
chips: {type: Boolean, default: undefined,},
|
||||||
|
|
||||||
|
itemName: {type: String, default: 'name'},
|
||||||
|
itemValue: {type: String, default: 'id'},
|
||||||
|
model: {type: String as PropType<Models>, required: true},
|
||||||
|
|
||||||
|
|
||||||
|
// old props
|
||||||
|
|
||||||
|
|
||||||
|
placeholder: {type: String, default: undefined},
|
||||||
|
label: {type: String, default: "name"},
|
||||||
|
parent_variable: {type: String, default: undefined},
|
||||||
|
limit: {type: Number, default: 25},
|
||||||
|
sticky_options: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
initial_selection: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
initial_single_selection: {
|
||||||
|
type: Object,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
disabled: {type: Boolean, default: false,},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const items: Ref<Array<any>> = ref([])
|
||||||
|
const selected_items: Ref<Array<any> | any> = ref(undefined)
|
||||||
|
const search_query = ref('')
|
||||||
|
const search_loading = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* watch selected items to detect if new items were added or old items removed
|
||||||
|
*/
|
||||||
|
watch(selected_items, (new_items, old_items) => {
|
||||||
|
if (!(new_items instanceof Array) && !(old_items instanceof Array)) {
|
||||||
|
//TODO detect creation of single selects
|
||||||
|
} else {
|
||||||
|
if (old_items == undefined && new_items instanceof Array) {
|
||||||
|
old_items = []
|
||||||
|
}
|
||||||
|
if (new_items == undefined && old_items instanceof Array) {
|
||||||
|
new_items = []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_items.length > new_items.length) {
|
||||||
|
// item was removed
|
||||||
|
} else if (old_items.length < new_items.length) {
|
||||||
|
console.log('items created')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.search_on_load) {
|
||||||
|
debouncedSearchFunction('')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* debounced search function bound to search input changing
|
||||||
|
*/
|
||||||
|
const debouncedSearchFunction = useDebounceFn((query: string) => {
|
||||||
|
search(query)
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* performs the API request to search for the selected input
|
||||||
|
* @param query input to search for on the API
|
||||||
|
*/
|
||||||
|
function search(query: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
search_loading.value = true
|
||||||
|
api[`api${props.model}List`]({query: query}).then(r => {
|
||||||
|
if (r.results) {
|
||||||
|
items.value = r.results
|
||||||
|
if (props.allowCreate && search_query.value != '') {
|
||||||
|
// TODO check if search_query is already in items
|
||||||
|
items.value.unshift({id: null, name: `Create "${search_query.value}"`})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
//useMessageStore().addMessage(MessageType.ERROR, err, 8000)
|
||||||
|
}).finally(() => {
|
||||||
|
search_loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -16,7 +16,10 @@
|
|||||||
label="Step Name"
|
label="Step Name"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-chip-group>
|
<v-chip-group>
|
||||||
<v-chip><i class="fas fa-plus-circle"></i> Time</v-chip>
|
<v-chip v-if="step.time == 0"><i class="fas fa-plus-circle fa-fw mr-1"></i> Time</v-chip>
|
||||||
|
<v-chip v-if="step.instruction == ''"><i class="fas fa-plus-circle fa-fw mr-1"></i> Instructions</v-chip>
|
||||||
|
<v-chip v-if="step.file == null"><i class="fas fa-plus-circle fa-fw mr-1"></i> File</v-chip>
|
||||||
|
<v-chip v-if="step.stepRecipe == null"><i class="fas fa-plus-circle fa-fw mr-1"></i> Recipe</v-chip>
|
||||||
</v-chip-group>
|
</v-chip-group>
|
||||||
|
|
||||||
<v-table density="compact">
|
<v-table density="compact">
|
||||||
@@ -34,7 +37,13 @@
|
|||||||
<v-form>
|
<v-form>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
label="Amount"
|
label="Amount"
|
||||||
v-model="element.amount"
|
v-model.number="element.amount"
|
||||||
|
></v-text-field>
|
||||||
|
<model-select model="Unit" v-model="element.unit" :multiple="false"></model-select>
|
||||||
|
<model-select model="Food" v-model="element.food" :multiple="false"></model-select>
|
||||||
|
<v-text-field
|
||||||
|
label="Note"
|
||||||
|
v-model="element.note"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-form>
|
</v-form>
|
||||||
|
|
||||||
@@ -87,10 +96,11 @@ import StepMarkdownEditor from "@/components/inputs/StepMarkdownEditor.vue";
|
|||||||
import IngredientsTable from "@/components/display/IngredientsTable.vue";
|
import IngredientsTable from "@/components/display/IngredientsTable.vue";
|
||||||
import IngredientsTableRow from "@/components/display/IngredientsTableRow.vue";
|
import IngredientsTableRow from "@/components/display/IngredientsTableRow.vue";
|
||||||
import draggable from "vuedraggable";
|
import draggable from "vuedraggable";
|
||||||
|
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "StepEditor",
|
name: "StepEditor",
|
||||||
components: {draggable, IngredientsTableRow, IngredientsTable, StepMarkdownEditor},
|
components: {ModelSelect, draggable, IngredientsTableRow, IngredientsTable, StepMarkdownEditor},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -715,8 +715,12 @@ export interface ApiKeywordDestroyRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiKeywordListRequest {
|
export interface ApiKeywordListRequest {
|
||||||
|
limit?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
|
query?: string;
|
||||||
|
random?: string;
|
||||||
|
updatedAt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiKeywordMergeUpdateRequest {
|
export interface ApiKeywordMergeUpdateRequest {
|
||||||
@@ -953,6 +957,11 @@ export interface ApiOpenDataVersionUpdateRequest {
|
|||||||
openDataVersion: OpenDataVersion;
|
openDataVersion: OpenDataVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiPlanIcalRetrieveRequest {
|
||||||
|
fromDate: string;
|
||||||
|
toDate: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiRecipeBookCreateRequest {
|
export interface ApiRecipeBookCreateRequest {
|
||||||
recipeBook: RecipeBook;
|
recipeBook: RecipeBook;
|
||||||
}
|
}
|
||||||
@@ -1349,8 +1358,12 @@ export interface ApiUnitDestroyRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiUnitListRequest {
|
export interface ApiUnitListRequest {
|
||||||
|
limit?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
|
query?: string;
|
||||||
|
random?: string;
|
||||||
|
updatedAt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiUnitMergeUpdateRequest {
|
export interface ApiUnitMergeUpdateRequest {
|
||||||
@@ -4795,6 +4808,10 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
async apiKeywordListRaw(requestParameters: ApiKeywordListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedKeywordList>> {
|
async apiKeywordListRaw(requestParameters: ApiKeywordListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedKeywordList>> {
|
||||||
const queryParameters: any = {};
|
const queryParameters: any = {};
|
||||||
|
|
||||||
|
if (requestParameters['limit'] != null) {
|
||||||
|
queryParameters['limit'] = requestParameters['limit'];
|
||||||
|
}
|
||||||
|
|
||||||
if (requestParameters['page'] != null) {
|
if (requestParameters['page'] != null) {
|
||||||
queryParameters['page'] = requestParameters['page'];
|
queryParameters['page'] = requestParameters['page'];
|
||||||
}
|
}
|
||||||
@@ -4803,6 +4820,18 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
queryParameters['page_size'] = requestParameters['pageSize'];
|
queryParameters['page_size'] = requestParameters['pageSize'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requestParameters['query'] != null) {
|
||||||
|
queryParameters['query'] = requestParameters['query'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestParameters['random'] != null) {
|
||||||
|
queryParameters['random'] = requestParameters['random'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestParameters['updatedAt'] != null) {
|
||||||
|
queryParameters['updated_at'] = requestParameters['updatedAt'];
|
||||||
|
}
|
||||||
|
|
||||||
const headerParameters: runtime.HTTPHeaders = {};
|
const headerParameters: runtime.HTTPHeaders = {};
|
||||||
|
|
||||||
if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) {
|
if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) {
|
||||||
@@ -7047,7 +7076,21 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async apiPlanIcalRetrieveRaw(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
async apiPlanIcalRetrieveRaw(requestParameters: ApiPlanIcalRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> {
|
||||||
|
if (requestParameters['fromDate'] == null) {
|
||||||
|
throw new runtime.RequiredError(
|
||||||
|
'fromDate',
|
||||||
|
'Required parameter "fromDate" was null or undefined when calling apiPlanIcalRetrieve().'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestParameters['toDate'] == null) {
|
||||||
|
throw new runtime.RequiredError(
|
||||||
|
'toDate',
|
||||||
|
'Required parameter "toDate" was null or undefined when calling apiPlanIcalRetrieve().'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const queryParameters: any = {};
|
const queryParameters: any = {};
|
||||||
|
|
||||||
const headerParameters: runtime.HTTPHeaders = {};
|
const headerParameters: runtime.HTTPHeaders = {};
|
||||||
@@ -7056,7 +7099,7 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
headerParameters["Authorization"] = "Basic " + btoa(this.configuration.username + ":" + this.configuration.password);
|
headerParameters["Authorization"] = "Basic " + btoa(this.configuration.username + ":" + this.configuration.password);
|
||||||
}
|
}
|
||||||
const response = await this.request({
|
const response = await this.request({
|
||||||
path: `/api/plan-ical/`,
|
path: `/api/plan-ical/{from_date}/{to_date}/`.replace(`{${"from_date"}}`, encodeURIComponent(String(requestParameters['fromDate']))).replace(`{${"to_date"}}`, encodeURIComponent(String(requestParameters['toDate']))),
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: headerParameters,
|
headers: headerParameters,
|
||||||
query: queryParameters,
|
query: queryParameters,
|
||||||
@@ -7067,8 +7110,8 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async apiPlanIcalRetrieve(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
async apiPlanIcalRetrieve(requestParameters: ApiPlanIcalRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> {
|
||||||
await this.apiPlanIcalRetrieveRaw(initOverrides);
|
await this.apiPlanIcalRetrieveRaw(requestParameters, initOverrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10440,6 +10483,10 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
async apiUnitListRaw(requestParameters: ApiUnitListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedUnitList>> {
|
async apiUnitListRaw(requestParameters: ApiUnitListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<PaginatedUnitList>> {
|
||||||
const queryParameters: any = {};
|
const queryParameters: any = {};
|
||||||
|
|
||||||
|
if (requestParameters['limit'] != null) {
|
||||||
|
queryParameters['limit'] = requestParameters['limit'];
|
||||||
|
}
|
||||||
|
|
||||||
if (requestParameters['page'] != null) {
|
if (requestParameters['page'] != null) {
|
||||||
queryParameters['page'] = requestParameters['page'];
|
queryParameters['page'] = requestParameters['page'];
|
||||||
}
|
}
|
||||||
@@ -10448,6 +10495,18 @@ export class ApiApi extends runtime.BaseAPI {
|
|||||||
queryParameters['page_size'] = requestParameters['pageSize'];
|
queryParameters['page_size'] = requestParameters['pageSize'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requestParameters['query'] != null) {
|
||||||
|
queryParameters['query'] = requestParameters['query'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestParameters['random'] != null) {
|
||||||
|
queryParameters['random'] = requestParameters['random'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestParameters['updatedAt'] != null) {
|
||||||
|
queryParameters['updated_at'] = requestParameters['updatedAt'];
|
||||||
|
}
|
||||||
|
|
||||||
const headerParameters: runtime.HTTPHeaders = {};
|
const headerParameters: runtime.HTTPHeaders = {};
|
||||||
|
|
||||||
if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) {
|
if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) {
|
||||||
|
|||||||
@@ -37,13 +37,13 @@ export interface RecipeOverview {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
name: string;
|
readonly name: string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
description?: string;
|
readonly description: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -55,19 +55,19 @@ export interface RecipeOverview {
|
|||||||
* @type {Array<KeywordLabel>}
|
* @type {Array<KeywordLabel>}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
keywords: Array<KeywordLabel>;
|
readonly keywords: Array<KeywordLabel>;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
workingTime?: number;
|
readonly workingTime: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
waitingTime?: number;
|
readonly waitingTime: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@@ -91,31 +91,31 @@ export interface RecipeOverview {
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
internal?: boolean;
|
readonly internal: boolean;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
servings?: number;
|
readonly servings: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
servingsText?: string;
|
readonly servingsText: string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
rating?: string;
|
readonly rating: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Date}
|
* @type {Date}
|
||||||
* @memberof RecipeOverview
|
* @memberof RecipeOverview
|
||||||
*/
|
*/
|
||||||
lastCooked?: Date;
|
readonly lastCooked: Date | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -136,11 +136,19 @@ export interface RecipeOverview {
|
|||||||
export function instanceOfRecipeOverview(value: object): boolean {
|
export function instanceOfRecipeOverview(value: object): boolean {
|
||||||
if (!('id' in value)) return false;
|
if (!('id' in value)) return false;
|
||||||
if (!('name' in value)) return false;
|
if (!('name' in value)) return false;
|
||||||
|
if (!('description' in value)) return false;
|
||||||
if (!('image' in value)) return false;
|
if (!('image' in value)) return false;
|
||||||
if (!('keywords' in value)) return false;
|
if (!('keywords' in value)) return false;
|
||||||
|
if (!('workingTime' in value)) return false;
|
||||||
|
if (!('waitingTime' in value)) return false;
|
||||||
if (!('createdBy' in value)) return false;
|
if (!('createdBy' in value)) return false;
|
||||||
if (!('createdAt' in value)) return false;
|
if (!('createdAt' in value)) return false;
|
||||||
if (!('updatedAt' in value)) return false;
|
if (!('updatedAt' in value)) return false;
|
||||||
|
if (!('internal' in value)) return false;
|
||||||
|
if (!('servings' in value)) return false;
|
||||||
|
if (!('servingsText' in value)) return false;
|
||||||
|
if (!('rating' in value)) return false;
|
||||||
|
if (!('lastCooked' in value)) return false;
|
||||||
if (!('_new' in value)) return false;
|
if (!('_new' in value)) return false;
|
||||||
if (!('recent' in value)) return false;
|
if (!('recent' in value)) return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -158,19 +166,19 @@ export function RecipeOverviewFromJSONTyped(json: any, ignoreDiscriminator: bool
|
|||||||
|
|
||||||
'id': json['id'],
|
'id': json['id'],
|
||||||
'name': json['name'],
|
'name': json['name'],
|
||||||
'description': json['description'] == null ? undefined : json['description'],
|
'description': json['description'],
|
||||||
'image': json['image'],
|
'image': json['image'],
|
||||||
'keywords': ((json['keywords'] as Array<any>).map(KeywordLabelFromJSON)),
|
'keywords': ((json['keywords'] as Array<any>).map(KeywordLabelFromJSON)),
|
||||||
'workingTime': json['working_time'] == null ? undefined : json['working_time'],
|
'workingTime': json['working_time'],
|
||||||
'waitingTime': json['waiting_time'] == null ? undefined : json['waiting_time'],
|
'waitingTime': json['waiting_time'],
|
||||||
'createdBy': json['created_by'],
|
'createdBy': json['created_by'],
|
||||||
'createdAt': (new Date(json['created_at'])),
|
'createdAt': (new Date(json['created_at'])),
|
||||||
'updatedAt': (new Date(json['updated_at'])),
|
'updatedAt': (new Date(json['updated_at'])),
|
||||||
'internal': json['internal'] == null ? undefined : json['internal'],
|
'internal': json['internal'],
|
||||||
'servings': json['servings'] == null ? undefined : json['servings'],
|
'servings': json['servings'],
|
||||||
'servingsText': json['servings_text'] == null ? undefined : json['servings_text'],
|
'servingsText': json['servings_text'],
|
||||||
'rating': json['rating'] == null ? undefined : json['rating'],
|
'rating': json['rating'],
|
||||||
'lastCooked': json['last_cooked'] == null ? undefined : (new Date(json['last_cooked'])),
|
'lastCooked': (json['last_cooked'] == null ? null : new Date(json['last_cooked'])),
|
||||||
'_new': json['new'],
|
'_new': json['new'],
|
||||||
'recent': json['recent'],
|
'recent': json['recent'],
|
||||||
};
|
};
|
||||||
@@ -182,16 +190,6 @@ export function RecipeOverviewToJSON(value?: RecipeOverview | null): any {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
||||||
'name': value['name'],
|
|
||||||
'description': value['description'],
|
|
||||||
'keywords': ((value['keywords'] as Array<any>).map(KeywordLabelToJSON)),
|
|
||||||
'working_time': value['workingTime'],
|
|
||||||
'waiting_time': value['waitingTime'],
|
|
||||||
'internal': value['internal'],
|
|
||||||
'servings': value['servings'],
|
|
||||||
'servings_text': value['servingsText'],
|
|
||||||
'rating': value['rating'],
|
|
||||||
'last_cooked': value['lastCooked'] == null ? undefined : ((value['lastCooked'] as any).toISOString()),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-card>
|
||||||
|
<v-card-text>
|
||||||
|
multiple food
|
||||||
|
<model-select model="Food" allow-create clearable></model-select>
|
||||||
|
single food
|
||||||
|
<model-select model="Food" :multiple="false" allow-create clearable></model-select>
|
||||||
|
multiple keyowrd
|
||||||
|
<model-select model="Keyword" allow-create clearable></model-select>
|
||||||
|
|
||||||
<model-select model="Food" allow-create clearable></model-select>
|
<v-autocomplete></v-autocomplete>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
|
||||||
|
|
||||||
<model-select model="Food" :multiple="false" allow-create clearable></model-select>
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import {defineComponent} from 'vue'
|
||||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||||
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "MealPlanPage",
|
name: "MealPlanPage",
|
||||||
components: {ModelSelect},
|
components: {ModelSelect},
|
||||||
data(){
|
data() {
|
||||||
return {
|
return {}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ export default defineComponent({
|
|||||||
|
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
api.apiKeywordList({page: 1, pageSize: 100}).then(r => {
|
api.apiKeywordList({page: 1, pageSize: 100}).then(r => {
|
||||||
this.keywords = r.results
|
if(r.results){
|
||||||
|
this.keywords = r.results
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
102
vue3/src/types/Models.ts
Normal file
102
vue3/src/types/Models.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import {ApiApi, Keyword as IKeyword, Food as IFood, RecipeOverview as IRecipeOverview, Recipe as IRecipe, Unit as IUnit} from "@/openapi";
|
||||||
|
|
||||||
|
export function getModelFromStr(model_name: String) {
|
||||||
|
switch (model_name.toLowerCase()) {
|
||||||
|
case 'food': {
|
||||||
|
return new Food
|
||||||
|
}
|
||||||
|
case 'unit': {
|
||||||
|
return new Unit
|
||||||
|
}
|
||||||
|
case 'keyword': {
|
||||||
|
return new Keyword
|
||||||
|
}
|
||||||
|
case 'recipe': {
|
||||||
|
return new Recipe
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw Error(`Invalid Model ${model_name}, did you forget to register it in Models.ts?`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class GenericModel<T> {
|
||||||
|
abstract list(query: string): Promise<Array<T>>
|
||||||
|
|
||||||
|
abstract create(name: string): Promise<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO this can probably be achieved by manipulating the client generation https://openapi-generator.tech/docs/templating/#models
|
||||||
|
export class Keyword extends GenericModel<IKeyword> {
|
||||||
|
create(name: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiKeywordCreate({keyword: {name: name} as IKeyword})
|
||||||
|
}
|
||||||
|
|
||||||
|
list(query: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiKeywordList({query: query}).then(r => {
|
||||||
|
if (r.results) {
|
||||||
|
return r.results
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Food extends GenericModel<IFood> {
|
||||||
|
create(name: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiFoodCreate({food: {name: name} as IFood})
|
||||||
|
}
|
||||||
|
|
||||||
|
list(query: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiFoodList({query: query}).then(r => {
|
||||||
|
if (r.results) {
|
||||||
|
return r.results
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Unit extends GenericModel<IUnit> {
|
||||||
|
create(name: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiUnitCreate({unit: {name: name} as IUnit})
|
||||||
|
}
|
||||||
|
|
||||||
|
list(query: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiUnitList({query: query}).then(r => {
|
||||||
|
if (r.results) {
|
||||||
|
return r.results
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Recipe extends GenericModel<IRecipeOverview> {
|
||||||
|
create(name: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiRecipeCreate({recipe: {name: name} as IRecipe}).then(r => {
|
||||||
|
return r as unknown as IRecipeOverview
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
list(query: string) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiRecipeList({query: query}).then(r => {
|
||||||
|
if (r.results) {
|
||||||
|
return r.results
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,11 @@ import {createVuetify} from 'vuetify'
|
|||||||
|
|
||||||
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
||||||
export default createVuetify({
|
export default createVuetify({
|
||||||
|
defaults: {
|
||||||
|
VCard: {
|
||||||
|
class: 'overflow-visible' // this is needed so that vue-multiselect options show above a card, vuetify uses overlay container to avoid this
|
||||||
|
}
|
||||||
|
},
|
||||||
theme: {
|
theme: {
|
||||||
defaultTheme: 'light',
|
defaultTheme: 'light',
|
||||||
themes: {
|
themes: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue","src/**/*.ts"],
|
||||||
"exclude": ["src/**/__tests__/*"],
|
"exclude": ["src/**/__tests__/*"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
|
|||||||
@@ -889,6 +889,11 @@ vue-demi@>=0.14.5, vue-demi@>=0.14.7:
|
|||||||
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.7.tgz#8317536b3ef74c5b09f268f7782e70194567d8f2"
|
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.7.tgz#8317536b3ef74c5b09f268f7782e70194567d8f2"
|
||||||
integrity sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==
|
integrity sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==
|
||||||
|
|
||||||
|
vue-multiselect@^3.0.0-beta.3:
|
||||||
|
version "3.0.0-beta.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-multiselect/-/vue-multiselect-3.0.0-beta.3.tgz#b1348238a84c435582c3f46f2a9c045b29bb976c"
|
||||||
|
integrity sha512-P7Fx+ovVF7WMERSZ0lw6N3p4H4bnQ3NcaY3ORjzFPv0r/6lpIqvFWmK9Xnwze9mgAvmNV1foI1VWrBmjnfBTLQ==
|
||||||
|
|
||||||
vue-router@4:
|
vue-router@4:
|
||||||
version "4.2.5"
|
version "4.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.5.tgz#b9e3e08f1bd9ea363fdd173032620bc50cf0e98a"
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.2.5.tgz#b9e3e08f1bd9ea363fdd173032620bc50cf0e98a"
|
||||||
|
|||||||
Reference in New Issue
Block a user