Continue converting to composition api
This commit is contained in:
parent
0d35f50dbf
commit
67c23015ab
@ -42,8 +42,18 @@ class ApplicationController < ActionController::Base
|
|||||||
if owner
|
if owner
|
||||||
yield if block_given?
|
yield if block_given?
|
||||||
else
|
else
|
||||||
flash[:warning] = "Operation Not Permitted"
|
respond_to do |format|
|
||||||
redirect_to root_path
|
format.html do
|
||||||
|
flash[:warning] = "Operation Not Permitted"
|
||||||
|
redirect_to root_path
|
||||||
|
end
|
||||||
|
|
||||||
|
format.json do
|
||||||
|
render json: { error: "Operation Not Permitted" }, status: current_user.nil? ? :unauthorized : :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,8 +34,10 @@ class CalculatorController < ApplicationController
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
input_unit = UnitConversion.parse(input)
|
input_unit = UnitConversion.parse(input)
|
||||||
|
input_unit.unitwise
|
||||||
rescue UnitConversion::UnparseableUnitError => e
|
rescue UnitConversion::UnparseableUnitError => e
|
||||||
data[:errors][:input] << 'invalid string'
|
data[:errors][:input] << e.message
|
||||||
|
input_unit = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if !input_unit.nil?
|
if !input_unit.nil?
|
||||||
|
@ -50,7 +50,7 @@ class UsersController < ApplicationController
|
|||||||
if @user.save
|
if @user.save
|
||||||
set_current_user(@user)
|
set_current_user(@user)
|
||||||
format.html { redirect_to root_path, notice: 'User created.' }
|
format.html { redirect_to root_path, notice: 'User created.' }
|
||||||
format.json { render :show, status: :created, location: @user }
|
format.json { render json: UserSerializer.for(@user), status: :created, location: @user }
|
||||||
else
|
else
|
||||||
format.html { render :new }
|
format.html { render :new }
|
||||||
format.json { render json: @user.errors, status: :unprocessable_entity }
|
format.json { render json: @user.errors, status: :unprocessable_entity }
|
||||||
@ -68,7 +68,7 @@ class UsersController < ApplicationController
|
|||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @user.update(user_params)
|
if @user.update(user_params)
|
||||||
format.html { redirect_to root_path, notice: 'User updated.' }
|
format.html { redirect_to root_path, notice: 'User updated.' }
|
||||||
format.json { render :show, status: :created, location: @user }
|
format.json { render json: UserSerializer.for(@user) , status: :created, location: @user }
|
||||||
else
|
else
|
||||||
format.html { render :edit }
|
format.html { render :edit }
|
||||||
format.json { render json: @user.errors, status: :unprocessable_entity }
|
format.json { render json: @user.errors, status: :unprocessable_entity }
|
||||||
|
@ -145,7 +145,7 @@
|
|||||||
emit("update:modelValue", newValue);
|
emit("update:modelValue", newValue);
|
||||||
|
|
||||||
if (newValue.length >= Math.max(1, props.minLength)) {
|
if (newValue.length >= Math.max(1, props.minLength)) {
|
||||||
this.updateOptions(newValue);
|
updateOptions(newValue);
|
||||||
} else {
|
} else {
|
||||||
isListOpen.value = false;
|
isListOpen.value = false;
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<app-modal :open="open" :title="message" @dismiss="runCancel">
|
<app-modal :open="open" :title="title" @dismiss="runCancel">
|
||||||
<div class="buttons">
|
<p class="is-size-5">{{ message }}</p>
|
||||||
<button type="button" class="button is-primary" @click="runConfirm">OK</button>
|
|
||||||
<button type="button" class="button" @click="runCancel">Cancel</button>
|
<template #footer>
|
||||||
</div>
|
<div class="buttons">
|
||||||
|
<button type="button" class="button is-primary" @click="runConfirm">OK</button>
|
||||||
|
<button type="button" class="button" @click="runCancel">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</app-modal>
|
</app-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
|
const emit = defineEmits(["cancel", "confirm"]);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
cancel: {
|
|
||||||
type: Function,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
|
|
||||||
confirm: {
|
|
||||||
type: Function,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
|
|
||||||
message: {
|
message: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: 'Are you sure?'
|
default: 'Are you sure?'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: "Confirm"
|
||||||
|
},
|
||||||
|
|
||||||
open: {
|
open: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true
|
required: true
|
||||||
@ -33,11 +35,11 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function runConfirm() {
|
function runConfirm() {
|
||||||
props.confirm();
|
emit("confirm");
|
||||||
}
|
}
|
||||||
|
|
||||||
function runCancel() {
|
function runCancel() {
|
||||||
props.cancel();
|
emit("cancel");
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<app-text-field :value="stringValue" @input="input" :label="label" type="date"></app-text-field>
|
<app-text-field :value="stringValue" @update:modelValue="input" :label="label" type="date"></app-text-field>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<div :class="['popup', 'modal', { 'is-wide': wide, 'is-active': open && error === null }]">
|
<div :class="['modal', { 'is-wide': wide, 'is-active': open && error === null }]">
|
||||||
<div class="modal-background" @click="close"></div>
|
<div class="modal-background" @click="close"></div>
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
@ -13,6 +13,11 @@
|
|||||||
<section class="modal-card-body">
|
<section class="modal-card-body">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<slot name="footer">
|
||||||
|
</slot>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav v-show="totalPages > 1 || showWithSinglePage" class="pagination" role="navigation" :aria-label="pagedItemName + ' page navigation'">
|
<nav v-show="totalPages > 1 || showWithSinglePage" class="pagination" role="navigation" :aria-label="pagedItemName + ' page navigation'">
|
||||||
<a class="pagination-previous" :title="isFirstPage ? 'This is the first page' : ''" :disabled="isFirstPage" @click.prevent="changePage(currentPage - 1)">Previous</a>
|
<a :class="{'pagination-previous': true, 'is-disabled': isFirstPage}" :title="isFirstPage ? 'This is the first page' : ''" @click.prevent="changePage(currentPage - 1)">Previous</a>
|
||||||
<a class="pagination-next" :title="isLastPage ? 'This is the last page' : ''" :disabled="isLastPage" @click.prevent="changePage(currentPage + 1)">Next page</a>
|
<a :class="{'pagination-next': true, 'is-disabled': isLastPage}" :title="isLastPage ? 'This is the last page' : ''" @click.prevent="changePage(currentPage + 1)">Next page</a>
|
||||||
<ul class="pagination-list">
|
<ul class="pagination-list">
|
||||||
<li v-for="page in pageItems" :key="page">
|
<li v-for="page in pageItems" :key="page">
|
||||||
<a v-if="page > 0" class="pagination-link" :class="{'is-current': page === currentPage}" href="#" @click.prevent="changePage(page)">{{page}}</a>
|
<a v-if="page > 0" class="pagination-link" :class="{'is-current': page === currentPage}" href="#" @click.prevent="changePage(page)">{{page}}</a>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<app-rating readonly :value="log.rating"></app-rating>
|
<app-rating readonly :model-value="log.rating"></app-rating>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,173 +43,155 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { useMediaQueryStore } from "../stores/mediaQuery";
|
||||||
import RecipeEditIngredientItem from "./RecipeEditIngredientItem";
|
import RecipeEditIngredientItem from "./RecipeEditIngredientItem";
|
||||||
|
|
||||||
import { mapState } from "pinia";
|
const mediaQueryStore = useMediaQueryStore();
|
||||||
import { useMediaQueryStore } from "../stores/mediaQuery";
|
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
ingredients: {
|
||||||
ingredients: {
|
required: true,
|
||||||
required: true,
|
type: Array
|
||||||
type: Array
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isBulkEditing: false,
|
|
||||||
bulkEditText: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapState(useMediaQueryStore, {
|
|
||||||
isMobile: store => store.mobile
|
|
||||||
}),
|
|
||||||
bulkIngredientPreview() {
|
|
||||||
if (this.bulkEditText === null) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const regex = /^\s*(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,|]+?|.+\|)(?:,\s*([^|]*?))?(?:\s*\[(\d+)\]\s*)?$/i;
|
|
||||||
|
|
||||||
const magicFunc = function(str) {
|
|
||||||
if (str === "-") {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const parsed = [];
|
|
||||||
const lines = this.bulkEditText.replace("\r", "").split("\n");
|
|
||||||
|
|
||||||
for (let line of lines) {
|
|
||||||
if (line.length === 0) { continue; }
|
|
||||||
|
|
||||||
const match = line.match(regex);
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
const matchedName = match[3].replace(/\|\s*$/, "");
|
|
||||||
let item = {quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(matchedName), preparation: magicFunc(match[4]), id: match[5] || null};
|
|
||||||
parsed.push(item);
|
|
||||||
} else {
|
|
||||||
parsed.push(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsed;
|
|
||||||
},
|
|
||||||
|
|
||||||
visibleIngredients() {
|
|
||||||
return this.ingredients.filter(i => i._destroy !== true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
createIngredient() {
|
|
||||||
const sort_orders = this.ingredients.map(i => i.sort_order);
|
|
||||||
sort_orders.push(0);
|
|
||||||
const next_sort_order = Math.max(...sort_orders) + 5;
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: null,
|
|
||||||
quantity: null,
|
|
||||||
units: null,
|
|
||||||
name: null,
|
|
||||||
preparation: null,
|
|
||||||
ingredient_id: null,
|
|
||||||
sort_order: next_sort_order
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
addIngredient() {
|
|
||||||
this.ingredients.push(this.createIngredient());
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteFood(food) {
|
|
||||||
if (food.id) {
|
|
||||||
food._destroy = true;
|
|
||||||
} else {
|
|
||||||
const idx = this.ingredients.findIndex(i => i === food);
|
|
||||||
this.ingredients.splice(idx, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
bulkEditIngredients() {
|
|
||||||
this.isBulkEditing = true;
|
|
||||||
|
|
||||||
let text = [];
|
|
||||||
|
|
||||||
for (let item of this.visibleIngredients) {
|
|
||||||
text.push(
|
|
||||||
item.quantity + " " +
|
|
||||||
(item.units || "-") + " " +
|
|
||||||
(item.name.indexOf(",") >= 0 ? item.name + "|" : item.name) +
|
|
||||||
(item.preparation ? (", " + item.preparation) : "") +
|
|
||||||
(item.id ? (" [" + item.id + "]") : "")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bulkEditText = text.join("\n");
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelBulkEditing() {
|
|
||||||
this.isBulkEditing = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
saveBulkEditing() {
|
|
||||||
const parsed = this.bulkIngredientPreview.filter(i => i !== null);
|
|
||||||
const existing = [...this.ingredients];
|
|
||||||
const newList = [];
|
|
||||||
|
|
||||||
for (let parsedIngredient of parsed) {
|
|
||||||
let newIngredient = null;
|
|
||||||
|
|
||||||
if (parsedIngredient.id !== null) {
|
|
||||||
let intId = parseInt(parsedIngredient.id);
|
|
||||||
let exIdx = existing.findIndex(i => i.id === intId);
|
|
||||||
if (exIdx >= 0) {
|
|
||||||
let ex = existing[exIdx];
|
|
||||||
if (ex.name === parsedIngredient.name) {
|
|
||||||
newIngredient = ex;
|
|
||||||
existing.splice(exIdx, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newIngredient === null) {
|
|
||||||
newIngredient = this.createIngredient();
|
|
||||||
}
|
|
||||||
|
|
||||||
newIngredient.quantity = parsedIngredient.quantity;
|
|
||||||
newIngredient.units = parsedIngredient.units;
|
|
||||||
newIngredient.name = parsedIngredient.name;
|
|
||||||
newIngredient.preparation = parsedIngredient.preparation;
|
|
||||||
newList.push(newIngredient);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let oldExisting of existing.filter(i => i.id !== null)) {
|
|
||||||
newList.push({id: oldExisting.id, _destroy: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ingredients.splice(0);
|
|
||||||
let sortIdx = 0;
|
|
||||||
for (let n of newList) {
|
|
||||||
n.sort_order = sortIdx++;
|
|
||||||
this.ingredients.push(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isBulkEditing = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
RecipeEditIngredientItem
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const isBulkEditing = ref(false);
|
||||||
|
const bulkEditText = ref(null);
|
||||||
|
const isMobile = computed(() => mediaQueryStore.mobile);
|
||||||
|
const visibleIngredients = computed(() => props.ingredients.filter(i => i._destroy !== true));
|
||||||
|
|
||||||
|
const bulkIngredientPreview = computed(() => {
|
||||||
|
if (bulkEditText.value === null || bulkEditText.value === "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const regex = /^\s*(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,|]+?|.+\|)(?:,\s*([^|]*?))?(?:\s*\[(\d+)\]\s*)?$/i;
|
||||||
|
|
||||||
|
const magicFunc = function(str) {
|
||||||
|
if (str === "-") {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsed = [];
|
||||||
|
const lines = bulkEditText.value.replace("\r", "").split("\n");
|
||||||
|
|
||||||
|
for (let line of lines) {
|
||||||
|
if (line.length === 0) { continue; }
|
||||||
|
|
||||||
|
const match = line.match(regex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const matchedName = match[3].replace(/\|\s*$/, "");
|
||||||
|
let item = {quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(matchedName), preparation: magicFunc(match[4]), id: match[5] || null};
|
||||||
|
parsed.push(item);
|
||||||
|
} else {
|
||||||
|
parsed.push(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
|
||||||
|
function createIngredient() {
|
||||||
|
const sort_orders = props.ingredients.map(i => i.sort_order);
|
||||||
|
sort_orders.push(0);
|
||||||
|
const next_sort_order = Math.max(...sort_orders) + 5;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: null,
|
||||||
|
quantity: null,
|
||||||
|
units: null,
|
||||||
|
name: null,
|
||||||
|
preparation: null,
|
||||||
|
ingredient_id: null,
|
||||||
|
sort_order: next_sort_order
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addIngredient() {
|
||||||
|
props.ingredients.push(createIngredient());
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFood(food) {
|
||||||
|
if (food.id) {
|
||||||
|
food._destroy = true;
|
||||||
|
} else {
|
||||||
|
const idx = props.ingredients.findIndex(i => i === food);
|
||||||
|
props.ingredients.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bulkEditIngredients() {
|
||||||
|
isBulkEditing.value = true;
|
||||||
|
|
||||||
|
let text = [];
|
||||||
|
|
||||||
|
for (let item of visibleIngredients.value) {
|
||||||
|
text.push(
|
||||||
|
item.quantity + " " +
|
||||||
|
(item.units || "-") + " " +
|
||||||
|
(item.name.indexOf(",") >= 0 ? item.name + "|" : item.name) +
|
||||||
|
(item.preparation ? (", " + item.preparation) : "") +
|
||||||
|
(item.id ? (" [" + item.id + "]") : "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkEditText.value = text.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelBulkEditing() {
|
||||||
|
isBulkEditing.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveBulkEditing() {
|
||||||
|
const parsed = bulkIngredientPreview.value.filter(i => i !== null);
|
||||||
|
const existing = [...props.ingredients];
|
||||||
|
const newList = [];
|
||||||
|
|
||||||
|
for (let parsedIngredient of parsed) {
|
||||||
|
let newIngredient = null;
|
||||||
|
|
||||||
|
if (parsedIngredient.id !== null) {
|
||||||
|
let intId = parseInt(parsedIngredient.id);
|
||||||
|
let exIdx = existing.findIndex(i => i.id === intId);
|
||||||
|
if (exIdx >= 0) {
|
||||||
|
let ex = existing[exIdx];
|
||||||
|
if (ex.name === parsedIngredient.name) {
|
||||||
|
newIngredient = ex;
|
||||||
|
existing.splice(exIdx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIngredient === null) {
|
||||||
|
newIngredient = createIngredient();
|
||||||
|
}
|
||||||
|
|
||||||
|
newIngredient.quantity = parsedIngredient.quantity;
|
||||||
|
newIngredient.units = parsedIngredient.units;
|
||||||
|
newIngredient.name = parsedIngredient.name;
|
||||||
|
newIngredient.preparation = parsedIngredient.preparation;
|
||||||
|
newList.push(newIngredient);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let oldExisting of existing.filter(i => i.id !== null)) {
|
||||||
|
newList.push({id: oldExisting.id, _destroy: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
props.ingredients.splice(0);
|
||||||
|
let sortIdx = 0;
|
||||||
|
for (let n of newList) {
|
||||||
|
n.sort_order = sortIdx++;
|
||||||
|
props.ingredients.push(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
isBulkEditing.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -44,55 +44,50 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { useTemplateRef, watch } from "vue";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
export default {
|
const emit = defineEmits(["deleteFood"]);
|
||||||
props: {
|
const props = defineProps({
|
||||||
ingredient: {
|
ingredient: {
|
||||||
required: true,
|
required: true,
|
||||||
type: Object
|
type: Object
|
||||||
},
|
|
||||||
showLabels: {
|
|
||||||
required: false,
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
showLabels: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
methods: {
|
const autocompleteElement = useTemplateRef("autocomplete");
|
||||||
deleteFood(ingredient) {
|
|
||||||
this.$emit("deleteFood", ingredient);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSearchItems(text) {
|
watch(props.ingredient.name, (val) => {
|
||||||
return api.getSearchIngredients(text);
|
if (props.ingredient.ingredient && props.ingredient.ingredient.name !== val) {
|
||||||
},
|
props.ingredient.ingredient_id = null;
|
||||||
|
props.ingredient.ingredient = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
searchItemSelected(ingredient) {
|
function deleteFood(ingredient) {
|
||||||
this.ingredient.ingredient_id = ingredient.id;
|
emit("deleteFood", ingredient);
|
||||||
this.ingredient.ingredient = ingredient;
|
}
|
||||||
this.ingredient.name = ingredient.name;
|
|
||||||
},
|
|
||||||
|
|
||||||
nameClick() {
|
function updateSearchItems(text) {
|
||||||
if (this.ingredient.ingredient_id === null && this.ingredient.name !== null && this.ingredient.name.length > 2) {
|
return api.getSearchIngredients(text);
|
||||||
this.$refs.autocomplete.updateOptions(this.ingredient.name);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
function searchItemSelected(ingredient) {
|
||||||
'ingredient.name': function(val) {
|
props.ingredient.ingredient_id = ingredient.id;
|
||||||
if (this.ingredient.ingredient && this.ingredient.ingredient.name !== val) {
|
props.ingredient.ingredient = ingredient;
|
||||||
this.ingredient.ingredient_id = null;
|
props.ingredient.name = ingredient.name;
|
||||||
this.ingredient.ingredient = null;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
function nameClick() {
|
||||||
|
if (props.ingredient.ingredient_id === null && props.ingredient.name !== null && props.ingredient.name.length > 2) {
|
||||||
|
autocompleteElement.updateOptions(props.ingredient.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
Ingredients
|
Ingredients
|
||||||
<button class="button is-small is-primary" type="button" @click="showConvertDialog = true">Convert</button>
|
<button class="button is-small is-primary" type="button" @click="showConvertDialog = true">Convert</button>
|
||||||
<app-dropdown :open="addToTasksMenuOpen" label="Add to list" button-class="is-small is-primary" @open="addToTasksMenuOpen = true" @close="addToTasksMenuOpen = false">
|
<app-dropdown :open="addToTasksMenuOpen" label="Add to list" button-class="is-small is-primary" @open="addToTasksMenuOpen = true" @close="addToTasksMenuOpen = false">
|
||||||
<button class="button primary" v-for="tl in taskLists" :key="tl.id" @click="addRecipeToList(tl)">
|
<button class="button primary" v-for="tl in taskStore.taskLists" :key="tl.id" @click="addRecipeToList(tl)">
|
||||||
{{tl.name}}
|
{{tl.name}}
|
||||||
</button>
|
</button>
|
||||||
</app-dropdown>
|
</app-dropdown>
|
||||||
@ -150,147 +150,118 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onMounted, ref, watch } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import { mapActions, mapState } from "pinia";
|
|
||||||
import { useTaskStore } from "../stores/task";
|
import { useTaskStore } from "../stores/task";
|
||||||
|
|
||||||
export default {
|
const taskStore = useTaskStore();
|
||||||
props: {
|
const router = useRouter();
|
||||||
recipe: {
|
|
||||||
required: true,
|
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
const props = defineProps({
|
||||||
return {
|
recipe: {
|
||||||
showNutrition: false,
|
required: true,
|
||||||
showConvertDialog: false,
|
type: Object
|
||||||
addToTasksMenuOpen: false,
|
}
|
||||||
|
});
|
||||||
|
|
||||||
scaleValue: '1',
|
const showNutrition = ref(false);
|
||||||
systemConvertValue: "",
|
const showConvertDialog = ref(false);
|
||||||
unitConvertValue: "",
|
const addToTasksMenuOpen = ref(false);
|
||||||
|
|
||||||
scaleOptions: [
|
const scaleValue = ref('1');
|
||||||
'1/4',
|
const systemConvertValue = ref('');
|
||||||
'1/3',
|
const unitConvertValue = ref('');
|
||||||
'1/2',
|
|
||||||
'2/3',
|
|
||||||
'3/4',
|
|
||||||
'1',
|
|
||||||
'1 1/2',
|
|
||||||
'2',
|
|
||||||
'3',
|
|
||||||
'4'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const scaleOptions = [
|
||||||
...mapState(useTaskStore, [
|
'1/4',
|
||||||
'taskLists'
|
'1/3',
|
||||||
]),
|
'1/2',
|
||||||
|
'2/3',
|
||||||
|
'3/4',
|
||||||
|
'1',
|
||||||
|
'1 1/2',
|
||||||
|
'2',
|
||||||
|
'3',
|
||||||
|
'4'
|
||||||
|
];
|
||||||
|
|
||||||
timeDisplay() {
|
const timeDisplay = computed(() => {
|
||||||
let a = this.formatMinutes(this.recipe.active_time);
|
let a = formatMinutes(props.recipe.active_time);
|
||||||
const t = this.formatMinutes(this.recipe.total_time);
|
const t = formatMinutes(props.recipe.total_time);
|
||||||
|
|
||||||
if (a) {
|
if (a) {
|
||||||
a = ` (${a} active)`;
|
a = ` (${a} active)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return t + a;
|
return t + a;
|
||||||
},
|
});
|
||||||
|
|
||||||
sourceUrl() {
|
const sourceUrl = computed(() => {
|
||||||
try {
|
try {
|
||||||
return new URL(this.recipe.source);
|
return new URL(props.recipe.source);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
|
|
||||||
isSourceUrl() {
|
const isSourceUrl = computed(() => sourceUrl.value !== null);
|
||||||
return this.sourceUrl !== null;
|
const sourceText = computed(() => isSourceUrl.value ? sourceUrl.value.host : props.recipe.source);
|
||||||
},
|
|
||||||
|
|
||||||
sourceText() {
|
watch(props.recipe, (r) => {
|
||||||
if (this.isSourceUrl) {
|
if (r) {
|
||||||
return this.sourceUrl.host;
|
scaleValue.value = r.converted_scale || '1';
|
||||||
} else {
|
systemConvertValue.value = r.converted_system;
|
||||||
return this.recipe.source;
|
unitConvertValue.value = r.converted_unit;
|
||||||
}
|
}
|
||||||
}
|
}, { immediate: true });
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
onMounted(() => {
|
||||||
recipe: {
|
taskStore.ensureTaskLists();
|
||||||
handler: function(r) {
|
});
|
||||||
if (r) {
|
|
||||||
this.scaleValue = r.converted_scale || '1';
|
|
||||||
this.systemConvertValue = r.converted_system;
|
|
||||||
this.unitConvertValue = r.converted_unit;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
function addRecipeToList(list) {
|
||||||
...mapActions(useTaskStore, [
|
api.addRecipeToTaskList(list.id, props.recipe.id)
|
||||||
'ensureTaskLists',
|
|
||||||
'setCurrentTaskList'
|
|
||||||
]),
|
|
||||||
|
|
||||||
addRecipeToList(list) {
|
|
||||||
api.addRecipeToTaskList(list.id, this.recipe.id)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.setCurrentTaskList(list);
|
taskStore.setCurrentTaskList(list);
|
||||||
this.$router.push({name: 'task_lists'})
|
router.push({name: 'task_lists'})
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
convert() {
|
function convert() {
|
||||||
this.showConvertDialog = false;
|
showConvertDialog.value = false;
|
||||||
this.$router.push({name: 'recipe', query: { scale: this.scaleValue, system: this.systemConvertValue, unit: this.unitConvertValue }});
|
router.push({name: 'recipe', query: { scale: scaleValue.value, system: systemConvertValue.value, unit: unitConvertValue.value }});
|
||||||
},
|
}
|
||||||
|
|
||||||
roundValue(v) {
|
function roundValue(v) {
|
||||||
return parseFloat(v).toFixed(2);
|
return parseFloat(v).toFixed(2);
|
||||||
},
|
}
|
||||||
|
|
||||||
formatMinutes(min) {
|
function formatMinutes(min) {
|
||||||
if (min) {
|
if (min) {
|
||||||
const partUnits = [
|
const partUnits = [
|
||||||
{unit: "d", minutes: 60 * 24},
|
{unit: "d", minutes: 60 * 24},
|
||||||
{unit: "h", minutes: 60},
|
{unit: "h", minutes: 60},
|
||||||
{unit: "m", minutes: 1}
|
{unit: "m", minutes: 1}
|
||||||
];
|
];
|
||||||
|
|
||||||
const parts = [];
|
const parts = [];
|
||||||
let remaining = min;
|
let remaining = min;
|
||||||
|
|
||||||
for (let unit of partUnits) {
|
for (let unit of partUnits) {
|
||||||
let val = Math.floor(remaining / unit.minutes);
|
let val = Math.floor(remaining / unit.minutes);
|
||||||
remaining = remaining % unit.minutes;
|
remaining = remaining % unit.minutes;
|
||||||
|
|
||||||
if (val > 0) {
|
if (val > 0) {
|
||||||
parts.push(`${val} ${unit.unit}`);
|
parts.push(`${val} ${unit.unit}`);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join(" ");
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
return parts.join(" ");
|
||||||
this.ensureTaskLists();
|
} else {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,39 +24,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
import { onMounted, useTemplateRef } from "vue";
|
||||||
props: {
|
|
||||||
taskItem: {
|
|
||||||
required: true,
|
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const emit = defineEmits(["save"]);
|
||||||
inputKeydown(evt) {
|
const props = defineProps({
|
||||||
switch (evt.key) {
|
taskItem: {
|
||||||
case "Enter":
|
required: true,
|
||||||
evt.preventDefault();
|
type: Object
|
||||||
this.save();
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
save() {
|
const nameElement = useTemplateRef("nameInput");
|
||||||
this.$emit("save", this.taskItem);
|
|
||||||
},
|
|
||||||
|
|
||||||
focus() {
|
onMounted(() => focus());
|
||||||
this.$refs.nameInput.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
function inputKeydown(evt) {
|
||||||
this.focus();
|
switch (evt.key) {
|
||||||
|
case "Enter":
|
||||||
|
evt.preventDefault();
|
||||||
|
save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
emit("save", props.taskItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function focus() {
|
||||||
|
nameElement.value.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
focus
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -60,130 +60,100 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, ref, useTemplateRef } from "vue";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
import { mapActions } from "pinia";
|
|
||||||
import { useTaskStore } from "../stores/task";
|
import { useTaskStore } from "../stores/task";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
import TaskItemEdit from "./TaskItemEdit";
|
import TaskItemEdit from "./TaskItemEdit";
|
||||||
|
|
||||||
const newItemTemplate = function(listId) {
|
const { loadResource } = useLoadResource();
|
||||||
|
const taskStore = useTaskStore();
|
||||||
|
const itemEditElement = useTemplateRef("itemEdit");
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
taskList: {
|
||||||
|
required: true,
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showAddItem = ref(false);
|
||||||
|
const newItem = ref(null);
|
||||||
|
const newItemValidationErrors = ref({});
|
||||||
|
|
||||||
|
const completedTaskItems = computed(() => (props.taskList ? props.taskList.task_items : []).filter(i => i.completed));
|
||||||
|
const uncompletedTaskItems = computed(() => (props.taskList ? props.taskList.task_items : []).filter(i => !i.completed));
|
||||||
|
const completedItemCount = computed(() => completedTaskItems.value.length);
|
||||||
|
const uncompletedItemCount = computed(() => uncompletedTaskItems.value.length);
|
||||||
|
const taskItems = computed(() => uncompletedTaskItems.value.concat(completedTaskItems.value));
|
||||||
|
|
||||||
|
function newItemTemplate() {
|
||||||
return {
|
return {
|
||||||
task_list_id: listId,
|
task_list_id: null,
|
||||||
name: '',
|
name: '',
|
||||||
quantity: '',
|
quantity: '',
|
||||||
completed: false
|
completed: false
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export default {
|
function save() {
|
||||||
props: {
|
newItem.value.task_list_id = props.taskList.id;
|
||||||
taskList: {
|
loadResource(
|
||||||
required: true,
|
taskStore.createTaskItem(newItem.value)
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showAddItem: false,
|
|
||||||
newItem: null,
|
|
||||||
newItemValidationErrors: {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
completedItemCount() {
|
|
||||||
return this.taskList === null ? 0 : this.taskList.task_items.filter(i => i.completed).length;
|
|
||||||
},
|
|
||||||
|
|
||||||
uncompletedItemCount() {
|
|
||||||
return this.taskList === null ? 0 : this.taskList.task_items.filter(i => !i.completed).length;
|
|
||||||
},
|
|
||||||
completedTaskItems() {
|
|
||||||
return (this.taskList ? this.taskList.task_items : []).filter(i => i.completed);
|
|
||||||
},
|
|
||||||
|
|
||||||
uncompletedTaskItems() {
|
|
||||||
return (this.taskList ? this.taskList.task_items : []).filter(i => !i.completed);
|
|
||||||
},
|
|
||||||
|
|
||||||
taskItems() {
|
|
||||||
return this.uncompletedTaskItems.concat(this.completedTaskItems);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
...mapActions(useTaskStore, [
|
|
||||||
'createTaskItem',
|
|
||||||
'updateTaskItem',
|
|
||||||
'deleteTaskItems',
|
|
||||||
'completeTaskItems'
|
|
||||||
]),
|
|
||||||
|
|
||||||
save() {
|
|
||||||
this.loadResource(
|
|
||||||
this.createTaskItem(this.newItem)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.newItem = newItemTemplate(this.taskList.id);
|
newItem.value = newItemTemplate();
|
||||||
this.$refs.itemEdit.focus();
|
itemEditElement.value.focus();
|
||||||
})
|
})
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.newItemValidationErrors = err.validationErrors()))
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => newItemValidationErrors.value = err.validationErrors()))
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
|
|
||||||
toggleItem(i) {
|
function toggleItem(i) {
|
||||||
this.loadResource(
|
loadResource(
|
||||||
this.completeTaskItems({
|
taskStore.completeTaskItems({
|
||||||
taskList: this.taskList,
|
taskList: props.taskList,
|
||||||
taskItems: [i],
|
taskItems: [i],
|
||||||
completed: !i.completed
|
completed: !i.completed
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
toggleShowAddItem() {
|
function toggleShowAddItem() {
|
||||||
this.newItem = newItemTemplate(this.taskList.id);
|
newItem.value = newItemTemplate();
|
||||||
this.showAddItem = !this.showAddItem;
|
showAddItem.value = !showAddItem.value;
|
||||||
},
|
}
|
||||||
|
|
||||||
completeAllItems() {
|
function completeAllItems() {
|
||||||
const toComplete = this.taskList.task_items.filter(i => !i.completed);
|
const toComplete = props.taskList.task_items.filter(i => !i.completed);
|
||||||
this.loadResource(
|
loadResource(
|
||||||
this.completeTaskItems({
|
taskStore.completeTaskItems({
|
||||||
taskList: this.taskList,
|
taskList: props.taskList,
|
||||||
taskItems: toComplete,
|
taskItems: toComplete,
|
||||||
completed: true
|
completed: true
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
|
|
||||||
unCompleteAllItems() {
|
function unCompleteAllItems() {
|
||||||
const toUnComplete = this.taskList.task_items.filter(i => i.completed);
|
const toUnComplete = props.taskList.task_items.filter(i => i.completed);
|
||||||
this.loadResource(
|
loadResource(
|
||||||
this.completeTaskItems({
|
taskStore.completeTaskItems({
|
||||||
taskList: this.taskList,
|
taskList: props.taskList,
|
||||||
taskItems: toUnComplete,
|
taskItems: toUnComplete,
|
||||||
completed: false
|
completed: false
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
|
|
||||||
deleteCompletedItems() {
|
function deleteCompletedItems() {
|
||||||
this.loadResource(
|
loadResource(
|
||||||
this.deleteTaskItems({
|
taskStore.deleteTaskItems({
|
||||||
taskList: this.taskList,
|
taskList: props.taskList,
|
||||||
taskItems: this.taskList.task_items.filter(i => i.completed)
|
taskItems: props.taskList.task_items.filter(i => i.completed)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
TaskItemEdit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,39 +14,35 @@
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
import { ref } from "vue";
|
||||||
props: {
|
|
||||||
taskList: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
|
|
||||||
active: {
|
const emit = defineEmits(["select", "delete"]);
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
const props = defineProps({
|
||||||
default: false
|
taskList: {
|
||||||
}
|
type: Object,
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
active: {
|
||||||
return {
|
type: Boolean,
|
||||||
hovering: false,
|
required: false,
|
||||||
confirmingDelete: false
|
default: false
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
selectList() {
|
|
||||||
this.$emit("select", this.taskList);
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteList() {
|
|
||||||
this.confirmingDelete = false;
|
|
||||||
this.$emit("delete", this.taskList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const hovering = ref(false);
|
||||||
|
const confirmingDelete = ref(false);
|
||||||
|
|
||||||
|
function selectList() {
|
||||||
|
emit("select", props.taskList);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteList() {
|
||||||
|
confirmingDelete.value = false;
|
||||||
|
emit("delete", props.taskList);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,34 +14,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
const emit = defineEmits(["save"]);
|
||||||
props: {
|
|
||||||
taskList: {
|
|
||||||
required: true,
|
|
||||||
type: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
validationErrors: {
|
const props = defineProps({
|
||||||
required: false,
|
taskList: {
|
||||||
type: Object,
|
required: true,
|
||||||
default: function() { return {}; }
|
type: Object
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
validationErrors: {
|
||||||
save() {
|
required: false,
|
||||||
this.$emit("save");
|
type: Object,
|
||||||
},
|
default: function() { return {}; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
nameKeydownHandler(evt) {
|
function save() {
|
||||||
switch (evt.key) {
|
emit("save");
|
||||||
case "Enter":
|
}
|
||||||
evt.preventDefault();
|
|
||||||
this.save();
|
function nameKeydownHandler(evt) {
|
||||||
}
|
switch (evt.key) {
|
||||||
}
|
case "Enter":
|
||||||
|
evt.preventDefault();
|
||||||
|
save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,10 +5,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
@ -19,11 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -2,11 +2,8 @@
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -29,24 +29,21 @@
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { onBeforeMount, ref } from "vue";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
const userList = ref([]);
|
||||||
return {
|
|
||||||
userList: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
onBeforeMount(() => {
|
||||||
this.loadResource(
|
loadResource(
|
||||||
api.getAdminUserList()
|
api.getAdminUserList()
|
||||||
.then(list => this.userList = list)
|
.then(list => userList.value = list)
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -40,93 +40,64 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
input: '',
|
|
||||||
outputUnit: '',
|
|
||||||
ingredient_name: '',
|
|
||||||
ingredient: null,
|
|
||||||
density: '',
|
|
||||||
output: '',
|
|
||||||
errors: {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const input = ref("");
|
||||||
inputErrors() {
|
const outputUnit = ref("");
|
||||||
if (this.errors.input && this.errors.input.length > 0) {
|
const ingredient_name = ref("");
|
||||||
return this.errors.input.join(", ");
|
const ingredient = ref(null);
|
||||||
} else {
|
const density = ref("");
|
||||||
return null;
|
const output = ref("");
|
||||||
}
|
const errors = ref({});
|
||||||
},
|
|
||||||
|
|
||||||
outputUnitErrors() {
|
const inputErrors = computed(() => getErrors("input"));
|
||||||
if (this.errors.output_unit && this.errors.output_unit.length > 0) {
|
const outputUnitErrors = computed(() => getErrors("output_unit"));
|
||||||
return this.errors.output_unit.join(", ");
|
const densityErrors = computed(() => getErrors("density"));
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
densityErrors() {
|
const updateOutput = debounce(function() {
|
||||||
if (this.errors.density && this.errors.density.length > 0) {
|
if (input.value && input.value.length > 0) {
|
||||||
return this.errors.density.join(", ");
|
loadResource(api.getCalculate(input.value, outputUnit.value, ingredient.value ? ingredient.value.ingredient_id : null, density.value)
|
||||||
} else {
|
.then(data => {
|
||||||
return null;
|
output.value = data.output;
|
||||||
}
|
errors.value = data.errors;
|
||||||
}
|
})
|
||||||
},
|
);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
methods: {
|
watch(ingredient_name, function(val) {
|
||||||
updateSearchItems(text) {
|
if (ingredient.value && ingredient.value.name !== val) {
|
||||||
return api.getSearchIngredients(text);
|
ingredient.value = null;
|
||||||
},
|
}
|
||||||
|
});
|
||||||
|
|
||||||
searchItemSelected(ingredient) {
|
watch(
|
||||||
this.ingredient = ingredient || null;
|
[input, outputUnit, density, ingredient],
|
||||||
this.ingredient_name = ingredient.name || null;
|
() => updateOutput()
|
||||||
this.density = ingredient.density || null;
|
);
|
||||||
},
|
|
||||||
|
|
||||||
updateOutput: debounce(function() {
|
function updateSearchItems(text) {
|
||||||
if (this.input && this.input.length > 0) {
|
return api.getSearchIngredients(text);
|
||||||
this.loadResource(api.getCalculate(this.input, this.outputUnit, this.ingredient ? this.ingredient.ingredient_id : null, this.density)
|
}
|
||||||
.then(data => {
|
|
||||||
this.output = data.output;
|
|
||||||
this.errors = data.errors;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, 500)
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
function searchItemSelected(ingredient) {
|
||||||
'ingredient_name': function(val) {
|
ingredient.value = ingredient || null;
|
||||||
if (this.ingredient && this.ingredient.name !== val) {
|
ingredient_name.value = ingredient.name || null;
|
||||||
this.ingredient = null;
|
density.value = ingredient.density || null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
function getErrors(type) {
|
||||||
this.$watch(
|
if (errors.value[type] && errors.value[type].length > 0) {
|
||||||
function() {
|
return errors.value[type].join(", ");
|
||||||
return [this.input, this.outputUnit, this.density, this.ingredient];
|
} else {
|
||||||
},
|
return null;
|
||||||
function() {
|
|
||||||
this.updateOutput();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,40 +7,33 @@
|
|||||||
<food-show :food="food"></food-show>
|
<food-show :food="food"></food-show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<router-link v-if="isLoggedIn" class="button" :to="{name: 'edit_food', params: { id: foodId }}">Edit</router-link>
|
<router-link v-if="appConfig.isLoggedIn" class="button" :to="{name: 'edit_food', params: { id: foodId }}">Edit</router-link>
|
||||||
<router-link class="button" to="/foods">Back</router-link>
|
<router-link class="button" to="/foods">Back</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeMount, ref } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
import FoodShow from "./FoodShow";
|
import FoodShow from "./FoodShow";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data: function () {
|
const appConfig = useAppConfigStore();
|
||||||
return {
|
const route = useRoute();
|
||||||
food: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const food = ref(null);
|
||||||
foodId() {
|
const foodId = computed(() => route.params.id);
|
||||||
return this.$route.params.id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
onBeforeMount(() => {
|
||||||
this.loadResource(
|
loadResource(
|
||||||
api.getFood(this.foodId)
|
api.getFood(foodId.value)
|
||||||
.then(data => { this.food = data; return data; })
|
.then(data => { food.value = data; return data; })
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
|
|
||||||
components: {
|
|
||||||
FoodShow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -9,65 +9,59 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { reactive, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import FoodEdit from "./FoodEdit";
|
import FoodEdit from "./FoodEdit";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
const router = useRouter();
|
||||||
return {
|
|
||||||
food: {
|
|
||||||
name: null,
|
|
||||||
notes: null,
|
|
||||||
ndbn: null,
|
|
||||||
density: null,
|
|
||||||
water: null,
|
|
||||||
ash: null,
|
|
||||||
protein: null,
|
|
||||||
kcal: null,
|
|
||||||
fiber: null,
|
|
||||||
sugar: null,
|
|
||||||
carbohydrates: null,
|
|
||||||
calcium: null,
|
|
||||||
iron: null,
|
|
||||||
magnesium: null,
|
|
||||||
phosphorus: null,
|
|
||||||
potassium: null,
|
|
||||||
sodium: null,
|
|
||||||
zinc: null,
|
|
||||||
copper: null,
|
|
||||||
manganese: null,
|
|
||||||
vit_c: null,
|
|
||||||
vit_b6: null,
|
|
||||||
vit_b12: null,
|
|
||||||
vit_a: null,
|
|
||||||
vit_e: null,
|
|
||||||
vit_d: null,
|
|
||||||
vit_k: null,
|
|
||||||
cholesterol: null,
|
|
||||||
lipids: null,
|
|
||||||
food_units: []
|
|
||||||
},
|
|
||||||
validationErrors: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const validationErrors = ref({});
|
||||||
save() {
|
const food = reactive({
|
||||||
this.validationErrors = {}
|
name: null,
|
||||||
this.loadResource(
|
notes: null,
|
||||||
api.postFood(this.food)
|
ndbn: null,
|
||||||
.then(() => this.$router.push('/foods'))
|
density: null,
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
water: null,
|
||||||
);
|
ash: null,
|
||||||
}
|
protein: null,
|
||||||
},
|
kcal: null,
|
||||||
|
fiber: null,
|
||||||
|
sugar: null,
|
||||||
|
carbohydrates: null,
|
||||||
|
calcium: null,
|
||||||
|
iron: null,
|
||||||
|
magnesium: null,
|
||||||
|
phosphorus: null,
|
||||||
|
potassium: null,
|
||||||
|
sodium: null,
|
||||||
|
zinc: null,
|
||||||
|
copper: null,
|
||||||
|
manganese: null,
|
||||||
|
vit_c: null,
|
||||||
|
vit_b6: null,
|
||||||
|
vit_b12: null,
|
||||||
|
vit_a: null,
|
||||||
|
vit_e: null,
|
||||||
|
vit_d: null,
|
||||||
|
vit_k: null,
|
||||||
|
cholesterol: null,
|
||||||
|
lipids: null,
|
||||||
|
food_units: []
|
||||||
|
});
|
||||||
|
|
||||||
components: {
|
function save() {
|
||||||
FoodEdit
|
validationErrors.value = {}
|
||||||
}
|
loadResource(
|
||||||
|
api.postFood(food)
|
||||||
|
.then(() => router.push('/foods'))
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,47 +14,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeMount, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import FoodEdit from "./FoodEdit";
|
import FoodEdit from "./FoodEdit";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data: function () {
|
const router = useRouter();
|
||||||
return {
|
const route = useRoute();
|
||||||
food: null,
|
|
||||||
validationErrors: {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const food = ref(null);
|
||||||
foodId() {
|
const validationErrors = ref({});
|
||||||
return this.$route.params.id;
|
const foodId = computed(() => route.params.id);
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
onBeforeMount(() => {
|
||||||
save() {
|
loadResource(
|
||||||
this.validationErrors = {};
|
api.getFood(foodId.value)
|
||||||
this.loadResource(
|
.then(data => { food.value = data; return data; })
|
||||||
api.patchFood(this.food)
|
);
|
||||||
.then(() => this.$router.push({name: 'food', params: {id: this.foodId }}))
|
});
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
function save() {
|
||||||
this.loadResource(
|
validationErrors.value = {};
|
||||||
api.getFood(this.foodId)
|
loadResource(
|
||||||
.then(data => { this.food = data; return data; })
|
api.patchFood(food.value)
|
||||||
);
|
.then(() => router.push({name: 'food', params: {id: foodId.value }}))
|
||||||
},
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
components: {
|
|
||||||
FoodEdit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<h1 class="title">Ingredients</h1>
|
<h1 class="title">Ingredients</h1>
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<router-link v-if="isLoggedIn" :to="{name: 'new_food'}" class="button is-primary">Create Ingredient</router-link>
|
<router-link v-if="appConfig.isLoggedIn" :to="{name: 'new_food'}" class="button is-primary">Create Ingredient</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="food" @changePage="changePage"></app-pager>
|
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="food" @changePage="changePage"></app-pager>
|
||||||
@ -35,12 +35,14 @@
|
|||||||
<td>{{i.kcal}}</td>
|
<td>{{i.kcal}}</td>
|
||||||
<td>{{i.density}}</td>
|
<td>{{i.density}}</td>
|
||||||
<td>
|
<td>
|
||||||
<router-link v-if="isLoggedIn" class="button" :to="{name: 'edit_food', params: { id: i.id } }">
|
<template v-if="appConfig.isLoggedIn">
|
||||||
<app-icon icon="pencil"></app-icon>
|
<router-link class="button" :to="{name: 'edit_food', params: { id: i.id } }">
|
||||||
</router-link>
|
<app-icon icon="pencil"></app-icon>
|
||||||
<button v-if="isLoggedIn" type="button" class="button is-danger" @click="deleteFood(i)">
|
</router-link>
|
||||||
<app-icon icon="x"></app-icon>
|
<button type="button" class="button is-danger" @click="deleteFood(i)">
|
||||||
</button>
|
<app-icon icon="x"></app-icon>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
@ -49,114 +51,80 @@
|
|||||||
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="food" @changePage="changePage"></app-pager>
|
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="food" @changePage="changePage"></app-pager>
|
||||||
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<router-link v-if="isLoggedIn" :to="{name: 'new_food'}" class="button is-primary">Create Ingredient</router-link>
|
<router-link v-if="appConfig.isLoggedIn" :to="{name: 'new_food'}" class="button is-primary">Create Ingredient</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-confirm :open="showConfirmFoodDelete" :message="confirmFoodDeleteMessage" :cancel="foodDeleteCancel" :confirm="foodDeleteConfirm"></app-confirm>
|
<app-confirm :open="showConfirmFoodDelete" title="Delete Ingredient?" :message="confirmFoodDeleteMessage" @cancel="foodDeleteCancel" @confirm="foodDeleteConfirm"></app-confirm>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, reactive, ref, watch } from "vue";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const appConfig = useAppConfigStore();
|
||||||
data() {
|
const { loadResource } = useLoadResource();
|
||||||
return {
|
|
||||||
foodData: null,
|
|
||||||
foodForDeletion: null,
|
|
||||||
search: {
|
|
||||||
page: 1,
|
|
||||||
per: 25,
|
|
||||||
name: null
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const foodData = ref(null);
|
||||||
foods() {
|
const foodForDeletion = ref(null);
|
||||||
if (this.foodData) {
|
const search = reactive({
|
||||||
return this.foodData.foods;
|
page: 1,
|
||||||
} else {
|
per: 25,
|
||||||
return [];
|
name: null
|
||||||
}
|
});
|
||||||
},
|
|
||||||
|
|
||||||
totalPages() {
|
const foods = computed(() => foodData.value?.foods || []);
|
||||||
if (this.foodData) {
|
const totalPages = computed(() => foodData.value?.total_pages || 0);
|
||||||
return this.foodData.total_pages
|
const currentPage = computed(() => foodData.value?.current_page || 0);
|
||||||
}
|
const showConfirmFoodDelete = computed(() => foodForDeletion.value !== null);
|
||||||
return 0;
|
const confirmFoodDeleteMessage = computed(() => {
|
||||||
},
|
if (foodForDeletion.value !== null) {
|
||||||
|
return `Are you sure you want to delete ${foodForDeletion.value.name}?`;
|
||||||
|
} else {
|
||||||
|
return "??";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
currentPage() {
|
const getList = debounce(function() {
|
||||||
if (this.foodData) {
|
return loadResource(
|
||||||
return this.foodData.current_page
|
api.getFoodList(search.page, search.per, search.name)
|
||||||
}
|
.then(data => foodData.value = data)
|
||||||
return 0;
|
);
|
||||||
},
|
}, 500, {leading: true, trailing: true});
|
||||||
|
|
||||||
showConfirmFoodDelete() {
|
watch(search,
|
||||||
return this.foodForDeletion !== null;
|
() => getList(),
|
||||||
},
|
{
|
||||||
|
deep: true,
|
||||||
confirmFoodDeleteMessage() {
|
immediate: true
|
||||||
if (this.foodForDeletion !== null) {
|
|
||||||
return `Are you sure you want to delete ${this.foodForDeletion.name}?`;
|
|
||||||
} else {
|
|
||||||
return "??";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
);
|
||||||
|
|
||||||
methods: {
|
function changePage(idx) {
|
||||||
changePage(idx) {
|
search.page = idx;
|
||||||
this.search.page = idx;
|
}
|
||||||
},
|
|
||||||
|
|
||||||
getList: debounce(function() {
|
function deleteFood(food) {
|
||||||
return this.loadResource(
|
foodForDeletion.value = food;
|
||||||
api.getFoodList(this.search.page, this.search.per, this.search.name)
|
}
|
||||||
.then(data => this.foodData = data)
|
|
||||||
);
|
|
||||||
}, 500, {leading: true, trailing: true}),
|
|
||||||
|
|
||||||
deleteFood(food) {
|
function foodDeleteCancel() {
|
||||||
this.foodForDeletion = food;
|
foodForDeletion.value = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
foodDeleteCancel() {
|
function foodDeleteConfirm() {
|
||||||
this.foodForDeletion = null;
|
if (foodForDeletion.value !== null) {
|
||||||
},
|
loadResource(
|
||||||
|
api.deleteFood(foodForDeletion.value.id).then(res => {
|
||||||
foodDeleteConfirm() {
|
foodForDeletion.value = null;
|
||||||
if (this.foodForDeletion !== null) {
|
return getList();
|
||||||
this.loadResource(
|
})
|
||||||
api.deleteFood(this.foodForDeletion.id).then(res => {
|
|
||||||
this.foodForDeletion = null;
|
|
||||||
return this.getList();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("This is where the thing happens!!");
|
|
||||||
this.foodForDeletion = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.$watch("search",
|
|
||||||
() => this.getList(),
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,35 +14,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeMount, ref } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
import LogShow from "./LogShow";
|
import LogShow from "./LogShow";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data: function () {
|
const route = useRoute();
|
||||||
return {
|
|
||||||
log: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const log = ref(null);
|
||||||
logId() {
|
const logId = computed(() => route.params.id);
|
||||||
return this.$route.params.id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
onBeforeMount(() => {
|
||||||
this.loadResource(
|
loadResource(
|
||||||
api.getLog(this.logId)
|
api.getLog(logId.value)
|
||||||
.then(data => { this.log = data; return data; })
|
.then(data => { log.value = data; return data; })
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
|
|
||||||
components: {
|
|
||||||
LogShow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -16,53 +16,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeMount, reactive, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import LogEdit from "./LogEdit";
|
import LogEdit from "./LogEdit";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
validationErrors: {},
|
|
||||||
log: {
|
|
||||||
date: null,
|
|
||||||
rating: null,
|
|
||||||
notes: null,
|
|
||||||
recipe: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const validationErrors = ref({});
|
||||||
recipeId() {
|
const log = reactive({
|
||||||
return this.$route.params.recipeId;
|
date: null,
|
||||||
}
|
rating: null,
|
||||||
},
|
notes: null,
|
||||||
|
recipe: null
|
||||||
|
});
|
||||||
|
|
||||||
methods: {
|
const recipeId = computed(() => route.params.recipeId);
|
||||||
save() {
|
|
||||||
this.log.original_recipe_id = this.recipeId;
|
|
||||||
this.validationErrors = {};
|
|
||||||
|
|
||||||
this.loadResource(
|
onBeforeMount(() => {
|
||||||
api.postLog(this.log)
|
loadResource(
|
||||||
.then(() => this.$router.push('/'))
|
api.getRecipe(recipeId.value, null, null, null, data => log.recipe = data)
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
);
|
||||||
);
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
function save() {
|
||||||
this.loadResource(
|
log.original_recipe_id = recipeId.value;
|
||||||
api.getRecipe(this.recipeId, null, null, null, data => this.log.recipe = data)
|
validationErrors.value = {};
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
loadResource(
|
||||||
LogEdit
|
api.postLog(log)
|
||||||
}
|
.then(() => router.push('/'))
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,47 +16,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeMount, ref, watch } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from "../lib/Errors";
|
import * as Errors from "../lib/Errors";
|
||||||
import LogEdit from "./LogEdit";
|
import LogEdit from "./LogEdit";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
validationErrors: {},
|
|
||||||
log: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const validationErrors = ref({});
|
||||||
logId() {
|
const log = ref(null);
|
||||||
return this.$route.params.id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const logId = computed(() => route.params.id);
|
||||||
save() {
|
|
||||||
this.validationErrors = {};
|
|
||||||
this.loadResource(
|
|
||||||
api.patchLog(this.log)
|
|
||||||
.then(() => this.$router.push('/'))
|
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
onBeforeMount(() => {
|
||||||
this.loadResource(
|
loadResource(
|
||||||
api.getLog(this.logId)
|
api.getLog(logId.value)
|
||||||
.then(data => { this.log = data; return data; })
|
.then(data => { log.value = data; return data; })
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
|
|
||||||
components: {
|
function save() {
|
||||||
LogEdit
|
validationErrors.value = {};
|
||||||
}
|
loadResource(
|
||||||
|
api.patchLog(log.value)
|
||||||
|
.then(() => router.push('/'))
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<tr v-for="l in logs" :key="l.id">
|
<tr v-for="l in logs" :key="l.id">
|
||||||
<td> <router-link :to="{name: 'log', params: {id: l.id}}">{{l.recipe.name}}</router-link></td>
|
<td> <router-link :to="{name: 'log', params: {id: l.id}}">{{l.recipe.name}}</router-link></td>
|
||||||
<td><app-date-time :date-time="l.date" :show-time="false"></app-date-time> </td>
|
<td><app-date-time :date-time="l.date" :show-time="false"></app-date-time> </td>
|
||||||
<td><app-rating :value="l.rating" readonly></app-rating></td>
|
<td><app-rating :model-value="l.rating" readonly></app-rating></td>
|
||||||
<td>{{l.notes}}</td>
|
<td>{{l.notes}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -27,68 +27,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, reactive, ref, watch } from "vue";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
logData: null,
|
|
||||||
search: {
|
|
||||||
page: 1,
|
|
||||||
per: 25
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const logData = ref(null);
|
||||||
logs() {
|
const search = reactive({
|
||||||
if (this.logData) {
|
page: 1,
|
||||||
return this.logData.logs;
|
per: 25
|
||||||
} else {
|
});
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
totalPages() {
|
const logs = computed(() => logData.value?.logs || []);
|
||||||
if (this.logData) {
|
const totalPages = computed(() => logData.value?.total_pages || 0);
|
||||||
return this.logData.total_pages
|
const currentPage = computed(() => logData.value?.current_page || 0);
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
currentPage() {
|
const getList = debounce(function() {
|
||||||
if (this.logData) {
|
loadResource(
|
||||||
return this.logData.current_page
|
api.getLogList(search.page, search.per)
|
||||||
}
|
.then(data => logData.value = data)
|
||||||
return 0;
|
);
|
||||||
|
}, 500, {leading: true, trailing: true});
|
||||||
|
|
||||||
|
watch(search,
|
||||||
|
() => getList(),
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
},
|
);
|
||||||
|
|
||||||
methods: {
|
function changePage(idx) {
|
||||||
changePage(idx) {
|
search.page = idx;
|
||||||
this.search.page = idx;
|
|
||||||
},
|
|
||||||
|
|
||||||
getList: debounce(function() {
|
|
||||||
this.loadResource(
|
|
||||||
api.getLogList(this.search.page, this.search.per)
|
|
||||||
.then(data => this.logData = data)
|
|
||||||
);
|
|
||||||
}, 500, {leading: true, trailing: true})
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.$watch("search",
|
|
||||||
() => this.getList(),
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -38,62 +38,53 @@
|
|||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { onBeforeMount, ref } from "vue";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import NoteEdit from "./NoteEdit";
|
import NoteEdit from "./NoteEdit";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
const notes = ref([]);
|
||||||
return {
|
const editNote = ref(null);
|
||||||
notes: [],
|
|
||||||
editNote: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
onBeforeMount(() => {
|
||||||
refreshList() {
|
refreshList();
|
||||||
this.loadResource(
|
});
|
||||||
api.getNoteList()
|
|
||||||
.then(data => this.notes = data)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
addNote() {
|
function refreshList() {
|
||||||
this.editNote = { id: null, content: "" };
|
loadResource(
|
||||||
},
|
api.getNoteList()
|
||||||
|
.then(data => notes.value = data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
saveNote() {
|
function addNote() {
|
||||||
this.loadResource(
|
editNote.value = { id: null, content: "" };
|
||||||
api.postNote(this.editNote)
|
}
|
||||||
|
|
||||||
|
function saveNote() {
|
||||||
|
loadResource(
|
||||||
|
api.postNote(editNote.value)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.editNote = null;
|
editNote.value = null;
|
||||||
return this.refreshList();
|
return refreshList();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
cancelNote() {
|
function cancelNote() {
|
||||||
this.editNote = null;
|
editNote.value = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
deleteNote(n) {
|
function deleteNote(n) {
|
||||||
this.loadResource(
|
loadResource(
|
||||||
api.deleteNote(n)
|
api.deleteNote(n)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.refreshList();
|
return refreshList();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.refreshList();
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
NoteEdit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -22,58 +22,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
import RecipeShow from "./RecipeShow";
|
import RecipeShow from "./RecipeShow";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const route = useRoute();
|
||||||
data: function () {
|
const { loadResource } = useLoadResource();
|
||||||
return {
|
const recipe = ref(null);
|
||||||
recipe: null,
|
|
||||||
showNutrition: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const recipeId = computed(() => route.params.id);
|
||||||
recipeId() { return this.$route.params.id },
|
const scale = computed(() => route.query.scale || null);
|
||||||
routeQuery() { return this.$route.query },
|
const system = computed(() => route.query.system || null);
|
||||||
scale() { return this.$route.query.scale || null },
|
const unit = computed(() => route.query.unit || null);
|
||||||
system() { return this.$route.query.system || null },
|
const isScaled = computed(() => recipe.value?.converted_scale?.length !== undefined && recipe.value.converted_scale.length > 0 && recipe.value.converted_scale !== "1");
|
||||||
unit() { return this.$route.query.unit || null },
|
|
||||||
|
|
||||||
isScaled() {
|
watch(
|
||||||
return this.recipe.converted_scale !== null && this.recipe.converted_scale.length > 0 && this.recipe.converted_scale !== "1";
|
() => route.query,
|
||||||
}
|
() => refreshData(),
|
||||||
},
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
watch: {
|
watch(
|
||||||
routeQuery() {
|
() => recipe.value?.name,
|
||||||
this.refreshData();
|
(newRecipe) => {
|
||||||
},
|
|
||||||
|
|
||||||
recipe(newRecipe) {
|
|
||||||
if (newRecipe) {
|
if (newRecipe) {
|
||||||
document.title = `${newRecipe.name}`;
|
document.title = `${newRecipe.name}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
)
|
||||||
|
|
||||||
methods: {
|
function refreshData() {
|
||||||
refreshData() {
|
loadResource(
|
||||||
this.loadResource(
|
api.getRecipe(recipeId.value, scale.value, system.value, unit.value, data => recipe.value = data)
|
||||||
api.getRecipe(this.recipeId, this.scale, this.system, this.unit, data => this.recipe = data)
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.refreshData();
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
RecipeShow
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -13,44 +13,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import RecipeEdit from "./RecipeEdit";
|
import RecipeEdit from "./RecipeEdit";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const router = useRouter();
|
||||||
data() {
|
const { loadResource } = useLoadResource();
|
||||||
return {
|
|
||||||
validationErrors: {},
|
|
||||||
recipe: {
|
|
||||||
name: null,
|
|
||||||
source: null,
|
|
||||||
description: null,
|
|
||||||
yields: null,
|
|
||||||
total_time: null,
|
|
||||||
active_time: null,
|
|
||||||
step_text: null,
|
|
||||||
tags: [],
|
|
||||||
ingredients: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const validationErrors = ref({});
|
||||||
save() {
|
const recipe = ref({
|
||||||
this.validationErrors = {};
|
name: null,
|
||||||
this.loadResource(
|
source: null,
|
||||||
api.postRecipe(this.recipe)
|
description: null,
|
||||||
.then(() => this.$router.push('/'))
|
yields: null,
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
total_time: null,
|
||||||
);
|
active_time: null,
|
||||||
}
|
step_text: null,
|
||||||
},
|
tags: [],
|
||||||
|
ingredients: []
|
||||||
|
});
|
||||||
|
|
||||||
components: {
|
function save() {
|
||||||
RecipeEdit
|
validationErrors.value = {};
|
||||||
}
|
loadResource(
|
||||||
|
api.postRecipe(recipe.value)
|
||||||
|
.then(() => router.push('/'))
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -18,48 +18,39 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeMount, ref } from "vue";
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
import RecipeEdit from "./RecipeEdit";
|
import RecipeEdit from "./RecipeEdit";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data: function () {
|
const route = useRoute();
|
||||||
return {
|
const router = useRouter();
|
||||||
recipe: null,
|
const recipe = ref(null);
|
||||||
validationErrors: {}
|
const validationErrors = ref({});
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const recipeId = computed(() => route.params.id);
|
||||||
recipeId() {
|
|
||||||
return this.$route.params.id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
onBeforeMount(() => {
|
||||||
save() {
|
loadResource(
|
||||||
this.validationErrors = {};
|
api.getRecipe(recipeId.value, null, null, null, data => { recipe.value = data; return data; })
|
||||||
this.loadResource(
|
);
|
||||||
api.patchRecipe(this.recipe)
|
});
|
||||||
.then(() => this.$router.push({name: 'recipe', params: {id: this.recipeId }}))
|
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
function save() {
|
||||||
this.loadResource(
|
validationErrors.value = {};
|
||||||
api.getRecipe(this.recipeId, null, null, null, data => { this.recipe = data; return data; })
|
loadResource(
|
||||||
);
|
api.patchRecipe(recipe.value)
|
||||||
},
|
.then(() => router.push({name: 'recipe', params: {id: recipeId.value }}))
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
components: {
|
);
|
||||||
RecipeEdit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<app-rating v-if="r.rating !== null" :value="r.rating" readonly></app-rating>
|
<app-rating v-if="r.rating !== null" :model-value="r.rating" readonly></app-rating>
|
||||||
<span v-else>--</span>
|
<span v-else>--</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ r.yields }}</td>
|
<td>{{ r.yields }}</td>
|
||||||
@ -85,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="recipe" @changePage="changePage"></app-pager>
|
<app-pager :current-page="currentPage" :total-pages="totalPages" paged-item-name="recipe" @changePage="changePage"></app-pager>
|
||||||
<app-confirm :open="showConfirmRecipeDelete" :message="confirmRecipeDeleteMessage" :cancel="recipeDeleteCancel" :confirm="recipeDeleteConfirm"></app-confirm>
|
<app-confirm :open="showConfirmRecipeDelete" title="Delete Recipe?" :message="confirmRecipeDeleteMessage" @cancel="recipeDeleteCancel" @confirm="recipeDeleteConfirm"></app-confirm>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -22,15 +22,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, onBeforeMount, ref } from "vue";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
import { mapActions, mapState } from "pinia";
|
|
||||||
import { useTaskStore } from "../stores/task";
|
import { useTaskStore } from "../stores/task";
|
||||||
|
|
||||||
import TaskListMiniForm from "./TaskListMiniForm";
|
import TaskListMiniForm from "./TaskListMiniForm";
|
||||||
import TaskListDropdownItem from "./TaskListDropdownItem";
|
import TaskListDropdownItem from "./TaskListDropdownItem";
|
||||||
import TaskItemList from "./TaskItemList";
|
import TaskItemList from "./TaskItemList";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
const newListTemplate = function() {
|
const newListTemplate = function() {
|
||||||
return {
|
return {
|
||||||
@ -38,82 +39,52 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
const taskStore = useTaskStore();
|
||||||
return {
|
|
||||||
showListDropdown: false,
|
|
||||||
newList: newListTemplate(),
|
|
||||||
newListValidationErrors: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const showListDropdown = ref(false);
|
||||||
...mapState(useTaskStore, [
|
const newList = ref(newListTemplate());
|
||||||
'taskLists',
|
const newListValidationErrors = ref({});
|
||||||
'currentTaskList'
|
|
||||||
]),
|
|
||||||
listSelectLabel() {
|
|
||||||
if (this.currentTaskList === null) {
|
|
||||||
return "Select or Create a List";
|
|
||||||
} else {
|
|
||||||
return this.currentTaskList.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const taskLists = computed(() => taskStore.taskLists);
|
||||||
...mapActions(useTaskStore, [
|
const currentTaskList = computed(() => taskStore.currentTaskList);
|
||||||
'refreshTaskLists',
|
const listSelectLabel = computed(() => {
|
||||||
'createTaskList',
|
if (currentTaskList.value === null) {
|
||||||
'deleteTaskList',
|
return "Select or Create a List";
|
||||||
'deleteTaskItems',
|
} else {
|
||||||
'completeTaskItems',
|
return currentTaskList.value.name;
|
||||||
'setCurrentTaskList'
|
|
||||||
]),
|
|
||||||
|
|
||||||
selectList(list) {
|
|
||||||
this.setCurrentTaskList(list);
|
|
||||||
this.showListDropdown = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
saveNewList() {
|
|
||||||
this.loadResource(
|
|
||||||
this.createTaskList(this.newList)
|
|
||||||
.then(() => this.showListDropdown = false)
|
|
||||||
.then(() => { this.newList = newListTemplate(); this.newListValidationErrors = {}; } )
|
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.newListValidationErrors = err.validationErrors()))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteList(list) {
|
|
||||||
this.loadResource(
|
|
||||||
this.deleteTaskList(list)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
deleteAllItems() {
|
|
||||||
this.loadResource(
|
|
||||||
this.deleteTaskItems({
|
|
||||||
taskList: this.currentTaskList,
|
|
||||||
taskItems: this.currentTaskList.task_items
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.loadResource(
|
|
||||||
this.refreshTaskLists()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
TaskListMiniForm,
|
|
||||||
TaskListDropdownItem,
|
|
||||||
TaskItemList
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
loadResource(taskStore.refreshTaskLists());
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectList(list) {
|
||||||
|
taskStore.setCurrentTaskList(list);
|
||||||
|
showListDropdown.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveNewList() {
|
||||||
|
loadResource(
|
||||||
|
taskStore.createTaskList(newList.value)
|
||||||
|
.then(() => showListDropdown.value = false)
|
||||||
|
.then(() => { newList.value = newListTemplate(); newListValidationErrors.value = {}; } )
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => newListValidationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteList(list) {
|
||||||
|
loadResource(taskStore.deleteTaskList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAllItems() {
|
||||||
|
loadResource(
|
||||||
|
taskStore.deleteTaskItems({
|
||||||
|
taskList: currentTaskList.value,
|
||||||
|
taskItems: currentTaskList.value.task_items
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -13,41 +13,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import UserEdit from "./UserEdit";
|
import UserEdit from "./UserEdit";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
import { useCheckAuthentication } from "../lib/useCheckAuthentication";
|
||||||
|
|
||||||
export default {
|
const { loadResource } = useLoadResource();
|
||||||
data() {
|
const { checkAuthentication } = useCheckAuthentication(loadResource);
|
||||||
return {
|
const router = useRouter();
|
||||||
validationErrors: {},
|
|
||||||
userObj: {
|
|
||||||
username: '',
|
|
||||||
full_name: '',
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
password_confirmation: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const validationErrors = ref({});
|
||||||
save() {
|
const userObj = ref({
|
||||||
this.validationErrors = {};
|
username: '',
|
||||||
this.loadResource(
|
full_name: '',
|
||||||
api.postUser(this.userObj)
|
email: '',
|
||||||
.then(() => this.checkAuthentication())
|
password: '',
|
||||||
.then(() => this.$router.push('/'))
|
password_confirmation: ''
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
function save() {
|
||||||
UserEdit
|
validationErrors.value = {};
|
||||||
}
|
loadResource(
|
||||||
|
api.postUser(userObj.value)
|
||||||
|
.then(() => checkAuthentication())
|
||||||
|
.then(() => router.push('/'))
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -13,63 +13,56 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
|
import { ref, watch } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
import UserEdit from "./UserEdit";
|
import UserEdit from "./UserEdit";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
import { useCheckAuthentication } from "../lib/useCheckAuthentication";
|
||||||
|
|
||||||
export default {
|
const appConfig = useAppConfigStore();
|
||||||
data() {
|
const { loadResource } = useLoadResource();
|
||||||
return {
|
const { checkAuthentication } = useCheckAuthentication(loadResource);
|
||||||
validationErrors: {},
|
const router = useRouter();
|
||||||
userObj: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
const validationErrors = ref({});
|
||||||
this.refreshUser();
|
const userObj = ref(null);
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
watch(
|
||||||
user() {
|
() => appConfig.user,
|
||||||
this.refreshUser();
|
() => refreshUser(),
|
||||||
}
|
{ immediate: true });
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
function refreshUser() {
|
||||||
refreshUser() {
|
if (appConfig.user) {
|
||||||
if (this.user) {
|
userObj.value = {
|
||||||
this.userObj = {
|
username: appConfig.user.username,
|
||||||
username: this.user.username,
|
full_name: appConfig.user.full_name,
|
||||||
full_name: this.user.full_name,
|
email: appConfig.user.email,
|
||||||
email: this.user.email,
|
password: '',
|
||||||
password: '',
|
password_confirmation: ''
|
||||||
password_confirmation: ''
|
};
|
||||||
};
|
} else {
|
||||||
} else {
|
userObj.value = null;
|
||||||
this.userObj = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
save() {
|
|
||||||
this.validationErrors = {};
|
|
||||||
this.loadResource(
|
|
||||||
api.patchUser(this.userObj)
|
|
||||||
.then(() => this.checkAuthentication())
|
|
||||||
.then(() => {
|
|
||||||
this.$router.push('/');
|
|
||||||
})
|
|
||||||
.catch(Errors.onlyFor(Errors.ApiValidationError, err => this.validationErrors = err.validationErrors()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
UserEdit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
validationErrors.value = {};
|
||||||
|
loadResource(
|
||||||
|
api.patchUser(userObj.value)
|
||||||
|
.then(() => checkAuthentication())
|
||||||
|
.then(() => {
|
||||||
|
router.push('/');
|
||||||
|
})
|
||||||
|
.catch(Errors.onlyFor(Errors.ApiValidationError, err => validationErrors.value = err.validationErrors()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -3,21 +3,19 @@
|
|||||||
<app-text-field label="Username" v-model="userObj.username"></app-text-field>
|
<app-text-field label="Username" v-model="userObj.username"></app-text-field>
|
||||||
<app-text-field label="Name" v-model="userObj.full_name"></app-text-field>
|
<app-text-field label="Name" v-model="userObj.full_name"></app-text-field>
|
||||||
<app-text-field label="Email" v-model="userObj.email"></app-text-field>
|
<app-text-field label="Email" v-model="userObj.email"></app-text-field>
|
||||||
<app-text-field label="Password" v-model="userObj.password"></app-text-field>
|
<app-text-field type="password" label="Password" v-model="userObj.password"></app-text-field>
|
||||||
<app-text-field label="Password Confirmation" v-model="userObj.password_confirmation"></app-text-field>
|
<app-text-field type="password" label="Password Confirmation" v-model="userObj.password_confirmation"></app-text-field>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
userObj: {
|
||||||
userObj: {
|
required: true,
|
||||||
required: true,
|
type: Object
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -45,58 +45,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
|
||||||
import { mapActions, mapState } from "pinia";
|
import { computed, nextTick, ref, useTemplateRef } from "vue";
|
||||||
import { useAppConfigStore } from "../stores/appConfig";
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
import { useLoadResource } from "../lib/useLoadResource";
|
||||||
|
|
||||||
export default {
|
const appConfig = useAppConfigStore();
|
||||||
data() {
|
const { loadResource } = useLoadResource();
|
||||||
return {
|
|
||||||
showLogin: false,
|
|
||||||
error: '',
|
|
||||||
username: '',
|
|
||||||
password: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const userNameElement = useTemplateRef("usernameInput");
|
||||||
...mapState(useAppConfigStore, [
|
|
||||||
'loginMessage'
|
|
||||||
]),
|
|
||||||
enableSubmit() {
|
|
||||||
return this.username !== '' && this.password !== '' && !this.isLoading;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
const showLogin = ref(false);
|
||||||
...mapActions(useAppConfigStore, [
|
const error = ref('');
|
||||||
'login'
|
const username = ref("");
|
||||||
]),
|
const password = ref("");
|
||||||
|
|
||||||
openDialog() {
|
const loginMessage = computed(() => appConfig.loginMessage);
|
||||||
this.showLogin = true;
|
const enableSubmit = computed(() => username.value !== "" && password.value !== "" && !appConfig.isLoading);
|
||||||
this.$nextTick(() => this.$refs.usernameInput.focus());
|
|
||||||
},
|
|
||||||
|
|
||||||
performLogin() {
|
function openDialog() {
|
||||||
if (this.username !== '' && this.password !== '') {
|
showLogin.value = true;
|
||||||
const params = {username: this.username, password: this.password};
|
nextTick(() => {
|
||||||
|
userNameElement.value.focus();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this.loadResource(
|
function performLogin() {
|
||||||
this.login(params)
|
if (username.value !== '' && password.value !== '') {
|
||||||
|
const params = {username: username.value, password: password.value};
|
||||||
|
|
||||||
|
loadResource(
|
||||||
|
appConfig.login(params)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log(data);
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.showLogin = false;
|
showLogin.value = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user