+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+ {{ this.this_model.name }}
+
+
+
+
+
+ {{ $t("show_split_screen") }}
+
+
+
-
-
-
-
-
-
-
-
-
- {{ this.this_model.name }}
-
-
-
-
-
- {{ $t('show_split_screen') }}
-
-
-
-
-
-
-
-
+
diff --git a/vue/src/components/GenericPill.vue b/vue/src/components/GenericPill.vue
index c443652a..fbb73f29 100644
--- a/vue/src/components/GenericPill.vue
+++ b/vue/src/components/GenericPill.vue
@@ -1,44 +1,45 @@
-
- {{thisLabel(k)}}
-
+
+ {{ thisLabel(k) }}
+
diff --git a/vue/src/components/Modals/GenericModalForm.vue b/vue/src/components/Modals/GenericModalForm.vue
index 20571bc1..237fd903 100644
--- a/vue/src/components/Modals/GenericModalForm.vue
+++ b/vue/src/components/Modals/GenericModalForm.vue
@@ -1,143 +1,250 @@
-
-
- {{ form.title }}
-
-
{{ f.label }}
-
-
-
-
-
-
-
-
-
+
+
+ {{ form.title }}
+
+
{{ f.label }}
+
+
+
+
+
+
+
+
+
+
-
- {{ $t('Cancel') }}
- {{ form.ok_label }}
-
-
-
+
+ {{ $t("Cancel") }}
+ {{ form.ok_label }}
+
+
+
\ No newline at end of file
+
diff --git a/vue/src/components/Modals/LookupInput.vue b/vue/src/components/Modals/LookupInput.vue
index 30035025..ddfced6c 100644
--- a/vue/src/components/Modals/LookupInput.vue
+++ b/vue/src/components/Modals/LookupInput.vue
@@ -1,157 +1,171 @@
-
-
-
+
+
+ {{ form.label }}
+
+
+
\ No newline at end of file
+
diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json
index f211e044..589ee6fa 100644
--- a/vue/src/locales/en.json
+++ b/vue/src/locales/en.json
@@ -4,10 +4,14 @@
"err_creating_resource": "There was an error creating a resource!",
"err_updating_resource": "There was an error updating a resource!",
"err_deleting_resource": "There was an error deleting a resource!",
+ "err_moving_resource": "There was an error moving a resource!",
+ "err_merging_resource": "There was an error merging a resource!",
"success_fetching_resource": "Successfully fetched a resource!",
"success_creating_resource": "Successfully created a resource!",
"success_updating_resource": "Successfully updated a resource!",
"success_deleting_resource": "Successfully deleted a resource!",
+ "success_moving_resource": "Successfully moved a resource!",
+ "success_merging_resource": "Successfully merged a resource!",
"file_upload_disabled": "File upload is not enabled for your space.",
"step_time_minutes": "Step time in minutes",
"confirm_delete": "Are you sure you want to delete this {object}?",
@@ -207,5 +211,8 @@
"New_Cookbook": "New cookbook",
"Hide_Keyword": "Hide keywords",
"Clear": "Clear",
+ "err_move_self": "Cannot move item to itself",
+ "nothing": "Nothing to do",
+ "err_merge_self": "Cannot merge item with itself",
"show_sql": "Show SQL"
}
diff --git a/vue/src/utils/models.js b/vue/src/utils/models.js
index 0f437097..23911b3d 100644
--- a/vue/src/utils/models.js
+++ b/vue/src/utils/models.js
@@ -1,7 +1,7 @@
/*
-* Utility CLASS to define model configurations
-* */
-import i18n from "@/i18n";
+ * Utility CLASS to define model configurations
+ * */
+import i18n from "@/i18n"
// TODO this needs rethought and simplified
// maybe a function that returns a single dictionary based on action?
@@ -12,621 +12,631 @@ export class Models {
// MODEL_TYPES - inherited by MODELS, inherits and takes precedence over ACTIONS
static TREE = {
- 'list': {
- 'params': ['query', 'root', 'tree', 'page', 'pageSize', 'options'],
- 'config': {
- 'root': {
- 'default': {
- 'function': 'CONDITIONAL',
- 'check': 'query',
- 'operator': 'not_exist',
- 'true': 0,
- 'false': undefined
- }
+ list: {
+ params: ["query", "root", "tree", "page", "pageSize", "options"],
+ config: {
+ root: {
+ default: {
+ function: "CONDITIONAL",
+ check: "query",
+ operator: "not_exist",
+ true: 0,
+ false: undefined,
+ },
},
- 'tree': {'default': undefined},
+ tree: { default: undefined },
},
},
- 'delete': {
- "form": {
- 'instruction': {
- 'form_field': true,
- 'type': 'instruction',
- 'function': 'translate',
- 'phrase': "del_confimation_tree",
- 'params': [
+ delete: {
+ form: {
+ instruction: {
+ form_field: true,
+ type: "instruction",
+ function: "translate",
+ phrase: "del_confimation_tree",
+ params: [
{
- 'token': 'source',
- 'from': 'item1',
- 'attribute': "name"
- }
- ]
- }
- }
+ token: "source",
+ from: "item1",
+ attribute: "name",
+ },
+ ],
+ },
+ },
+ },
+ move: {
+ form: {
+ target: {
+ form_field: true,
+ type: "lookup",
+ field: "target",
+ list: "self",
+ sticky_options: [{ id: 0, name: i18n.t("tree_root") }],
+ },
+ },
},
- 'move': {
- 'form': {
- 'target': {
- 'form_field': true,
- 'type': 'lookup',
- 'field': 'target',
- 'list': 'self',
- 'sticky_options': [{'id': 0, 'name': i18n.t('tree_root')}]
- }
- }
- }
}
// MODELS - inherits and takes precedence over MODEL_TYPES and ACTIONS
static FOOD = {
- '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
- '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': {
- 'linked_recipe': true,
+ 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
+ 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: {
+ linked_recipe: true,
},
- 'tags': [{'field': 'supermarket_category', 'label': 'name', 'color': 'info'}],
+ tags: [{ field: "supermarket_category", label: "name", color: "info" }],
// REQUIRED: unordered array of fields that can be set during create
- 'create': {
+ create: {
// if not defined partialUpdate will use the same parameters, prepending 'id'
- 'params': [['name', 'description', 'recipe', 'ignore_shopping', 'supermarket_category']],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ params: [["name", "description", "recipe", "ignore_shopping", "supermarket_category"]],
+ 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': ''
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
},
- 'recipe': {
- 'form_field': true,
- 'type': 'lookup',
- 'field': 'recipe',
- 'list': 'RECIPE',
- 'label': i18n.t('Recipe')
+ recipe: {
+ form_field: true,
+ type: "lookup",
+ field: "recipe",
+ list: "RECIPE",
+ label: i18n.t("Recipe"),
},
- 'shopping': {
- 'form_field': true,
- 'type': 'checkbox',
- 'field': 'ignore_shopping',
- 'label': i18n.t('Ignore_Shopping')
+ shopping: {
+ form_field: true,
+ type: "checkbox",
+ field: "ignore_shopping",
+ label: i18n.t("Ignore_Shopping"),
},
- 'shopping_category': {
- 'form_field': true,
- 'type': 'lookup',
- 'field': 'supermarket_category',
- 'list': 'SHOPPING_CATEGORY',
- 'label': i18n.t('Shopping_Category'),
- 'allow_create': true
+ shopping_category: {
+ form_field: true,
+ type: "lookup",
+ field: "supermarket_category",
+ list: "SHOPPING_CATEGORY",
+ label: i18n.t("Shopping_Category"),
+ allow_create: true,
},
- }
+ },
},
-
}
static KEYWORD = {
- 'name': i18n.t('Keyword'), // *OPTIONAL: parameters will be built model -> model_type -> default
- 'apiName': 'Keyword',
- 'model_type': this.TREE,
- 'paginated': true,
- 'move': true,
- 'merge': true,
- 'badges': {
- 'icon': true
+ name: i18n.t("Keyword"), // *OPTIONAL: parameters will be built model -> model_type -> default
+ apiName: "Keyword",
+ model_type: this.TREE,
+ paginated: true,
+ move: true,
+ merge: true,
+ badges: {
+ icon: true,
},
- 'create': {
+ create: {
// if not defined partialUpdate will use the same parameters, prepending 'id'
- 'params': [['name', 'description', 'icon']],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ params: [["name", "description", "icon"]],
+ 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': ''
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
},
- 'icon': {
- 'form_field': true,
- 'type': 'emoji',
- 'field': 'icon',
- 'label': i18n.t('Icon')
+ icon: {
+ form_field: true,
+ type: "emoji",
+ field: "icon",
+ label: i18n.t("Icon"),
},
- }
+ },
},
}
static UNIT = {
- 'name': i18n.t('Unit'),
- 'apiName': 'Unit',
- 'paginated': true,
- 'create': {
- 'params': [['name', 'description']],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ name: i18n.t("Unit"),
+ apiName: "Unit",
+ paginated: true,
+ create: {
+ params: [["name", "description"]],
+ 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': ''
- }
- }
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
+ },
+ },
},
- 'merge': true
+ merge: true,
}
static SHOPPING_LIST = {
- 'name': i18n.t('Shopping_list'),
- 'apiName': 'ShoppingListEntry',
+ name: i18n.t("Shopping_list"),
+ apiName: "ShoppingListEntry",
}
static RECIPE_BOOK = {
- 'name': i18n.t('Recipe_Book'),
- 'apiName': 'RecipeBook',
- 'create': {
- 'params': [['name', 'description', 'icon']],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ name: i18n.t("Recipe_Book"),
+ apiName: "RecipeBook",
+ create: {
+ params: [["name", "description", "icon"]],
+ 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': ''
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
},
- 'icon': {
- 'form_field': true,
- 'type': 'emoji',
- 'field': 'icon',
- 'label': i18n.t('Icon')
+ icon: {
+ form_field: true,
+ type: "emoji",
+ field: "icon",
+ label: i18n.t("Icon"),
},
- }
+ },
},
}
static SHOPPING_CATEGORY = {
- 'name': i18n.t('Shopping_Category'),
- 'apiName': 'SupermarketCategory',
- 'create': {
- 'params': [['name', 'description']],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ name: i18n.t("Shopping_Category"),
+ apiName: "SupermarketCategory",
+ create: {
+ params: [["name", "description"]],
+ 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': ''
- }
- }
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
+ },
+ },
},
}
static SHOPPING_CATEGORY_RELATION = {
- 'name': i18n.t('Shopping_Category_Relation'),
- 'apiName': 'SupermarketCategoryRelation',
- 'create': {
- 'params': [['category', 'supermarket', 'order']],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ name: i18n.t("Shopping_Category_Relation"),
+ apiName: "SupermarketCategoryRelation",
+ 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': ''
- }
- }
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
+ },
+ },
},
}
static SUPERMARKET = {
- 'name': i18n.t('Supermarket'),
- 'apiName': 'Supermarket',
- 'ordered_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': ''
+ name: i18n.t("Supermarket"),
+ apiName: "Supermarket",
+ ordered_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': ''
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
},
- 'categories': {
- 'form_field': true,
- 'type': 'lookup',
- 'list': 'SHOPPING_CATEGORY',
- 'list_label': 'category::name',
- 'ordered': true, // ordered lookups assume working with relation field
- 'field': 'category_to_supermarket',
- 'label': i18n.t('Categories'),
- 'placeholder': ''
+ categories: {
+ form_field: true,
+ type: "lookup",
+ list: "SHOPPING_CATEGORY",
+ list_label: "category::name",
+ ordered: true, // ordered lookups assume working with relation field
+ field: "category_to_supermarket",
+ label: i18n.t("Categories"),
+ placeholder: "",
},
},
- 'config': {
- 'function': 'SupermarketWithCategories',
- }
+ config: {
+ function: "SupermarketWithCategories",
+ },
+ },
+ partialUpdate: {
+ config: {
+ function: "SupermarketWithCategories",
+ },
},
- 'partialUpdate': {
- 'config': {
- 'function': 'SupermarketWithCategories',
- }
- }
}
static AUTOMATION = {
- 'name': i18n.t('Automation'),
- 'apiName': 'Automation',
- 'paginated': true,
- 'list': {
- 'header_component': {
- 'name': 'BetaWarning'
+ name: i18n.t("Automation"),
+ apiName: "Automation",
+ paginated: true,
+ list: {
+ header_component: {
+ name: "BetaWarning",
},
},
- 'create': {
- 'params': [['name', 'description', 'type', 'param_1', 'param_2', 'param_3']],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ create: {
+ params: [["name", "description", "type", "param_1", "param_2", "param_3"]],
+ 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': ''
+ description: {
+ form_field: true,
+ type: "text",
+ field: "description",
+ label: i18n.t("Description"),
+ placeholder: "",
},
- 'type': {
- 'form_field': true,
- 'type': 'choice',
- 'options': [
- {value: 'FOOD_ALIAS', text: i18n.t('Food_Alias')},
- {value: 'UNIT_ALIAS', text: i18n.t('Unit_Alias')},
- {value: 'KEYWORD_ALIAS', text: i18n.t('Keyword_Alias')},
+ type: {
+ form_field: true,
+ type: "choice",
+ options: [
+ { value: "FOOD_ALIAS", text: i18n.t("Food_Alias") },
+ { value: "UNIT_ALIAS", text: i18n.t("Unit_Alias") },
+ { value: "KEYWORD_ALIAS", text: i18n.t("Keyword_Alias") },
],
- 'field': 'type',
- 'label': i18n.t('Type'),
- 'placeholder': ''
+ field: "type",
+ label: i18n.t("Type"),
+ placeholder: "",
},
- 'param_1': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'param_1',
- 'label': i18n.t('Parameter') + ' 1',
- 'placeholder': ''
+ param_1: {
+ form_field: true,
+ type: "text",
+ field: "param_1",
+ label: i18n.t("Parameter") + " 1",
+ placeholder: "",
},
- 'param_2': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'param_2',
- 'label': i18n.t('Parameter') + ' 2',
- 'placeholder': ''
+ param_2: {
+ form_field: true,
+ type: "text",
+ field: "param_2",
+ label: i18n.t("Parameter") + " 2",
+ placeholder: "",
},
- 'param_3': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'param_3',
- 'label': i18n.t('Parameter') + ' 3',
- 'placeholder': ''
+ param_3: {
+ form_field: true,
+ type: "text",
+ field: "param_3",
+ label: i18n.t("Parameter") + " 3",
+ placeholder: "",
},
- }
+ },
},
}
static RECIPE = {
- 'name': i18n.t('Recipe'),
- 'apiName': 'Recipe',
- 'list': {
- 'params': ['query', 'keywords', 'foods', 'units', 'rating', 'books', 'steps', 'keywordsOr', 'foodsOr', 'booksOr', 'internal', 'random', '_new', 'page', 'pageSize', 'options'],
- 'config': {
- 'foods': {'type': 'string'},
- 'keywords': {'type': 'string'},
- 'books': {'type': 'string'},
- }
+ name: i18n.t("Recipe"),
+ apiName: "Recipe",
+ list: {
+ params: [
+ "query",
+ "keywords",
+ "foods",
+ "units",
+ "rating",
+ "books",
+ "steps",
+ "keywordsOr",
+ "foodsOr",
+ "booksOr",
+ "internal",
+ "random",
+ "_new",
+ "page",
+ "pageSize",
+ "options",
+ ],
+ config: {
+ foods: { type: "string" },
+ keywords: { type: "string" },
+ books: { type: "string" },
+ },
},
}
static STEP = {
- 'name': i18n.t('Step'),
- 'apiName': 'Step',
- 'paginated': true,
- 'list': {
- 'header_component': {
- 'name': 'BetaWarning'
+ name: i18n.t("Step"),
+ apiName: "Step",
+ paginated: true,
+ list: {
+ header_component: {
+ name: "BetaWarning",
},
- 'params': ['query', 'page', 'pageSize', 'options'],
+ params: ["query", "page", "pageSize", "options"],
},
}
static USER_NAME = {
- 'name': i18n.t('User'),
- 'apiName': 'User',
- 'list': {
- 'params': ['filter_list'],
+ name: i18n.t("User"),
+ apiName: "User",
+ list: {
+ params: ["filter_list"],
},
-
}
static MEAL_TYPE = {
- 'name': i18n.t('Meal_Type'),
- 'apiName': 'MealType',
- 'list': {
- 'params': ['filter_list'],
+ name: i18n.t("Meal_Type"),
+ apiName: "MealType",
+ list: {
+ params: ["filter_list"],
},
-
}
static MEAL_PLAN = {
- 'name': i18n.t('Meal_Plan'),
- 'apiName': 'MealPlan',
- 'list': {
- 'params': ['options'],
+ name: i18n.t("Meal_Plan"),
+ apiName: "MealPlan",
+ list: {
+ params: ["options"],
},
-
}
static USERFILE = {
- 'name': i18n.t('File'),
- 'apiName': 'UserFile',
- 'paginated': false,
- 'list': {
- 'header_component': {
- 'name': 'StorageQuota'
+ name: i18n.t("File"),
+ apiName: "UserFile",
+ paginated: false,
+ list: {
+ header_component: {
+ name: "StorageQuota",
},
},
- 'create': {
- 'params': ['name', 'file',],
- 'form': {
- 'name': {
- 'form_field': true,
- 'type': 'text',
- 'field': 'name',
- 'label': i18n.t('Name'),
- 'placeholder': ''
+ create: {
+ params: ["name", "file"],
+ form: {
+ name: {
+ form_field: true,
+ type: "text",
+ field: "name",
+ label: i18n.t("Name"),
+ placeholder: "",
},
- 'file': {
- 'form_field': true,
- 'type': 'file',
- 'field': 'file',
- 'label': i18n.t('File'),
- 'placeholder': ''
+ file: {
+ form_field: true,
+ type: "file",
+ field: "file",
+ label: i18n.t("File"),
+ placeholder: "",
},
- }
+ },
},
}
}
-
export class Actions {
static CREATE = {
- "function": "create",
- 'form': {
- 'title': {
- 'function': 'translate',
- 'phrase': 'create_title',
- 'params': [
+ function: "create",
+ form: {
+ title: {
+ function: "translate",
+ phrase: "create_title",
+ params: [
{
- 'token': 'type',
- 'from': 'model',
- 'attribute': 'name'
- }
+ token: "type",
+ from: "model",
+ attribute: "name",
+ },
],
},
- 'ok_label': i18n.t('Save'),
- }
+ ok_label: i18n.t("Save"),
+ },
}
static UPDATE = {
- "function": "partialUpdate",
+ function: "partialUpdate",
// special case for update only - updated assumes create form is sufficient, but a different title is required.
- "form_title": {
- 'function': 'translate',
- 'phrase': 'edit_title',
- 'params': [
+ form_title: {
+ function: "translate",
+ phrase: "edit_title",
+ params: [
{
- 'token': 'type',
- 'from': 'model',
- 'attribute': 'name'
- }
+ token: "type",
+ from: "model",
+ attribute: "name",
+ },
],
},
}
static DELETE = {
- "function": "destroy",
- 'params': ['id'],
- 'form': {
- 'title': {
- 'function': 'translate',
- 'phrase': 'delete_title',
- 'params': [
+ function: "destroy",
+ params: ["id"],
+ form: {
+ title: {
+ function: "translate",
+ phrase: "delete_title",
+ params: [
{
- 'token': 'type',
- 'from': 'model',
- 'attribute': 'name'
- }
+ token: "type",
+ from: "model",
+ attribute: "name",
+ },
],
},
- 'ok_label': i18n.t('Delete'),
- 'instruction': {
- 'form_field': true,
- 'type': 'instruction',
- 'label': {
- 'function': 'translate',
- 'phrase': "delete_confirmation",
- 'params': [
+ ok_label: i18n.t("Delete"),
+ instruction: {
+ form_field: true,
+ type: "instruction",
+ label: {
+ function: "translate",
+ phrase: "delete_confirmation",
+ params: [
{
- 'token': 'source',
- 'from': 'item1',
- 'attribute': "name"
- }
- ]
- }
- }
- }
+ token: "source",
+ from: "item1",
+ attribute: "name",
+ },
+ ],
+ },
+ },
+ },
}
static FETCH = {
- "function": "retrieve",
- 'params': ['id']
+ function: "retrieve",
+ params: ["id"],
}
static LIST = {
- "function": "list",
- "suffix": "s",
- "params": ['query', 'page', 'pageSize', 'options'],
- "config": {
- 'query': {'default': undefined},
- 'page': {'default': 1},
- 'pageSize': {'default': 25}
- }
+ function: "list",
+ suffix: "s",
+ params: ["query", "page", "pageSize", "options"],
+ config: {
+ query: { default: undefined },
+ page: { default: 1 },
+ pageSize: { default: 25 },
+ },
}
static MERGE = {
- "function": "merge",
- 'params': ['source', 'target'],
- "config": {
- 'source': {'type': 'string'},
- 'target': {'type': 'string'}
+ function: "merge",
+ params: ["source", "target"],
+ config: {
+ source: { type: "string" },
+ target: { type: "string" },
},
- 'form': {
- 'title': {
- 'function': 'translate',
- 'phrase': 'merge_title',
- 'params': [
+ form: {
+ title: {
+ function: "translate",
+ phrase: "merge_title",
+ params: [
{
- 'token': 'type',
- 'from': 'model',
- 'attribute': 'name'
- }
+ token: "type",
+ from: "model",
+ attribute: "name",
+ },
],
},
- 'ok_label': i18n.t('Merge'),
- 'instruction': {
- 'form_field': true,
- 'type': 'instruction',
- 'label': {
- 'function': 'translate',
- 'phrase': "merge_selection",
- 'params': [
+ ok_label: i18n.t("Merge"),
+ instruction: {
+ form_field: true,
+ type: "instruction",
+ label: {
+ function: "translate",
+ phrase: "merge_selection",
+ params: [
{
- 'token': 'source',
- 'from': 'item1',
- 'attribute': "name"
+ token: "source",
+ from: "item1",
+ attribute: "name",
},
{
- 'token': 'type',
- 'from': 'model',
- 'attribute': "name"
+ token: "type",
+ from: "model",
+ attribute: "name",
},
- ]
- }
+ ],
+ },
},
- 'target': {
- 'form_field': true,
- 'type': 'lookup',
- 'field': 'target',
- 'list': 'self'
- }
- }
+ target: {
+ form_field: true,
+ type: "lookup",
+ field: "target",
+ list: "self",
+ },
+ },
}
static MOVE = {
- "function": "move",
- 'params': ['source', 'target'],
- "config": {
- 'source': {'type': 'string'},
- 'target': {'type': 'string'}
+ function: "move",
+ params: ["source", "target"],
+ config: {
+ source: { type: "string" },
+ target: { type: "string" },
},
- 'form': {
- 'title': {
- 'function': 'translate',
- 'phrase': 'move_title',
- 'params': [
+ form: {
+ title: {
+ function: "translate",
+ phrase: "move_title",
+ params: [
{
- 'token': 'type',
- 'from': 'model',
- 'attribute': 'name'
- }
+ token: "type",
+ from: "model",
+ attribute: "name",
+ },
],
},
- 'ok_label': i18n.t('Move'),
- 'instruction': {
- 'form_field': true,
- 'type': 'instruction',
- 'label': {
- 'function': 'translate',
- 'phrase': "move_selection",
- 'params': [
+ ok_label: i18n.t("Move"),
+ instruction: {
+ form_field: true,
+ type: "instruction",
+ label: {
+ function: "translate",
+ phrase: "move_selection",
+ params: [
{
- 'token': 'source',
- 'from': 'item1',
- 'attribute': "name"
+ token: "source",
+ from: "item1",
+ attribute: "name",
},
{
- 'token': 'type',
- 'from': 'model',
- 'attribute': "name"
+ token: "type",
+ from: "model",
+ attribute: "name",
},
- ]
- }
-
+ ],
+ },
},
- 'target': {
- 'form_field': true,
- 'type': 'lookup',
- 'field': 'target',
- 'list': 'self'
- }
- }
-
+ target: {
+ form_field: true,
+ type: "lookup",
+ field: "target",
+ list: "self",
+ },
+ },
}
}
diff --git a/vue/src/utils/utils.js b/vue/src/utils/utils.js
index 4afdf002..2be64a46 100644
--- a/vue/src/utils/utils.js
+++ b/vue/src/utils/utils.js
@@ -1,15 +1,26 @@
/*
-* Utility functions to call bootstrap toasts
-* */
-import {BToast} from 'bootstrap-vue'
-import i18n from "@/i18n";
+ * Utility functions to call bootstrap toasts
+ * */
+import i18n from "@/i18n"
+import { frac } from "@/utils/fractions"
+/*
+ * Utility functions to use OpenAPIs generically
+ * */
+import { ApiApiFactory } from "@/utils/openapi/api.ts"
+import axios from "axios"
+import { BToast } from "bootstrap-vue"
+// /*
+// * Utility functions to use manipulate nested components
+// * */
+import Vue from "vue"
+import { Actions, Models } from "./models"
export const ToastMixin = {
methods: {
makeToast: function (title, message, variant = null) {
return makeToast(title, message, variant)
- }
- }
+ },
+ },
}
export function makeToast(title, message, variant = null) {
@@ -17,57 +28,71 @@ export function makeToast(title, message, variant = null) {
toaster.$bvToast.toast(message, {
title: title,
variant: variant,
- toaster: 'b-toaster-bottom-right',
- solid: true
+ toaster: "b-toaster-bottom-right",
+ solid: true,
})
}
export class StandardToasts {
- static SUCCESS_CREATE = 'SUCCESS_CREATE'
- static SUCCESS_FETCH = 'SUCCESS_FETCH'
- static SUCCESS_UPDATE = 'SUCCESS_UPDATE'
- static SUCCESS_DELETE = 'SUCCESS_DELETE'
+ static SUCCESS_CREATE = "SUCCESS_CREATE"
+ static SUCCESS_FETCH = "SUCCESS_FETCH"
+ static SUCCESS_UPDATE = "SUCCESS_UPDATE"
+ static SUCCESS_DELETE = "SUCCESS_DELETE"
+ static SUCCESS_MOVE = "SUCCESS_MOVE"
+ static SUCCESS_MERGE = "SUCCESS_MERGE"
- static FAIL_CREATE = 'FAIL_CREATE'
- static FAIL_FETCH = 'FAIL_FETCH'
- static FAIL_UPDATE = 'FAIL_UPDATE'
- static FAIL_DELETE = 'FAIL_DELETE'
+ static FAIL_CREATE = "FAIL_CREATE"
+ static FAIL_FETCH = "FAIL_FETCH"
+ static FAIL_UPDATE = "FAIL_UPDATE"
+ static FAIL_DELETE = "FAIL_DELETE"
+ static FAIL_MOVE = "FAIL_MOVE"
+ static FAIL_MERGE = "FAIL_MERGE"
- static makeStandardToast(toast) {
+ static makeStandardToast(toast, err_details = undefined) {
switch (toast) {
case StandardToasts.SUCCESS_CREATE:
- makeToast(i18n.tc('Success'), i18n.tc('success_creating_resource'), 'success')
- break;
+ makeToast(i18n.tc("Success"), i18n.tc("success_creating_resource"), "success")
+ break
case StandardToasts.SUCCESS_FETCH:
- makeToast(i18n.tc('Success'), i18n.tc('success_fetching_resource'), 'success')
- break;
+ makeToast(i18n.tc("Success"), i18n.tc("success_fetching_resource"), "success")
+ break
case StandardToasts.SUCCESS_UPDATE:
- makeToast(i18n.tc('Success'), i18n.tc('success_updating_resource'), 'success')
- break;
+ makeToast(i18n.tc("Success"), i18n.tc("success_updating_resource"), "success")
+ break
case StandardToasts.SUCCESS_DELETE:
- makeToast(i18n.tc('Success'), i18n.tc('success_deleting_resource'), 'success')
- break;
+ makeToast(i18n.tc("Success"), i18n.tc("success_deleting_resource"), "success")
+ break
+ case StandardToasts.SUCCESS_MOVE:
+ makeToast(i18n.tc("Success"), i18n.tc("success_moving_resource"), "success")
+ break
+ case StandardToasts.SUCCESS_MERGE:
+ makeToast(i18n.tc("Success"), i18n.tc("success_merging_resource"), "success")
+ break
case StandardToasts.FAIL_CREATE:
- makeToast(i18n.tc('Failure'), i18n.tc('err_creating_resource'), 'danger')
- break;
+ makeToast(i18n.tc("Failure"), i18n.tc("err_creating_resource"), "danger")
+ break
case StandardToasts.FAIL_FETCH:
- makeToast(i18n.tc('Failure'), i18n.tc('err_fetching_resource'), 'danger')
- break;
+ makeToast(i18n.tc("Failure"), i18n.tc("err_fetching_resource"), "danger")
+ break
case StandardToasts.FAIL_UPDATE:
- makeToast(i18n.tc('Failure'), i18n.tc('err_updating_resource'), 'danger')
- break;
+ makeToast(i18n.tc("Failure"), i18n.tc("err_updating_resource"), "danger")
+ break
case StandardToasts.FAIL_DELETE:
- makeToast(i18n.tc('Failure'), i18n.tc('err_deleting_resource'), 'danger')
- break;
-
+ makeToast(i18n.tc("Failure"), i18n.tc("err_deleting_resource"), "danger")
+ break
+ case StandardToasts.FAIL_MOVE:
+ makeToast(i18n.tc("Failure"), i18n.tc("err_moving_resource") + (err_details ? "\n" + err_details : ""), "danger")
+ break
+ case StandardToasts.FAIL_MERGE:
+ makeToast(i18n.tc("Failure"), i18n.tc("err_merging_resource") + (err_details ? "\n" + err_details : ""), "danger")
+ break
}
}
}
-
/*
-* Utility functions to use djangos gettext
-* */
+ * Utility functions to use djangos gettext
+ * */
export const GettextMixin = {
methods: {
@@ -77,8 +102,8 @@ export const GettextMixin = {
*/
_: function (param) {
return djangoGettext(param)
- }
- }
+ },
+ },
}
export function djangoGettext(param) {
@@ -86,8 +111,8 @@ export function djangoGettext(param) {
}
/*
-* Utility function to use djangos named urls
-* */
+ * Utility function to use djangos named urls
+ * */
// uses https://github.com/ierror/django-js-reverse#use-the-urls-in-javascript
export const ResolveUrlMixin = {
@@ -99,50 +124,48 @@ export const ResolveUrlMixin = {
*/
resolveDjangoUrl: function (url, params = null) {
return resolveDjangoUrl(url, params)
- }
- }
+ },
+ },
}
export function resolveDjangoUrl(url, params = null) {
if (params == null) {
return window.Urls[url]()
- } else if (typeof(params) != "object") {
+ } else if (typeof params != "object") {
return window.Urls[url](params)
- } else if (typeof(params) == "object") {
+ } else if (typeof params == "object") {
if (params.length === 1) {
return window.Urls[url](params)
} else if (params.length === 2) {
- return window.Urls[url](params[0],params[1])
+ return window.Urls[url](params[0], params[1])
} else if (params.length === 3) {
- return window.Urls[url](params[0],params[1],params[2])
+ return window.Urls[url](params[0], params[1], params[2])
}
}
}
/*
-* other utilities
-* */
+ * other utilities
+ * */
export function getUserPreference(pref) {
- if(window.USER_PREF === undefined) {
- return undefined;
+ if (window.USER_PREF === undefined) {
+ return undefined
}
return window.USER_PREF[pref]
}
-import {frac} from "@/utils/fractions";
-
export function calculateAmount(amount, factor) {
- if (getUserPreference('use_fractions')) {
- let return_string = ''
- let fraction = frac((amount * factor), 10, true)
+ if (getUserPreference("use_fractions")) {
+ let return_string = ""
+ let fraction = frac(amount * factor, 10, true)
if (fraction[0] > 0) {
return_string += fraction[0]
}
if (fraction[1] > 0) {
- return_string += `
${(fraction[1])}⁄
${(fraction[2])}`
+ return_string += `
${fraction[1]}⁄
${fraction[2]}`
}
return return_string
@@ -152,23 +175,23 @@ export function calculateAmount(amount, factor) {
}
export function roundDecimals(num) {
- let decimals = ((getUserPreference('user_fractions')) ? getUserPreference('user_fractions') : 2);
- return +(Math.round(num + `e+${decimals}`) + `e-${decimals}`);
+ let decimals = getUserPreference("user_fractions") ? getUserPreference("user_fractions") : 2
+ return +(Math.round(num + `e+${decimals}`) + `e-${decimals}`)
}
const KILOJOULES_PER_CALORIE = 4.18
export function calculateEnergy(amount, factor) {
- if (getUserPreference('use_kj')) {
+ if (getUserPreference("use_kj")) {
let joules = amount * KILOJOULES_PER_CALORIE
- return calculateAmount(joules, factor) + ' kJ'
+ return calculateAmount(joules, factor) + " kJ"
} else {
- return calculateAmount(amount, factor) + ' kcal'
+ return calculateAmount(amount, factor) + " kcal"
}
}
export function convertEnergyToCalories(amount) {
- if (getUserPreference('use_kj')) {
+ if (getUserPreference("use_kj")) {
return amount / KILOJOULES_PER_CALORIE
} else {
return amount
@@ -176,33 +199,25 @@ export function convertEnergyToCalories(amount) {
}
export function energyHeading() {
- if (getUserPreference('use_kj')) {
- return 'Energy'
+ if (getUserPreference("use_kj")) {
+ return "Energy"
} else {
- return 'Calories'
+ return "Calories"
}
}
-/*
-* Utility functions to use OpenAPIs generically
-* */
-import {ApiApiFactory} from "@/utils/openapi/api.ts";
-
-import axios from "axios";
-axios.defaults.xsrfCookieName = 'csrftoken'
+axios.defaults.xsrfCookieName = "csrftoken"
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
-import { Actions, Models } from './models';
-import {RequestArgs} from "@/utils/openapi/base";
export const ApiMixin = {
data() {
return {
Models: Models,
- Actions: Actions
+ Actions: Actions,
}
},
methods: {
- genericAPI: function(model, action, options) {
+ genericAPI: function (model, action, options) {
let setup = getConfig(model, action)
if (setup?.config?.function) {
return specialCases[setup.config.function](action, options, setup)
@@ -212,10 +227,10 @@ export const ApiMixin = {
let apiClient = new ApiApiFactory()
return apiClient[func](...parameters)
},
- genericGetAPI: function(url, options) {
- return axios.get(this.resolveDjangoUrl(url), {'params':options, 'emulateJSON': true})
- }
- }
+ genericGetAPI: function (url, options) {
+ return axios.get(this.resolveDjangoUrl(url), { params: options, emulateJSON: true })
+ },
+ },
}
// /*
@@ -223,37 +238,37 @@ export const ApiMixin = {
// * */
function formatParam(config, value, options) {
if (config) {
- for (const [k, v] of Object.entries(config)) {
- switch(k) {
- case 'type':
- switch(v) {
- case 'string':
+ for (const [k, v] of Object.entries(config)) {
+ switch (k) {
+ case "type":
+ switch (v) {
+ case "string":
if (Array.isArray(value)) {
let tmpValue = []
- value.forEach(x => tmpValue.push(String(x)))
+ value.forEach((x) => tmpValue.push(String(x)))
value = tmpValue
} else if (value !== undefined) {
value = String(value)
}
- break;
- case 'integer':
+ break
+ case "integer":
if (Array.isArray(value)) {
let tmpValue = []
- value.forEach(x => tmpValue.push(parseInt(x)))
+ value.forEach((x) => tmpValue.push(parseInt(x)))
value = tmpValue
} else if (value !== undefined) {
value = parseInt(value)
}
- break;
+ break
}
- break;
- case 'function':
+ break
+ case "function":
// needs wrapped in a promise and wait for the called function to complete before moving on
specialCases[v](value, options)
- break;
+ break
}
}
- }
+ }
return value
}
function buildParams(options, setup) {
@@ -280,60 +295,56 @@ function buildParams(options, setup) {
this_value = getDefault(config?.[item], options)
}
parameters.push(this_value)
- });
+ })
return parameters
}
function getDefault(config, options) {
let value = undefined
value = config?.default ?? undefined
- if (typeof(value) === 'object') {
+ if (typeof value === "object") {
let condition = false
- switch(value.function) {
+ switch (value.function) {
// CONDITIONAL case requires 4 keys:
// - check: which other OPTIONS key to check against
// - operator: what type of operation to perform
// - true: what value to assign when true
// - false: what value to assign when false
- case 'CONDITIONAL':
- switch(value.operator) {
- case 'not_exist':
- condition = (
- (!options?.[value.check] ?? undefined)
- || options?.[value.check]?.length == 0
- )
+ case "CONDITIONAL":
+ switch (value.operator) {
+ case "not_exist":
+ condition = (!options?.[value.check] ?? undefined) || options?.[value.check]?.length == 0
if (condition) {
value = value.true
} else {
value = value.false
}
- break;
+ break
}
- break;
+ break
}
}
return value
}
export function getConfig(model, action) {
-
let f = action.function
// if not defined partialUpdate will use params from create
- if (f === 'partialUpdate' && !model?.[f]?.params) {
- model[f] = {'params': [...['id'], ...model.create.params]}
+ if (f === "partialUpdate" && !model?.[f]?.params) {
+ model[f] = { params: [...["id"], ...model.create.params] }
}
-
+
let config = {
- 'name': model.name,
- 'apiName': model.apiName,
+ name: model.name,
+ apiName: model.apiName,
}
// spread operator merges dictionaries - last item in list takes precedence
- config = {...config, ...action, ...model.model_type?.[f], ...model?.[f]}
+ config = { ...config, ...action, ...model.model_type?.[f], ...model?.[f] }
// nested dictionaries are not merged - so merge again on any nested keys
- config.config = {...action?.config, ...model.model_type?.[f]?.config, ...model?.[f]?.config}
+ config.config = { ...action?.config, ...model.model_type?.[f]?.config, ...model?.[f]?.config }
// look in partialUpdate again if necessary
- if (f === 'partialUpdate' && Object.keys(config.config).length === 0) {
- config.config = {...model.model_type?.create?.config, ...model?.create?.config}
+ if (f === "partialUpdate" && Object.keys(config.config).length === 0) {
+ config.config = { ...model.model_type?.create?.config, ...model?.create?.config }
}
- config['function'] = f + config.apiName + (config?.suffix ?? '') // parens are required to force optional chaining to evaluate before concat
+ config["function"] = f + config.apiName + (config?.suffix ?? "") // parens are required to force optional chaining to evaluate before concat
return config
}
@@ -342,181 +353,175 @@ export function getConfig(model, action) {
// * */
export function getForm(model, action, item1, item2) {
let f = action.function
- let config = {...action?.form, ...model.model_type?.[f]?.form, ...model?.[f]?.form}
- // if not defined partialUpdate will use form from create
- if (f === 'partialUpdate' && Object.keys(config).length == 0) {
- config = {...Actions.CREATE?.form, ...model.model_type?.['create']?.form, ...model?.['create']?.form}
- config['title'] = {...action?.form_title, ...model.model_type?.[f]?.form_title, ...model?.[f]?.form_title}
+ let config = { ...action?.form, ...model.model_type?.[f]?.form, ...model?.[f]?.form }
+ // if not defined partialUpdate will use form from create
+ if (f === "partialUpdate" && Object.keys(config).length == 0) {
+ config = { ...Actions.CREATE?.form, ...model.model_type?.["create"]?.form, ...model?.["create"]?.form }
+ config["title"] = { ...action?.form_title, ...model.model_type?.[f]?.form_title, ...model?.[f]?.form_title }
}
- let form = {'fields': []}
- let value = ''
+ let form = { fields: [] }
+ let value = ""
for (const [k, v] of Object.entries(config)) {
- if (v?.function){
- switch(v.function) {
- case 'translate':
+ if (v?.function) {
+ switch (v.function) {
+ case "translate":
value = formTranslate(v, model, item1, item2)
}
} else {
value = v
}
if (value?.form_field) {
- value['value'] = item1?.[value?.field] ?? undefined
- form.fields.push(
- {
- ...value,
- ...{
- 'label': formTranslate(value?.label, model, item1, item2),
- 'placeholder': formTranslate(value?.placeholder, model, item1, item2)
- }
- }
- )
+ value["value"] = item1?.[value?.field] ?? undefined
+ form.fields.push({
+ ...value,
+ ...{
+ label: formTranslate(value?.label, model, item1, item2),
+ placeholder: formTranslate(value?.placeholder, model, item1, item2),
+ },
+ })
} else {
form[k] = value
}
}
return form
-
}
function formTranslate(translate, model, item1, item2) {
- if (typeof(translate) !== 'object') {return translate}
+ if (typeof translate !== "object") {
+ return translate
+ }
let phrase = translate.phrase
let options = {}
let obj = undefined
translate?.params.forEach(function (x, index) {
- switch(x.from){
- case 'item1':
+ switch (x.from) {
+ case "item1":
obj = item1
- break;
- case 'item2':
+ break
+ case "item2":
obj = item2
- break;
- case 'model':
+ break
+ case "model":
obj = model
}
options[x.token] = obj[x.attribute]
})
return i18n.t(phrase, options)
-
}
-// /*
-// * Utility functions to use manipulate nested components
-// * */
-import Vue from 'vue'
export const CardMixin = {
methods: {
- findCard: function(id, card_list){
+ findCard: function (id, card_list) {
let card_length = card_list?.length ?? 0
if (card_length == 0) {
- return false
+ return false
}
- let cards = card_list.filter(obj => obj.id == id)
+ let cards = card_list.filter((obj) => obj.id == id)
if (cards.length == 1) {
- return cards[0]
+ return cards[0]
} else if (cards.length == 0) {
- for (const c of card_list.filter(x => x.show_children == true)) {
- cards = this.findCard(id, c.children)
- if (cards) {
- return cards
+ for (const c of card_list.filter((x) => x.show_children == true)) {
+ cards = this.findCard(id, c.children)
+ if (cards) {
+ return cards
+ }
}
- }
} else {
- console.log('something terrible happened')
+ console.log("something terrible happened")
}
},
- destroyCard: function(id, card_list) {
+ destroyCard: function (id, card_list) {
let card = this.findCard(id, card_list)
let p_id = card?.parent ?? undefined
-
+
if (p_id) {
let parent = this.findCard(p_id, card_list)
- if (parent){
- Vue.set(parent, 'numchild', parent.numchild - 1)
+ if (parent) {
+ Vue.set(parent, "numchild", parent.numchild - 1)
if (parent.show_children) {
- let idx = parent.children.indexOf(parent.children.find(x => x.id === id))
+ let idx = parent.children.indexOf(parent.children.find((x) => x.id === id))
Vue.delete(parent.children, idx)
}
}
}
- return card_list.filter(x => x.id != id)
- },
- refreshCard: function(obj, card_list){
+ return card_list.filter((x) => x.id != id)
+ },
+ refreshCard: function (obj, card_list) {
let target = {}
let idx = undefined
target = this.findCard(obj.id, card_list)
-
+
if (target) {
- idx = card_list.indexOf(card_list.find(x => x.id === target.id))
+ idx = card_list.indexOf(card_list.find((x) => x.id === target.id))
Vue.set(card_list, idx, obj)
}
if (target?.parent) {
let parent = this.findCard(target.parent, card_list)
if (parent) {
- if (parent.show_children){
- idx = parent.children.indexOf(parent.children.find(x => x.id === target.id))
+ if (parent.show_children) {
+ idx = parent.children.indexOf(parent.children.find((x) => x.id === target.id))
Vue.set(parent.children, idx, obj)
}
}
}
},
- }
+ },
}
-
const specialCases = {
// the supermarket API requires chaining promises together, instead of trying to make
// this use case generic just treat it as a unique use case
- SupermarketWithCategories: function(action, options, setup) {
+ SupermarketWithCategories: function (action, options, setup) {
let API = undefined
let GenericAPI = ApiMixin.methods.genericAPI
let params = []
- if (action.function === 'partialUpdate') {
+ if (action.function === "partialUpdate") {
API = GenericAPI
- params = [Models.SUPERMARKET, Actions.FETCH, {'id': options.id}]
-
- } else if (action.function === 'create') {
+ params = [Models.SUPERMARKET, Actions.FETCH, { id: options.id }]
+ } else if (action.function === "create") {
API = new ApiApiFactory()[setup.function]
params = buildParams(options, setup)
}
- return API(...params).then((result) => {
- // either get the supermarket or create the supermarket (but without the category relations)
- return result.data
- }).then((result) => {
- // delete, update or change all of the category/relations
- let id = result.id
- let existing_categories = result.category_to_supermarket
- let updated_categories = options.category_to_supermarket
-
- let promises = []
- // if the 'category.name' key does not exist on the updated_categories, the categories were not updated
- if (updated_categories?.[0]?.category?.name) {
- // list of category relationship ids that are not part of the updated supermarket
- let removed_categories = existing_categories.filter(x => !updated_categories.map(x => x.category.id).includes(x.category.id))
- let added_categories = updated_categories.filter(x => !existing_categories.map(x => x.category.id).includes(x.category.id))
- let changed_categories = updated_categories.filter(x => existing_categories.map(x => x.category.id).includes(x.category.id))
-
- removed_categories.forEach(x => {
- promises.push(GenericAPI(Models.SHOPPING_CATEGORY_RELATION, Actions.DELETE, {'id': x.id}))
- })
- let item = {'supermarket': id}
- added_categories.forEach(x => {
- item.order = x.order
- item.category = {'id': x.category.id, 'name': x.category.name}
- promises.push(GenericAPI(Models.SHOPPING_CATEGORY_RELATION, Actions.CREATE, item))
- })
- changed_categories.forEach(x => {
- item.id = x?.id ?? existing_categories.find(y => y.category.id === x.category.id).id;
- item.order = x.order
- item.category = {'id': x.category.id, 'name': x.category.name}
- promises.push(GenericAPI(Models.SHOPPING_CATEGORY_RELATION, Actions.UPDATE, item))
- })
- }
-
- return Promise.all(promises).then(() => {
- // finally get and return the Supermarket which everything downstream is expecting
- return GenericAPI(Models.SUPERMARKET, Actions.FETCH, {'id': id})
+ return API(...params)
+ .then((result) => {
+ // either get the supermarket or create the supermarket (but without the category relations)
+ return result.data
})
- })
- }
+ .then((result) => {
+ // delete, update or change all of the category/relations
+ let id = result.id
+ let existing_categories = result.category_to_supermarket
+ let updated_categories = options.category_to_supermarket
+
+ let promises = []
+ // if the 'category.name' key does not exist on the updated_categories, the categories were not updated
+ if (updated_categories?.[0]?.category?.name) {
+ // list of category relationship ids that are not part of the updated supermarket
+ let removed_categories = existing_categories.filter((x) => !updated_categories.map((x) => x.category.id).includes(x.category.id))
+ let added_categories = updated_categories.filter((x) => !existing_categories.map((x) => x.category.id).includes(x.category.id))
+ let changed_categories = updated_categories.filter((x) => existing_categories.map((x) => x.category.id).includes(x.category.id))
+
+ removed_categories.forEach((x) => {
+ promises.push(GenericAPI(Models.SHOPPING_CATEGORY_RELATION, Actions.DELETE, { id: x.id }))
+ })
+ let item = { supermarket: id }
+ added_categories.forEach((x) => {
+ item.order = x.order
+ item.category = { id: x.category.id, name: x.category.name }
+ promises.push(GenericAPI(Models.SHOPPING_CATEGORY_RELATION, Actions.CREATE, item))
+ })
+ changed_categories.forEach((x) => {
+ item.id = x?.id ?? existing_categories.find((y) => y.category.id === x.category.id).id
+ item.order = x.order
+ item.category = { id: x.category.id, name: x.category.name }
+ promises.push(GenericAPI(Models.SHOPPING_CATEGORY_RELATION, Actions.UPDATE, item))
+ })
+ }
+
+ return Promise.all(promises).then(() => {
+ // finally get and return the Supermarket which everything downstream is expecting
+ return GenericAPI(Models.SUPERMARKET, Actions.FETCH, { id: id })
+ })
+ })
+ },
}