parsley/app/javascript/components/RecipeShow.vue

272 lines
7.4 KiB
Vue
Raw Normal View History

2018-03-30 14:31:09 -05:00
<template>
2018-04-01 21:43:23 -05:00
<div>
<div v-if="recipe === null">
Loading...
</div>
<div v-else>
<div class="level is-mobile">
2018-04-15 15:19:50 -05:00
<div class="level-item has-text-centered">
<div>
<p class="heading">Time</p>
<p class="title is-6">{{timeDisplay}}</p>
</div>
2018-04-01 21:43:23 -05:00
</div>
2018-03-30 14:31:09 -05:00
2018-04-15 14:15:42 -05:00
<div class="level-item has-text-centered">
2018-04-15 15:19:50 -05:00
<div>
<p class="heading">Yields</p>
<p class="title is-6">{{recipe.yields}}</p>
</div>
2018-04-01 21:43:23 -05:00
</div>
2018-04-15 15:19:50 -05:00
<div class="level-item has-text-centered">
<div>
<p class="heading">Source</p>
2018-07-13 16:01:56 -05:00
<p class="title is-6">
<a v-if="isSourceUrl" :href="sourceUrl">{{sourceText}}</a>
<span v-else>{{sourceText}}</span>
</p>
2018-04-15 15:19:50 -05:00
</div>
2018-04-01 21:43:23 -05:00
</div>
</div>
<div class="columns">
2018-04-17 09:59:38 -05:00
<div class="column is-one-third-desktop">
2018-04-01 21:43:23 -05:00
<div class="message">
2018-04-15 15:19:50 -05:00
<div class="message-header">
Ingredients
2018-04-17 09:59:38 -05:00
<button class="button is-small is-primary" type="button" @click="showConvertDialog = true">Convert</button>
2021-05-09 23:31:44 -05:00
<app-dropdown :open="addToTasksMenuOpen" label="Add to list" button-class="is-small is-primary" @open="addToTasksMenuOpen = true" @close="addToTasksMenuOpen = false">
2024-10-02 14:34:50 -05:00
<button class="button primary" v-for="tl in taskStore.taskLists" :key="tl.id" @click="addRecipeToList(tl)">
2021-05-09 23:31:44 -05:00
{{tl.name}}
</button>
</app-dropdown>
2018-04-15 15:19:50 -05:00
</div>
2018-04-01 21:43:23 -05:00
<div class="message-body content">
2018-04-17 09:59:38 -05:00
<ul v-if="recipe.ingredients.length > 0" v-click-strike>
2018-04-01 21:43:23 -05:00
<li v-for="i in recipe.ingredients">
{{i.display_name}}
</li>
</ul>
</div>
</div>
</div>
<div class="column">
<div class="message">
<div class="message-header">Directions</div>
2018-04-17 09:59:38 -05:00
<div class="message-body content" v-html="recipe.rendered_steps" v-click-strike>
2018-04-01 21:43:23 -05:00
</div>
</div>
</div>
</div>
<div class="message">
<div class="message-header" @click="showNutrition = !showNutrition">Nutrition Data</div>
<div class="message-body" v-show="showNutrition">
<table class="table">
<thead>
2018-04-01 21:43:23 -05:00
<tr>
<th>Item</th>
<th>Value</th>
</tr>
</thead>
<tbody>
2018-04-01 21:43:23 -05:00
<tr v-for="nutrient in recipe.nutrition_data.nutrients" :key="nutrient.name">
<td>{{nutrient.label}}</td>
2018-09-12 14:17:18 -05:00
<td>{{ roundValue(nutrient.value) }}</td>
2018-04-01 21:43:23 -05:00
</tr>
</tbody>
2018-04-01 21:43:23 -05:00
</table>
<h3 class="title is-5">Nutrition Calculation Warnings</h3>
<ul>
<li v-for="warn in recipe.nutrition_data.errors" :key="warn">
{{warn}}
</li>
</ul>
</div>
</div>
</div>
2018-04-15 15:19:50 -05:00
<app-modal :open="showConvertDialog" @dismiss="showConvertDialog = false" title="Convert Recipe">
<div class="field">
<label class="label">Scale</label>
<div class="control">
<div class="select">
<select v-model="scaleValue">
<option v-for="s in scaleOptions" :key="s" :value="s">{{s}}</option>
</select>
</div>
</div>
</div>
<div class="field">
<label class="label">System</label>
<div class="control">
<label class="radio">
<input type="radio" value="" v-model="systemConvertValue" />
No System Conversion
</label>
<label class="radio">
<input type="radio" value="standard" v-model="systemConvertValue" />
Convert to Standard Units
</label>
<label class="radio">
<input type="radio" value="metric" v-model="systemConvertValue" />
Convert to Metric Units
</label>
</div>
</div>
<div class="field">
<label class="label">Unit</label>
<div class="control">
<label class="radio">
<input type="radio" value="" v-model="unitConvertValue" />
No Unit Conversion
</label>
<label class="radio">
<input type="radio" value="volume" v-model="unitConvertValue" />
Convert to Volume Units
</label>
<label class="radio">
<input type="radio" value="mass" v-model="unitConvertValue" />
Convert to Mass Units
</label>
</div>
</div>
2018-04-16 11:28:58 -05:00
<div class="buttons">
2018-04-17 09:59:38 -05:00
<button type="button" class="button is-primary" @click="convert">Convert</button>
<button type="button" class="button" @click="showConvertDialog = false">Close</button>
2018-04-16 11:28:58 -05:00
</div>
2018-04-15 15:19:50 -05:00
</app-modal>
2018-04-01 21:43:23 -05:00
</div>
2018-03-30 14:31:09 -05:00
</template>
2024-10-02 14:34:50 -05:00
<script setup>
2018-03-30 14:31:09 -05:00
2024-10-02 14:34:50 -05:00
import { computed, onMounted, ref, watch } from "vue";
import { useRouter } from "vue-router";
2021-05-09 23:31:44 -05:00
import api from "../lib/Api";
import { useTaskStore } from "../stores/task";
2021-05-09 23:31:44 -05:00
2024-10-02 14:34:50 -05:00
const taskStore = useTaskStore();
const router = useRouter();
2018-04-15 15:19:50 -05:00
2024-10-02 14:34:50 -05:00
const props = defineProps({
recipe: {
required: true,
type: Object
}
});
const showNutrition = ref(false);
const showConvertDialog = ref(false);
const addToTasksMenuOpen = ref(false);
const scaleValue = ref('1');
const systemConvertValue = ref('');
const unitConvertValue = ref('');
const scaleOptions = [
'1/4',
'1/3',
'1/2',
'2/3',
'3/4',
'1',
'1 1/2',
'2',
'3',
'4'
];
const timeDisplay = computed(() => {
let a = formatMinutes(props.recipe.active_time);
const t = formatMinutes(props.recipe.total_time);
if (a) {
a = ` (${a} active)`;
}
2018-07-13 16:01:56 -05:00
2024-10-02 14:34:50 -05:00
return t + a;
});
2018-07-13 16:01:56 -05:00
2024-10-02 14:34:50 -05:00
const sourceUrl = computed(() => {
try {
return new URL(props.recipe.source);
} catch(err) {
return null;
}
});
2018-07-13 16:01:56 -05:00
2024-10-02 14:34:50 -05:00
const isSourceUrl = computed(() => sourceUrl.value !== null);
const sourceText = computed(() => isSourceUrl.value ? sourceUrl.value.host : props.recipe.source);
watch(props.recipe, (r) => {
if (r) {
scaleValue.value = r.converted_scale || '1';
systemConvertValue.value = r.converted_system;
unitConvertValue.value = r.converted_unit;
}
}, { immediate: true });
2018-04-17 09:59:38 -05:00
2024-10-02 14:34:50 -05:00
onMounted(() => {
taskStore.ensureTaskLists();
});
2021-05-09 23:31:44 -05:00
2024-10-02 14:34:50 -05:00
function addRecipeToList(list) {
api.addRecipeToTaskList(list.id, props.recipe.id)
2021-05-09 23:31:44 -05:00
.then(() => {
2024-10-02 14:34:50 -05:00
taskStore.setCurrentTaskList(list);
router.push({name: 'task_lists'})
2021-05-09 23:31:44 -05:00
});
2024-10-02 14:34:50 -05:00
}
function convert() {
showConvertDialog.value = false;
router.push({name: 'recipe', query: { scale: scaleValue.value, system: systemConvertValue.value, unit: unitConvertValue.value }});
}
function roundValue(v) {
return parseFloat(v).toFixed(2);
}
function formatMinutes(min) {
if (min) {
const partUnits = [
{unit: "d", minutes: 60 * 24},
{unit: "h", minutes: 60},
{unit: "m", minutes: 1}
];
const parts = [];
let remaining = min;
for (let unit of partUnits) {
let val = Math.floor(remaining / unit.minutes);
remaining = remaining % unit.minutes;
if (val > 0) {
parts.push(`${val} ${unit.unit}`);
2018-04-15 15:19:50 -05:00
}
}
2022-02-02 15:55:24 -06:00
2024-10-02 14:34:50 -05:00
return parts.join(" ");
} else {
return "";
2018-04-01 21:43:23 -05:00
}
2018-03-30 14:31:09 -05:00
}
</script>
<style lang="scss" scoped>
2022-02-02 15:55:24 -06:00
</style>