feature/finish-recipe-assets (#384)

* add features to readme

* Copy markdown reference

* prop as whole recipe

* parameter as url instead of query

* add card styling to editor

* move images to /recipes/{slug}/images

* add image to breaking changes

* fix delete and import errors

* fix debug/about response

* logger updates

* dashboard ui

* add server side events

* unorganized routes

* default slot

* add backup viewer to dashboard

* format

* add dialog to backup imports

* initial event support

* delete assets when removed

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden
2021-05-03 19:32:37 -08:00
committed by GitHub
parent f2d2b79a57
commit 5580d177c3
61 changed files with 1276 additions and 266 deletions

View File

@@ -18,15 +18,20 @@
v-if="!edit"
color="primary"
icon
:href="`/api/recipes/${slug}/asset?file_name=${item.fileName}`"
:href="`/api/recipes/media/${slug}/assets/${item.fileName}`"
target="_blank"
top
>
<v-icon> mdi-download</v-icon>
</v-btn>
<v-btn v-else color="error" icon @click="deleteAsset(i)" top>
<v-icon>mdi-delete</v-icon>
</v-btn>
<div v-else>
<v-btn color="error" icon @click="deleteAsset(i)" top>
<v-icon>mdi-delete</v-icon>
</v-btn>
<v-btn color="primary" icon @click="copyLink(item.name, item.fileName)" top>
<v-icon>mdi-content-copy</v-icon>
</v-btn>
</div>
</v-list-item-action>
</v-list-item>
</v-list>
@@ -107,6 +112,11 @@ export default {
],
};
},
computed: {
baseURL() {
return window.location.origin;
},
},
methods: {
setFileObject(obj) {
this.fileObject = obj;
@@ -124,6 +134,13 @@ export default {
deleteAsset(index) {
this.value.splice(index, 1);
},
copyLink(name, fileName) {
const copyText = `![${name}](${this.baseURL}/api/recipes/media/${this.slug}/assets/${fileName})`;
navigator.clipboard.writeText(copyText).then(
() => console.log("Copied", copyText),
() => console.log("Copied Failed", copyText)
);
},
},
};
</script>

View File

@@ -27,23 +27,36 @@
<v-row>
<v-col cols="12" sm="12" md="4" lg="4">
<Ingredients :edit="true" v-model="value.recipeIngredient" />
<v-card class="mt-6">
<v-card-title class="py-2">
{{ $t("recipe.categories") }}
</v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text>
<CategoryTagSelector
:return-object="false"
v-model="value.recipeCategory"
:show-add="true"
:show-label="false"
/>
</v-card-text>
</v-card>
<h2 class="mt-6">{{ $t("recipe.categories") }}</h2>
<CategoryTagSelector
:return-object="false"
v-model="value.recipeCategory"
:show-add="true"
:show-label="false"
/>
<h2 class="mt-4">{{ $t("tag.tags") }}</h2>
<CategoryTagSelector
:return-object="false"
v-model="value.tags"
:show-add="true"
:tag-selector="true"
:show-label="false"
/>
<v-card class="mt-2">
<v-card-title class="py-2">
{{ $t("tag.tags") }}
</v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text>
<CategoryTagSelector
:return-object="false"
v-model="value.tags"
:show-add="true"
:tag-selector="true"
:show-label="false"
/>
</v-card-text>
</v-card>
<Nutrition v-model="value.nutrition" :edit="true" />
<Assets v-model="value.assets" :edit="true" :slug="value.slug" />
<ExtrasEditor :extras="value.extras" @save="saveExtras" />

View File

@@ -1,14 +1,14 @@
<template>
<div>
<v-card-title class="headline">
{{ name }}
{{ recipe.name }}
</v-card-title>
<v-card-text>
<vue-markdown :source="description"> </vue-markdown>
<vue-markdown :source="recipe.description"> </vue-markdown>
<v-row dense disabled>
<v-col>
<v-btn
v-if="yields"
v-if="recipe.yields"
dense
small
:hover="false"
@@ -21,59 +21,59 @@
{{ yields }}
</v-btn>
</v-col>
<Rating :value="rating" :name="name" :slug="slug" />
<Rating :value="recipe.rating" :name="recipe.name" :slug="recipe.slug" />
</v-row>
<v-row>
<v-col cols="12" sm="12" md="4" lg="4">
<Ingredients :value="ingredients" :edit="false" />
<Ingredients :value="recipe.recipeIngredient" :edit="false" />
<div v-if="medium">
<v-card class="mt-2" v-if="categories.length > 0">
<v-card class="mt-2" v-if="recipe.recipeCategory.length > 0">
<v-card-title class="py-2">
{{ $t("recipe.categories") }}
</v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text>
<RecipeChips :items="categories" />
<RecipeChips :items="recipe.recipeCategory" />
</v-card-text>
</v-card>
<v-card class="mt-2" v-if="tags.length > 0">
<v-card class="mt-2" v-if="recipe.tags.length > 0">
<v-card-title class="py-2">
{{ $t("tag.tags") }}
</v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text>
<RecipeChips :items="tags" :isCategory="false" />
<RecipeChips :items="recipe.tags" :isCategory="false" />
</v-card-text>
</v-card>
<Nutrition :value="nutrition" :edit="false" />
<Assets :value="assets" :edit="false" :slug="slug" />
<Nutrition v-if="recipe.settings.showNutrition" :value="recipe.nutrition" :edit="false" />
<Assets v-if="recipe.settings.showAssets" :value="recipe.assets" :edit="false" :slug="recipe.slug" />
</div>
</v-col>
<v-divider v-if="medium" class="my-divider" :vertical="true"></v-divider>
<v-col cols="12" sm="12" md="8" lg="8">
<Instructions :value="instructions" :edit="false" />
<Notes :value="notes" :edit="false" />
<Instructions :value="recipe.recipeInstructions" :edit="false" />
<Notes :value="recipe.notes" :edit="false" />
</v-col>
</v-row>
<div v-if="!medium">
<RecipeChips :title="$t('recipe.categories')" :items="categories" />
<RecipeChips :title="$t('tag.tags')" :items="tags" />
<Nutrition :value="nutrition" :edit="false" />
<Assets :value="assets" :edit="false" :slug="slug" />
<RecipeChips :title="$t('recipe.categories')" :items="recipe.recipeCategory" />
<RecipeChips :title="$t('tag.tags')" :items="recipe.tags" />
<Nutrition v-if="recipe.settings.showNutrition" :value="recipe.nutrition" :edit="false" />
<Assets v-if="recipe.settings.showAssets" :value="recipe.assets" :edit="false" :slug="recipe.slug" />
</div>
<v-row class="mt-2 mb-1">
<v-col></v-col>
<v-btn
v-if="orgURL"
v-if="recipe.orgURL"
dense
small
:hover="false"
type="label"
:ripple="false"
elevation="0"
:href="orgURL"
:href="recipe.orgURL"
color="secondary darken-1"
target="_blank"
class="rounded-sm mr-4"
@@ -107,19 +107,7 @@ export default {
Rating,
},
props: {
name: String,
slug: String,
description: String,
ingredients: Array,
instructions: Array,
categories: Array,
tags: Array,
notes: Array,
rating: Number,
yields: String,
orgURL: String,
nutrition: Object,
assets: Array,
recipe: Object,
},
data() {
return {

View File

@@ -1,7 +1,11 @@
<template>
<v-btn color="accent" text :loading="downloading" @click="downloadFile">
{{ showButtonText }}
</v-btn>
<div>
<slot v-bind="{ downloading, downloadFile }">
<v-btn color="accent" text :loading="downloading" @click="downloadFile">
{{ showButtonText }}
</v-btn>
</slot>
</div>
</template>
<script>

View File

@@ -1,10 +1,12 @@
<template>
<v-form ref="file">
<input ref="uploader" class="d-none" type="file" @change="onFileChanged" />
<v-btn :loading="isSelecting" @click="onButtonClick" color="accent" :text="textBtn">
<v-icon left> {{ icon }}</v-icon>
{{ text ? text : defaultText }}
</v-btn>
<slot v-bind="{ isSelecting, onButtonClick }">
<v-btn :loading="isSelecting" @click="onButtonClick" color="accent" :text="textBtn">
<v-icon left> {{ icon }}</v-icon>
{{ text ? text : defaultText }}
</v-btn>
</slot>
</v-form>
</template>
@@ -25,7 +27,7 @@ export default {
default: true,
},
},
data: () => ({
data: () => ({
file: null,
isSelecting: false,
}),

View File

@@ -0,0 +1,81 @@
<template>
<div class="mt-2">
<v-card>
<v-card-title class="headline">
Log
<v-spacer></v-spacer>
<v-text-field
class="ml-auto shrink mb-n7"
solo
label="Log Lines"
type="number"
append-icon="mdi-refresh-circle"
v-model="lines"
@click:append="getLogText"
suffix="lines"
single-line
>
</v-text-field>
<TheDownloadBtn :button-text="$t('about.download-log')" download-url="/api/debug/log">
<template v-slot:default="{ downloadFile }">
<v-btn bottom right relative fab icon color="primary" @click="downloadFile">
<v-icon> mdi-download </v-icon>
</v-btn>
</template>
</TheDownloadBtn>
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<div v-for="(item, index) in splitText" :key="index" :class="getClass(item)">
{{ item }}
</div>
</v-card-text>
</v-card>
</div>
</template>
<script>
import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn";
import { api } from "@/api";
export default {
components: { TheDownloadBtn },
data() {
return {
lines: 200,
text: "",
};
},
mounted() {
this.getLogText();
},
computed: {
splitText() {
return this.text.split("/n");
},
},
methods: {
async getLogText() {
this.text = await api.meta.getLogText(this.lines);
},
getClass(text) {
const isError = text.includes("ERROR:");
if (isError) {
return "log--error";
}
},
},
};
</script>
<style scoped>
.log-text {
background-color: #e0e0e077;
}
.log--error {
color: #ef5350;
}
.line-number {
color: black;
font-weight: bold;
}
</style>

View File

@@ -147,6 +147,11 @@ export default {
},
adminLinks() {
return [
{
icon: "mdi-view-dashboard",
to: "/admin/dashboard",
title: this.$t("general.dashboard"),
},
{
icon: "mdi-cog",
to: "/admin/settings",