recipe stuff
This commit is contained in:
parent
816ced83b5
commit
24ed6a1cd2
1
boot.sh
1
boot.sh
@ -4,6 +4,7 @@ source venv/bin/activate
|
|||||||
echo "Updating database"
|
echo "Updating database"
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
python manage.py collectstatic --noinput
|
python manage.py collectstatic --noinput
|
||||||
|
python manage.py collectstatic_js_reverse
|
||||||
echo "Done"
|
echo "Done"
|
||||||
|
|
||||||
chmod -R 755 /opt/recipes/mediafiles
|
chmod -R 755 /opt/recipes/mediafiles
|
||||||
|
18
cookbook/templates/javascript_urls.html
Normal file
18
cookbook/templates/javascript_urls.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!--
|
||||||
|
As there is apparently no good way to pass django named URLs to Vue/Webpack we will pack the urls we need into
|
||||||
|
this object and load it in all the templates where we load Vue apps
|
||||||
|
|
||||||
|
Reason for not using other alternatives
|
||||||
|
|
||||||
|
## django-js-reverse
|
||||||
|
bad performance because the 25kb or so path file needs to be loaded before any other request can be made
|
||||||
|
or all paths need to be printed in template which is apparently not recommended for CSP reasons (although this here
|
||||||
|
might do the same)
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
window.DJANGO_URLS = {
|
||||||
|
'edit_storage'
|
||||||
|
}
|
||||||
|
</script>
|
@ -16,8 +16,15 @@
|
|||||||
{% block script %}
|
{% block script %}
|
||||||
<script src="{% url 'javascript-catalog' %}"></script>
|
<script src="{% url 'javascript-catalog' %}"></script>
|
||||||
|
|
||||||
|
{% if debug %}
|
||||||
|
<script src="{% url 'js_reverse' %}"></script>
|
||||||
|
{% else %}
|
||||||
|
<script src="{% static 'django_js_reverse/js/reverse.js' %}"></script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
window.RECIPE_ID = 5;
|
window.RECIPE_ID = 5;
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% render_bundle 'chunk-vendors' %}
|
{% render_bundle 'chunk-vendors' %}
|
||||||
|
@ -24,6 +24,10 @@ SECRET_KEY = os.getenv('SECRET_KEY') if os.getenv('SECRET_KEY') else 'INSECURE_S
|
|||||||
|
|
||||||
DEBUG = bool(int(os.getenv('DEBUG', True)))
|
DEBUG = bool(int(os.getenv('DEBUG', True)))
|
||||||
|
|
||||||
|
INTERNAL_IPS = [
|
||||||
|
'127.0.0.1',
|
||||||
|
]
|
||||||
|
|
||||||
# allow djangos wsgi server to server mediafiles
|
# allow djangos wsgi server to server mediafiles
|
||||||
GUNICORN_MEDIA = bool(int(os.getenv('GUNICORN_MEDIA', True)))
|
GUNICORN_MEDIA = bool(int(os.getenv('GUNICORN_MEDIA', True)))
|
||||||
|
|
||||||
@ -74,6 +78,7 @@ INSTALLED_APPS = [
|
|||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'django_cleanup.apps.CleanupConfig',
|
'django_cleanup.apps.CleanupConfig',
|
||||||
'webpack_loader',
|
'webpack_loader',
|
||||||
|
'django_js_reverse',
|
||||||
'cookbook.apps.CookbookConfig',
|
'cookbook.apps.CookbookConfig',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ from django.contrib import admin
|
|||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from django.views.i18n import JavaScriptCatalog
|
from django.views.i18n import JavaScriptCatalog
|
||||||
from django.views.static import serve
|
from django.views.static import serve
|
||||||
|
from django_js_reverse import views as reverse_views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include('cookbook.urls')),
|
path('', include('cookbook.urls')),
|
||||||
@ -31,6 +32,7 @@ urlpatterns = [
|
|||||||
JavaScriptCatalog.as_view(domain='django'),
|
JavaScriptCatalog.as_view(domain='django'),
|
||||||
name='javascript-catalog'
|
name='javascript-catalog'
|
||||||
),
|
),
|
||||||
|
url(r'^jsreverse.json$', reverse_views.urls_js, name='js_reverse'),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.GUNICORN_MEDIA or settings.DEBUG:
|
if settings.GUNICORN_MEDIA or settings.DEBUG:
|
||||||
|
@ -29,3 +29,4 @@ microdata==0.7.1
|
|||||||
django-random-queryset==0.1.3
|
django-random-queryset==0.1.3
|
||||||
Jinja2==2.11.2
|
Jinja2==2.11.2
|
||||||
django-webpack-loader==0.7.0
|
django-webpack-loader==0.7.0
|
||||||
|
django-js-reverse==0.9.1
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app" v-if="!loading">
|
<div id="app" v-if="!loading">
|
||||||
<h1>{{ recipe.name }}</h1>
|
<h1>{{ recipe.name }}</h1>
|
||||||
|
<recipe-context-menu v-bind:recipe="recipe"></recipe-context-menu>
|
||||||
|
|
||||||
<img v-bind:src="recipe.image">
|
<img v-bind:src="recipe.image">
|
||||||
|
|
||||||
@ -17,31 +18,23 @@ import {BootstrapVue} from 'bootstrap-vue'
|
|||||||
|
|
||||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||||
|
|
||||||
import {makeToast} from "@/utils/utils.js";
|
import {apiLoadRecipe} from "@/utils/api";
|
||||||
|
|
||||||
const _ = window.gettext
|
|
||||||
|
|
||||||
import Step from "@/components/Step";
|
import Step from "@/components/Step";
|
||||||
|
import RecipeContextMenu from "@/components/RecipeContextMenu";
|
||||||
|
import {GettextMixin, ToastMixin} from "@/utils/utils";
|
||||||
|
|
||||||
Vue.use(BootstrapVue)
|
Vue.use(BootstrapVue)
|
||||||
|
|
||||||
import Vuex from 'vuex'
|
|
||||||
|
|
||||||
Vue.use(Vuex)
|
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
|
||||||
state: {
|
|
||||||
servings: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
import {apiLoadRecipe} from "@/utils/django";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RecipeView',
|
name: 'RecipeView',
|
||||||
store: store,
|
mixins: [
|
||||||
|
GettextMixin,
|
||||||
|
ToastMixin,
|
||||||
|
],
|
||||||
components: {
|
components: {
|
||||||
Step
|
Step,
|
||||||
|
RecipeContextMenu,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
64
vue/src/components/RecipeContextMenu.vue
Normal file
64
vue/src/components/RecipeContextMenu.vue
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
{{ resolveDjangoUrl('api:recipe-detail', 5) }}
|
||||||
|
|
||||||
|
<div class="col col-md-4 d-print-none" style="text-align: right">
|
||||||
|
<div class="dropdown">
|
||||||
|
<a class="btn shadow-none" href="#" role="button" id="dropdownMenuLink"
|
||||||
|
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuLink">
|
||||||
|
<a class="dropdown-item" :href="resolveDjangoUrl('edit_recipe', recipe.id)"><i
|
||||||
|
class="fas fa-pencil-alt fa-fw"></i> {{ _('Edit') }}</a>
|
||||||
|
<button class="dropdown-item" onclick="$('#bookmarkModal').modal({'show':true})">
|
||||||
|
<i class="fas fa-bookmark fa-fw"></i> {{ _('Add to Book') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a class="dropdown-item" :href="recipe.name" v-if="true"> <!--TODO implement -->
|
||||||
|
<i class="fas fa-shopping-cart fa-fw"></i> {{ _('Add to Shopping') }} </a>
|
||||||
|
|
||||||
|
<a class="dropdown-item" :href="resolveDjangoUrl('new_meal_plan', recipe.id)"><i
|
||||||
|
class="fas fa-calendar fa-fw"></i> {{ _('Add to Plan') }}</a>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<button class="dropdown-item" :onclick="openCookLogModal(recipe.id)"><i
|
||||||
|
class="fas fa-clipboard-list fa-fw"></i> {{ _('Log Cooking') }}
|
||||||
|
</button>
|
||||||
|
-->
|
||||||
|
<button class="dropdown-item" onclick="window.print()"><i
|
||||||
|
class="fas fa-print fa-fw"></i> {{ _('Print') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a class="dropdown-item" :href="resolveDjangoUrl('view_export', recipe.id)" target="_blank"
|
||||||
|
rel="noopener noreferrer"><i class="fas fa-file-export fa-fw"></i> {{ _('Export') }}</a>
|
||||||
|
|
||||||
|
<a class="dropdown-item" :href="resolveDjangoUrl('new_share_link', recipe.id)" target="_blank"
|
||||||
|
rel="noopener noreferrer" v-if="recipe.internal"><i class="fas fa-share-alt fa-fw"></i> {{
|
||||||
|
_('Share')
|
||||||
|
}}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import {GettextMixin, ResolveUrlMixin} from "@/utils/utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RecipeContextMenu',
|
||||||
|
mixins: [
|
||||||
|
ResolveUrlMixin,
|
||||||
|
GettextMixin
|
||||||
|
],
|
||||||
|
props: {
|
||||||
|
recipe: Object,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,12 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import {makeToast} from "@/utils/utils";
|
||||||
|
import {resolveDjangoUrl} from "@/utils/utils";
|
||||||
|
|
||||||
|
export function apiLoadRecipe(recipe_id) {
|
||||||
|
return axios.get(resolveDjangoUrl('api:recipe-detail', recipe_id)).then((response) => {
|
||||||
|
return response.data
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
makeToast('Error', 'There was an error loading a resource!', 'danger')
|
||||||
|
})
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import {makeToast} from "@/utils/utils";
|
|
||||||
|
|
||||||
|
|
||||||
export function apiLoadRecipe(recipe_id) {
|
|
||||||
return axios.get(`/api/recipe/${recipe_id}`).then((response) => {
|
|
||||||
return response.data
|
|
||||||
}).catch((err) => {
|
|
||||||
console.log(err)
|
|
||||||
makeToast('Error', 'There was an error loading a resource!', 'danger')
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,4 +1,15 @@
|
|||||||
import { BToast } from 'bootstrap-vue'
|
/*
|
||||||
|
* Utility functions to call bootstrap toasts
|
||||||
|
* */
|
||||||
|
import {BToast} from 'bootstrap-vue'
|
||||||
|
|
||||||
|
export const ToastMixin = {
|
||||||
|
methods: {
|
||||||
|
makeToast: function (title, message, variant = null) {
|
||||||
|
return makeToast(title, message, variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function makeToast(title, message, variant = null) {
|
export function makeToast(title, message, variant = null) {
|
||||||
let toaster = new BToast()
|
let toaster = new BToast()
|
||||||
@ -8,4 +19,46 @@ export function makeToast(title, message, variant = null) {
|
|||||||
toaster: 'b-toaster-top-center',
|
toaster: 'b-toaster-top-center',
|
||||||
solid: true
|
solid: true
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility functions to use djangos gettext
|
||||||
|
* */
|
||||||
|
|
||||||
|
export const GettextMixin = {
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* uses djangos javascript gettext implementation to localize text
|
||||||
|
* @param {string} param string to translate
|
||||||
|
*/
|
||||||
|
_: function (param) {
|
||||||
|
return djangoGettext(param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function djangoGettext(param) {
|
||||||
|
return window.gettext(param)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility function to use djangos named urls
|
||||||
|
* */
|
||||||
|
|
||||||
|
// uses https://github.com/ierror/django-js-reverse#use-the-urls-in-javascript
|
||||||
|
export const ResolveUrlMixin = {
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Returns path of a django named URL
|
||||||
|
* @param {string} url name of url
|
||||||
|
* @param {*} params tuple of params to pass to django named url
|
||||||
|
*/
|
||||||
|
resolveDjangoUrl: function (url, params) {
|
||||||
|
return resolveDjangoUrl(url, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveDjangoUrl(url, params) {
|
||||||
|
return window.Urls[url](params)
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
{"status":"done","publicPath":"http://localhost:8080/","error":"ModuleError","message":"Module Error (from ./node_modules/eslint-loader/index.js):\n\nF:\\Developement\\Django\\recipes\\vue\\src\\apps\\RecipeView\\RecipeView.vue\n 47:11 error 'root_element' is not defined no-undef\n\n✖ 1 problem (1 error, 0 warnings)\n","chunks":{"chunk-vendors":[{"name":"js/chunk-vendors.js","publicPath":"http://localhost:8080/js/chunk-vendors.js","path":"F:\\Developement\\Django\\recipes\\cookbook\\static\\vue\\js\\chunk-vendors.js"}],"recipe_view":[{"name":"js/recipe_view.js","publicPath":"http://localhost:8080/js/recipe_view.js","path":"F:\\Developement\\Django\\recipes\\cookbook\\static\\vue\\js\\recipe_view.js"},{"name":"recipe_view.85b0fba40931a99bbc77.hot-update.js","publicPath":"http://localhost:8080/recipe_view.85b0fba40931a99bbc77.hot-update.js","path":"F:\\Developement\\Django\\recipes\\cookbook\\static\\vue\\recipe_view.85b0fba40931a99bbc77.hot-update.js"}]}}
|
{"status":"done","publicPath":"http://localhost:8080/","chunks":{"chunk-vendors":[{"name":"js/chunk-vendors.js","publicPath":"http://localhost:8080/js/chunk-vendors.js","path":"F:\\Developement\\Django\\recipes\\cookbook\\static\\vue\\js\\chunk-vendors.js"}],"recipe_view":[{"name":"js/recipe_view.js","publicPath":"http://localhost:8080/js/recipe_view.js","path":"F:\\Developement\\Django\\recipes\\cookbook\\static\\vue\\js\\recipe_view.js"},{"name":"recipe_view.298e10642301889fe8ed.hot-update.js","publicPath":"http://localhost:8080/recipe_view.298e10642301889fe8ed.hot-update.js","path":"F:\\Developement\\Django\\recipes\\cookbook\\static\\vue\\recipe_view.298e10642301889fe8ed.hot-update.js"}]},"error":"ModuleError","message":"Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):\n(Emitted value instead of an instance of Error) \n\n Errors compiling template:\n\n href=\"{% url 'view_export' %}?r={{ recipe.pk }}\": Interpolation inside attributes has been removed. Use v-bind or the colon shorthand instead. For example, instead of <div id=\"{{ val }}\">, use <div :id=\"val\">.\n\n 34 | </button>\n 35 | \n 36 | <a class=\"dropdown-item\" href=\"{% url 'view_export' %}?r={{ recipe.pk }}\" target=\"_blank\"\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n 37 | rel=\"noopener noreferrer\"><i class=\"fas fa-file-export fa-fw\"></i> {% trans 'Export' %}</a>\n 38 | {% if recipe.internal %}\n"}
|
Loading…
Reference in New Issue
Block a user