mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 12:18:45 -05:00
recipe view timer
This commit is contained in:
@@ -10,6 +10,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "7.2.96",
|
"@mdi/font": "7.2.96",
|
||||||
|
"@types/luxon": "^3.4.2",
|
||||||
|
"luxon": "^3.4.4",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"vue": "^3.4.15",
|
"vue": "^3.4.15",
|
||||||
"vue-router": "4",
|
"vue-router": "4",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import vuetify from "@/vuetify";
|
|||||||
import ShoppingListPage from "@/pages/ShoppingListPage.vue";
|
import ShoppingListPage from "@/pages/ShoppingListPage.vue";
|
||||||
import RecipeSearchPage from "@/pages/RecipeSearchPage.vue";
|
import RecipeSearchPage from "@/pages/RecipeSearchPage.vue";
|
||||||
import RecipeViewPage from "@/pages/RecipeViewPage.vue";
|
import RecipeViewPage from "@/pages/RecipeViewPage.vue";
|
||||||
|
import luxonPlugin from "@/plugins/luxonPlugin";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{path: '/', redirect: '/search', name: 'index'},
|
{path: '/', redirect: '/search', name: 'index'},
|
||||||
@@ -33,5 +34,6 @@ const app = createApp(App)
|
|||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.use(vuetify)
|
app.use(vuetify)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
|
app.use(luxonPlugin)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<tr>
|
<tr>
|
||||||
<template v-if="ingredient.isHeader">
|
<template v-if="ingredient.isHeader">
|
||||||
<td colspan="4">{{ ingredient.note }}</td>
|
<td colspan="4"><b>{{ ingredient.note }}</b></td>
|
||||||
</template>
|
</template>
|
||||||
<td>{{ ingredient.amount }}</td>
|
<template v-else>
|
||||||
<td><span v-if="ingredient.unit != null">{{ ingredient.unit.name }}</span></td>
|
<td>{{ ingredient.amount }}</td>
|
||||||
<td ><span v-if="ingredient.food != null">{{ ingredient.food.name }}</span></td>
|
<td><span v-if="ingredient.unit != null">{{ ingredient.unit.name }}</span></td>
|
||||||
<td>
|
<td><span v-if="ingredient.food != null">{{ ingredient.food.name }}</span></td>
|
||||||
<v-icon class="far fa-comment" v-if="ingredient.note != ''" @click="show_tooltip = !show_tooltip">
|
<td>
|
||||||
<v-tooltip v-model="show_tooltip" activator="parent" location="start">{{ ingredient.note }}</v-tooltip>
|
<v-icon class="far fa-comment" v-if="ingredient.note != ''" @click="show_tooltip = !show_tooltip">
|
||||||
</v-icon>
|
<v-tooltip v-model="show_tooltip" activator="parent" location="start">{{ ingredient.note }}</v-tooltip>
|
||||||
</td>
|
</v-icon>
|
||||||
|
</td>
|
||||||
|
</template>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -26,10 +29,10 @@ export default defineComponent({
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data(){
|
data() {
|
||||||
return {
|
return {
|
||||||
show_tooltip: false,
|
show_tooltip: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<v-img cover lazy :src="recipe.image"></v-img>
|
|
||||||
|
|
||||||
|
|
||||||
<v-card>
|
<v-card>
|
||||||
|
|
||||||
<v-card-title>{{ recipe.name }}</v-card-title>
|
<v-card-title>{{ recipe.name }}</v-card-title>
|
||||||
|
|
||||||
|
<v-img max-height="25vh" cover lazy :src="recipe.image">
|
||||||
|
<!-- TODO placement in image -->
|
||||||
|
<KeywordsBar :keywords="recipe?.keywords"></KeywordsBar>
|
||||||
|
|
||||||
|
<v-chip size="small" color="primary" label>
|
||||||
|
<v-icon icon="fas fa-calendar" class="mr-2"></v-icon>
|
||||||
|
{{ recipe.lastCooked }}
|
||||||
|
</v-chip>
|
||||||
|
|
||||||
|
<v-rating v-model="recipe.rating" color="tandoor"></v-rating>
|
||||||
|
</v-img>
|
||||||
|
|
||||||
|
|
||||||
<v-container>
|
<v-container>
|
||||||
<v-row class="text-center text-body-2">
|
<v-row class="text-center text-body-2">
|
||||||
<v-col class="pt-1 pb-1">
|
<v-col class="pt-1 pb-1">
|
||||||
@@ -24,36 +33,11 @@
|
|||||||
<div class="text-grey"><span v-if="recipe?.servingsText">{{ recipe.servingsText }}</span><span v-else>Servings</span></div>
|
<div class="text-grey"><span v-if="recipe?.servingsText">{{ recipe.servingsText }}</span><span v-else>Servings</span></div>
|
||||||
</template>
|
</template>
|
||||||
</NumberScalerDialog>
|
</NumberScalerDialog>
|
||||||
|
|
||||||
|
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
||||||
<v-card-subtitle v-if="recipe?.description"> {{ recipe.description }}</v-card-subtitle>
|
|
||||||
<v-card-subtitle>
|
|
||||||
<KeywordsBar :keywords="recipe?.keywords"></KeywordsBar>
|
|
||||||
</v-card-subtitle>
|
|
||||||
|
|
||||||
|
|
||||||
<v-card-text>
|
|
||||||
|
|
||||||
<v-chip size="small" color="primary" label>
|
|
||||||
<v-icon icon="fa fa-clock" class="mr-2"></v-icon>
|
|
||||||
{{ recipe.workingTime }} min
|
|
||||||
</v-chip>
|
|
||||||
<v-chip size="small" color="primary" label>
|
|
||||||
<v-icon icon="fas fa-hourglass-half" class="mr-2"></v-icon>
|
|
||||||
{{ recipe.waitingTime }} min
|
|
||||||
</v-chip>
|
|
||||||
<v-chip size="small" color="primary" label>
|
|
||||||
<v-icon icon="fas fa-calendar" class="mr-2"></v-icon>
|
|
||||||
{{ recipe.lastCooked }}
|
|
||||||
</v-chip>
|
|
||||||
<v-rating v-model="recipe.rating" color="tandoor"></v-rating>
|
|
||||||
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<v-card class="mt-1">
|
<v-card class="mt-1">
|
||||||
@@ -65,7 +49,7 @@
|
|||||||
<Step :step="s"></Step>
|
<Step :step="s"></Step>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
<!-- <RecipeActivity :recipe="recipe"></RecipeActivity>-->
|
<!-- <RecipeActivity :recipe="recipe"></RecipeActivity>-->
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -85,9 +69,7 @@ export default defineComponent({
|
|||||||
components: {RecipeActivity, Step, StepsOverview, IngredientsTable, NumberScalerDialog, KeywordsBar},
|
components: {RecipeActivity, Step, StepsOverview, IngredientsTable, NumberScalerDialog, KeywordsBar},
|
||||||
computed: {},
|
computed: {},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {}
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
recipe: {
|
recipe: {
|
||||||
|
|||||||
@@ -5,13 +5,18 @@
|
|||||||
<v-col>{{ step.name }}</v-col>
|
<v-col>{{ step.name }}</v-col>
|
||||||
<v-col class="text-right">
|
<v-col class="text-right">
|
||||||
<v-btn-group density="compact" variant="tonal">
|
<v-btn-group density="compact" variant="tonal">
|
||||||
<v-btn size="small" color="info" v-if="step.time > 0"><i class="fas fa-stopwatch mr-1"></i> {{ step.time }}</v-btn>
|
<v-btn size="small" color="info" v-if="step.time > 0" @click="startTimer(step.time)"><i class="fas fa-stopwatch mr-1"></i> {{ step.time }}</v-btn>
|
||||||
<v-btn size="small" color="success" ><i class="fas fa-check"></i></v-btn>
|
<v-btn size="small" color="success"><i class="fas fa-check"></i></v-btn>
|
||||||
</v-btn-group>
|
</v-btn-group>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
|
|
||||||
|
<v-alert v-if="timer_end != null" :color="timer_color" closable @click:close="timer_end = null">
|
||||||
|
<v-alert-title><i class="fas fa-stopwatch mr-1"></i> {{ remaining_time }}</v-alert-title>
|
||||||
|
Finished at {{ finished_at }}
|
||||||
|
</v-alert>
|
||||||
|
|
||||||
<IngredientsTable :ingredients="step.ingredients"></IngredientsTable>
|
<IngredientsTable :ingredients="step.ingredients"></IngredientsTable>
|
||||||
|
|
||||||
<v-card-text v-if="step.instruction?.length > 0">
|
<v-card-text v-if="step.instruction?.length > 0">
|
||||||
@@ -24,15 +29,58 @@
|
|||||||
import {defineComponent, PropType} from 'vue'
|
import {defineComponent, PropType} from 'vue'
|
||||||
import IngredientsTable from "@/components/display/IngredientsTable.vue";
|
import IngredientsTable from "@/components/display/IngredientsTable.vue";
|
||||||
import {Step} from "@/openapi";
|
import {Step} from "@/openapi";
|
||||||
|
import {DateTime, Duration, Interval} from "luxon";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "Step",
|
name: "Step",
|
||||||
|
computed: {
|
||||||
|
timer_color: function () {
|
||||||
|
if (this.timer_end != null) {
|
||||||
|
if (this.time_now > this.timer_end) {
|
||||||
|
return 'warning'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
remaining_time: function () {
|
||||||
|
if (this.timer_end != null) {
|
||||||
|
if (Interval.fromDateTimes(this.time_now, this.timer_end).length() > 0){
|
||||||
|
return Duration.fromMillis(Interval.fromDateTimes(this.time_now, this.timer_end).length()).toFormat('hh:mm:ss')
|
||||||
|
} else {
|
||||||
|
return '00:00:00'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
finished_at: function () {
|
||||||
|
if (this.timer_end != null) {
|
||||||
|
return this.timer_end.toLocaleString(DateTime.TIME_SIMPLE)
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {IngredientsTable},
|
components: {IngredientsTable},
|
||||||
props: {
|
props: {
|
||||||
step: {
|
step: {
|
||||||
type: {} as PropType<Step>,
|
type: {} as PropType<Step>,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
timer_end: null as null | DateTime,
|
||||||
|
time_now: DateTime.now(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
setInterval(() => {
|
||||||
|
this.time_now = DateTime.now()
|
||||||
|
}, 500)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
startTimer(minutes: number) {
|
||||||
|
this.timer_end = DateTime.now().plus({minutes: minutes})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -226,6 +226,11 @@
|
|||||||
"@types/tough-cookie" "*"
|
"@types/tough-cookie" "*"
|
||||||
parse5 "^7.0.0"
|
parse5 "^7.0.0"
|
||||||
|
|
||||||
|
"@types/luxon@^3.4.2":
|
||||||
|
version "3.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7"
|
||||||
|
integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "20.11.19"
|
version "20.11.19"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.19.tgz#b466de054e9cb5b3831bee38938de64ac7f81195"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.19.tgz#b466de054e9cb5b3831bee38938de64ac7f81195"
|
||||||
@@ -604,6 +609,11 @@ lru-cache@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
luxon@^3.4.4:
|
||||||
|
version "3.4.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af"
|
||||||
|
integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==
|
||||||
|
|
||||||
magic-string@^0.30.6:
|
magic-string@^0.30.6:
|
||||||
version "0.30.7"
|
version "0.30.7"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.7.tgz#0cecd0527d473298679da95a2d7aeb8c64048505"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.7.tgz#0cecd0527d473298679da95a2d7aeb8c64048505"
|
||||||
|
|||||||
Reference in New Issue
Block a user