Skip to content

Commit 496d7a6

Browse files
committed
Send user dietary preferences on query
1 parent d85948f commit 496d7a6

File tree

17 files changed

+215
-61
lines changed

17 files changed

+215
-61
lines changed

client/src/hooks/useAuth.tsx

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { useState } from "react";
21
import { useAuth as useOAuth } from "react-oidc-context";
32
import { useNavigate } from "react-router-dom";
3+
import { useUserInfo } from "./useUserInfo";
44

55
export const useAuth = () => {
66
const auth = useOAuth();
77
const navigate = useNavigate();
8-
const [user, setUser] = useState<User | null>(null);
9-
8+
const { user } = useUserInfo();
109
if (auth.isLoading) {
1110
return { user, isLoading: auth.isLoading, signout: auth.signoutRedirect };
1211
}
@@ -15,26 +14,5 @@ export const useAuth = () => {
1514
navigate("/login");
1615
}
1716

18-
if (auth.user?.access_token && !user) {
19-
fetch("https://gitlab.lrz.de/api/v4/user", {
20-
method: "GET",
21-
headers: {
22-
Authorization: `Bearer ${auth.user.access_token}`,
23-
},
24-
})
25-
.then((response) => {
26-
if (!response.ok) {
27-
throw new Error(`Error: ${response.status} ${response.statusText}`);
28-
}
29-
return response.json();
30-
})
31-
.then((userData) => {
32-
setUser({ ...userData, token: auth.user?.access_token });
33-
})
34-
.catch((error) => {
35-
console.error("Failed to fetch user data:", error);
36-
});
37-
}
38-
3917
return { user, isLoading: auth.isLoading, signout: auth.signoutRedirect };
4018
};

client/src/hooks/useUserInfo.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { useAuth as useOAuth } from "react-oidc-context";
3+
import { getUserInfo } from "../services/userService";
4+
5+
export const useUserInfo = () => {
6+
const auth = useOAuth();
7+
8+
const { isLoading, isError, data, error } = useQuery({
9+
queryKey: ["userInfo", auth.user?.access_token],
10+
queryFn: () => {
11+
if (!auth.user?.access_token) {
12+
throw new Error("No access token available");
13+
}
14+
return getUserInfo(auth.user.access_token);
15+
},
16+
enabled: !!auth.user?.access_token,
17+
});
18+
19+
return {
20+
isLoading,
21+
isError,
22+
user: data ?? null,
23+
error,
24+
};
25+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useMutation, useQueryClient } from "@tanstack/react-query";
2+
import { useUser } from "../contexts/userContext";
3+
import { updateUserPreferences } from "../services/userService";
4+
5+
interface UseUserPreferencesUpdate {
6+
onSuccess?: () => void;
7+
onError?: (error: Error) => void;
8+
}
9+
10+
export const useUserPreferencesUpdate = ({
11+
onSuccess,
12+
onError,
13+
}: UseUserPreferencesUpdate) => {
14+
const { user } = useUser();
15+
const queryClient = useQueryClient();
16+
const { mutate, isError, error } = useMutation({
17+
mutationKey: ["userPreferenceUpdate", user?.id],
18+
mutationFn: ({ preferences }: { preferences: DietaryPreference[] }) =>
19+
updateUserPreferences(user!.token, preferences),
20+
onSuccess: () => {
21+
queryClient.invalidateQueries({ queryKey: ["userInfo"] });
22+
if (onSuccess) {
23+
onSuccess();
24+
}
25+
},
26+
onError: (error: Error) => {
27+
console.error("Error updating user preferences:", error);
28+
if (onError) {
29+
onError(error);
30+
}
31+
},
32+
});
33+
34+
return { updateUserPreferences: mutate, isError, error };
35+
};

client/src/pages/PreferencesPage.tsx

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,54 @@
1-
import React, { useState } from "react";
1+
import { useEffect, useState } from "react";
2+
import { useUser } from "../contexts/userContext";
3+
import { useUserPreferencesUpdate } from "../hooks/useUserPreferencesUpdate";
4+
import { DIETARY_PREFERENCES } from "../types/enums";
5+
6+
const defaultPreferences = {
7+
vegetarian: false,
8+
vegan: false,
9+
glutenFree: false,
10+
dairyFree: false,
11+
nutFree: false,
12+
spicyFood: false,
13+
};
14+
15+
const getPreferenceMap = (list: DietaryPreference[]) => ({
16+
vegetarian: list.includes(DIETARY_PREFERENCES.VEGETARIAN),
17+
vegan: list.includes(DIETARY_PREFERENCES.VEGAN),
18+
glutenFree: list.includes(DIETARY_PREFERENCES.GLUTEN_FREE),
19+
dairyFree: list.includes(DIETARY_PREFERENCES.DAIRY_FREE),
20+
nutFree: list.includes(DIETARY_PREFERENCES.NUT_FREE),
21+
spicyFood: list.includes(DIETARY_PREFERENCES.SPICY_FOOD),
22+
});
23+
24+
const getPreferenceList = (preferences: typeof defaultPreferences) => {
25+
const list: DietaryPreference[] = [];
26+
if (preferences.vegetarian) list.push(DIETARY_PREFERENCES.VEGETARIAN);
27+
if (preferences.vegan) list.push(DIETARY_PREFERENCES.VEGAN);
28+
if (preferences.glutenFree) list.push(DIETARY_PREFERENCES.GLUTEN_FREE);
29+
if (preferences.dairyFree) list.push(DIETARY_PREFERENCES.DAIRY_FREE);
30+
if (preferences.nutFree) list.push(DIETARY_PREFERENCES.NUT_FREE);
31+
if (preferences.spicyFood) list.push(DIETARY_PREFERENCES.SPICY_FOOD);
32+
return list;
33+
};
234

335
const PreferencesPage: React.FC = () => {
4-
const [preferences, setPreferences] = useState({
5-
vegetarian: false,
6-
vegan: false,
7-
glutenFree: false,
8-
dairyFree: false,
9-
nutFree: false,
10-
spicyFood: false,
11-
});
36+
const { user } = useUser();
37+
const { updateUserPreferences } = useUserPreferencesUpdate({});
38+
const [preferences, setPreferences] = useState(defaultPreferences);
39+
40+
useEffect(() => {
41+
setPreferences(
42+
user?.dietaryPreferences
43+
? getPreferenceMap(user.dietaryPreferences)
44+
: defaultPreferences
45+
);
46+
}, [user?.dietaryPreferences]);
1247

1348
const handleToggle = (key: keyof typeof preferences) => {
14-
setPreferences((prev) => ({
15-
...prev,
16-
[key]: !prev[key],
17-
}));
49+
const updatedPreferences = { ...preferences, [key]: !preferences[key] };
50+
const updatedPreferenceList = getPreferenceList(updatedPreferences);
51+
updateUserPreferences({ preferences: updatedPreferenceList });
1852
};
1953

2054
return (

client/src/services/userService.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
export const getUserInfo = async (token: string): Promise<User> => {
2+
try {
3+
const response = await fetch("/api/user/info", {
4+
method: "GET",
5+
headers: {
6+
Authorization: `Bearer ${token}`,
7+
},
8+
});
9+
10+
if (!response.ok) {
11+
throw new Error(`Error: ${response.status} ${response.statusText}`);
12+
}
13+
14+
const userData = await response.json();
15+
return { ...userData, token };
16+
} catch (error) {
17+
console.error("Failed to fetch user data:", error);
18+
throw error;
19+
}
20+
};
21+
22+
export const updateUserPreferences = async (
23+
token: string,
24+
preferences: DietaryPreference[]
25+
): Promise<void> => {
26+
try {
27+
const response = await fetch("/api/user/preferences", {
28+
method: "POST",
29+
headers: {
30+
"Content-Type": "application/json",
31+
Authorization: `Bearer ${token}`,
32+
},
33+
body: JSON.stringify({ dietaryPreferences: preferences }),
34+
});
35+
36+
if (!response.ok) {
37+
throw new Error(`Error: ${response.status} ${response.statusText}`);
38+
}
39+
} catch (error) {
40+
console.error("Failed to update user preferences:", error);
41+
throw error;
42+
}
43+
};

client/src/types/enums.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const DIETARY_PREFERENCES = {
2+
VEGETARIAN: "VEGETARIAN",
3+
VEGAN: "VEGAN",
4+
GLUTEN_FREE: "GLUTEN_FREE",
5+
DAIRY_FREE: "DAIRY_FREE",
6+
NUT_FREE: "NUT_FREE",
7+
SPICY_FOOD: "SPICY_FOOD",
8+
};

client/src/types/global.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { DIETARY_PREFERENCES } from "./enums";
2+
13
export {};
24
declare global {
35
type Message = {
@@ -17,8 +19,10 @@ declare global {
1719
type User = {
1820
id: number;
1921
username: string;
20-
name: string;
21-
email: string;
22+
dietaryPreferences: DIETARY_PREFERENCES[];
2223
token: string; //bearer token
2324
};
25+
26+
type DietaryPreference =
27+
(typeof DIETARY_PREFERENCES)[keyof typeof DIETARY_PREFERENCES];
2428
}

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ services:
7474
image: mongo-express:latest
7575
container_name: mongo-express
7676
ports:
77-
- "8083:8083"
77+
- "8083:8081"
7878
environment:
7979
- ME_CONFIG_MONGODB_ADMINUSERNAME=admin
8080
- ME_CONFIG_MONGODB_ADMINPASSWORD=admin

server/chat/src/main/java/com/continiousdisappointment/chat/controller/ChatController.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.continiousdisappointment.chat.domain.User;
44
import com.continiousdisappointment.chat.domain.chat.Chat;
55
import com.continiousdisappointment.chat.domain.chat.Message;
6-
import com.continiousdisappointment.chat.domain.chat.Role;
76
import com.continiousdisappointment.chat.dto.AddMessageToChatDto;
87
import com.continiousdisappointment.chat.dto.CreateChatDto;
98
import com.continiousdisappointment.chat.service.ChatService;
@@ -55,6 +54,6 @@ public ResponseEntity<Message> addMessage(
5554
@PathVariable("chatId") String chatId,
5655
@RequestBody AddMessageToChatDto dto,
5756
@RequestAttribute("user") User user) {
58-
return ResponseEntity.ok(chatService.addMessageToChat(user.id(), chatId, dto.content(), Role.USER));
57+
return ResponseEntity.ok(chatService.addMessageToChat(user, chatId, dto.content()));
5958
}
6059
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.continiousdisappointment.chat.domain;
2+
3+
public enum DietaryPreference {
4+
VEGETARIAN,
5+
VEGAN,
6+
GLUTEN_FREE,
7+
DAIRY_FREE,
8+
NUT_FREE,
9+
SPICY_FOOD,
10+
}

0 commit comments

Comments
 (0)