mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
Merge branch 'develop' of https://github.com/vabene1111/recipes into develop
# Conflicts: # cookbook/templates/base.html
This commit is contained in:
@@ -276,8 +276,9 @@ class UnitSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
|
|||||||
class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
obj, created = SupermarketCategory.objects.get_or_create(name=validated_data['name'],
|
validated_data['name'] = validated_data['name'].strip()
|
||||||
space=self.context['request'].space)
|
validated_data['space'] = self.context['request'].space
|
||||||
|
obj, created = SupermarketCategory.objects.get_or_create(**validated_data)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
@@ -285,7 +286,7 @@ class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerial
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SupermarketCategory
|
model = SupermarketCategory
|
||||||
fields = ('id', 'name')
|
fields = ('id', 'name', 'description')
|
||||||
|
|
||||||
|
|
||||||
class SupermarketCategoryRelationSerializer(WritableNestedModelSerializer):
|
class SupermarketCategoryRelationSerializer(WritableNestedModelSerializer):
|
||||||
@@ -301,7 +302,7 @@ class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Supermarket
|
model = Supermarket
|
||||||
fields = ('id', 'name', 'category_to_supermarket')
|
fields = ('id', 'name', 'description', 'category_to_supermarket')
|
||||||
|
|
||||||
|
|
||||||
class RecipeSimpleSerializer(serializers.ModelSerializer):
|
class RecipeSimpleSerializer(serializers.ModelSerializer):
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
.flip-enter-active[data-v-8633bda0]{-webkit-animation-name:bounceUp-data-v-8633bda0;animation-name:bounceUp-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite}.bounceleft[data-v-8633bda0]{-webkit-animation-name:bounceLeft-data-v-8633bda0;animation-name:bounceLeft-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}.bounceright[data-v-8633bda0]{-webkit-animation-name:bounceRight-data-v-8633bda0;animation-name:bounceRight-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}@-webkit-keyframes bounceUp-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateY(-7px)}}@keyframes bounceUp-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateY(-7px)}}@-webkit-keyframes bounceLeft-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(-10px)}}@keyframes bounceLeft-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(-10px)}}@-webkit-keyframes bounceRight-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(10px)}}@keyframes bounceRight-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(10px)}}.slide-fade-enter-active{transition:all .6s ease}.slide-fade-enter,.slide-fade-leave-to{transform:translateX(10px);opacity:0}
|
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.flip-enter-active[data-v-8633bda0]{-webkit-animation-name:bounceUp-data-v-8633bda0;animation-name:bounceUp-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite}.bounceleft[data-v-8633bda0]{-webkit-animation-name:bounceLeft-data-v-8633bda0;animation-name:bounceLeft-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}.bounceright[data-v-8633bda0]{-webkit-animation-name:bounceRight-data-v-8633bda0;animation-name:bounceRight-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}@-webkit-keyframes bounceUp-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateY(-7px)}}@keyframes bounceUp-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateY(-7px)}}@-webkit-keyframes bounceLeft-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(-10px)}}@keyframes bounceLeft-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(-10px)}}@-webkit-keyframes bounceRight-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(10px)}}@keyframes bounceRight-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(10px)}}.slide-fade-enter-active{transition:all .6s ease}.slide-fade-enter,.slide-fade-leave-to{transform:translateX(10px);opacity:0}
|
||||||
@@ -1 +1 @@
|
|||||||
.shake[data-v-54948e30]{-webkit-animation:shake-data-v-54948e30 .82s cubic-bezier(.36,.07,.19,.97) both;animation:shake-data-v-54948e30 .82s cubic-bezier(.36,.07,.19,.97) both;transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden;perspective:1000px}@-webkit-keyframes shake-data-v-54948e30{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}@keyframes shake-data-v-54948e30{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}
|
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.shake[data-v-94120e12]{-webkit-animation:shake-data-v-94120e12 .82s cubic-bezier(.36,.07,.19,.97) both;animation:shake-data-v-94120e12 .82s cubic-bezier(.36,.07,.19,.97) both;transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden;perspective:1000px}@-webkit-keyframes shake-data-v-94120e12{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}@keyframes shake-data-v-94120e12{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}
|
||||||
1
cookbook/static/vue/css/recipe_search_view.css
Normal file
1
cookbook/static/vue/css/recipe_search_view.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}
|
||||||
1
cookbook/static/vue/css/recipe_view.css
Normal file
1
cookbook/static/vue/css/recipe_view.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}
|
||||||
@@ -1 +1 @@
|
|||||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Vue App</title><link href="css/chunk-vendors.css" rel="preload" as="style"><link href="js/chunk-vendors.js" rel="preload" as="script"><link href="js/recipe_search_view.js" rel="preload" as="script"><link href="css/chunk-vendors.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="img/icons/favicon-16x16.png"><link rel="manifest" href="manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-title" content="Recipes"><link rel="apple-touch-icon" href="img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><div id="app"></div><script src="js/chunk-vendors.js"></script></body></html>
|
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Vue App</title><link href="css/chunk-vendors.css" rel="preload" as="style"><link href="css/recipe_search_view.css" rel="preload" as="style"><link href="js/chunk-vendors.js" rel="preload" as="script"><link href="js/recipe_search_view.js" rel="preload" as="script"><link href="css/chunk-vendors.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="img/icons/favicon-16x16.png"><link rel="manifest" href="manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-title" content="Recipes"><link rel="apple-touch-icon" href="img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><div id="app"></div><script src="js/chunk-vendors.js"></script></body></html>
|
||||||
@@ -1 +1 @@
|
|||||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Vue App</title><link href="css/chunk-vendors.css" rel="preload" as="style"><link href="js/chunk-vendors.js" rel="preload" as="script"><link href="js/recipe_view.js" rel="preload" as="script"><link href="css/chunk-vendors.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="img/icons/favicon-16x16.png"><link rel="manifest" href="manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-title" content="Recipes"><link rel="apple-touch-icon" href="img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><div id="app"></div><script src="js/chunk-vendors.js"></script></body></html>
|
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Vue App</title><link href="css/chunk-vendors.css" rel="preload" as="style"><link href="css/recipe_view.css" rel="preload" as="style"><link href="js/chunk-vendors.js" rel="preload" as="script"><link href="js/recipe_view.js" rel="preload" as="script"><link href="css/chunk-vendors.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="img/icons/favicon-16x16.png"><link rel="manifest" href="manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-title" content="Recipes"><link rel="apple-touch-icon" href="img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><div id="app"></div><script src="js/chunk-vendors.js"></script></body></html>
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
<form method="POST" class="post-form">{% csrf_token %}
|
<form method="POST" class="post-form">{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
<input type="submit" value="Submit" class="btn btn-success">
|
<input type="submit" value="Submit" class="btn btn-success">
|
||||||
|
<a href="{% url 'list_storage' %}"><button type="button" class="btn btn-primary">{% trans 'Manage External Storage' %}</button></a>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -25,6 +26,8 @@
|
|||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<a href="{% url 'data_sync_wait' %}" class="btn btn-warning">{% trans 'Sync Now!' %}</a>
|
<a href="{% url 'data_sync_wait' %}" class="btn btn-warning">{% trans 'Sync Now!' %}</a>
|
||||||
|
<a href="{% url 'list_recipe_import' %}" class="btn btn-info">{% trans 'Show Recipes' %}</a>
|
||||||
|
<a href="{% url 'list_sync_log' %}" class="btn btn-secondary">{% trans 'Show Log' %}</a>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
|
||||||
{% render_table monitored_paths %}
|
{% render_table monitored_paths %}
|
||||||
|
|||||||
@@ -8,6 +8,15 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
{% if request.resolver_match.url_name in 'list_storage,list_recipe_import,list_sync_log' %}
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'data_sync' %}">{% trans 'External Recipes' %}</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">{{ title }}</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<h3 style="margin-bottom: 2vh">{{ title }} {% trans 'List' %}
|
<h3 style="margin-bottom: 2vh">{{ title }} {% trans 'List' %}
|
||||||
{% if create_url %}
|
{% if create_url %}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from cookbook.helper import dal
|
|||||||
|
|
||||||
from .models import (Comment, Food, InviteLink, Keyword, MealPlan, Recipe,
|
from .models import (Comment, Food, InviteLink, Keyword, MealPlan, Recipe,
|
||||||
RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList,
|
RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList,
|
||||||
Storage, Sync, SyncLog, Unit, get_model_name)
|
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, get_model_name)
|
||||||
from .views import api, data, delete, edit, import_export, lists, new, views, telegram
|
from .views import api, data, delete, edit, import_export, lists, new, views, telegram
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
@@ -176,7 +176,7 @@ for m in generic_models:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
vue_models = [Food, Keyword, Unit]
|
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory]
|
||||||
for m in vue_models:
|
for m in vue_models:
|
||||||
py_name = get_model_name(m)
|
py_name = get_model_name(m)
|
||||||
url_name = py_name.replace('_', '-')
|
url_name = py_name.replace('_', '-')
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ class SupermarketCategoryRelationViewSet(viewsets.ModelViewSet, StandardFilterMi
|
|||||||
pagination_class = DefaultPagination
|
pagination_class = DefaultPagination
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.queryset = self.queryset.filter(supermarket__space=self.request.space)
|
self.queryset = self.queryset.filter(supermarket__space=self.request.space).order_by('order')
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,39 @@ def unit(request):
|
|||||||
"title": _("Units"),
|
"title": _("Units"),
|
||||||
"config": {
|
"config": {
|
||||||
'model': "UNIT", # *REQUIRED* name of the model in models.js
|
'model': "UNIT", # *REQUIRED* name of the model in models.js
|
||||||
'recipe_param': 'units' # *OPTIONAL* name of the listRecipes parameter if filtering on this attribute
|
'recipe_param': 'units', # *OPTIONAL* name of the listRecipes parameter if filtering on this attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@group_required('user')
|
||||||
|
def supermarket(request):
|
||||||
|
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||||
|
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'generic/model_template.html',
|
||||||
|
{
|
||||||
|
"title": _("Supermarkets"),
|
||||||
|
"config": {
|
||||||
|
'model': "SUPERMARKET", # *REQUIRED* name of the model in models.js
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@group_required('user')
|
||||||
|
def supermarket_category(request):
|
||||||
|
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||||
|
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'generic/model_template.html',
|
||||||
|
{
|
||||||
|
"title": _("Shopping Categories"),
|
||||||
|
"config": {
|
||||||
|
'model': "SHOPPING_CATEGORY", # *REQUIRED* name of the model in models.js
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
13
vue/package-lock.json
generated
13
vue/package-lock.json
generated
@@ -24,6 +24,7 @@
|
|||||||
"vue-multiselect": "^2.1.6",
|
"vue-multiselect": "^2.1.6",
|
||||||
"vue-property-decorator": "^9.1.2",
|
"vue-property-decorator": "^9.1.2",
|
||||||
"vue-template-compiler": "^2.6.14",
|
"vue-template-compiler": "^2.6.14",
|
||||||
|
"vue2-touch-events": "^3.2.2",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuex": "^3.6.0",
|
"vuex": "^3.6.0",
|
||||||
"workbox-webpack-plugin": "^6.1.5"
|
"workbox-webpack-plugin": "^6.1.5"
|
||||||
@@ -14312,6 +14313,11 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/vue2-touch-events": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue2-touch-events/-/vue2-touch-events-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-rGV8jxgOQEJYkJCp7uOBe3hjvmG1arThrq1wGtJHwJTgi65+P2a+0l4CYcQO/U1ZFqTq2/TT2+oTE6H7Y+6Eog=="
|
||||||
|
},
|
||||||
"node_modules/vuedraggable": {
|
"node_modules/vuedraggable": {
|
||||||
"version": "2.24.3",
|
"version": "2.24.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -17200,7 +17206,6 @@
|
|||||||
"version": "4.5.13",
|
"version": "4.5.13",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/core": "^7.11.0",
|
|
||||||
"@babel/helper-compilation-targets": "^7.9.6",
|
"@babel/helper-compilation-targets": "^7.9.6",
|
||||||
"@babel/helper-module-imports": "^7.8.3",
|
"@babel/helper-module-imports": "^7.8.3",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
@@ -17213,7 +17218,6 @@
|
|||||||
"@vue/babel-plugin-jsx": "^1.0.3",
|
"@vue/babel-plugin-jsx": "^1.0.3",
|
||||||
"@vue/babel-preset-jsx": "^1.2.4",
|
"@vue/babel-preset-jsx": "^1.2.4",
|
||||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||||
"core-js": "^3.6.5",
|
|
||||||
"core-js-compat": "^3.6.5",
|
"core-js-compat": "^3.6.5",
|
||||||
"semver": "^6.1.0"
|
"semver": "^6.1.0"
|
||||||
}
|
}
|
||||||
@@ -25155,6 +25159,11 @@
|
|||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"vue2-touch-events": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue2-touch-events/-/vue2-touch-events-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-rGV8jxgOQEJYkJCp7uOBe3hjvmG1arThrq1wGtJHwJTgi65+P2a+0l4CYcQO/U1ZFqTq2/TT2+oTE6H7Y+6Eog=="
|
||||||
|
},
|
||||||
"vuedraggable": {
|
"vuedraggable": {
|
||||||
"version": "2.24.3",
|
"version": "2.24.3",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app" style="margin-bottom: 4vh">
|
<div id="app" style="margin-bottom: 4vh" v-if="this_model">
|
||||||
<!-- v-if prevents component from loading before this_model has been assigned -->
|
|
||||||
<generic-modal-form v-if="this_model"
|
<generic-modal-form v-if="this_model"
|
||||||
:model="this_model"
|
:model="this_model"
|
||||||
:action="this_action"
|
:action="this_action"
|
||||||
@@ -8,30 +7,74 @@
|
|||||||
:item2="this_target"
|
:item2="this_target"
|
||||||
:show="show_modal"
|
:show="show_modal"
|
||||||
@finish-action="finishAction"/>
|
@finish-action="finishAction"/>
|
||||||
<generic-split-lists v-if="this_model"
|
|
||||||
:list_name="this_model.name"
|
<div class="row">
|
||||||
:right_counts="right_counts"
|
<div class="col-md-2 d-none d-md-block">
|
||||||
:left_counts="left_counts"
|
</div>
|
||||||
@reset="resetList"
|
<div class="col-xl-8 col-12">
|
||||||
@get-list="getItems"
|
<div class="container-fluid d-flex flex-column flex-grow-1">
|
||||||
@item-action="startAction">
|
<div class="row">
|
||||||
<template v-slot:cards-left>
|
<div class="col-md-6" style="margin-top: 1vh">
|
||||||
|
<h3>
|
||||||
|
<!-- <span><b-button variant="link" size="sm" class="text-dark shadow-none"><i class="fas fa-chevron-down"></i></b-button></span> -->
|
||||||
|
<model-menu/>
|
||||||
|
<span>{{this.this_model.name}}</span>
|
||||||
|
<span><b-button variant="link" size="lg" @click="startAction({'action':'new'})"><i class="fas fa-plus-circle"></i></b-button></span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3" />
|
||||||
|
<div class="col-md-3" style="position: relative; margin-top: 1vh">
|
||||||
|
<b-form-checkbox v-model="show_split" name="check-button" v-if="paginated"
|
||||||
|
class="shadow-none"
|
||||||
|
style="position:relative;top: 50%; transform: translateY(-50%);" switch>
|
||||||
|
{{ $t('show_split_screen') }}
|
||||||
|
</b-form-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" >
|
||||||
|
<div class="col" :class="{'col-md-6' : show_split}">
|
||||||
|
<!-- model isn't paginated and loads in one API call -->
|
||||||
|
<div v-if="!paginated">
|
||||||
|
<generic-horizontal-card v-for="i in items_left" v-bind:key="i.id"
|
||||||
|
:item=i
|
||||||
|
:model="this_model"
|
||||||
|
@item-action="startAction($event, 'left')"/>
|
||||||
|
</div>
|
||||||
|
<!-- model is paginated and needs managed -->
|
||||||
|
<generic-infinite-cards v-if="paginated"
|
||||||
|
:card_counts="left_counts"
|
||||||
|
:scroll="show_split"
|
||||||
|
@search="getItems($event, 'left')"
|
||||||
|
@reset="resetList('left')">
|
||||||
|
<template v-slot:cards>
|
||||||
<generic-horizontal-card
|
<generic-horizontal-card
|
||||||
v-for="i in items_left" v-bind:key="i.id"
|
v-for="i in items_left" v-bind:key="i.id"
|
||||||
:item=i
|
:item=i
|
||||||
:model="this_model"
|
:model="this_model"
|
||||||
:draggable="true"
|
|
||||||
@item-action="startAction($event, 'left')"/>
|
@item-action="startAction($event, 'left')"/>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:cards-right>
|
</generic-infinite-cards>
|
||||||
<generic-horizontal-card v-for="i in items_right" v-bind:key="i.id"
|
</div>
|
||||||
|
<div class="col col-md-6" v-if="show_split">
|
||||||
|
<generic-infinite-cards v-if="this_model"
|
||||||
|
:card_counts="right_counts"
|
||||||
|
:scroll="show_split"
|
||||||
|
@search="getItems($event, 'right')"
|
||||||
|
@reset="resetList('right')">
|
||||||
|
<template v-slot:cards>
|
||||||
|
<generic-horizontal-card
|
||||||
|
v-for="i in items_right" v-bind:key="i.id"
|
||||||
:item=i
|
:item=i
|
||||||
:model="this_model"
|
:model="this_model"
|
||||||
:draggable="true"
|
|
||||||
@item-action="startAction($event, 'right')"/>
|
@item-action="startAction($event, 'right')"/>
|
||||||
</template>
|
</template>
|
||||||
</generic-split-lists>
|
</generic-infinite-cards>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -46,9 +89,10 @@ import 'bootstrap-vue/dist/bootstrap-vue.css'
|
|||||||
import {CardMixin, ApiMixin} from "@/utils/utils";
|
import {CardMixin, ApiMixin} from "@/utils/utils";
|
||||||
import {StandardToasts, ToastMixin} from "@/utils/utils";
|
import {StandardToasts, ToastMixin} from "@/utils/utils";
|
||||||
|
|
||||||
import GenericSplitLists from "@/components/GenericSplitLists";
|
import GenericInfiniteCards from "@/components/GenericInfiniteCards";
|
||||||
import GenericHorizontalCard from "@/components/GenericHorizontalCard";
|
import GenericHorizontalCard from "@/components/GenericHorizontalCard";
|
||||||
import GenericModalForm from "@/components/Modals/GenericModalForm";
|
import GenericModalForm from "@/components/Modals/GenericModalForm";
|
||||||
|
import ModelMenu from "@/components/ModelMenu";
|
||||||
|
|
||||||
Vue.use(BootstrapVue)
|
Vue.use(BootstrapVue)
|
||||||
|
|
||||||
@@ -57,7 +101,7 @@ export default {
|
|||||||
// or i'm capturing it incorrectly
|
// or i'm capturing it incorrectly
|
||||||
name: 'ModelListView',
|
name: 'ModelListView',
|
||||||
mixins: [CardMixin, ApiMixin, ToastMixin],
|
mixins: [CardMixin, ApiMixin, ToastMixin],
|
||||||
components: {GenericHorizontalCard, GenericSplitLists, GenericModalForm},
|
components: {GenericHorizontalCard, GenericModalForm, GenericInfiniteCards, ModelMenu},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// this.Models and this.Actions inherited from ApiMixin
|
// this.Models and this.Actions inherited from ApiMixin
|
||||||
@@ -66,11 +110,14 @@ export default {
|
|||||||
right_counts: {'max': 9999, 'current': 0},
|
right_counts: {'max': 9999, 'current': 0},
|
||||||
left_counts: {'max': 9999, 'current': 0},
|
left_counts: {'max': 9999, 'current': 0},
|
||||||
this_model: undefined,
|
this_model: undefined,
|
||||||
|
model_menu: undefined,
|
||||||
this_action: undefined,
|
this_action: undefined,
|
||||||
this_recipe_param: undefined,
|
this_recipe_param: undefined,
|
||||||
this_item: {},
|
this_item: {},
|
||||||
this_target: {},
|
this_target: {},
|
||||||
show_modal: false
|
show_modal: false,
|
||||||
|
show_split: false,
|
||||||
|
paginated: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -78,6 +125,13 @@ export default {
|
|||||||
let model_config = JSON.parse(document.getElementById('model_config').textContent)
|
let model_config = JSON.parse(document.getElementById('model_config').textContent)
|
||||||
this.this_model = this.Models[model_config?.model]
|
this.this_model = this.Models[model_config?.model]
|
||||||
this.this_recipe_param = model_config?.recipe_param
|
this.this_recipe_param = model_config?.recipe_param
|
||||||
|
this.paginated = this.this_model?.paginated ?? false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.paginated) {
|
||||||
|
this.getItems()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// this.genericAPI inherited from ApiMixin
|
// this.genericAPI inherited from ApiMixin
|
||||||
@@ -165,13 +219,14 @@ export default {
|
|||||||
}
|
}
|
||||||
this.clearState()
|
this.clearState()
|
||||||
},
|
},
|
||||||
getItems: function (params) {
|
getItems: function (params, col) {
|
||||||
let column = params?.column ?? 'left'
|
let column = col || 'left'
|
||||||
this.genericAPI(this.this_model, this.Actions.LIST, params).then((result) => {
|
this.genericAPI(this.this_model, this.Actions.LIST, params).then((result) => {
|
||||||
if (result.data.results.length) {
|
let results = result.data?.results ?? result.data
|
||||||
this['items_' + column] = this['items_' + column].concat(result.data?.results)
|
if (results?.length) {
|
||||||
this[column + '_counts']['max'] = result.data.count
|
this['items_' + column] = this['items_' + column].concat(results)
|
||||||
this[column + '_counts']['current'] = this['items_' + column].length
|
this[column + '_counts']['max'] = result.data?.count ?? 0
|
||||||
|
this[column + '_counts']['current'] = this['items_' + column]?.length
|
||||||
} else {
|
} else {
|
||||||
this[column + '_counts']['max'] = 0
|
this[column + '_counts']['max'] = 0
|
||||||
this[column + '_counts']['current'] = 0
|
this[column + '_counts']['current'] = 0
|
||||||
|
|||||||
@@ -155,7 +155,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import {BootstrapVue} from 'bootstrap-vue'
|
import {BootstrapVue} from 'bootstrap-vue'
|
||||||
|
|
||||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||||
|
|
||||||
import {apiLoadRecipe} from "@/utils/api";
|
import {apiLoadRecipe} from "@/utils/api";
|
||||||
|
|||||||
@@ -1,34 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div row style="margin: 4px">
|
<div row style="margin: 4px">
|
||||||
<b-card no-body d-flex flex-column :class="{'border border-primary' : over, 'shake': isError}"
|
<b-card no-body d-flex flex-column :class="{'border border-primary' : over, 'shake': isError}"
|
||||||
style="height: 10vh;" :style="{'cursor:grab' : draggable}"
|
:style="{'cursor:grab' : useDrag}"
|
||||||
@dragover.prevent
|
@dragover.prevent
|
||||||
@dragenter.prevent
|
@dragenter.prevent
|
||||||
:draggable="draggable"
|
:draggable="useDrag"
|
||||||
@dragstart="handleDragStart($event)"
|
@dragstart="handleDragStart($event)"
|
||||||
@dragenter="handleDragEnter($event)"
|
@dragenter="handleDragEnter($event)"
|
||||||
@dragleave="handleDragLeave($event)"
|
@dragleave="handleDragLeave($event)"
|
||||||
@drop="handleDragDrop($event)">
|
@drop="handleDragDrop($event)">
|
||||||
<b-row no-gutters style="height:inherit;">
|
<b-row no-gutters >
|
||||||
<b-col no-gutters md="3" style="height:inherit;">
|
<b-col no-gutters class="col-sm-3">
|
||||||
<b-card-img-lazy style="object-fit: cover; height: 10vh;" :src="item_image" v-bind:alt="$t('Recipe_Image')"></b-card-img-lazy>
|
<b-card-img-lazy style="object-fit: cover; height: 6em;" :src="item_image" v-bind:alt="$t('Recipe_Image')"></b-card-img-lazy>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col no-gutters md="9" style="height:inherit;">
|
<b-col no-gutters class="col-sm-9">
|
||||||
<b-card-body class="m-0 py-0" style="height:inherit;">
|
<b-card-body class="m-0 py-0">
|
||||||
<b-card-text class=" h-100 my-0 d-flex flex-column" style="text-overflow: ellipsis">
|
<b-card-text class=" h-100 my-0 d-flex flex-column" style="text-overflow: ellipsis">
|
||||||
<h5 class="m-0 mt-1 text-truncate">{{ item[title] }}</h5>
|
<h5 class="m-0 mt-1 text-truncate">{{ item[title] }}</h5>
|
||||||
<div class= "m-0 text-truncate">{{ item[subtitle] }}</div>
|
<div class= "m-0 text-truncate">{{ item[subtitle] }}</div>
|
||||||
<div class="mt-auto mb-1 d-flex flex-row justify-content-end">
|
<!-- <span>{{this_item[itemTags.field]}}</span> -->
|
||||||
<div v-if="item[child_count]" class="mx-2 btn btn-link btn-sm"
|
<generic-pill v-for="x in itemTags" :key="x.field"
|
||||||
|
:item_list="item[x.field]"
|
||||||
|
:label="x.label"
|
||||||
|
:color="x.color"/>
|
||||||
|
<div class="mt-auto mb-1" align="right">
|
||||||
|
<span v-if="item[child_count]" class="mx-2 btn btn-link btn-sm"
|
||||||
style="z-index: 800;" v-on:click="$emit('item-action',{'action':'get-children','source':item})">
|
style="z-index: 800;" v-on:click="$emit('item-action',{'action':'get-children','source':item})">
|
||||||
<div v-if="!item.show_children">{{ item[child_count] }} {{ itemName }}</div>
|
<div v-if="!item.show_children">{{ item[child_count] }} {{ itemName }}</div>
|
||||||
<div v-else>{{ text.hide_children }}</div>
|
<div v-else>{{ text.hide_children }}</div>
|
||||||
</div>
|
</span>
|
||||||
<div v-if="item[recipe_count]" class="mx-2 btn btn-link btn-sm" style="z-index: 800;"
|
<span v-if="item[recipe_count]" class="mx-2 btn btn-link btn-sm" style="z-index: 800;"
|
||||||
v-on:click="$emit('item-action',{'action':'get-recipes','source':item})">
|
v-on:click="$emit('item-action',{'action':'get-recipes','source':item})">
|
||||||
<div v-if="!item.show_recipes">{{ item[recipe_count] }} {{$t('Recipes')}}</div>
|
<div v-if="!item.show_recipes">{{ item[recipe_count] }} {{$t('Recipes')}}</div>
|
||||||
<div v-else>{{$t('Hide_Recipes')}}</div>
|
<div v-else>{{$t('Hide_Recipes')}}</div>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</b-card-text>
|
</b-card-text>
|
||||||
</b-card-body>
|
</b-card-body>
|
||||||
@@ -45,24 +50,17 @@
|
|||||||
</b-card>
|
</b-card>
|
||||||
<!-- recursively add child cards -->
|
<!-- recursively add child cards -->
|
||||||
<div class="row" v-if="item.show_children">
|
<div class="row" v-if="item.show_children">
|
||||||
<div class="col-md-11 offset-md-1">
|
<div class="col-md-10 offset-md-2">
|
||||||
<generic-horizontal-card v-for="child in item[children]" v-bind:key="child.id"
|
<generic-horizontal-card v-for="child in item[children]" v-bind:key="child.id"
|
||||||
:draggable="draggable"
|
|
||||||
:item="child"
|
:item="child"
|
||||||
:model="model"
|
:model="model"
|
||||||
:title="title"
|
|
||||||
:subtitle="subtitle"
|
|
||||||
:child_count="child_count"
|
|
||||||
:children="children"
|
|
||||||
:recipe_count="recipe_count"
|
|
||||||
:recipes="recipes"
|
|
||||||
@item-action="$emit('item-action', $event)">
|
@item-action="$emit('item-action', $event)">
|
||||||
</generic-horizontal-card>
|
</generic-horizontal-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- conditionally view recipes -->
|
<!-- conditionally view recipes -->
|
||||||
<div class="row" v-if="item.show_recipes">
|
<div class="row" v-if="item.show_recipes">
|
||||||
<div class="col-md-11 offset-md-1">
|
<div class="col-md-10 offset-md-2">
|
||||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));grid-gap: 1rem;">
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));grid-gap: 1rem;">
|
||||||
<recipe-card v-for="r in item[recipes]"
|
<recipe-card v-for="r in item[recipes]"
|
||||||
v-bind:key="r.id"
|
v-bind:key="r.id"
|
||||||
@@ -91,19 +89,19 @@
|
|||||||
<script>
|
<script>
|
||||||
import GenericContextMenu from "@/components/GenericContextMenu";
|
import GenericContextMenu from "@/components/GenericContextMenu";
|
||||||
import Badges from "@/components/Badges";
|
import Badges from "@/components/Badges";
|
||||||
|
import GenericPill from "@/components/GenericPill";
|
||||||
import RecipeCard from "@/components/RecipeCard";
|
import RecipeCard from "@/components/RecipeCard";
|
||||||
import { mixin as clickaway } from 'vue-clickaway';
|
import { mixin as clickaway } from 'vue-clickaway';
|
||||||
import { createPopper } from '@popperjs/core';
|
import { createPopper } from '@popperjs/core';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "GenericHorizontalCard",
|
name: "GenericHorizontalCard",
|
||||||
components: { GenericContextMenu, RecipeCard, Badges},
|
components: { GenericContextMenu, RecipeCard, Badges, GenericPill},
|
||||||
mixins: [clickaway],
|
mixins: [clickaway],
|
||||||
props: {
|
props: {
|
||||||
item: {type: Object},
|
item: {type: Object},
|
||||||
model: {type: Object},
|
model: {type: Object},
|
||||||
draggable: {type: Boolean, default: false},
|
title: {type: String, default: 'name'}, // this and the following props need to be moved to model.js and made computed values
|
||||||
title: {type: String, default: 'name'}, // this and the following props can probably be moved to model.js and made computed values
|
|
||||||
subtitle: {type: String, default: 'description'},
|
subtitle: {type: String, default: 'description'},
|
||||||
child_count: {type: String, default: 'numchild'},
|
child_count: {type: String, default: 'numchild'},
|
||||||
children: {type: String, default: 'children'},
|
children: {type: String, default: 'children'},
|
||||||
@@ -134,10 +132,16 @@ export default {
|
|||||||
return this.model?.name ?? "You Forgot To Set Model Name in model.js"
|
return this.model?.name ?? "You Forgot To Set Model Name in model.js"
|
||||||
},
|
},
|
||||||
useMove: function() {
|
useMove: function() {
|
||||||
return this.model['move'] !== false
|
return (this.model?.['move'] ?? false) ? true : false
|
||||||
},
|
},
|
||||||
useMerge: function() {
|
useMerge: function() {
|
||||||
return this.model['merge'] !== false
|
return (this.model?.['merge'] ?? false) ? true : false
|
||||||
|
},
|
||||||
|
useDrag: function() {
|
||||||
|
return this.useMove || this.useMerge
|
||||||
|
},
|
||||||
|
itemTags: function() {
|
||||||
|
return this.model?.tags ?? []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
92
vue/src/components/GenericInfiniteCards.vue
Normal file
92
vue/src/components/GenericInfiniteCards.vue
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app" style="margin-bottom: 4vh">
|
||||||
|
<div class="row flex-shrink-0">
|
||||||
|
<div class="col col-md">
|
||||||
|
<!-- search box -->
|
||||||
|
<b-input-group class="mt-3">
|
||||||
|
<b-input class="form-control" type="search" v-model="search_left"
|
||||||
|
v-bind:placeholder="this.text.search"></b-input>
|
||||||
|
</b-input-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" :class="{'vh-100 mh-100 overflow-auto' : scroll}">
|
||||||
|
<div class="col col-md">
|
||||||
|
<slot name="cards"></slot>
|
||||||
|
<infinite-loading
|
||||||
|
:identifier='column'
|
||||||
|
@infinite="infiniteHandler"
|
||||||
|
spinner="waveDots">
|
||||||
|
<template v-slot:no-more><span/></template>
|
||||||
|
<template v-slot:no-results><span>{{$t('No_Results')}}</span></template>
|
||||||
|
</infinite-loading>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||||
|
import _debounce from 'lodash/debounce'
|
||||||
|
import InfiniteLoading from 'vue-infinite-loading';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GenericInfiniteCards',
|
||||||
|
components: {InfiniteLoading},
|
||||||
|
props: {
|
||||||
|
card_list: {type: Array, default(){return []}},
|
||||||
|
card_counts: {type: Object},
|
||||||
|
scroll: {type:Boolean, default: false}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
search: '',
|
||||||
|
page: 0,
|
||||||
|
state: undefined,
|
||||||
|
column: +new Date(),
|
||||||
|
text: {
|
||||||
|
'new': '',
|
||||||
|
'search': this.$t('Search')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
search: _debounce(function() {
|
||||||
|
this.page = 0
|
||||||
|
this.$emit('reset')
|
||||||
|
this.column += 1
|
||||||
|
}, 700),
|
||||||
|
|
||||||
|
card_counts: {
|
||||||
|
deep: true,
|
||||||
|
handler(newVal, oldVal) {
|
||||||
|
if (newVal.current > 0) {
|
||||||
|
this.state.loaded()
|
||||||
|
}
|
||||||
|
if (newVal.current >= newVal.max) {
|
||||||
|
this.state.complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
infiniteHandler: function($state, col) {
|
||||||
|
let params = {
|
||||||
|
'query': this.search,
|
||||||
|
'page': this.page + 1
|
||||||
|
}
|
||||||
|
this.state = $state
|
||||||
|
this.$emit('search', params)
|
||||||
|
this.page+= 1
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
44
vue/src/components/GenericPill.vue
Normal file
44
vue/src/components/GenericPill.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="itemList">
|
||||||
|
<span :key="k.id" v-for="k in itemList" class="pl-1">
|
||||||
|
<b-badge pill :variant="color">{{thisLabel(k)}}</b-badge>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GenericPill',
|
||||||
|
props: {
|
||||||
|
item_list: {required: true, type: Array},
|
||||||
|
label: {type: String, default: 'name'},
|
||||||
|
color: {type: String, default: 'light'}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
itemList: function() {
|
||||||
|
if(Array.isArray(this.item_list)) {
|
||||||
|
return this.item_list
|
||||||
|
} else if (!this.item_list?.id) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return [this.item_list]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
thisLabel: function (item) {
|
||||||
|
let fields = this.label.split('::')
|
||||||
|
let value = item
|
||||||
|
fields.forEach(x => {
|
||||||
|
value = value[x]
|
||||||
|
});
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="app" style="margin-bottom: 4vh">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-2 d-none d-md-block">
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-8 col-12">
|
|
||||||
<div class="container-fluid d-flex flex-column flex-grow-1" :class="{'vh-100' : show_split}">
|
|
||||||
<!-- expanded options box -->
|
|
||||||
<div class="row flex-shrink-0">
|
|
||||||
<div class="col col-md-12">
|
|
||||||
<b-collapse id="collapse_advanced" class="mt-2" v-model="advanced_visible">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3" style="margin-top: 1vh">
|
|
||||||
<div class="btn btn-primary btn-block text-uppercase" @click="$emit('item-action', {'action':'new'})">
|
|
||||||
{{ this.text.new }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-3" style="margin-top: 1vh">
|
|
||||||
<button class="btn btn-primary btn-block text-uppercase" @click="resetSearch">
|
|
||||||
{{ this.text.reset }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3" style="position: relative; margin-top: 1vh">
|
|
||||||
<b-form-checkbox v-model="show_split" name="check-button"
|
|
||||||
class="shadow-none"
|
|
||||||
style="position:relative;top: 50%; transform: translateY(-50%);" switch>
|
|
||||||
{{ this.text.split }}
|
|
||||||
</b-form-checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</b-collapse>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row flex-shrink-0">
|
|
||||||
<!-- search box -->
|
|
||||||
<div class="col col-md">
|
|
||||||
<b-input-group class="mt-3">
|
|
||||||
<b-input class="form-control" v-model="search_left"
|
|
||||||
v-bind:placeholder="this.text.search"></b-input>
|
|
||||||
<b-input-group-append>
|
|
||||||
<b-button v-b-toggle.collapse_advanced variant="primary" class="shadow-none">
|
|
||||||
<i class="fas fa-caret-down" v-if="!advanced_visible"></i>
|
|
||||||
<i class="fas fa-caret-up" v-if="advanced_visible"></i>
|
|
||||||
</b-button>
|
|
||||||
</b-input-group-append>
|
|
||||||
</b-input-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- split side search -->
|
|
||||||
<div class="col col-md" v-if="show_split">
|
|
||||||
<b-input-group class="mt-3">
|
|
||||||
<b-input class="form-control" v-model="search_right"
|
|
||||||
v-bind:placeholder="this.text.search"></b-input>
|
|
||||||
</b-input-group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- only show scollbars in split mode -->
|
|
||||||
<!-- TODO: weird behavior when switching to split mode, infinite scoll doesn't trigger if
|
|
||||||
bottom of page is in viewport can trigger by scrolling page (not column) up -->
|
|
||||||
<div class="row" :class="{'overflow-hidden' : show_split}">
|
|
||||||
<div class="col col-md" :class="{'mh-100 overflow-auto' : show_split}">
|
|
||||||
<slot name="cards-left"></slot>
|
|
||||||
<infinite-loading
|
|
||||||
:identifier='left'
|
|
||||||
@infinite="infiniteHandler($event, 'left')"
|
|
||||||
spinner="waveDots">
|
|
||||||
<template v-slot:no-more><span/></template>
|
|
||||||
<template v-slot:no-results><span>{{$t('No_Results')}}</span></template>
|
|
||||||
</infinite-loading>
|
|
||||||
</div>
|
|
||||||
<!-- right side cards -->
|
|
||||||
<div class="col col-md mh-100 overflow-auto" v-if="show_split">
|
|
||||||
<slot name="cards-right"></slot>
|
|
||||||
<infinite-loading
|
|
||||||
:identifier='right'
|
|
||||||
@infinite="infiniteHandler($event, 'right')"
|
|
||||||
spinner="waveDots">
|
|
||||||
<template v-slot:no-more><span/></template>
|
|
||||||
<template v-slot:no-results><span>{{$t('No_Results')}}</span></template>
|
|
||||||
</infinite-loading>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-2 d-none d-md-block">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
|
||||||
import _debounce from 'lodash/debounce'
|
|
||||||
import InfiniteLoading from 'vue-infinite-loading';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
// TODO: this should be simplified into a Generic Infinitely Scrolling List and added as two components when split lists desired
|
|
||||||
name: 'GenericSplitLists',
|
|
||||||
components: {InfiniteLoading},
|
|
||||||
props: {
|
|
||||||
list_name: {type: String, default: 'Blank List'}, // TODO update translations to handle plural translations
|
|
||||||
left_list: {type: Array, default(){return []}},
|
|
||||||
left_counts: {type: Object},
|
|
||||||
right_list: {type:Array, default(){return []}},
|
|
||||||
right_counts: {type: Object},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
advanced_visible: false,
|
|
||||||
show_split: false,
|
|
||||||
search_right: '',
|
|
||||||
search_left: '',
|
|
||||||
right_page: 0,
|
|
||||||
left_page: 0,
|
|
||||||
right_state: undefined,
|
|
||||||
left_state: undefined,
|
|
||||||
right_dirty: false,
|
|
||||||
left_dirty: false,
|
|
||||||
right: +new Date(),
|
|
||||||
left: +new Date(),
|
|
||||||
text: {
|
|
||||||
'new': '',
|
|
||||||
'name': '',
|
|
||||||
'reset': this.$t('Reset_Search'),
|
|
||||||
'split': this.$t('show_split_screen'),
|
|
||||||
'search': this.$t('Search')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.dragMenu = this.$refs.tooltip
|
|
||||||
this.text.new = this.$t('New_' + this.list_name)
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
search_left: _debounce(function() {
|
|
||||||
if (this.left_dirty) {
|
|
||||||
//prevents running twice if search is reset
|
|
||||||
this.left_dirty = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.left_page = 0
|
|
||||||
this.$emit('reset', {'column':'left'})
|
|
||||||
this.left += 1
|
|
||||||
}, 700),
|
|
||||||
search_right: _debounce(function(newVal, oldVal) {
|
|
||||||
if (this.right_dirty) {
|
|
||||||
//prevents running twice if search is reset
|
|
||||||
this.right_dirty = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.right_page = 0
|
|
||||||
this.$emit('reset', {'column':'right'})
|
|
||||||
this.right += 1
|
|
||||||
}, 700),
|
|
||||||
right_counts: {
|
|
||||||
deep: true,
|
|
||||||
handler(newVal, oldVal) {
|
|
||||||
if (newVal.current > 0) {
|
|
||||||
this.right_state.loaded()
|
|
||||||
}
|
|
||||||
if (newVal.current >= newVal.max) {
|
|
||||||
this.right_state.complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
left_counts: {
|
|
||||||
deep: true,
|
|
||||||
handler(newVal, oldVal) {
|
|
||||||
if (newVal.current > 0) {
|
|
||||||
this.left_state.loaded()
|
|
||||||
}
|
|
||||||
if (newVal.current >= newVal.max) {
|
|
||||||
this.left_state.complete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
resetSearch: function () {
|
|
||||||
this.right_dirty = true
|
|
||||||
this.search_right = ''
|
|
||||||
this.right_page = 0
|
|
||||||
this.right += 1
|
|
||||||
this.$emit('reset', {'column':'right'})
|
|
||||||
|
|
||||||
this.left_dirty = true
|
|
||||||
this.search_left = ''
|
|
||||||
this.left_page = 0
|
|
||||||
this.left += 1
|
|
||||||
this.$emit('reset', {'column':'left'})
|
|
||||||
},
|
|
||||||
infiniteHandler: function($state, col) {
|
|
||||||
let params = {
|
|
||||||
'query': (col==='left') ? this.search_left : this.search_right,
|
|
||||||
'page': (col==='left') ? this.left_page + 1 : this.right_page + 1,
|
|
||||||
'column': col
|
|
||||||
}
|
|
||||||
this[col+'_state'] = $state
|
|
||||||
this.$emit('get-list', params)
|
|
||||||
this[col+'_page'] += 1
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -26,8 +26,11 @@
|
|||||||
</td>
|
</td>
|
||||||
<td v-if="detailed">
|
<td v-if="detailed">
|
||||||
<div v-if="ingredient.note">
|
<div v-if="ingredient.note">
|
||||||
<span v-b-popover.hover="ingredient.note"
|
<span v-b-popover.hover="ingredient.note" v-if="ingredient.note.length > 15"
|
||||||
class="d-print-none"> <i class="far fa-comment"></i>
|
class="d-print-none touchable"> <i class="far fa-comment"></i>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ ingredient.note }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="d-none d-print-block">
|
<div class="d-none d-print-block">
|
||||||
@@ -72,3 +75,13 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* increase size of hover/touchable space without changing spacing */
|
||||||
|
.touchable {
|
||||||
|
padding-right: 2em;
|
||||||
|
padding-left: 2em;
|
||||||
|
margin-right: -2em;
|
||||||
|
margin-left: -2em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
61
vue/src/components/ModelMenu.vue
Normal file
61
vue/src/components/ModelMenu.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<!-- <b-button variant="link" size="sm" class="text-dark shadow-none"><i class="fas fa-chevron-down"></i></b-button> -->
|
||||||
|
<span>
|
||||||
|
<b-dropdown variant="link" toggle-class="text-decoration-none text-dark shadow-none" no-caret style="boundary:window">
|
||||||
|
<template #button-content>
|
||||||
|
<i class="fas fa-chevron-down">
|
||||||
|
</template>
|
||||||
|
<b-dropdown-item :href="resolveDjangoUrl('list_food')">
|
||||||
|
<i class="fas fa-leaf fa-fw"></i> {{ Models['FOOD'].name }}
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
<b-dropdown-item :href="resolveDjangoUrl('list_keyword')">
|
||||||
|
<i class="fas fa-tags fa-fw"></i> {{ Models['KEYWORD'].name }}
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
<b-dropdown-item :href="resolveDjangoUrl('list_unit')">
|
||||||
|
<i class="fas fa-balance-scale fa-fw"></i> {{ Models['UNIT'].name }}
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
<b-dropdown-item :href="resolveDjangoUrl('list_supermarket')">
|
||||||
|
<i class="fas fa-store-alt fa-fw"></i> {{ Models['SUPERMARKET'].name }}
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
<b-dropdown-item :href="resolveDjangoUrl('list_supermarket_category')">
|
||||||
|
<i class="fas fa-cubes fa-fw"></i> {{ Models['SHOPPING_CATEGORY'].name }}
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
</b-dropdown>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import Vue from 'vue'
|
||||||
|
import {BootstrapVue} from 'bootstrap-vue'
|
||||||
|
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||||
|
|
||||||
|
import {Models} from "@/utils/models";
|
||||||
|
import {ResolveUrlMixin} from "@/utils/utils";
|
||||||
|
|
||||||
|
|
||||||
|
Vue.use(BootstrapVue)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModelMenu',
|
||||||
|
mixins: [ResolveUrlMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
Models: Models
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
gotoURL: function(model) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"all_fields_optional": "All fields are optional and can be left empty.",
|
"all_fields_optional": "All fields are optional and can be left empty.",
|
||||||
"convert_internal": "Convert to internal recipe",
|
"convert_internal": "Convert to internal recipe",
|
||||||
"show_only_internal": "Show only internal recipes",
|
"show_only_internal": "Show only internal recipes",
|
||||||
"show_split_screen": "Show split view",
|
"show_split_screen": "Split View",
|
||||||
|
|
||||||
"Log_Recipe_Cooking": "Log Recipe Cooking",
|
"Log_Recipe_Cooking": "Log Recipe Cooking",
|
||||||
"External_Recipe_Image": "External Recipe Image",
|
"External_Recipe_Image": "External Recipe Image",
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
"Edit_Recipe": "Edit Recipe",
|
"Edit_Recipe": "Edit Recipe",
|
||||||
"Move_Keyword": "Move Keyword",
|
"Move_Keyword": "Move Keyword",
|
||||||
"Merge_Keyword": "Merge Keyword",
|
"Merge_Keyword": "Merge Keyword",
|
||||||
"Hide_Keywords": "Hide Keywords",
|
"Hide_Keywords": "Hide Keyword",
|
||||||
"Hide_Recipes": "Hide Recipes",
|
"Hide_Recipes": "Hide Recipes",
|
||||||
"Move_Up": "Move up",
|
"Move_Up": "Move up",
|
||||||
"Move_Down": "Move down",
|
"Move_Down": "Move down",
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
"Parent": "Ouder",
|
"Parent": "Ouder",
|
||||||
"move_confirmation": "Verplaats {child} naar ouder {parent}",
|
"move_confirmation": "Verplaats {child} naar ouder {parent}",
|
||||||
"merge_confirmation": "Vervang {source} with {target}",
|
"merge_confirmation": "Vervang {source} with {target}",
|
||||||
"move_selection": "Selecteer een ouder om {child} naar te verplaatsen.",
|
"move_selection": "Selecteer een ouder {type} om {source} naar te verplaatsen.",
|
||||||
"merge_selection": "Vervang alle voorvallen van {source} door het type {type}.",
|
"merge_selection": "Vervang alle voorvallen van {source} door het type {type}.",
|
||||||
"Root": "Bron",
|
"Root": "Bron",
|
||||||
"show_split_screen": "Toon gesplitste weergave",
|
"show_split_screen": "Toon gesplitste weergave",
|
||||||
@@ -97,5 +97,35 @@
|
|||||||
"Advanced Search Settings": "Geavanceerde zoekinstellingen",
|
"Advanced Search Settings": "Geavanceerde zoekinstellingen",
|
||||||
"Merge": "Voeg samen",
|
"Merge": "Voeg samen",
|
||||||
"delete_confimation": "Weet je zeker dat je {kw} en zijn kinderen wil verwijderen?",
|
"delete_confimation": "Weet je zeker dat je {kw} en zijn kinderen wil verwijderen?",
|
||||||
"Merge_Keyword": "Voeg Etiket samen"
|
"Merge_Keyword": "Voeg Etiket samen",
|
||||||
|
"step_time_minutes": "Stap duur in minuten",
|
||||||
|
"confirm_delete": "Weet je zeker dat je dit {object} wil verwijderen?",
|
||||||
|
"Show_as_header": "Toon als koptekst",
|
||||||
|
"Hide_as_header": "Verberg als koptekst",
|
||||||
|
"Copy_template_reference": "Kopieer sjabloon verwijzing",
|
||||||
|
"Save_and_View": "Sla op & Bekijk",
|
||||||
|
"Edit_Recipe": "Bewerk Recept",
|
||||||
|
"Move_Up": "Verplaats omhoog",
|
||||||
|
"Move_Down": "Verplaats omlaag",
|
||||||
|
"Step_Name": "Stap Naam",
|
||||||
|
"Step_Type": "Stap Type",
|
||||||
|
"Make_Header": "Maak_Koptekst",
|
||||||
|
"Make_Ingredient": "Maak_Ingrediënt",
|
||||||
|
"Enable_Amount": "Schakel Hoeveelheid in",
|
||||||
|
"Disable_Amount": "Schakel Hoeveelheid uit",
|
||||||
|
"Add_Step": "Voeg Stap toe",
|
||||||
|
"Note": "Notitie",
|
||||||
|
"delete_confirmation": "Weet je zeker dat je {source} wil verwijderen?",
|
||||||
|
"Ignore_Shopping": "Negeer Boodschappen",
|
||||||
|
"Shopping_Category": "Boodschappen Categorie",
|
||||||
|
"Edit_Food": "Bewerk Eten",
|
||||||
|
"Move_Food": "Verplaats Eten",
|
||||||
|
"New_Food": "Nieuw Eten",
|
||||||
|
"Hide_Food": "Verberg Eten",
|
||||||
|
"Delete_Food": "Verwijder Eten",
|
||||||
|
"No_ID": "ID niet gevonden, verwijderen niet mogelijk.",
|
||||||
|
"Meal_Plan_Days": "Toekomstige maaltijdplannen",
|
||||||
|
"merge_title": "Voeg {type} samen",
|
||||||
|
"move_title": "Verplaats {type}",
|
||||||
|
"Food": "Eten"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,13 @@ export class Models {
|
|||||||
'name': i18n.t('Food'), // *OPTIONAL* : parameters will be built model -> model_type -> default
|
'name': i18n.t('Food'), // *OPTIONAL* : parameters will be built model -> model_type -> default
|
||||||
'apiName': 'Food', // *REQUIRED* : the name that is used in api.ts for this model
|
'apiName': 'Food', // *REQUIRED* : the name that is used in api.ts for this model
|
||||||
'model_type': this.TREE, // *OPTIONAL* : model specific params for api, if not present will attempt modeltype_create then default_create
|
'model_type': this.TREE, // *OPTIONAL* : model specific params for api, if not present will attempt modeltype_create then default_create
|
||||||
|
'paginated': true,
|
||||||
|
'move': true,
|
||||||
|
'merge': true,
|
||||||
'badges': {
|
'badges': {
|
||||||
'linked_recipe': true
|
'linked_recipe': true,
|
||||||
},
|
},
|
||||||
|
'tags': [{'field': 'supermarket_category', 'label': 'name', 'color': 'info'}],
|
||||||
// REQUIRED: unordered array of fields that can be set during create
|
// REQUIRED: unordered array of fields that can be set during create
|
||||||
'create': {
|
'create': {
|
||||||
// if not defined partialUpdate will use the same parameters, prepending 'id'
|
// if not defined partialUpdate will use the same parameters, prepending 'id'
|
||||||
@@ -113,6 +117,9 @@ export class Models {
|
|||||||
'name': i18n.t('Keyword'), // *OPTIONAL: parameters will be built model -> model_type -> default
|
'name': i18n.t('Keyword'), // *OPTIONAL: parameters will be built model -> model_type -> default
|
||||||
'apiName': 'Keyword',
|
'apiName': 'Keyword',
|
||||||
'model_type': this.TREE,
|
'model_type': this.TREE,
|
||||||
|
'paginated': true,
|
||||||
|
'move': true,
|
||||||
|
'merge': true,
|
||||||
'badges': {
|
'badges': {
|
||||||
'icon': true
|
'icon': true
|
||||||
},
|
},
|
||||||
@@ -146,6 +153,7 @@ export class Models {
|
|||||||
static UNIT = {
|
static UNIT = {
|
||||||
'name': i18n.t('Unit'),
|
'name': i18n.t('Unit'),
|
||||||
'apiName': 'Unit',
|
'apiName': 'Unit',
|
||||||
|
'paginated': true,
|
||||||
'create': {
|
'create': {
|
||||||
'params': [['name', 'description']],
|
'params': [['name', 'description']],
|
||||||
'form': {
|
'form': {
|
||||||
@@ -165,7 +173,7 @@ export class Models {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'move': false
|
'merge': true
|
||||||
}
|
}
|
||||||
static SHOPPING_LIST = {}
|
static SHOPPING_LIST = {}
|
||||||
static RECIPE_BOOK = {
|
static RECIPE_BOOK = {
|
||||||
@@ -220,6 +228,53 @@ export class Models {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
static SHOPPING_CATEGORY_RELATION = {
|
||||||
|
'name': i18n.t('Shopping_Category'),
|
||||||
|
'apiName': 'SupermarketCategory',
|
||||||
|
'create': {
|
||||||
|
'params': [['category', 'supermarket', 'order']],
|
||||||
|
'form': {
|
||||||
|
'name': {
|
||||||
|
'form_field': true,
|
||||||
|
'type': 'text',
|
||||||
|
'field': 'name',
|
||||||
|
'label': i18n.t('Name'),
|
||||||
|
'placeholder': ''
|
||||||
|
},
|
||||||
|
'description': {
|
||||||
|
'form_field': true,
|
||||||
|
'type': 'text',
|
||||||
|
'field': 'description',
|
||||||
|
'label': i18n.t('Description'),
|
||||||
|
'placeholder': ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
static SUPERMARKET = {
|
||||||
|
'name': i18n.t('Supermarket'),
|
||||||
|
'apiName': 'Supermarket',
|
||||||
|
'tags': [{'field': 'category_to_supermarket', 'label': 'category::name', 'color': 'info'}],
|
||||||
|
'create': {
|
||||||
|
'params': [['name', 'description', 'category_to_supermarket']],
|
||||||
|
'form': {
|
||||||
|
'name': {
|
||||||
|
'form_field': true,
|
||||||
|
'type': 'text',
|
||||||
|
'field': 'name',
|
||||||
|
'label': i18n.t('Name'),
|
||||||
|
'placeholder': ''
|
||||||
|
},
|
||||||
|
'description': {
|
||||||
|
'form_field': true,
|
||||||
|
'type': 'text',
|
||||||
|
'field': 'description',
|
||||||
|
'label': i18n.t('Description'),
|
||||||
|
'placeholder': ''
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
static RECIPE = {
|
static RECIPE = {
|
||||||
'name': i18n.t('Recipe'),
|
'name': i18n.t('Recipe'),
|
||||||
|
|||||||
@@ -3,23 +3,23 @@
|
|||||||
"assets": {
|
"assets": {
|
||||||
"../../templates/sw.js": {
|
"../../templates/sw.js": {
|
||||||
"name": "../../templates/sw.js",
|
"name": "../../templates/sw.js",
|
||||||
"path": "..\\..\\templates\\sw.js"
|
"path": "../../templates/sw.js"
|
||||||
},
|
},
|
||||||
"css/chunk-vendors.css": {
|
"css/chunk-vendors.css": {
|
||||||
"name": "css/chunk-vendors.css",
|
"name": "css/chunk-vendors.css",
|
||||||
"path": "css\\chunk-vendors.css"
|
"path": "css/chunk-vendors.css"
|
||||||
},
|
},
|
||||||
"js/chunk-vendors.js": {
|
"js/chunk-vendors.js": {
|
||||||
"name": "js/chunk-vendors.js",
|
"name": "js/chunk-vendors.js",
|
||||||
"path": "js\\chunk-vendors.js"
|
"path": "js/chunk-vendors.js"
|
||||||
},
|
},
|
||||||
"css/cookbook_view.css": {
|
"css/cookbook_view.css": {
|
||||||
"name": "css/cookbook_view.css",
|
"name": "css/cookbook_view.css",
|
||||||
"path": "css\\cookbook_view.css"
|
"path": "css/cookbook_view.css"
|
||||||
},
|
},
|
||||||
"js/cookbook_view.js": {
|
"js/cookbook_view.js": {
|
||||||
"name": "js/cookbook_view.js",
|
"name": "js/cookbook_view.js",
|
||||||
"path": "js\\cookbook_view.js"
|
"path": "js/cookbook_view.js"
|
||||||
},
|
},
|
||||||
"css/edit_internal_recipe.css": {
|
"css/edit_internal_recipe.css": {
|
||||||
"name": "css/edit_internal_recipe.css",
|
"name": "css/edit_internal_recipe.css",
|
||||||
@@ -31,35 +31,43 @@
|
|||||||
},
|
},
|
||||||
"js/import_response_view.js": {
|
"js/import_response_view.js": {
|
||||||
"name": "js/import_response_view.js",
|
"name": "js/import_response_view.js",
|
||||||
"path": "js\\import_response_view.js"
|
"path": "js/import_response_view.js"
|
||||||
},
|
},
|
||||||
"css/model_list_view.css": {
|
"css/model_list_view.css": {
|
||||||
"name": "css/model_list_view.css",
|
"name": "css/model_list_view.css",
|
||||||
"path": "css\\model_list_view.css"
|
"path": "css/model_list_view.css"
|
||||||
},
|
},
|
||||||
"js/model_list_view.js": {
|
"js/model_list_view.js": {
|
||||||
"name": "js/model_list_view.js",
|
"name": "js/model_list_view.js",
|
||||||
"path": "js\\model_list_view.js"
|
"path": "js/model_list_view.js"
|
||||||
},
|
},
|
||||||
"js/offline_view.js": {
|
"js/offline_view.js": {
|
||||||
"name": "js/offline_view.js",
|
"name": "js/offline_view.js",
|
||||||
"path": "js\\offline_view.js"
|
"path": "js/offline_view.js"
|
||||||
|
},
|
||||||
|
"css/recipe_search_view.css": {
|
||||||
|
"name": "css/recipe_search_view.css",
|
||||||
|
"path": "css/recipe_search_view.css"
|
||||||
},
|
},
|
||||||
"js/recipe_search_view.js": {
|
"js/recipe_search_view.js": {
|
||||||
"name": "js/recipe_search_view.js",
|
"name": "js/recipe_search_view.js",
|
||||||
"path": "js\\recipe_search_view.js"
|
"path": "js/recipe_search_view.js"
|
||||||
|
},
|
||||||
|
"css/recipe_view.css": {
|
||||||
|
"name": "css/recipe_view.css",
|
||||||
|
"path": "css/recipe_view.css"
|
||||||
},
|
},
|
||||||
"js/recipe_view.js": {
|
"js/recipe_view.js": {
|
||||||
"name": "js/recipe_view.js",
|
"name": "js/recipe_view.js",
|
||||||
"path": "js\\recipe_view.js"
|
"path": "js/recipe_view.js"
|
||||||
},
|
},
|
||||||
"js/supermarket_view.js": {
|
"js/supermarket_view.js": {
|
||||||
"name": "js/supermarket_view.js",
|
"name": "js/supermarket_view.js",
|
||||||
"path": "js\\supermarket_view.js"
|
"path": "js/supermarket_view.js"
|
||||||
},
|
},
|
||||||
"js/user_file_view.js": {
|
"js/user_file_view.js": {
|
||||||
"name": "js/user_file_view.js",
|
"name": "js/user_file_view.js",
|
||||||
"path": "js\\user_file_view.js"
|
"path": "js/user_file_view.js"
|
||||||
},
|
},
|
||||||
"recipe_search_view.html": {
|
"recipe_search_view.html": {
|
||||||
"name": "recipe_search_view.html",
|
"name": "recipe_search_view.html",
|
||||||
@@ -106,11 +114,13 @@
|
|||||||
"recipe_search_view": [
|
"recipe_search_view": [
|
||||||
"css/chunk-vendors.css",
|
"css/chunk-vendors.css",
|
||||||
"js/chunk-vendors.js",
|
"js/chunk-vendors.js",
|
||||||
|
"css/recipe_search_view.css",
|
||||||
"js/recipe_search_view.js"
|
"js/recipe_search_view.js"
|
||||||
],
|
],
|
||||||
"recipe_view": [
|
"recipe_view": [
|
||||||
"css/chunk-vendors.css",
|
"css/chunk-vendors.css",
|
||||||
"js/chunk-vendors.js",
|
"js/chunk-vendors.js",
|
||||||
|
"css/recipe_view.css",
|
||||||
"js/recipe_view.js"
|
"js/recipe_view.js"
|
||||||
],
|
],
|
||||||
"offline_view": [
|
"offline_view": [
|
||||||
|
|||||||
Reference in New Issue
Block a user