mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
Bug/general fixes (#450)
* Fix asset link * remove unused var * fix no meal-plan returned * cleanup redundant code * Fix dates off in UI * quick set dark/light mode * user image fixes Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
785ab184af
commit
22d9309112
16343
frontend/package-lock.json
generated
16343
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -160,7 +160,7 @@ export const recipeAPI = {
|
||||
},
|
||||
|
||||
recipeAssetPath(recipeSlug, assetName) {
|
||||
return `api/media/recipes/${recipeSlug}/assets/${assetName}`;
|
||||
return `/api/media/recipes/${recipeSlug}/assets/${assetName}`;
|
||||
},
|
||||
|
||||
/** Create comment in the Database
|
||||
|
@ -113,6 +113,11 @@ export const userAPI = {
|
||||
const response = await apiReq.delete(API_ROUTES.usersIdFavoritesSlug(id, slug));
|
||||
return response.data;
|
||||
},
|
||||
|
||||
userProfileImage(id) {
|
||||
if (!id || id === undefined) return;
|
||||
return `/api/users/${id}/image`;
|
||||
},
|
||||
};
|
||||
|
||||
const deleteErrorText = response => {
|
||||
|
@ -67,12 +67,13 @@
|
||||
</v-card-text>
|
||||
<v-row align="center" justify="end">
|
||||
<v-card-actions class="mr-5">
|
||||
<v-btn color="success" @click="random" v-if="planDays.length > 0" text>
|
||||
<TheButton edit @click="random" v-if="planDays.length > 0" text>
|
||||
<template v-slot:icon>
|
||||
mdi-dice-multiple
|
||||
</template>
|
||||
{{ $t("general.random") }}
|
||||
</v-btn>
|
||||
<v-btn color="success" @click="save" text :disabled="planDays.length == 0">
|
||||
{{ $t("general.save") }}
|
||||
</v-btn>
|
||||
</TheButton>
|
||||
<TheButton create @click="save" :disabled="planDays.length == 0" />
|
||||
</v-card-actions>
|
||||
</v-row>
|
||||
</v-card>
|
||||
@ -105,6 +106,9 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
startDate(val) {
|
||||
console.log(val);
|
||||
},
|
||||
dateDif() {
|
||||
this.planDays = [];
|
||||
for (let i = 0; i < this.dateDif; i++) {
|
||||
@ -132,22 +136,19 @@ export default {
|
||||
return this.$store.getters.getCurrentGroup;
|
||||
},
|
||||
actualStartDate() {
|
||||
return Date.parse(this.startDate);
|
||||
if (!this.startDate) return null;
|
||||
return Date.parse(this.startDate.replaceAll("-", "/"));
|
||||
},
|
||||
actualEndDate() {
|
||||
return Date.parse(this.endDate);
|
||||
if (!this.endDate) return null;
|
||||
return Date.parse(this.endDate.replaceAll("-", "/"));
|
||||
},
|
||||
dateDif() {
|
||||
let startDate = new Date(this.startDate);
|
||||
let endDate = new Date(this.endDate);
|
||||
console.log(startDate, endDate);
|
||||
|
||||
let dateDif = (endDate - startDate) / (1000 * 3600 * 24) + 1;
|
||||
|
||||
if (!this.actualEndDate || !this.actualStartDate) return null;
|
||||
let dateDif = (this.actualEndDate - this.actualStartDate) / (1000 * 3600 * 24) + 1;
|
||||
if (dateDif < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dateDif;
|
||||
},
|
||||
startComputedDateFormatted() {
|
||||
@ -179,22 +180,17 @@ export default {
|
||||
},
|
||||
random() {
|
||||
this.usedRecipes = [1];
|
||||
this.planDays.forEach((element, index) => {
|
||||
this.planDays.forEach((_, index) => {
|
||||
let recipe = this.getRandom(this.filteredRecipes);
|
||||
this.planDays[index]["meals"][0]["slug"] = recipe.slug;
|
||||
this.planDays[index]["meals"][0]["name"] = recipe.name;
|
||||
this.usedRecipes.push(recipe);
|
||||
});
|
||||
},
|
||||
processTime(index) {
|
||||
let dateText = new Date(this.actualStartDate.valueOf() + 1000 * 3600 * 24 * index);
|
||||
return dateText;
|
||||
},
|
||||
getDate(index) {
|
||||
const dateObj = this.processTime(index);
|
||||
const dateObj = new Date(this.actualStartDate.valueOf() + 1000 * 3600 * 24 * index);
|
||||
return utils.getDateAsPythonDate(dateObj);
|
||||
},
|
||||
|
||||
async save() {
|
||||
const mealBody = {
|
||||
group: this.groupSettings.name,
|
||||
@ -209,7 +205,6 @@ export default {
|
||||
this.endDate = null;
|
||||
}
|
||||
},
|
||||
|
||||
formatDate(date) {
|
||||
if (!date) return null;
|
||||
|
||||
@ -227,12 +222,9 @@ export default {
|
||||
const nextEndDate = new Date(nextMonday);
|
||||
nextEndDate.setDate(nextEndDate.getDate() + 4);
|
||||
|
||||
this.startDate = nextMonday.toISOString().substr(0, 10);
|
||||
|
||||
this.endDate = nextEndDate.toISOString().substr(0, 10);
|
||||
this.startDate = utils.getDateAsPythonDate(nextMonday);
|
||||
this.endDate = utils.getDateAsPythonDate(nextEndDate);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
@ -91,7 +91,7 @@ export default {
|
||||
this.hideImage == false;
|
||||
},
|
||||
getProfileImage(id) {
|
||||
return `api/users/${id}/image`;
|
||||
return api.users.userProfileImage(id);
|
||||
},
|
||||
editComment(id) {
|
||||
this.$set(this.editKeys, id, true);
|
||||
|
@ -21,9 +21,7 @@
|
||||
<v-btn color="error" icon @click="deleteAsset(i)" top>
|
||||
<v-icon>{{ $globals.icons.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>
|
||||
<TheCopyButton :copy-text="copyLink(item.fileName)" />
|
||||
</div>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
@ -60,6 +58,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TheCopyButton from "@/components/UI/Buttons/TheCopyButton";
|
||||
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
|
||||
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
|
||||
import { api } from "@/api";
|
||||
@ -67,6 +66,7 @@ export default {
|
||||
components: {
|
||||
BaseDialog,
|
||||
TheUploadBtn,
|
||||
TheCopyButton,
|
||||
},
|
||||
props: {
|
||||
slug: String,
|
||||
@ -130,13 +130,9 @@ export default {
|
||||
deleteAsset(index) {
|
||||
this.value.splice(index, 1);
|
||||
},
|
||||
copyLink(name, fileName) {
|
||||
copyLink(fileName) {
|
||||
const assetLink = api.recipes.recipeAssetPath(this.slug, fileName);
|
||||
const copyText = ``;
|
||||
navigator.clipboard.writeText(copyText).then(
|
||||
() => console.log("Copied", copyText),
|
||||
() => console.log("Copied Failed", copyText)
|
||||
);
|
||||
return `<img src="${this.baseURL}${assetLink}" height="100%" width="100%"> </img>`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
:small="small"
|
||||
:x-small="xSmall"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
@click="$emit('click')"
|
||||
:outlined="btnStyle.outlined"
|
||||
:text="btnStyle.text"
|
||||
@ -49,6 +50,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// Styles
|
||||
small: {
|
||||
type: Boolean,
|
||||
|
@ -18,6 +18,9 @@
|
||||
</div>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon class="mr-1" small @click="isDark = !isDark">
|
||||
<v-icon v-text="isDark ? 'mdi-weather-night' : 'mdi-weather-sunny'"> </v-icon>
|
||||
</v-btn>
|
||||
<div v-if="!isMobile" style="width: 350px;">
|
||||
<SearchBar :show-results="true" @selected="navigateFromSearch" :max-width="isMobile ? '100%' : '450px'" />
|
||||
</div>
|
||||
@ -67,12 +70,25 @@ export default {
|
||||
isMobile() {
|
||||
return this.$vuetify.breakpoint.name === "xs";
|
||||
},
|
||||
isDark: {
|
||||
get() {
|
||||
return this.$store.getters.getIsDark;
|
||||
},
|
||||
set() {
|
||||
let setVal = "dark";
|
||||
if (this.isDark) {
|
||||
setVal = "light";
|
||||
}
|
||||
this.$store.commit("setDarkMode", setVal);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
navigateFromSearch(slug) {
|
||||
this.$router.push(`/recipe/${slug}`);
|
||||
},
|
||||
openSidebar() {
|
||||
console.log(this.isDarkMode);
|
||||
this.$refs.theSidebar.toggleSidebar();
|
||||
},
|
||||
},
|
||||
|
@ -2,19 +2,7 @@
|
||||
<div class="d-print-none no-print">
|
||||
<v-navigation-drawer v-model="showSidebar" width="180px" clipped app>
|
||||
<template v-slot:prepend>
|
||||
<v-list-item two-line v-if="isLoggedIn" to="/admin/profile">
|
||||
<v-list-item-avatar color="accent" class="white--text">
|
||||
<img :src="userProfileImage" v-if="!hideImage" @error="hideImage = true" />
|
||||
<div v-else>
|
||||
{{ initials }}
|
||||
</div>
|
||||
</v-list-item-avatar>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title> {{ user.fullName }}</v-list-item-title>
|
||||
<v-list-item-subtitle> {{ user.admin ? $t("user.admin") : $t("user.user") }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<UserAvatar v-if="isLoggedIn" :user="user" />
|
||||
|
||||
<v-list-item dense v-if="isLoggedIn" :to="`/user/${user.id}/favorites`">
|
||||
<v-list-item-icon>
|
||||
@ -46,7 +34,7 @@
|
||||
mdi-heart
|
||||
</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title> {{$t('about.support')}} </v-list-item-title>
|
||||
<v-list-item-title> {{ $t("about.support") }} </v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item to="/admin/about">
|
||||
<v-list-item-icon class="mr-3 pt-1">
|
||||
@ -78,10 +66,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UserAvatar from "@/components/User/UserAvatar";
|
||||
import { initials } from "@/mixins/initials";
|
||||
import { user } from "@/mixins/user";
|
||||
import axios from "axios";
|
||||
export default {
|
||||
components: {
|
||||
UserAvatar,
|
||||
},
|
||||
mixins: [initials, user],
|
||||
data() {
|
||||
return {
|
||||
|
61
frontend/src/components/User/UserAvatar.vue
Normal file
61
frontend/src/components/User/UserAvatar.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<v-list-item two-line to="/admin/profile">
|
||||
<v-list-item-avatar color="accent" class="white--text">
|
||||
<v-img :src="profileImage" v-if="!noImage" />
|
||||
<div v-else>
|
||||
{{ initials }}
|
||||
</div>
|
||||
</v-list-item-avatar>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-title> {{ user.fullName }}</v-list-item-title>
|
||||
<v-list-item-subtitle> {{ user.admin ? $t("user.admin") : $t("user.user") }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { initials } from "@/mixins/initials";
|
||||
import axios from "axios";
|
||||
import { api } from "@/api";
|
||||
export default {
|
||||
mixins: [initials],
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
noImage: false,
|
||||
profileImage: "",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
async user() {
|
||||
this.setImage();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async setImage() {
|
||||
const userImageURL = api.users.userProfileImage(this.user.id);
|
||||
if (await this.imageExists(userImageURL)) {
|
||||
this.noImage = false;
|
||||
this.profileImage = userImageURL;
|
||||
} else {
|
||||
this.noImage = true;
|
||||
}
|
||||
},
|
||||
async imageExists(url) {
|
||||
const response = await axios.get(url).catch(() => {
|
||||
this.noImage = true;
|
||||
return { status: 404 };
|
||||
});
|
||||
return response.status !== 404;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
@ -73,8 +73,7 @@
|
||||
</v-virtual-scroll>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<TheButton class="mt-1 mb-n1" create @click="createTheme" />
|
||||
<TheButton class="ml-auto mt-1 mb-n1" create @click="createTheme" />
|
||||
</v-card-actions>
|
||||
</template>
|
||||
</StatCard>
|
||||
|
@ -134,7 +134,7 @@ export default {
|
||||
|
||||
computed: {
|
||||
userProfileImage() {
|
||||
return `api/users/${this.user.id}/image`;
|
||||
return api.users.userProfileImage(this.user.id);
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -17,7 +17,7 @@ function inDarkMode(payload) {
|
||||
|
||||
const state = {
|
||||
activeTheme: {},
|
||||
darkMode: "light",
|
||||
darkMode: "light", // light, dark, system
|
||||
isDark: false,
|
||||
isLoggedIn: false,
|
||||
token: "",
|
||||
|
@ -6,10 +6,10 @@ import { store } from "@/store";
|
||||
export const utils = {
|
||||
recipe: recipe,
|
||||
generateUniqueKey(item, index) {
|
||||
const uniqueKey = `${item}-${index}`;
|
||||
return uniqueKey;
|
||||
return `${item}-${index}`;
|
||||
},
|
||||
getDateAsPythonDate(dateObject) {
|
||||
if (!dateObject) return null;
|
||||
const month = dateObject.getMonth() + 1;
|
||||
const day = dateObject.getDate();
|
||||
const year = dateObject.getFullYear();
|
||||
|
@ -23,6 +23,28 @@ def get_all_meals(
|
||||
return db.groups.get_meals(session, current_user.group)
|
||||
|
||||
|
||||
@router.get("/this-week", response_model=MealPlanOut)
|
||||
def get_this_week(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)):
|
||||
""" Returns the meal plan data for this week """
|
||||
plans = db.groups.get_meals(session, current_user.group)
|
||||
print(plans)
|
||||
if plans:
|
||||
return plans[0]
|
||||
|
||||
|
||||
@router.get("/today", tags=["Meal Plan"])
|
||||
def get_today(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)):
|
||||
"""
|
||||
Returns the recipe slug for the meal scheduled for today.
|
||||
If no meal is scheduled nothing is returned
|
||||
"""
|
||||
|
||||
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
|
||||
recipe = get_todays_meal(session, group_in_db)
|
||||
if recipe:
|
||||
return recipe
|
||||
|
||||
|
||||
@router.get("/{id}", response_model=MealPlanOut)
|
||||
def get_meal_plan(
|
||||
id,
|
||||
@ -86,27 +108,6 @@ def delete_meal_plan(
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.get("/this-week", response_model=MealPlanOut)
|
||||
def get_this_week(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)):
|
||||
""" Returns the meal plan data for this week """
|
||||
plans = db.groups.get_meals(session, current_user.group)
|
||||
if plans:
|
||||
return plans[0]
|
||||
|
||||
|
||||
@router.get("/today", tags=["Meal Plan"])
|
||||
def get_today(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)):
|
||||
"""
|
||||
Returns the recipe slug for the meal scheduled for today.
|
||||
If no meal is scheduled nothing is returned
|
||||
"""
|
||||
|
||||
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
|
||||
recipe = get_todays_meal(session, group_in_db)
|
||||
if recipe:
|
||||
return recipe
|
||||
|
||||
|
||||
@router.get("/today/image", tags=["Meal Plan"])
|
||||
def get_todays_image(session: Session = Depends(generate_session), group_name: str = "Home"):
|
||||
"""
|
||||
|
@ -91,7 +91,7 @@ async def get_user_image(id: str):
|
||||
for recipe_image in user_dir.glob("profile_image.*"):
|
||||
return FileResponse(recipe_image)
|
||||
else:
|
||||
return False
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
@router.post("/{id}/image", dependencies=[Depends(get_current_user)])
|
||||
|
Loading…
x
Reference in New Issue
Block a user