finished making FoodList completely generic

This commit is contained in:
smilerz 2021-09-05 14:15:58 -05:00
parent d33a49538e
commit 638dd96812
16 changed files with 155441 additions and 257 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -16,11 +16,13 @@
{% block script %}
{% comment %} {% if debug %}
{{ config | json_script:"model_config" }}
{% if debug %}
<script src="{% url 'js_reverse' %}"></script>
{% else %}
<script src="{% static 'django_js_reverse/reverse.js' %}"></script>
{% endif %} {% endcomment %}
{% endif %}
<script type="application/javascript">
window.IMAGE_PLACEHOLDER = "{% static 'assets/recipe_no_image.svg' %}"

File diff suppressed because one or more lines are too long

View File

@ -110,4 +110,16 @@ def keyword(request):
@group_required('user')
def food(request):
return render(request, 'generic/model_template.html', {"title": _("Foods")})
# 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": _("Foods"),
"config": {
'model': "FOOD", # *REQUIRED* name of the model in models.js
'recipe_param': 'foods' # *OPTIONAL* name of the listRecipes parameter if filtering on this attribute
}
}
)

View File

@ -10,6 +10,8 @@
@finish-action="finishAction"/>
<generic-split-lists v-if="this_model"
:list_name="this_model.name"
:right_counts="right_counts"
:left_counts="left_counts"
@reset="resetList"
@get-list="getItems"
@item-action="startAction">
@ -67,7 +69,9 @@ import GenericModalForm from "@/components/Modals/GenericModalForm";
Vue.use(BootstrapVue)
export default {
name: 'ModelListView', // TODO: make generic name
// TODO ApiGenerator doesn't capture and share error information - would be nice to share error details when available
// or i'm capturing it incorrectly
name: 'ModelListView',
mixins: [CardMixin, ToastMixin, ApiMixin],
components: {GenericHorizontalCard, GenericSplitLists, GenericModalForm},
data() {
@ -75,18 +79,21 @@ export default {
// this.Models and this.Actions inherited from ApiMixin
items_left: [],
items_right: [],
load_more_left: true,
load_more_right: true,
right_counts: {'max': 9999, 'current': 0},
left_counts: {'max': 9999, 'current': 0},
this_model: undefined,
this_action: undefined,
this_recipe_param: undefined,
this_item: {},
this_target: {},
show_modal: false
}
},
mounted() {
let path = (window.location.pathname).split('/')
this.this_model = this.Models[path[path.length - 2].toUpperCase()]
// value is passed from lists.py
let model_config = JSON.parse(document.getElementById('model_config').textContent)
this.this_model = this.Models[model_config?.model]
this.this_recipe_param = model_config?.recipe_param
},
methods: {
// this.genericAPI inherited from ApiMixin
@ -176,28 +183,17 @@ export default {
}
this.clearState()
},
getItems: function (params, callback) {
getItems: function (params) {
let column = params?.column ?? 'left'
// TODO: does this need to be a callback?
this.genericAPI(this.this_model, this.Actions.LIST, params).then((result) => {
if (result.data.results.length) {
if (column === 'left') {
// if paginated results are in result.data.results otherwise just result.data
this.items_left = this.items_left.concat(result.data?.results ?? result.data)
} else if (column === 'right') {
this.items_right = this.items_right.concat(result.data?.results ?? result.data)
}
// are the total elements less than the length of the array? if so, stop loading
// TODO: generalize this to handle results in result.data
callback(result.data.count > (column === "left" ? this.items_left.length : this.items_right.length))
this['items_' + column] = this['items_' + column].concat(result.data?.results)
this[column + '_counts']['current'] = this['items_' + column].length
this[column + '_counts']['max'] = result.data.count
} else {
callback(false) // stop loading
console.log('no data returned')
}
// return true if total objects are still less than the length of the list
// TODO this needs generalized to handle non-paginated data
callback(result.data.count < (column === "left" ? this.items_left.length : this.items_right.length))
}).catch((err) => {
console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
@ -253,8 +249,6 @@ export default {
// TODO make standard toast
this.makeToast(this.$t('Success'), 'Succesfully moved resource', 'success')
}).catch((err) => {
// TODO none of the error checking works because the openapi generated functions don't throw an error?
// or i'm capturing it incorrectly
console.log(err)
this.makeToast(this.$t('Error'), err.bodyText, 'danger')
})
@ -293,7 +287,7 @@ export default {
'pageSize': 200
}
this.genericAPI(this.this_model, this.Actions.LIST, options).then((result) => {
parent = this.findCard(item.id, col === 'left' ? this.items_left : this.items_right)
parent = this.findCard(item.id, this['items_' + col])
if (parent) {
Vue.set(parent, 'children', result.data.results)
Vue.set(parent, 'show_children', true)
@ -304,15 +298,14 @@ export default {
this.makeToast(this.$t('Error'), err.bodyText, 'danger')
})
},
getRecipes: function (col, food) {
getRecipes: function (col, item) {
let parent = {}
// TODO: make this generic
let options = {
'foods': food.id,
'pageSize': 200
}
let options = {'pageSize': 200}
options[this.this_recipe_param] = item.id
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, options).then((result) => {
parent = this.findCard(food.id, col === 'left' ? this.items_left : this.items_right)
parent = this.findCard(item.id, this['items_' + col])
if (parent) {
Vue.set(parent, 'recipes', result.data.results)
Vue.set(parent, 'show_recipes', true)

View File

@ -64,7 +64,7 @@
</div>
<!-- only show scollbars in split mode -->
<!-- weird behavior when switching to split mode, infinite scoll doesn't trigger if
<!-- 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}">
@ -101,12 +101,15 @@ 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_list: {type: Array, default(){return []}},
left_counts: {type: Object},
right_list: {type:Array, default(){return []}},
right_counts: {type: Object},
},
data() {
return {
@ -116,6 +119,8 @@ export default {
search_left: '',
right_page: 0,
left_page: 0,
right_state: undefined,
left_state: undefined,
right: +new Date(),
left: +new Date(),
text: {
@ -142,6 +147,26 @@ export default {
this.$emit('reset', {'column':'right'})
this.right += 1
}, 700),
right_counts: {
deep: true,
handler(newVal, oldVal) {
if (newVal.current >= newVal.max) {
this.right_state.complete()
} else {
this.right_state.loaded()
}
}
},
left_counts: {
deep: true,
handler(newVal, oldVal) {
if (newVal.current >= newVal.max) {
this.left_state.complete()
} else {
this.left_state.loaded()
}
}
}
},
methods: {
resetSearch: function () {
@ -154,16 +179,9 @@ export default {
'page': (col==='left') ? this.left_page + 1 : this.right_page + 1,
'column': col
}
// TODO: change this to be an emit and watch a prop to determine if loaded or complete
new Promise((callback) => this.$emit('get-list', params, callback)).then((result) => {
this[col+'_state'] = $state
this.$emit('get-list', params)
this[col+'_page'] += 1
$state.loaded();
if (!result) { // callback needs to return true if handler should continue loading more data
$state.complete();
}
}).catch(() => {
$state.complete();
})
},
}
}

View File

@ -94,6 +94,7 @@ export default {
}
},
methods: {
// TODO: convert this to genericAPI
clickUrl: function () {
if (this.recipe !== null) {
return resolveDjangoUrl('view_recipe', this.recipe.id)

View File

@ -85,7 +85,7 @@ module.exports = {
},
},
// TODO make this conditional on .env DEBUG = FALSE
config.optimization.minimize(true)
config.optimization.minimize(false)
);
//TODO somehow remov them as they are also added to the manifest config of the service worker