From f029c7b9b3289caf75d8e41951634856b243f9cc Mon Sep 17 00:00:00 2001 From: FlorianSpeicher Date: Thu, 12 Jun 2025 22:56:35 +0200 Subject: [PATCH] Added Login backend connection. --- 00-backend/datasource/database.sqlite | Bin 2629632 -> 2629632 bytes .../public/locales/de/translation.json | 3 +- .../public/locales/en/translation.json | 3 +- 01-frontend/src/components/Account.tsx | 20 +- 01-frontend/src/helper/AccountProvider.tsx | 14 +- 01-frontend/src/helper/NavBar.css | 70 ++++++ 01-frontend/src/helper/NavBar.tsx | 216 ++++++++++++++++++ .../src/helper/adminpanel/FSModelInfo.tsx | 7 + .../src/helper/adminpanel/OrdersInfo.tsx | 17 +- 01-frontend/src/helper/navbar/LoginDialog.tsx | 145 ++++++++++-- 01-frontend/src/helper/navbar/NavBar.tsx | 2 +- 01-frontend/src/helper/query/Queries.tsx | 50 ++-- 01-frontend/src/pages/AdminPanel.tsx | 17 +- 13 files changed, 487 insertions(+), 77 deletions(-) create mode 100644 01-frontend/src/helper/NavBar.css create mode 100644 01-frontend/src/helper/NavBar.tsx create mode 100644 01-frontend/src/helper/adminpanel/FSModelInfo.tsx diff --git a/00-backend/datasource/database.sqlite b/00-backend/datasource/database.sqlite index 47cafb1c10c0b7feb90f970b36652ae75e729cf0..a32c59859af56b4e3c556498ac042356c1bcce04 100644 GIT binary patch delta 770 zcmZ9|T}V@L7{~E*ww>)9%l-5H<=D$|D%Fy?xv-09w#}S^lU|@@L}xi|y0tEhRa!o| zcrp=m#6m4G6EZI%6C7Oxy-3QlLng_RWMAUfw<#ku(3{67Di=L^&QG>Gpi zPe-ohIG6}kShqM{+aK1_WPl(b_$;ApxIlzzoBf0;1#^(4tF?mrlQypvbq|Y zs;Yc}TK{&Zs|^rn1QmiBL4&{`Xc2S>ECPp+fUpK(EkYuK9)TdNLr6kMMo2+OMMy(P zN60|PM94zOM&J>05Y{7XK-h?|3BiD{86g+Jh>(Yne;*1o=5$UUr^qJ~Bg3SVgvmZ) zBboa6M3Vi3zLn<~%EWN7#O$T_yFDY5H*X(I3)k4`o-3=WE}oU~M#pU5+<WdOY7=6H+wId-q;qMdnf6bnb*krcxmvjb<%lfYGEoaa0Y{f{sX*P#`4eK zOom?1=t#%q<*Y~JLn8iY$mCTrdOW|M^_ligw4IuNJ9z&3VG+N-80Kjin+^5-vo`lo z_sWx&n(0o1Pg3)*1d5_K;-pBF%#eBVmGlxXIY~x|Q_QrLw5F;}w6YN=jmkExG%B0u qq*2*ID~(cv{}V9Ms61Vhx=UP@H>;(u%3D<_Y7?ip(Rb^4LG?e$0Uz-I delta 219 zcmWl}ISztA06 void; + logout: () => void; +}; \ No newline at end of file diff --git a/01-frontend/src/helper/AccountProvider.tsx b/01-frontend/src/helper/AccountProvider.tsx index 3507e89..2f21d00 100644 --- a/01-frontend/src/helper/AccountProvider.tsx +++ b/01-frontend/src/helper/AccountProvider.tsx @@ -1,17 +1,5 @@ import { createContext, ReactNode, useContext, useState } from "react"; - -type User = { - password: string; - email: string; - customerId: number; - // weitere Felder nach Bedarf -}; - -type AccountContextType = { - user: User | null; - login: (userData: User) => void; - logout: () => void; -}; +import { AccountContextType, User } from "../components/Account"; const AccountContext = createContext(undefined); diff --git a/01-frontend/src/helper/NavBar.css b/01-frontend/src/helper/NavBar.css new file mode 100644 index 0000000..dc16864 --- /dev/null +++ b/01-frontend/src/helper/NavBar.css @@ -0,0 +1,70 @@ +/* Navbar styles */ +.navbar { + background-color: #1976d2; /* Material-UI Primary Color */ + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); + position: absolute; + top: 0; + height: 3rem; +} + +/* Logo styles */ +.navbar-logo { + font-family: 'Roboto', sans-serif; + font-weight: bold; + color: white; + text-decoration: none; + margin-right: auto; +} + +/* Menu styles */ +.navbar-menu { + display: flex; + align-items: center; + margin-left: auto; +} + +/* Search styles */ +.search { + position: relative; + border-radius: 4px; + background-color: rgba(255, 255, 255, 0.15); +} +.search:hover { + background-color: rgba(255, 255, 255, 0.25); +} +.search-icon-wrapper { + padding: 8px; + height: 100%; + position: absolute; + pointer-events: none; + display: flex; + align-items: center; + justify-content: center; +} +.search-input { + color: inherit; + width: 100%; + padding: 8px 8px 8px 40px; + font-size: 1rem; +} + +/* User avatar styles */ +.navbar-user { + margin-left: 16px; +} + +/* Typography styles */ +.navbar-typography { + font-family: 'monospace'; + font-weight: 700; + letter-spacing: .3rem; + color: inherit; + text-decoration: none; +} + +/* Button styles */ +.navbar-button { + margin: 2; + color: white; + display: block; +} \ No newline at end of file diff --git a/01-frontend/src/helper/NavBar.tsx b/01-frontend/src/helper/NavBar.tsx new file mode 100644 index 0000000..27838da --- /dev/null +++ b/01-frontend/src/helper/NavBar.tsx @@ -0,0 +1,216 @@ +import * as React from 'react'; +import AppBar from '@mui/material/AppBar'; +import Box from '@mui/material/Box'; +import Toolbar from '@mui/material/Toolbar'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import Menu from '@mui/material/Menu'; +import MenuIcon from '@mui/icons-material/Menu'; +import Container from '@mui/material/Container'; +import Avatar from '@mui/material/Avatar'; +import Button from '@mui/material/Button'; +import Tooltip from '@mui/material/Tooltip'; +import MenuItem from '@mui/material/MenuItem'; +import AdbIcon from '@mui/icons-material/Adb'; +import { alpha, InputBase, styled } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import { useNavigate } from 'react-router-dom'; + +const pages = ['Categories', 'Checkout', 'Contact']; +const settings = ['Account', 'Orders', 'Logout']; + +const Search = styled('div')(({ theme }) => ({ + position: 'relative', + borderRadius: theme.shape.borderRadius, + backgroundColor: alpha(theme.palette.common.white, 0.15), + '&:hover': { + backgroundColor: alpha(theme.palette.common.white, 0.25), + }, + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(1), + width: 'auto', + }, + })); + + const SearchIconWrapper = styled('div')(({ theme }) => ({ + padding: theme.spacing(0, 2), + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + })); + + const StyledInputBase = styled(InputBase)(({ theme }) => ({ + color: 'inherit', + width: '100%', + '& .MuiInputBase-input': { + padding: theme.spacing(1, 1, 1, 0), + // vertical padding + font size from searchIcon + paddingLeft: `calc(1em + ${theme.spacing(4)})`, + transition: theme.transitions.create('width'), + [theme.breakpoints.up('sm')]: { + width: '12ch', + '&:focus': { + width: '20ch', + }, + }, + }, + })); + +export default function NavBar() { + const navigate = useNavigate(); + const [anchorElNav, setAnchorElNav] = React.useState(null); + const [anchorElUser, setAnchorElUser] = React.useState(null); + + const handleOpenNavMenu = (event: React.MouseEvent) => { + setAnchorElNav(event.currentTarget); + }; + const handleOpenUserMenu = (event: React.MouseEvent) => { + setAnchorElUser(event.currentTarget); + }; + + const handleCloseNavMenu = (link: string) => { + setAnchorElNav(null); + navigate(`/${link.toLowerCase()}`); + }; + + const handleCloseUserMenu = () => { + setAnchorElUser(null); + }; + + + return ( + + + + + + Digitaler Produktionsshop + + + + + + + + + + + + + + + {pages.map((page) => ( + handleCloseNavMenu(page)}> + {page} + + ))} + + + + + DPS + + + {pages.map((page) => ( + + ))} + + + + + + + + + {settings.map((setting) => ( + + {setting} + + ))} + + + + + + ); +} \ No newline at end of file diff --git a/01-frontend/src/helper/adminpanel/FSModelInfo.tsx b/01-frontend/src/helper/adminpanel/FSModelInfo.tsx new file mode 100644 index 0000000..b5ecf31 --- /dev/null +++ b/01-frontend/src/helper/adminpanel/FSModelInfo.tsx @@ -0,0 +1,7 @@ +import { Typography } from "@mui/material"; + +export default function FSModelInfo() { + return ( + Under Construction + ); +} \ No newline at end of file diff --git a/01-frontend/src/helper/adminpanel/OrdersInfo.tsx b/01-frontend/src/helper/adminpanel/OrdersInfo.tsx index cab071f..5c33c5e 100644 --- a/01-frontend/src/helper/adminpanel/OrdersInfo.tsx +++ b/01-frontend/src/helper/adminpanel/OrdersInfo.tsx @@ -19,7 +19,7 @@ const mockOrders: OrderType[] = [ { id: "1001", date: "2025-05-20", - status: "active", + status: "CANCELLED", items: [ { name: "Tomatensamen", quantity: 2, price: 3.99 }, { name: "Blumenerde", quantity: 1, price: 7.49 } @@ -30,7 +30,7 @@ const mockOrders: OrderType[] = [ { id: "1000", date: "2025-05-10", - status: "inactive", + status: "ISSUES", items: [{ name: "Gießkanne", quantity: 1, price: 12.99 }], total: 12.99, address: "Musterstraße 1, 12345 Musterstadt" @@ -38,7 +38,7 @@ const mockOrders: OrderType[] = [ { id: "1002", date: "2025-05-15", - status: "running", + status: "DELIVERED", items: [{ name: "Pflanzendünger", quantity: 1, price: 8.99 }], total: 8.99, address: "Musterstraße 1, 12345 Musterstadt" @@ -46,10 +46,18 @@ const mockOrders: OrderType[] = [ { id: "1003", date: "2025-05-18", - status: "cancelled", + status: "ORDERED", items: [{ name: "Blumentopf", quantity: 2, price: 5.99 }], total: 11.98, address: "Musterstraße 1, 12345 Musterstadt" + }, + { + id: "1004", + date: "2025-05-18", + status: "IN_PROGRESS", + items: [{ name: "TimWall", quantity: 2, price: 5.99 }], + total: 12.99, + address: "Musterstraße 1, 12345 Musterstadt" } ]; @@ -61,6 +69,7 @@ export default function OrdersInfo() { const [orders, setOrders] = useState(mockOrders); const [selectedOrder, setSelectedOrder] = useState(null); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const handleDragEnd = (event: any) => { const { active, over } = event; if (!over || active.id === over.id) return; diff --git a/01-frontend/src/helper/navbar/LoginDialog.tsx b/01-frontend/src/helper/navbar/LoginDialog.tsx index 3af0e48..fc6409d 100644 --- a/01-frontend/src/helper/navbar/LoginDialog.tsx +++ b/01-frontend/src/helper/navbar/LoginDialog.tsx @@ -1,22 +1,71 @@ import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Link, TextField } from "@mui/material"; +import { useQuery } from "@tanstack/react-query"; +import i18next from "i18next"; import React, { useState } from "react"; +import AccountType from "../../components/Account"; +import { submitLogin, submitRegister } from "../query/Queries"; // Importiere die Funktion für die Registrierung +import { useAccount } from "../AccountProvider"; type LoginDialogProps = { open: boolean; onClose: () => void; - onSubmit: () => void; + onSubmit: () => void; // Funktion, die aufgerufen wird, wenn der Login erfolgreich ist loginData: { email: string; password: string }; setLoginData: React.Dispatch>; }; -const LoginDialog: React.FC = ({ open, onClose, onSubmit, loginData, setLoginData }) => { +const LoginDialog: React.FC = ({ open, onClose, loginData, setLoginData, onSubmit }) => { + const { login } = useAccount(); const [showRegister, setShowRegister] = useState(false); - const [registerData, setRegisterData] = useState({ email: "", password: "", confirmPassword: "" }); + const [registerData, setRegisterData] = useState({ email: "", password: "", id: 0, customer: { id: 0, name: "", surname: "", address: "", country: "", zip: "" }, langI18n: i18next.language, admin: false }); + const [showErrorRegister, setShowErrorRegister] = useState(false); // Neuer Zustand für die Anzeige der Fehlermeldung + const [showErrorLogin, setShowErrorLogin] = useState(false); // Neuer Zustand für die Anzeige der Login-Fehlermeldung - const handleRegister = () => { - setShowRegister(false); - setRegisterData({ email: "", password: "", confirmPassword: "" }); - onClose(); + // useQuery für Login + const { refetch: refetchLogin, isLoading: isLoadingLogin, error: errorLogin } = useQuery({ + queryKey: ["submitLogin", loginData], + queryFn: () => submitLogin(loginData), + retry: 0, + retryDelay: 1000, + enabled: false, + }); + + // useQuery für Registrierung + const { refetch: refetchRegister, isLoading: isLoadingRegister, error: errorRegister } = useQuery({ + queryKey: ["submitRegister", registerData], + queryFn: () => submitRegister(registerData), + retry: 0, + retryDelay: 1000, + enabled: false, + }); + + const handleLogin = async () => { + try { + setShowErrorLogin(false); // Fehlermeldung zurücksetzen + const response = await refetchLogin(); // Anfrage auslösen + if (response.status === "success") { + const userData = response.data; // Extrahiere die Benutzerdaten aus der Antwort + login({ email: userData.email, customerId: userData.token, password: userData.password }); // Speichere die Benutzerdaten im AccountProvider + setShowRegister(false); // Zurück zum Login wechseln + onSubmit(); // Dialog schließen + } else { + setShowErrorLogin(true); // Fehlermeldung anzeigen + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + setShowErrorLogin(true); // Fehlermeldung anzeigen + } + }; + + const handleRegister = async () => { + try { + setShowErrorRegister(false); // Fehlermeldung zurücksetzen + await refetchRegister(); // Beispiel für den Refetch-Aufruf + // Erfolgslogik hier + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (error) { + setShowErrorRegister(true); // Fehlermeldung anzeigen + } }; return ( @@ -43,11 +92,58 @@ const LoginDialog: React.FC = ({ open, onClose, onSubmit, logi /> setRegisterData(prev => ({ ...prev, confirmPassword: e.target.value }))} + value={registerData.customer.name} + onChange={e => setRegisterData(prev => ({ + ...prev, + customer: { ...prev.customer, name: e.target.value }, + }))} + /> + setRegisterData(prev => ({ + ...prev, + customer: { ...prev.customer, surname: e.target.value }, + }))} + /> + setRegisterData(prev => ({ + ...prev, + customer: { ...prev.customer, address: e.target.value }, + }))} + /> + setRegisterData(prev => ({ + ...prev, + customer: { ...prev.customer, country: e.target.value }, + }))} + /> + setRegisterData(prev => ({ + ...prev, + customer: { ...prev.customer, zip: e.target.value }, + }))} /> ) : ( @@ -61,7 +157,6 @@ const LoginDialog: React.FC = ({ open, onClose, onSubmit, logi onChange={e => setLoginData(prev => ({ ...prev, email: e.target.value }))} /> = ({ open, onClose, onSubmit, logi )} - + {showRegister ? ( - <> - - + ) : ( - <> - - + )} + {showErrorLogin && errorLogin && ( + Login fehlgeschlagen + )} + {showErrorRegister && errorRegister !== null && ( + Registrierung fehlgeschlagen + )} {showRegister ? ( { login(loginData); setLoginOpen(false); - setLoginData({ password: '', email: '', customerId: 0 }); + setLoginData(loginData); }; // useQuery, um die Item-Namen zu laden diff --git a/01-frontend/src/helper/query/Queries.tsx b/01-frontend/src/helper/query/Queries.tsx index 0dd32b9..cffe389 100644 --- a/01-frontend/src/helper/query/Queries.tsx +++ b/01-frontend/src/helper/query/Queries.tsx @@ -1,6 +1,6 @@ // api/queries.js -import AccountType, { CustomerType } from "../../components/Account"; +import AccountType, { CustomerType, SubmitLogin } from "../../components/Account"; import { SubmitOrder } from "../../components/Order"; import RatingSubmitType from "../../components/RatingSubmit"; @@ -44,24 +44,6 @@ export const fetchRatingList = async (itemId: string) => { return data; }; -export const submitLogin = async (ratingData: RatingSubmitType) => { - const response = await fetch('http://localhost:8085/account?uuid=' + ratingData.articleId + '&rating=' + ratingData.rating * 2, { - method: 'POST', - headers: { - 'Content-Type': 'text/plain', - }, - body: ratingData.content, - }); - - if (!response.ok) { - throw new Error('Fehler beim Senden der Bewertung'); - } - - const data = await response.json(); - console.log("Rating Submitted:", data); - return data; -} - export const submitOrder = async (data: SubmitOrder) => { const response = await fetch('http://localhost:8085/order', { method: 'POST', @@ -108,4 +90,32 @@ export const submitCustomer = async (data: CustomerType) => { } return await response.json(); -} \ No newline at end of file +} + +export const submitLogin = async (loginData: SubmitLogin) => { + const response = await fetch("http://localhost:8085/session?email=" + loginData.email + "&password=" + loginData.password, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(loginData), + }); + if (!response.ok) { + throw new Error("Login failed"); + } + return response.json(); +}; + +export const submitRegister = async (registerData: AccountType) => { + const response = await fetch("http://localhost:8085/account", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(registerData), + }); + if (!response.ok) { + throw new Error("Login failed"); + } + return response.json(); +}; \ No newline at end of file diff --git a/01-frontend/src/pages/AdminPanel.tsx b/01-frontend/src/pages/AdminPanel.tsx index 390b223..5957aed 100644 --- a/01-frontend/src/pages/AdminPanel.tsx +++ b/01-frontend/src/pages/AdminPanel.tsx @@ -11,15 +11,15 @@ import { ListItemButton, ListItemIcon, ListItemText, - Typography, - useTheme, + useTheme } from "@mui/material"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import AccountsInfo from "../helper/adminpanel/AccountsInfo"; +import FSModelInfo from "../helper/adminpanel/FSModelInfo"; +import ItemInfo from "../helper/adminpanel/ItemsInfo"; import OrdersInfo from "../helper/adminpanel/OrdersInfo"; import StatisticsInfo from "../helper/adminpanel/StatisticsInfo"; -import ItemInfo from "../helper/adminpanel/ItemsInfo"; export default function AdminPanel() { const { t } = useTranslation(); @@ -40,6 +40,8 @@ export default function AdminPanel() { return ; case "items": return ; + case "fsmodel": + return ; default: return ; } @@ -50,18 +52,11 @@ export default function AdminPanel() { { key: "orders", icon: , label: t("orders") }, { key: "accounts", icon: , label: t("accounts") }, { key: "items", icon: , label: t("items") }, + { key: "fsmodel", icon: , label: t("fsmodel") }, ]; return ( - - - Admin Panel - - - Manage your application settings and content here. - - {/* Sidebar */}