diff --git a/01-frontend/package-lock.json b/01-frontend/package-lock.json
index 9724050..d74c56d 100644
--- a/01-frontend/package-lock.json
+++ b/01-frontend/package-lock.json
@@ -8,16 +8,21 @@
"name": "01-frontend",
"version": "0.0.0",
"dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/modifiers": "^9.0.0",
+ "@dnd-kit/sortable": "^10.0.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^7.0.2",
"@mui/material": "^7.0.2",
"@tanstack/react-query": "^5.79.2",
+ "chart.js": "^4.4.9",
"i18next": "^25.2.0",
"i18next-browser-languagedetector": "^8.1.0",
"i18next-http-backend": "^3.0.2",
"mui": "^0.0.1",
"react": "^19.0.0",
+ "react-chartjs-2": "^5.3.0",
"react-dom": "^19.1.0",
"react-i18next": "^15.5.1",
"react-router-dom": "^7.5.3"
@@ -338,6 +343,73 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@dnd-kit/accessibility": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
+ "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/core": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
+ "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/accessibility": "^3.1.1",
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/modifiers": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz",
+ "integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.3.0",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/sortable": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
+ "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.3.0",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/utilities": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
+ "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@emotion/babel-plugin": {
"version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
@@ -1177,6 +1249,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@kurkle/color": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
+ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
+ "license": "MIT"
+ },
"node_modules/@mui/core-downloads-tracker": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.0.tgz",
@@ -2347,6 +2425,18 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/chart.js": {
+ "version": "4.4.9",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz",
+ "integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==",
+ "license": "MIT",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -3621,6 +3711,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-chartjs-2": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz",
+ "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-dom": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
@@ -4018,6 +4118,12 @@
"typescript": ">=4.8.4"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/01-frontend/package.json b/01-frontend/package.json
index 197ac46..d2db740 100644
--- a/01-frontend/package.json
+++ b/01-frontend/package.json
@@ -12,16 +12,21 @@
"preview": "vite preview"
},
"dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/modifiers": "^9.0.0",
+ "@dnd-kit/sortable": "^10.0.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^7.0.2",
"@mui/material": "^7.0.2",
"@tanstack/react-query": "^5.79.2",
+ "chart.js": "^4.4.9",
"i18next": "^25.2.0",
"i18next-browser-languagedetector": "^8.1.0",
"i18next-http-backend": "^3.0.2",
"mui": "^0.0.1",
"react": "^19.0.0",
+ "react-chartjs-2": "^5.3.0",
"react-dom": "^19.1.0",
"react-i18next": "^15.5.1",
"react-router-dom": "^7.5.3"
diff --git a/01-frontend/public/locales/de/translation.json b/01-frontend/public/locales/de/translation.json
index a17839f..94fc6cb 100644
--- a/01-frontend/public/locales/de/translation.json
+++ b/01-frontend/public/locales/de/translation.json
@@ -84,5 +84,7 @@
"wrongTurn": "Ein falscher Weg wurde eingeschlagen. Hier geht es zurück auf den richtigen Pfad.",
"yourOrderNumber": "Die Bestellnummer lautet",
"articleNumber": "Artikelnummer",
- "noRatingsYet": "Noch keine Bewertungen vorhanden."
+ "noRatingsYet": "Noch keine Bewertungen vorhanden.",
+ "accounts": "Konten",
+ "statistics": "Statistiken"
}
diff --git a/01-frontend/public/locales/en/translation.json b/01-frontend/public/locales/en/translation.json
index bf76cd0..037b067 100644
--- a/01-frontend/public/locales/en/translation.json
+++ b/01-frontend/public/locales/en/translation.json
@@ -84,5 +84,7 @@
"wrongTurn": "It seems you may have taken a wrong turn. Let's get you back on track.",
"yourOrderNumber": "Your order number is",
"articleNumber": "Article number",
- "noRatingsYet": "No ratings yet"
+ "noRatingsYet": "No ratings yet",
+ "accounts": "Accounts",
+ "statistics": "Statistics"
}
diff --git a/01-frontend/src/App.tsx b/01-frontend/src/App.tsx
index 3e13277..83a28ab 100644
--- a/01-frontend/src/App.tsx
+++ b/01-frontend/src/App.tsx
@@ -5,7 +5,6 @@ import './App.css';
import { BasketProvider } from './helper/BasketProvider';
import NavBar from './helper/navbar/NavBar';
import Account from './pages/Account';
-import Category from './pages/Category';
import Contact from './pages/Contact';
import Home from './pages/Home';
import NoPage from './pages/NoPage';
@@ -13,6 +12,8 @@ import Orders from './pages/Orders';
import Payment from './pages/Payment';
import Product from './pages/Product';
import { CustomThemeProvider } from './theme/ThemeContext';
+import FSModel from './pages/FSModel';
+import AdminPanel from './pages/AdminPanel';
export default function App() {
@@ -31,10 +32,11 @@ export default function App() {
} />
} />
} />
- } />
+ } />
} />
} />
} />
+ } />
diff --git a/01-frontend/src/components/Account.tsx b/01-frontend/src/components/Account.tsx
new file mode 100644
index 0000000..add147d
--- /dev/null
+++ b/01-frontend/src/components/Account.tsx
@@ -0,0 +1,8 @@
+type AccountType = {
+ id: string;
+ name: string;
+ email: string;
+ status: "active" | "inactive";
+};
+
+export default AccountType;
\ No newline at end of file
diff --git a/01-frontend/src/components/Order.tsx b/01-frontend/src/components/Order.tsx
new file mode 100644
index 0000000..42c388e
--- /dev/null
+++ b/01-frontend/src/components/Order.tsx
@@ -0,0 +1,10 @@
+type OrderType = {
+ id: string;
+ date: string;
+ status: "active" | "inactive" | "running" | "cancelled";
+ items: { name: string; quantity: number; price: number }[];
+ total: number;
+ address: string;
+};
+
+export default OrderType;
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DPS
+
+
+ {pages.map((page) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/helper/adminpanel/AccountsInfo.tsx b/01-frontend/src/helper/adminpanel/AccountsInfo.tsx
new file mode 100644
index 0000000..eb8cfdd
--- /dev/null
+++ b/01-frontend/src/helper/adminpanel/AccountsInfo.tsx
@@ -0,0 +1,156 @@
+import DeleteIcon from "@mui/icons-material/Delete";
+import EditIcon from "@mui/icons-material/Edit";
+import {
+ Box,
+ Button,
+ IconButton,
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+ TextField,
+ Typography,
+} from "@mui/material";
+import { useState } from "react";
+import AccountType from "../../components/Account";
+
+const mockAccounts: AccountType[] = [
+ { id: "1", name: "John Doe", email: "john.doe@example.com", status: "active" },
+ { id: "2", name: "Jane Smith", email: "jane.smith@example.com", status: "inactive" },
+ { id: "3", name: "Alice Johnson", email: "alice.johnson@example.com", status: "active" },
+];
+
+export default function AccountsInfo() {
+ const [accounts, setAccounts] = useState(mockAccounts);
+ const [searchTerm, setSearchTerm] = useState("");
+ const [editMode, setEditMode] = useState<{ [key: string]: boolean }>({});
+
+ const handleSearch = (event: React.ChangeEvent) => {
+ setSearchTerm(event.target.value.toLowerCase());
+ };
+
+ const handleDelete = (id: string) => {
+ setAccounts((prevAccounts) => prevAccounts.filter((account) => account.id !== id));
+ };
+
+ const handleToggleStatus = (id: string) => {
+ setAccounts((prevAccounts) =>
+ prevAccounts.map((account) =>
+ account.id === id
+ ? { ...account, status: account.status === "active" ? "inactive" : "active" }
+ : account
+ )
+ );
+ };
+
+ const handleEdit = (id: string, field: keyof AccountType, value: string) => {
+ setAccounts((prevAccounts) =>
+ prevAccounts.map((account) =>
+ account.id === id ? { ...account, [field]: value } : account
+ )
+ );
+ };
+
+ const toggleEditMode = (id: string) => {
+ setEditMode((prevEditMode) => ({
+ ...prevEditMode,
+ [id]: !prevEditMode[id],
+ }));
+ };
+
+ const filteredAccounts = accounts.filter(
+ (account) =>
+ account.name.toLowerCase().includes(searchTerm) ||
+ account.email.toLowerCase().includes(searchTerm)
+ );
+
+ return (
+
+
+ Accounts Management
+
+
+
+
+
+
+ ID
+ Name
+ Email
+ Status
+ Actions
+
+
+
+ {filteredAccounts.map((account) => (
+
+ {account.id}
+
+ {editMode[account.id] ? (
+
+ handleEdit(account.id, "name", e.target.value)
+ }
+ variant="standard"
+ />
+ ) : (
+ account.name
+ )}
+
+
+ {editMode[account.id] ? (
+
+ handleEdit(account.id, "email", e.target.value)
+ }
+ variant="standard"
+ />
+ ) : (
+ account.email
+ )}
+
+ {account.status}
+
+ handleDelete(account.id)}
+ >
+
+
+
+ toggleEditMode(account.id)}
+ >
+
+
+
+
+ ))}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/helper/adminpanel/DroppableContainer.tsx b/01-frontend/src/helper/adminpanel/DroppableContainer.tsx
new file mode 100644
index 0000000..015bd58
--- /dev/null
+++ b/01-frontend/src/helper/adminpanel/DroppableContainer.tsx
@@ -0,0 +1,27 @@
+import { useDroppable } from "@dnd-kit/core";
+import { Box } from "@mui/material";
+
+type DroppableContainerProps = {
+ id: string;
+ children: React.ReactNode;
+};
+
+export function DroppableContainer({ id, children }: DroppableContainerProps) {
+ const { setNodeRef } = useDroppable({ id });
+
+ return (
+
+ {children}
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/helper/adminpanel/OrdersInfo.tsx b/01-frontend/src/helper/adminpanel/OrdersInfo.tsx
new file mode 100644
index 0000000..361c4f4
--- /dev/null
+++ b/01-frontend/src/helper/adminpanel/OrdersInfo.tsx
@@ -0,0 +1,171 @@
+import { closestCenter, DndContext } from "@dnd-kit/core";
+import { restrictToParentElement } from "@dnd-kit/modifiers";
+import { Box, Button, Dialog, DialogContent, DialogTitle, Typography } from "@mui/material";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { DroppableContainer } from "./DroppableContainer.tsx"; // Hilfskomponente für Droppable-Bereiche
+import SortableItem from "./SortableItem.tsx"; // Hilfskomponente für Sortable Items
+import OrderType from "../../components/Order";
+
+const mockOrders: OrderType[] = [
+ {
+ id: "1001",
+ date: "2025-05-20",
+ status: "active",
+ items: [
+ { name: "Tomatensamen", quantity: 2, price: 3.99 },
+ { name: "Blumenerde", quantity: 1, price: 7.49 },
+ ],
+ total: 15.47,
+ address: "Musterstraße 1, 12345 Musterstadt",
+ },
+ {
+ id: "1000",
+ date: "2025-05-10",
+ status: "inactive",
+ items: [{ name: "Gießkanne", quantity: 1, price: 12.99 }],
+ total: 12.99,
+ address: "Musterstraße 1, 12345 Musterstadt",
+ },
+ {
+ id: "1002",
+ date: "2025-05-15",
+ status: "running",
+ items: [{ name: "Pflanzendünger", quantity: 1, price: 8.99 }],
+ total: 8.99,
+ address: "Musterstraße 1, 12345 Musterstadt",
+ },
+ {
+ id: "1003",
+ date: "2025-05-18",
+ status: "cancelled",
+ items: [{ name: "Blumentopf", quantity: 2, price: 5.99 }],
+ total: 11.98,
+ address: "Musterstraße 1, 12345 Musterstadt",
+ },
+];
+
+const statusOrder = ["active", "running", "inactive", "cancelled"];
+
+export default function OrdersInfo() {
+ const { t } = useTranslation();
+ const [orders, setOrders] = useState(mockOrders);
+ const [selectedOrder, setSelectedOrder] = useState(null);
+
+ const handleDragEnd = (event: any) => {
+ const { active, over } = event;
+
+ if (!over || active.id === over.id) return;
+
+ const newStatus = over.id; // Zielspalte (status)
+ setOrders((prevOrders) =>
+ prevOrders.map((order) =>
+ order.id === active.id ? { ...order, status: newStatus } : order
+ )
+ );
+ };
+
+ const handleNextStatus = (order: OrderType) => {
+ const currentIndex = statusOrder.indexOf(order.status);
+ if (currentIndex < statusOrder.length - 1) {
+ setOrders((prevOrders) =>
+ prevOrders.map((o) =>
+ o.id === order.id
+ ? { ...o, status: statusOrder[currentIndex + 1] as OrderType['status'] }
+ : o
+ )
+ );
+ }
+ };
+
+ const renderOrders = (status: string) => {
+ const filteredOrders = orders.filter((order) => order.status === status);
+
+ return (
+
+
+ {t(status)}
+
+ {filteredOrders.map((order) => (
+ setSelectedOrder(order)}
+ />
+ ))}
+
+ );
+ };
+
+ return (
+
+
+ {t("Orders Management")}
+
+
+
+ {statusOrder.map((status) => (
+
+ {renderOrders(status)}
+
+ ))}
+
+
+
+ {/* Dialog für Bestelldetails */}
+
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/helper/adminpanel/SortableItem.tsx b/01-frontend/src/helper/adminpanel/SortableItem.tsx
new file mode 100644
index 0000000..2f4c1ba
--- /dev/null
+++ b/01-frontend/src/helper/adminpanel/SortableItem.tsx
@@ -0,0 +1,43 @@
+import { useSortable } from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+import { Paper, Typography } from "@mui/material";
+
+type SortableItemProps = {
+ id: string;
+ order: {
+ id: string;
+ total: number;
+ };
+ onClick: () => void;
+};
+
+export default function SortableItem({ id, order, onClick }: SortableItemProps) {
+ const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ cursor: "grab",
+ };
+
+ return (
+
+ Order ID: {order.id}
+ Total: {order.total.toFixed(2)} €
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/helper/adminpanel/StatisticsInfo.tsx b/01-frontend/src/helper/adminpanel/StatisticsInfo.tsx
new file mode 100644
index 0000000..c1c8d8f
--- /dev/null
+++ b/01-frontend/src/helper/adminpanel/StatisticsInfo.tsx
@@ -0,0 +1,101 @@
+import { Box, Typography } from "@mui/material";
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend,
+ ArcElement,
+ LineElement,
+ PointElement,
+} from "chart.js";
+import { Bar, Pie, Line } from "react-chartjs-2";
+
+// Chart.js registrieren
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend,
+ ArcElement,
+ LineElement,
+ PointElement
+);
+
+// Fake-Daten
+const weeklySalesData = {
+ labels: ["Week 1", "Week 2", "Week 3", "Week 4"],
+ datasets: [
+ {
+ label: "Weekly Sales (€)",
+ data: [1200, 2100, 800, 1600],
+ backgroundColor: "rgba(75, 192, 192, 0.5)",
+ borderColor: "rgba(75, 192, 192, 1)",
+ borderWidth: 1,
+ },
+ ],
+};
+
+const itemSalesData = {
+ labels: ["Tomatensamen", "Blumenerde", "Gießkanne", "Pflanzendünger"],
+ datasets: [
+ {
+ label: "Item Sales",
+ data: [400, 300, 200, 100],
+ backgroundColor: ["#FF6384", "#36A2EB", "#FFCE56", "#4BC0C0"],
+ hoverBackgroundColor: ["#FF6384", "#36A2EB", "#FFCE56", "#4BC0C0"],
+ },
+ ],
+};
+
+const userSalesData = {
+ labels: ["John Doe", "Jane Smith", "Alice Johnson", "Bob Brown"],
+ datasets: [
+ {
+ label: "Sales by User",
+ data: [5, 8, 3, 6],
+ fill: false,
+ borderColor: "rgba(153, 102, 255, 1)",
+ backgroundColor: "rgba(153, 102, 255, 0.5)",
+ tension: 0.1,
+ },
+ ],
+};
+
+export default function StatisticsInfo() {
+ return (
+
+
+ Sales Statistics
+
+
+ {/* Gesamtverkaufszahlen pro Woche */}
+
+
+ Weekly Sales
+
+
+
+
+ {/* Verkaufsanzahl der einzelnen Items */}
+
+
+ Item Sales Distribution
+
+
+
+
+ {/* Verkäufe pro Benutzer */}
+
+
+ Sales by User
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/helper/homepage/ItemCard.tsx b/01-frontend/src/helper/homepage/ItemCard.tsx
index 962f3c0..57f64d7 100644
--- a/01-frontend/src/helper/homepage/ItemCard.tsx
+++ b/01-frontend/src/helper/homepage/ItemCard.tsx
@@ -1,11 +1,11 @@
import { AddShoppingCart } from "@mui/icons-material";
-import {Box, Card, CardActionArea, CardContent, CardMedia, IconButton, Paper, Rating, Typography } from "@mui/material";
+import { Box, Card, CardActionArea, CardContent, CardMedia, IconButton, Paper, Rating, Typography } from "@mui/material";
+import { useEffect, useState } from "react";
+import { useTranslation } from 'react-i18next';
import { useNavigate } from "react-router-dom";
import Item from "../../components/Item";
import { useBasket } from "../BasketProvider";
import "../helper.css";
-import { useTranslation } from 'react-i18next';
-import { useEffect, useMemo, useRef, useState } from "react";
export default function ItemCard({ item }: { item: Item }) {
diff --git a/01-frontend/src/helper/navbar/NavBar.tsx b/01-frontend/src/helper/navbar/NavBar.tsx
index fc6f05c..fa42f96 100644
--- a/01-frontend/src/helper/navbar/NavBar.tsx
+++ b/01-frontend/src/helper/navbar/NavBar.tsx
@@ -29,7 +29,7 @@ export default function NavBar() {
const [itemNames, setItemNames] = React.useState([]); // Für Autocomplete
- const pageKeys = ['categories', 'checkout', 'contact'];
+ const pageKeys = ['fsmodel', 'admin', 'checkout', 'contact'];
const settingKeys = ['account', 'orders', 'logout'];
const pages = pageKeys.map(key => ({ key, label: t(key) }));
diff --git a/01-frontend/src/pages/AdminPanel.tsx b/01-frontend/src/pages/AdminPanel.tsx
new file mode 100644
index 0000000..d44a48a
--- /dev/null
+++ b/01-frontend/src/pages/AdminPanel.tsx
@@ -0,0 +1,82 @@
+import { AccountCircle, QueryStats, ReceiptLong } from "@mui/icons-material";
+import { Box, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+import AccountsInfo from "../helper/adminpanel/AccountsInfo";
+import OrdersInfo from "../helper/adminpanel/OrdersInfo";
+import StatisticsInfo from "../helper/adminpanel/StatisticsInfo";
+
+export default function AdminPanel() {
+ const { t } = useTranslation();
+ const [infoStatus, setInfoStatus] = useState("statistics"); // Standardseite
+
+ const handleInfoStatus = (path: string) => {
+ console.log("Button clicked:", path); // Debugging
+ setInfoStatus(path);
+ };
+
+ const renderContent = () => {
+ console.log("Rendering content for:", infoStatus); // Debugging
+ switch (infoStatus) {
+ case "statistics":
+ return ;
+ case "orders":
+ return ;
+ case "accounts":
+ return ;
+ default:
+ return ; // Fallback, falls kein gültiger Status
+ }
+ };
+
+ return (
+
+
+
+ Admin Panel
+
+
+ Manage your application settings and content here.
+
+
+
+ {/* Sidebar */}
+
+
+
+
+ {/* Content */}
+
+ {renderContent()}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/pages/Category.tsx b/01-frontend/src/pages/Category.tsx
deleted file mode 100644
index 5e09d04..0000000
--- a/01-frontend/src/pages/Category.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import CategoryIcon from "@mui/icons-material/Category";
-import PrecisionManufacturingIcon from "@mui/icons-material/PrecisionManufacturing";
-import ScienceIcon from "@mui/icons-material/Science";
-import SpaIcon from "@mui/icons-material/Spa";
-import { Box, Card, CardActionArea, CardContent, Grid, Typography } from "@mui/material";
-import { useNavigate } from "react-router-dom";
-import "./pages.css";
-import {useTranslation} from "react-i18next";
-
-export default function Category() {
-
- const { t } = useTranslation();
- const navigate = useNavigate();
-
- const categories = [
- {
- name: t('seeds'),
- icon: ,
- filter: "Seeds",
- },
- {
- name: t('gardenSupplies'),
- icon: ,
- filter: "GardenSupplies"
- },
- {
- name: t('technicalComponents'),
- icon: ,
- filter: "TechnicalComponents"
- },
- {
- name: t('other'),
- icon: ,
- filter: "Other"
- }
- ];
-
- const handleCategoryClick = (filter: string) => {
- // Navigiere zur Home-Seite und übergebe die gewählte Kategorie als Query-Parameter
- navigate(`/?category=${encodeURIComponent(filter)}`);
- };
-
- return (
-
-
- {t('categories')}
-
-
- ...
-
-
- {categories.map((cat) => (
-
-
- handleCategoryClick(cat.filter)}>
-
- {cat.icon}
-
-
- {cat.name}
-
-
-
-
-
-
- ))}
-
-
- );
-}
\ No newline at end of file
diff --git a/01-frontend/src/pages/FSModel.tsx b/01-frontend/src/pages/FSModel.tsx
new file mode 100644
index 0000000..ea0710c
--- /dev/null
+++ b/01-frontend/src/pages/FSModel.tsx
@@ -0,0 +1,24 @@
+import { Box, Typography } from "@mui/material";
+import { useTranslation } from "react-i18next";
+import "./pages.css";
+
+export default function FSModel() {
+
+ const { t } = useTranslation();
+
+ return (
+
+
+ {t('categories')}
+
+
+ ...
+
+
+ );
+}
\ No newline at end of file
diff --git a/01-frontend/src/pages/Orders.tsx b/01-frontend/src/pages/Orders.tsx
index 27aa69b..0fe8ad9 100644
--- a/01-frontend/src/pages/Orders.tsx
+++ b/01-frontend/src/pages/Orders.tsx
@@ -2,17 +2,9 @@ import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider
import { useState } from "react";
import "./pages.css";
import {useTranslation} from "react-i18next";
+import OrderType from "../components/Order";
-type Order = {
- id: string;
- date: string;
- status: "laufend" | "inactive";
- items: { name: string; quantity: number; price: number }[];
- total: number;
- address: string;
-};
-
-const mockOrders: Order[] = [
+const mockOrders: OrderType[] = [
{
id: "1001",
date: "2025-05-20",
@@ -41,7 +33,7 @@ export default function Orders() {
const { t } = useTranslation();
const [tab, setTab] = useState(0);
- const [selectedOrder, setSelectedOrder] = useState(null);
+ const [selectedOrder, setSelectedOrder] = useState(null);
const activeOrders = mockOrders.filter(o => o.status === "active");
const inactiveOrders = mockOrders.filter(o => o.status === "inactive");