mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
fixed list groups
This commit is contained in:
@@ -1737,12 +1737,12 @@ class RecipeUrlImportView(APIView):
|
|||||||
if re.match('^(https?://)?(www\\.youtube\\.com|youtu\\.be)/.+$', url):
|
if re.match('^(https?://)?(www\\.youtube\\.com|youtu\\.be)/.+$', url):
|
||||||
if validate_import_url(url):
|
if validate_import_url(url):
|
||||||
# TODO new serializer
|
# TODO new serializer
|
||||||
return Response({'recipe_json': get_from_youtube_scraper(url, request), 'recipe_images': [], 'duplicate': duplicate}, status=status.HTTP_200_OK)
|
return Response({'recipe_json': get_from_youtube_scraper(url, request), 'recipe_images': []}, status=status.HTTP_200_OK)
|
||||||
if re.match('^(.)*/view/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
|
if re.match('^(.)*/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
|
||||||
recipe_json = requests.get(
|
recipe_json = requests.get(
|
||||||
url.replace('/view/recipe/', '/api/recipe/').replace(re.split('/view/recipe/[0-9]+', url)[1],
|
url.replace('/recipe/', '/api/recipe/').replace(re.split('/recipe/[0-9]+', url)[1],
|
||||||
'') + '?share='
|
'') + '?share='
|
||||||
+ re.split('/view/recipe/[0-9]+', url)[1].replace('/', '')).json()
|
+ re.split('/recipe/[0-9]+', url)[1].replace('/', '')).json()
|
||||||
recipe_json = clean_dict(recipe_json, 'id')
|
recipe_json = clean_dict(recipe_json, 'id')
|
||||||
serialized_recipe = RecipeExportSerializer(data=recipe_json, context={'request': request})
|
serialized_recipe = RecipeExportSerializer(data=recipe_json, context={'request': request})
|
||||||
if serialized_recipe.is_valid():
|
if serialized_recipe.is_valid():
|
||||||
@@ -1756,7 +1756,7 @@ class RecipeUrlImportView(APIView):
|
|||||||
name=f'{uuid.uuid4()}_{recipe.pk}{pathlib.Path(recipe_json["image"]).suffix}')
|
name=f'{uuid.uuid4()}_{recipe.pk}{pathlib.Path(recipe_json["image"]).suffix}')
|
||||||
recipe.save()
|
recipe.save()
|
||||||
# TODO new serializer
|
# TODO new serializer
|
||||||
return Response({'link': request.build_absolute_uri(reverse('view_recipe', args={recipe.pk})), 'duplicate': duplicate}, status=status.HTTP_201_CREATED)
|
return Response({'link': request.build_absolute_uri('recipe/' + recipe.pk)}, status=status.HTTP_201_CREATED)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if validate_import_url(url):
|
if validate_import_url(url):
|
||||||
@@ -2105,7 +2105,7 @@ def share_link(request, pk):
|
|||||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||||
link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
||||||
return JsonResponse({'pk': pk, 'share': link.uuid,
|
return JsonResponse({'pk': pk, 'share': link.uuid,
|
||||||
'link': request.build_absolute_uri(reverse('view_recipe', args=[pk, link.uuid]))})
|
'link': request.build_absolute_uri(f'recipe/{pk}/{link.uuid}')})
|
||||||
else:
|
else:
|
||||||
return JsonResponse({'error': 'sharing_disabled'}, status=403)
|
return JsonResponse({'error': 'sharing_disabled'}, status=403)
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-btn class="float-right" @click="dialog = false">{{$t('Close')}}</v-btn>
|
<v-btn class="float-right" @click="dialog = false">{{ $t('Close') }}</v-btn>
|
||||||
<v-btn class="float-right" color="success" prepend-icon="fa-solid fa-share-nodes" @click="shareIntend()">{{$t('Share')}}</v-btn>
|
<v-btn class="float-right" color="success" prepend-icon="fa-solid fa-share-nodes" @click="shareIntend()">{{ $t('Share') }}</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
@@ -21,14 +21,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||||
import {onMounted, PropType, ref} from "vue";
|
import {onMounted, PropType, ref, watch} from "vue";
|
||||||
import {ApiApi, Recipe, RecipeFlat, RecipeOverview, ShareLink} from "@/openapi";
|
import {ApiApi, Recipe, RecipeFlat, RecipeOverview, ShareLink} from "@/openapi";
|
||||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||||
import BtnCopy from "@/components/buttons/BtnCopy.vue";
|
import BtnCopy from "@/components/buttons/BtnCopy.vue";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
recipe: {type: Object as PropType<Recipe | RecipeFlat | RecipeOverview>, required: true}
|
recipe: {type: Object as PropType<Recipe | RecipeFlat | RecipeOverview>, required: true}
|
||||||
})
|
})
|
||||||
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
@@ -37,14 +37,17 @@ const dialog = ref(false)
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const shareLink = ref({} as ShareLink)
|
const shareLink = ref({} as ShareLink)
|
||||||
|
|
||||||
onMounted(() => {
|
// watch change to dialog open and generate share link when dialog is opened
|
||||||
generateShareLink()
|
watch(dialog, (newValue, oldValue) => {
|
||||||
|
if (!oldValue && newValue) {
|
||||||
|
generateShareLink()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* request api to generate share link
|
* request api to generate share link
|
||||||
*/
|
*/
|
||||||
function generateShareLink(){
|
function generateShareLink() {
|
||||||
let api = new ApiApi()
|
let api = new ApiApi()
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<v-card variant="outlined">
|
<v-card variant="outlined">
|
||||||
<template #title>
|
<template #title>
|
||||||
<v-card-title>
|
<v-card-title>
|
||||||
<v-chip color="primary">{{$t('Step')}} {{ props.stepIndex + 1 }}</v-chip>
|
<v-chip color="primary">{{ $t('Step') }} {{ props.stepIndex + 1 }}</v-chip>
|
||||||
{{ step.name }}
|
{{ step.name }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
</template>
|
</template>
|
||||||
@@ -50,42 +50,44 @@
|
|||||||
<v-row dense>
|
<v-row dense>
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-label>{{ $t('Ingredients') }}</v-label>
|
<v-label>{{ $t('Ingredients') }}</v-label>
|
||||||
|
<div v-if="!mobile">
|
||||||
|
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" empty-insert-threshold="25" group="ingredients">
|
||||||
|
<v-row v-for="(ingredient, index) in step.ingredients" dense>
|
||||||
|
<v-col cols="2">
|
||||||
|
<v-text-field :id="`id_input_amount_${step.id}_${index}`" :label="$t('Amount')" type="number" v-model="ingredient.amount" density="compact"
|
||||||
|
hide-details>
|
||||||
|
|
||||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" v-if="!mobile">
|
<template #prepend>
|
||||||
<v-row v-for="(ingredient, index) in step.ingredients" dense>
|
<v-icon icon="$dragHandle" class="drag-handle cursor-grab"></v-icon>
|
||||||
<v-col cols="2">
|
</template>
|
||||||
<v-text-field :id="`id_input_amount_${step.id}_${index}`" :label="$t('Amount')" type="number" v-model="ingredient.amount" density="compact" hide-details>
|
</v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="3">
|
||||||
|
<model-select model="Unit" v-model="ingredient.unit" density="compact" allow-create hide-details></model-select>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="3">
|
||||||
|
<model-select model="Food" v-model="ingredient.food" density="compact" allow-create hide-details></model-select>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="3" @keydown.tab="event => handleIngredientNoteTab(event, index)">
|
||||||
|
<v-text-field :label="$t('Note')" v-model="ingredient.note" density="compact" hide-details></v-text-field>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="1">
|
||||||
|
<v-btn variant="plain" icon>
|
||||||
|
<v-icon icon="$settings"></v-icon>
|
||||||
|
<v-menu activator="parent">
|
||||||
|
<v-list>
|
||||||
|
<v-list-item @click="step.ingredients.splice(index, 1)" prepend-icon="$delete">{{ $t('Delete') }}</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
<template #prepend>
|
</v-col>
|
||||||
<v-icon icon="$dragHandle" class="drag-handle cursor-grab"></v-icon>
|
</v-row>
|
||||||
</template>
|
</vue-draggable>
|
||||||
</v-text-field>
|
</div>
|
||||||
</v-col>
|
|
||||||
<v-col cols="3">
|
|
||||||
<model-select model="Unit" v-model="ingredient.unit" density="compact" allow-create hide-details></model-select>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="3">
|
|
||||||
<model-select model="Food" v-model="ingredient.food" density="compact" allow-create hide-details></model-select>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="3" @keydown.tab="event => handleIngredientNoteTab(event, index)">
|
|
||||||
<v-text-field :label="$t('Note')" v-model="ingredient.note" density="compact" hide-details></v-text-field>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="1">
|
|
||||||
<v-btn variant="plain" icon>
|
|
||||||
<v-icon icon="$settings"></v-icon>
|
|
||||||
<v-menu activator="parent">
|
|
||||||
<v-list>
|
|
||||||
<v-list-item @click="step.ingredients.splice(index, 1)" prepend-icon="$delete">{{ $t('Delete') }}</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-menu>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</vue-draggable>
|
|
||||||
|
|
||||||
<v-list v-if="mobile">
|
<v-list v-if="mobile">
|
||||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients">
|
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" group="ingredients">
|
||||||
<v-list-item v-for="(ingredient, index) in step.ingredients" border @click="editingIngredientIndex = index; dialogIngredientEditor = true">
|
<v-list-item v-for="(ingredient, index) in step.ingredients" border @click="editingIngredientIndex = index; dialogIngredientEditor = true">
|
||||||
<ingredient-string :ingredient="ingredient"></ingredient-string>
|
<ingredient-string :ingredient="ingredient"></ingredient-string>
|
||||||
<template #append>
|
<template #append>
|
||||||
@@ -203,6 +205,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const {mobile} = useDisplay()
|
const {mobile} = useDisplay()
|
||||||
|
const test = ref([])
|
||||||
|
|
||||||
const showName = ref(false)
|
const showName = ref(false)
|
||||||
const showTime = ref(false)
|
const showTime = ref(false)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<v-stepper-window>
|
<v-stepper-window>
|
||||||
<v-stepper-window-item value="1">
|
<v-stepper-window-item value="1">
|
||||||
<v-card :loading="loading">
|
<v-card :loading="loading" >
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-text-field :label="$t('Website') + ' (https://...)'" v-model="importUrl">
|
<v-text-field :label="$t('Website') + ' (https://...)'" v-model="importUrl">
|
||||||
<template #append>
|
<template #append>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
<v-list>
|
<v-list>
|
||||||
<vue-draggable v-model="s.ingredients" group="ingredients" drag-class="drag-handle">
|
<vue-draggable v-model="s.ingredients" group="ingredients" handle=".drag-handle" empty-insert-threshold="25">
|
||||||
<v-list-item v-for="i in s.ingredients" border>
|
<v-list-item v-for="i in s.ingredients" border>
|
||||||
<v-icon size="small" class="drag-handle cursor-grab" icon="$dragHandle"></v-icon>
|
<v-icon size="small" class="drag-handle cursor-grab" icon="$dragHandle"></v-icon>
|
||||||
{{ i.amount }} <span v-if="i.unit">{{ i.unit.name }}</span> <span v-if="i.food">{{ i.food.name }}</span>
|
{{ i.amount }} <span v-if="i.unit">{{ i.unit.name }}</span> <span v-if="i.food">{{ i.food.name }}</span>
|
||||||
|
|||||||
@@ -12,6 +12,34 @@
|
|||||||
<model-edit-dialog model="MealPlan" v-model="dialog" :item="defaultItem" :activator="activator"></model-edit-dialog>
|
<model-edit-dialog model="MealPlan" v-model="dialog" :item="defaultItem" :activator="activator"></model-edit-dialog>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
|
<v-divider></v-divider>
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<vue-draggable v-model="items1" group="test" handle=".drag-handle">
|
||||||
|
|
||||||
|
<v-card v-for="i in items1" class="mt-1">
|
||||||
|
<v-card-text>
|
||||||
|
<v-icon icon="$dragHandle" class="drag-handle"></v-icon>
|
||||||
|
{{ i.name }}
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
</vue-draggable>
|
||||||
|
</v-col>
|
||||||
|
<v-col>
|
||||||
|
<vue-draggable v-model="items2" group="test" handle=".drag-handle" empty-insert-threshold="25">
|
||||||
|
<v-card v-for="i in items2" class="mt-1">
|
||||||
|
<v-card-text>
|
||||||
|
<v-icon icon="$dragHandle" class="drag-handle"></v-icon>
|
||||||
|
{{ i.name }}
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</vue-draggable>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
|
||||||
<v-row class="mt-5">
|
<v-row class="mt-5">
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-text-field density="compact"></v-text-field>
|
<v-text-field density="compact"></v-text-field>
|
||||||
@@ -56,6 +84,7 @@ import {ref, useTemplateRef} from "vue";
|
|||||||
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
||||||
import {DateTime} from "luxon";
|
import {DateTime} from "luxon";
|
||||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||||
|
import {VueDraggable} from "vue-draggable-plus";
|
||||||
|
|
||||||
const image = ref(File)
|
const image = ref(File)
|
||||||
const response = ref('')
|
const response = ref('')
|
||||||
@@ -67,6 +96,41 @@ const defaultItem = ref({
|
|||||||
fromDate: DateTime.now().plus({day: 2}).toJSDate()
|
fromDate: DateTime.now().plus({day: 2}).toJSDate()
|
||||||
} as MealPlan)
|
} as MealPlan)
|
||||||
|
|
||||||
|
const items2 = ref([])
|
||||||
|
const items1 = ref([
|
||||||
|
{
|
||||||
|
"name": "Jean",
|
||||||
|
"id": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Johanna-2",
|
||||||
|
"id": "3-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Joao-2",
|
||||||
|
"id": "1-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Juan",
|
||||||
|
"id": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Joao",
|
||||||
|
"id": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jean-2",
|
||||||
|
"id": "2-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Johanna",
|
||||||
|
"id": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Juan-2",
|
||||||
|
"id": "4-2"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
function imageToRecipe() {
|
function imageToRecipe() {
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
|
|||||||
Reference in New Issue
Block a user