1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-24 15:49:42 +02:00

feat: Migrate to Nuxt 3 framework (#5184)

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
Hoa (Kyle) Trinh 2025-06-20 00:09:12 +07:00 committed by GitHub
parent 89ab7fac25
commit c24d532608
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
403 changed files with 23959 additions and 19557 deletions

View file

@ -1,61 +1,57 @@
<template>
<v-timeline-item
:class="attrs.class"
fill-dot
:small="attrs.small"
:icon="icon"
>
<v-timeline-item :class="attrs.class" fill-dot :small="attrs.small" :icon="icon" dot-color="primary">
<template v-if="!useMobileFormat" #opposite>
<v-chip v-if="event.timestamp" label large>
<v-icon class="mr-1"> {{ $globals.icons.calendar }} </v-icon>
<v-icon class="mr-1">
{{ $globals.icons.calendar }}
</v-icon>
{{ new Date(event.timestamp).toLocaleDateString($i18n.locale) }}
</v-chip>
</template>
<v-card
hover
:to="$listeners.selected || !recipe ? undefined : `/g/${groupSlug}/r/${recipe.slug}`"
:to="$attrs.selected || !recipe ? undefined : `/g/${groupSlug}/r/${recipe.slug}`"
class="elevation-12"
@click="$emit('selected')"
>
<v-card-title class="background">
<v-row>
<v-col align-self="center" :cols="useMobileFormat ? 'auto' : '2'" :class="attrs.avatar.class">
<UserAvatar :user-id="event.userId" :size="attrs.avatar.size" />
<UserAvatar :user-id="event.userId" :size="attrs.avatar.size" />
</v-col>
<v-col v-if="useMobileFormat" align-self="center" class="pr-0">
<v-chip label>
<v-chip label>
<v-icon> {{ $globals.icons.calendar }} </v-icon>
{{ new Date(event.timestamp || "").toLocaleDateString($i18n.locale) }}
</v-chip>
</v-chip>
</v-col>
<v-col v-else cols="9" style="margin: auto; text-align: center;">
{{ event.subject }}
<v-col v-else cols="9" style="margin: auto; text-align: center">
{{ event.subject }}
</v-col>
<v-spacer />
<v-col :cols="useMobileFormat ? 'auto' : '1'" class="px-0 pt-0">
<RecipeTimelineContextMenu
v-if="$auth.user && $auth.user.id == event.userId && event.eventType != 'system'"
:menu-top="false"
:event="event"
:menu-icon="$globals.icons.dotsVertical"
:use-mobile-format="useMobileFormat"
fab
color="transparent"
:elevation="0"
:card-menu="false"
:use-items="{
edit: true,
delete: true,
}"
@update="$emit('update')"
@delete="$emit('delete')"
/>
<RecipeTimelineContextMenu
v-if="currentUser && currentUser.id == event.userId && event.eventType != 'system'"
:menu-top="false"
:event="event"
:menu-icon="$globals.icons.dotsVertical"
:use-mobile-format="useMobileFormat"
color="transparent"
:elevation="0"
:card-menu="false"
:use-items="{
edit: true,
delete: true,
}"
@update="$emit('update')"
@delete="$emit('delete')"
/>
</v-col>
</v-row>
</v-card-title>
<v-card-text v-if="showRecipeCards && recipe" class="background">
<v-row :class="useMobileFormat ? 'py-3 mx-0' : 'py-3 mx-0'" style="max-width: 100%;">
<v-col align-self="center" class="pa-0">
<v-row :class="useMobileFormat ? 'py-3 mx-0' : 'py-3 mx-0'" style="max-width: 100%">
<v-col align-self="center" class="pa-0">
<RecipeCardMobile
:vertical="useMobileFormat"
:name="recipe.name"
@ -67,26 +63,26 @@
:is-flat="true"
/>
</v-col>
</v-row>
</v-row>
</v-card-text>
<v-divider v-if="showRecipeCards && recipe && (useMobileFormat || event.eventMessage)" />
<v-card-text class="background">
<v-row>
<v-col>
<strong v-if="useMobileFormat">{{ event.subject }}</strong>
<v-img
v-if="eventImageUrl"
:src="eventImageUrl"
min-height="50"
:height="hideImage ? undefined : 'auto'"
:max-height="attrs.image.maxHeight"
contain
:class=attrs.image.class
@error="hideImage = true"
/>
<div v-if="event.eventMessage" :class="useMobileFormat ? 'text-caption' : ''">
<SafeMarkdown :source="event.eventMessage" />
</div>
<strong v-if="useMobileFormat">{{ event.subject }}</strong>
<v-img
v-if="eventImageUrl"
:src="eventImageUrl"
min-height="50"
:height="hideImage ? undefined : 'auto'"
:max-height="attrs.image.maxHeight"
contain
:class="attrs.image.class"
@error="hideImage = true"
/>
<div v-if="event.eventMessage" :class="useMobileFormat ? 'text-caption' : ''">
<SafeMarkdown :source="event.eventMessage" />
</div>
</v-col>
</v-row>
</v-card-text>
@ -95,16 +91,15 @@
</template>
<script lang="ts">
import { computed, defineComponent, ref, useContext, useRoute } from "@nuxtjs/composition-api";
import RecipeCardMobile from "./RecipeCardMobile.vue";
import RecipeTimelineContextMenu from "./RecipeTimelineContextMenu.vue";
import { useStaticRoutes } from "~/composables/api";
import { useTimelineEventTypes } from "~/composables/recipes/use-recipe-timeline-events";
import { Recipe, RecipeTimelineEventOut } from "~/lib/api/types/recipe"
import type { Recipe, RecipeTimelineEventOut } from "~/lib/api/types/recipe";
import UserAvatar from "~/components/Domain/User/UserAvatar.vue";
import SafeMarkdown from "~/components/global/SafeMarkdown.vue";
export default defineComponent({
export default defineNuxtComponent({
components: { RecipeCardMobile, RecipeTimelineContextMenu, UserAvatar, SafeMarkdown },
props: {
@ -119,20 +114,23 @@ export default defineComponent({
showRecipeCards: {
type: Boolean,
default: false,
}
},
},
emits: ["selected", "update", "delete"],
setup(props) {
const { $auth, $globals, $vuetify } = useContext();
const { $vuetify, $globals } = useNuxtApp();
const { recipeTimelineEventImage } = useStaticRoutes();
const { eventTypeOptions } = useTimelineEventTypes();
const timelineEvents = ref([] as RecipeTimelineEventOut[]);
const { user: currentUser } = useMealieAuth();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const groupSlug = computed(() => (route.params.groupSlug as string) || currentUser?.value?.groupSlug || "");
const useMobileFormat = computed(() => {
return $vuetify.breakpoint.smAndDown;
return $vuetify.display.smAndDown.value;
});
const attrs = computed(() => {
@ -146,9 +144,9 @@ export default defineComponent({
},
image: {
maxHeight: "250",
class: "my-3"
class: "my-3",
},
}
};
}
else {
return {
@ -160,25 +158,25 @@ export default defineComponent({
},
image: {
maxHeight: "300",
class: "mb-5"
class: "mb-5",
},
}
};
}
})
});
const icon = computed(() => {
const option = eventTypeOptions.value.find((option) => option.value === props.event.eventType);
const option = eventTypeOptions.value.find(option => option.value === props.event.eventType);
return option ? option.icon : $globals.icons.informationVariant;
});
const hideImage = ref(false);
const eventImageUrl = computed<string>( () => {
const eventImageUrl = computed<string>(() => {
if (props.event.image !== "has image") {
return "";
}
return recipeTimelineEventImage(props.event.recipeId, props.event.id);
})
});
return {
attrs,
@ -188,6 +186,7 @@ export default defineComponent({
hideImage,
timelineEvents,
useMobileFormat,
currentUser,
};
},
});