mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-05 05:25:26 +02:00
feat(frontend): 🚧 CRUD Functionality
This commit is contained in:
parent
00a8fdda41
commit
afcad2f701
49 changed files with 845 additions and 275 deletions
|
@ -44,13 +44,13 @@
|
|||
target="_blank"
|
||||
rel="noreferrer nofollow"
|
||||
>
|
||||
{{ $t('new-recipe.google-ld-json-info') }}
|
||||
{{ $t("new-recipe.google-ld-json-info") }}
|
||||
</a>
|
||||
<a href="https://github.com/hay-kot/mealie/issues" target="_blank" rel="noreferrer nofollow">
|
||||
{{ $t('new-recipe.github-issues') }}
|
||||
{{ $t("new-recipe.github-issues") }}
|
||||
</a>
|
||||
<a href="https://schema.org/Recipe" target="_blank" rel="noreferrer nofollow">
|
||||
{{ $t('new-recipe.recipe-markup-specification') }}
|
||||
{{ $t("new-recipe.recipe-markup-specification") }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="d-flex justify-end">
|
||||
|
@ -61,7 +61,7 @@
|
|||
@click="addRecipe = false"
|
||||
>
|
||||
<v-icon left> {{ $globals.icons.externalLink }} </v-icon>
|
||||
{{ $t('new-recipe.view-scraped-data') }}
|
||||
{{ $t("new-recipe.view-scraped-data") }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-alert>
|
||||
|
@ -101,7 +101,7 @@
|
|||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<TheUploadBtn class="mx-auto" :text-btn="false" @uploaded="setFile" :post="false"> </TheUploadBtn>
|
||||
<AppButtonUpload class="mx-auto" :text-btn="false" @uploaded="setFile" :post="false"> </AppButtonUpload>
|
||||
</v-card-actions>
|
||||
</BaseDialog>
|
||||
<v-speed-dial v-model="fab" :open-on-hover="absolute" :fixed="absolute" :bottom="absolute" :right="absolute">
|
||||
|
@ -140,11 +140,11 @@
|
|||
|
||||
<script>
|
||||
import { api } from "@/api";
|
||||
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn.vue";
|
||||
import AppButtonUpload from "@/components/UI/Buttons/AppButtonUpload.vue";
|
||||
import BaseDialog from "@/components/UI/Dialogs/BaseDialog.vue";
|
||||
export default {
|
||||
components: {
|
||||
TheUploadBtn,
|
||||
AppButtonUpload,
|
||||
BaseDialog,
|
||||
},
|
||||
props: {
|
||||
|
@ -232,8 +232,9 @@ export default {
|
|||
this.processing = false;
|
||||
},
|
||||
isValidWebUrl(url) {
|
||||
let regEx = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,256}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/gm;
|
||||
return regEx.test(url) ? true : this.$t('new-recipe.must-be-a-valid-url');
|
||||
let regEx =
|
||||
/^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,256}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/gm;
|
||||
return regEx.test(url) ? true : this.$t("new-recipe.must-be-a-valid-url");
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -29,13 +29,13 @@
|
|||
</div>
|
||||
</template>
|
||||
<div class="d-flex row py-3 justify-end">
|
||||
<TheUploadBtn url="/api/backups/upload" @uploaded="getAvailableBackups">
|
||||
<AppButtonUpload url="/api/backups/upload" @uploaded="getAvailableBackups">
|
||||
<template v-slot="{ isSelecting, onButtonClick }">
|
||||
<v-btn :loading="isSelecting" class="mx-2" small color="info" @click="onButtonClick">
|
||||
<v-icon left> {{ $globals.icons.upload }} </v-icon> {{ $t("general.upload") }}
|
||||
</v-btn>
|
||||
</template>
|
||||
</TheUploadBtn>
|
||||
</AppButtonUpload>
|
||||
<BackupDialog :color="color" />
|
||||
|
||||
<v-btn :loading="loading" class="mx-2" small color="success" @click="createBackup">
|
||||
|
@ -74,7 +74,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
|
||||
import AppButtonUpload from "@/components/UI/Buttons/AppButtonUpload";
|
||||
import ImportSummaryDialog from "@/components/ImportSummaryDialog";
|
||||
import ConfirmationDialog from "@/components/UI/Dialogs/ConfirmationDialog";
|
||||
import { api } from "@/api";
|
||||
|
@ -85,7 +85,7 @@ const IMPORT_EVENT = "import";
|
|||
const DELETE_EVENT = "delete";
|
||||
|
||||
export default {
|
||||
components: { StatCard, ImportDialog, TheUploadBtn, ImportSummaryDialog, BackupDialog, ConfirmationDialog },
|
||||
components: { StatCard, ImportDialog, AppButtonUpload, ImportSummaryDialog, BackupDialog, ConfirmationDialog },
|
||||
data() {
|
||||
return {
|
||||
color: "accent",
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<v-data-table :headers="headers" :items="links" sort-by="calories">
|
||||
<template v-slot:item.token="{ item }">
|
||||
{{ `${baseURL}/sign-up/${item.token}` }}
|
||||
<TheCopyButton :copy-text="`${baseURL}/sign-up/${item.token}`" />
|
||||
<AppCopyButton :copy-text="`${baseURL}/sign-up/${item.token}`" />
|
||||
</template>
|
||||
<template v-slot:item.admin="{ item }">
|
||||
<v-btn small :color="item.admin ? 'success' : 'error'" text>
|
||||
|
@ -94,12 +94,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import TheCopyButton from "@/components/UI/Buttons/TheCopyButton";
|
||||
import AppCopyButton from "@/components/UI/Buttons/AppCopyButton";
|
||||
import ConfirmationDialog from "@/components/UI/Dialogs/ConfirmationDialog";
|
||||
import { api } from "@/api";
|
||||
import { validators } from "@/mixins/validators";
|
||||
export default {
|
||||
components: { ConfirmationDialog, TheCopyButton },
|
||||
components: { ConfirmationDialog, AppCopyButton },
|
||||
mixins: [validators],
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{{ title }}
|
||||
<v-spacer></v-spacer>
|
||||
<span>
|
||||
<TheUploadBtn
|
||||
<AppButtonUpload
|
||||
class="mt-1"
|
||||
:url="`/api/migrations/${folder}/upload`"
|
||||
fileName="archive"
|
||||
|
@ -55,7 +55,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
|
||||
import AppButtonUpload from "@/components/UI/Buttons/AppButtonUpload";
|
||||
import { api } from "@/api";
|
||||
import MigrationDialog from "./MigrationDialog";
|
||||
export default {
|
||||
|
@ -66,7 +66,7 @@ export default {
|
|||
available: Array,
|
||||
},
|
||||
components: {
|
||||
TheUploadBtn,
|
||||
AppButtonUpload,
|
||||
MigrationDialog,
|
||||
},
|
||||
data() {
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions class="pb-1 pt-3">
|
||||
<TheUploadBtn
|
||||
<AppButtonUpload
|
||||
:icon="$globals.icons.fileImage"
|
||||
:text="$t('user.upload-photo')"
|
||||
:url="userProfileImage"
|
||||
|
@ -106,14 +106,14 @@
|
|||
<script>
|
||||
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
|
||||
import StatCard from "@/components/UI/StatCard";
|
||||
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
|
||||
import AppButtonUpload from "@/components/UI/Buttons/AppButtonUpload";
|
||||
import { api } from "@/api";
|
||||
import { validators } from "@/mixins/validators";
|
||||
import { initials } from "@/mixins/initials";
|
||||
export default {
|
||||
components: {
|
||||
BaseDialog,
|
||||
TheUploadBtn,
|
||||
AppButtonUpload,
|
||||
StatCard,
|
||||
},
|
||||
mixins: [validators, initials],
|
||||
|
|
|
@ -38,9 +38,9 @@
|
|||
{{ $t("shopping-list.shopping-list") }}
|
||||
</v-btn>
|
||||
<v-spacer></v-spacer>
|
||||
<TheCopyButton color="info" :copy-text="mealPlanURL(mealplan.uid)">
|
||||
<AppCopyButton color="info" :copy-text="mealPlanURL(mealplan.uid)">
|
||||
{{ $t("general.link-copied") }}
|
||||
</TheCopyButton>
|
||||
</AppCopyButton>
|
||||
</v-card-actions>
|
||||
|
||||
<v-list class="mt-0 pt-0">
|
||||
|
@ -90,12 +90,12 @@ import { api } from "@/api";
|
|||
import { utils } from "@/utils";
|
||||
import NewMeal from "@/components/MealPlan/MealPlanNew";
|
||||
import EditPlan from "@/components/MealPlan/MealPlanEditor";
|
||||
import TheCopyButton from "@/components/UI/Buttons/TheCopyButton";
|
||||
import AppCopyButton from "@/components/UI/Buttons/AppCopyButton";
|
||||
export default {
|
||||
components: {
|
||||
NewMeal,
|
||||
EditPlan,
|
||||
TheCopyButton,
|
||||
AppCopyButton,
|
||||
},
|
||||
data: () => ({
|
||||
plannedMeals: [],
|
||||
|
@ -120,7 +120,7 @@ export default {
|
|||
},
|
||||
|
||||
editPlan(id) {
|
||||
this.plannedMeals.forEach(element => {
|
||||
this.plannedMeals.forEach((element) => {
|
||||
if (element.uid === id) {
|
||||
this.editMealPlan = element;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
<v-card v-else-if="activeList">
|
||||
<v-card-title class="headline">
|
||||
<TheCopyButton v-if="!edit" :copy-text="listAsText" color="info" />
|
||||
<AppCopyButton v-if="!edit" :copy-text="listAsText" color="info" />
|
||||
<v-text-field label="Name" single-line dense v-if="edit" v-model="activeList.name"> </v-text-field>
|
||||
<div v-else>
|
||||
{{ activeList.name }}
|
||||
|
@ -141,14 +141,14 @@
|
|||
<script>
|
||||
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
|
||||
import SearchDialog from "@/components/UI/Dialogs/SearchDialog";
|
||||
import TheCopyButton from "@/components/UI/Buttons/TheCopyButton";
|
||||
import AppCopyButton from "@/components/UI/Buttons/AppCopyButton";
|
||||
import VueMarkdown from "@adapttive/vue-markdown";
|
||||
import { api } from "@/api";
|
||||
export default {
|
||||
components: {
|
||||
BaseDialog,
|
||||
SearchDialog,
|
||||
TheCopyButton,
|
||||
AppCopyButton,
|
||||
VueMarkdown,
|
||||
},
|
||||
data() {
|
||||
|
@ -176,7 +176,7 @@ export default {
|
|||
},
|
||||
},
|
||||
listAsText() {
|
||||
const formatList = this.activeList.items.map(x => {
|
||||
const formatList = this.activeList.items.map((x) => {
|
||||
return `${x.quantity} - ${x.text}`;
|
||||
});
|
||||
|
||||
|
@ -206,7 +206,7 @@ export default {
|
|||
|
||||
const recipe = response.data;
|
||||
|
||||
const ingredients = recipe.recipeIngredient.map(x => ({
|
||||
const ingredients = recipe.recipeIngredient.map((x) => ({
|
||||
title: "",
|
||||
text: x.note,
|
||||
quantity: 1,
|
||||
|
@ -217,14 +217,14 @@ export default {
|
|||
this.consolidateList();
|
||||
},
|
||||
consolidateList() {
|
||||
const allText = this.activeList.items.map(x => x.text);
|
||||
const allText = this.activeList.items.map((x) => x.text);
|
||||
|
||||
const uniqueText = allText.filter((item, index) => {
|
||||
return allText.indexOf(item) === index;
|
||||
});
|
||||
|
||||
const newItems = uniqueText.map(x => {
|
||||
let matchingItems = this.activeList.items.filter(y => y.text === x);
|
||||
const newItems = uniqueText.map((x) => {
|
||||
let matchingItems = this.activeList.items.filter((y) => y.text === x);
|
||||
matchingItems[0].quantity = this.sumQuantiy(matchingItems);
|
||||
return matchingItems[0];
|
||||
});
|
||||
|
@ -233,7 +233,7 @@ export default {
|
|||
},
|
||||
sumQuantiy(itemList) {
|
||||
let quantity = 0;
|
||||
itemList.forEach(element => {
|
||||
itemList.forEach((element) => {
|
||||
quantity += element.quantity;
|
||||
});
|
||||
return quantity;
|
||||
|
@ -241,7 +241,7 @@ export default {
|
|||
setActiveList() {
|
||||
if (!this.list) return null;
|
||||
if (!this.group.shoppingLists) return null;
|
||||
this.activeList = this.group.shoppingLists.find(x => x.id == this.list);
|
||||
this.activeList = this.group.shoppingLists.find((x) => x.id == this.list);
|
||||
},
|
||||
async createNewList() {
|
||||
this.newList.group = this.group.name;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue