From 4cf671e32d990dc806c00a1b4e3505964f0df4c6 Mon Sep 17 00:00:00 2001 From: Schubert Ferenc Date: Thu, 2 Jul 2026 00:01:25 +0200 Subject: [PATCH] Dashboard und Benutzerverwaltung --- frontend/athena/app/layout.tsx | 45 +++-- frontend/athena/app/page.tsx | 77 ++------- frontend/athena/app/users/page.tsx | 66 ++++++++ frontend/athena/components/Header.tsx | 23 +++ frontend/athena/components/Sidebar.tsx | 86 ++++++++++ frontend/athena/components/StatCard.tsx | 26 +++ frontend/athena/components/UserCounter.tsx | 21 +++ frontend/athena/components/ui/dialog.tsx | 160 ++++++++++++++++++ frontend/athena/components/ui/input.tsx | 20 +++ frontend/athena/components/ui/label.tsx | 20 +++ .../athena/components/users/UserDialog.tsx | 133 +++++++++++++++ frontend/athena/components/users/UserForm.tsx | 7 + .../athena/components/users/UserTable.tsx | 63 +++++++ frontend/athena/components/utils.ts | 6 + frontend/athena/lib/api.ts | 5 + frontend/athena/types/user.ts | 5 + 16 files changed, 684 insertions(+), 79 deletions(-) create mode 100644 frontend/athena/app/users/page.tsx create mode 100644 frontend/athena/components/Header.tsx create mode 100644 frontend/athena/components/Sidebar.tsx create mode 100644 frontend/athena/components/StatCard.tsx create mode 100644 frontend/athena/components/UserCounter.tsx create mode 100644 frontend/athena/components/ui/dialog.tsx create mode 100644 frontend/athena/components/ui/input.tsx create mode 100644 frontend/athena/components/ui/label.tsx create mode 100644 frontend/athena/components/users/UserDialog.tsx create mode 100644 frontend/athena/components/users/UserForm.tsx create mode 100644 frontend/athena/components/users/UserTable.tsx create mode 100644 frontend/athena/components/utils.ts create mode 100644 frontend/athena/lib/api.ts create mode 100644 frontend/athena/types/user.ts diff --git a/frontend/athena/app/layout.tsx b/frontend/athena/app/layout.tsx index 976eb90..7de60c4 100644 --- a/frontend/athena/app/layout.tsx +++ b/frontend/athena/app/layout.tsx @@ -1,20 +1,12 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); +import Sidebar from "@/components/Sidebar"; +import Header from "@/components/Header"; export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Olympus ERP", + description: "Olympus ERP", }; export default function RootLayout({ @@ -23,11 +15,28 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - {children} + + + +
+ + + +
+ +
+ +
+ + {children} + +
+ +
+ +
+ + ); -} +} \ No newline at end of file diff --git a/frontend/athena/app/page.tsx b/frontend/athena/app/page.tsx index 3f36f7c..330cb12 100644 --- a/frontend/athena/app/page.tsx +++ b/frontend/athena/app/page.tsx @@ -1,65 +1,20 @@ -import Image from "next/image"; +import StatCard from "@/components/StatCard"; +import UserCounter from "@/components/UserCounter"; export default function Home() { return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
-
-
+ <> +
+ + } /> + + + + + + + +
+ ); -} +} \ No newline at end of file diff --git a/frontend/athena/app/users/page.tsx b/frontend/athena/app/users/page.tsx new file mode 100644 index 0000000..2ab2886 --- /dev/null +++ b/frontend/athena/app/users/page.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { useEffect, useState } from "react"; + +import { api } from "@/lib/api"; +import { User } from "@/types/user"; + +import UserDialog from "@/components/users/UserDialog"; +import UserTable from "@/components/users/UserTable"; + +export default function UsersPage() { + + const [users, setUsers] = useState([]); + + useEffect(() => { + loadUsers(); + }, []); + + function loadUsers() { + + api + .get("/users/") + .then((res) => { + + console.log("API:", res.data); + + setUsers(res.data); + + }) + .catch((err) => { + + console.error("API Fehler:", err); + + }); + + } + + return ( + + <> + +
+ +

+ + Benutzer + +

+ + + +
+ +
+
+                {JSON.stringify(users, null, 2)}
+
+            
+ + + + + + ); + +} \ No newline at end of file diff --git a/frontend/athena/components/Header.tsx b/frontend/athena/components/Header.tsx new file mode 100644 index 0000000..b609a78 --- /dev/null +++ b/frontend/athena/components/Header.tsx @@ -0,0 +1,23 @@ +export default function Header() { + + return ( + +
+ +

+ + Dashboard + +

+ +
+ + admin.schubert + +
+ +
+ + ); + +} \ No newline at end of file diff --git a/frontend/athena/components/Sidebar.tsx b/frontend/athena/components/Sidebar.tsx new file mode 100644 index 0000000..dcbe34b --- /dev/null +++ b/frontend/athena/components/Sidebar.tsx @@ -0,0 +1,86 @@ +import Link from "next/link"; +import { + LayoutDashboard, + Users, + Wrench, + Package, + FileText, + Settings, + ShoppingCart, +} from "lucide-react"; + +const menu = [ + { + icon: LayoutDashboard, + name: "Dashboard", + href: "/", + }, + { + icon: Users, + name: "Benutzer", + href: "/users", + }, + { + icon: Users, + name: "Kunden", + href: "/customers", + }, + { + icon: Wrench, + name: "Reparaturen", + href: "/repairs", + }, + { + icon: Package, + name: "Lager", + href: "/inventory", + }, + { + icon: ShoppingCart, + name: "eBay", + href: "/ebay", + }, + { + icon: FileText, + name: "Dokumente", + href: "/documents", + }, + { + icon: Settings, + name: "Einstellungen", + href: "/settings", + }, +]; + +export default function Sidebar() { + return ( + + ); +} \ No newline at end of file diff --git a/frontend/athena/components/StatCard.tsx b/frontend/athena/components/StatCard.tsx new file mode 100644 index 0000000..aa6046c --- /dev/null +++ b/frontend/athena/components/StatCard.tsx @@ -0,0 +1,26 @@ +import { ReactNode } from "react"; + +type Props = { + title: string; + value: ReactNode; +}; + +export default function StatCard({ title, value }: Props) { + + return ( + +
+ +

+ {title} +

+ +

+ {value} +

+ +
+ + ); + +} \ No newline at end of file diff --git a/frontend/athena/components/UserCounter.tsx b/frontend/athena/components/UserCounter.tsx new file mode 100644 index 0000000..f335467 --- /dev/null +++ b/frontend/athena/components/UserCounter.tsx @@ -0,0 +1,21 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { api } from "@/lib/api"; + +export default function UserCounter() { + + const [count, setCount] = useState(0); + + useEffect(() => { + + api.get("/users") + .then(res => setCount(res.data.length)) + .catch(console.error); + + }, []); + + return ( + <>{count} + ); +} \ No newline at end of file diff --git a/frontend/athena/components/ui/dialog.tsx b/frontend/athena/components/ui/dialog.tsx new file mode 100644 index 0000000..014f5aa --- /dev/null +++ b/frontend/athena/components/ui/dialog.tsx @@ -0,0 +1,160 @@ +"use client" + +import * as React from "react" +import { Dialog as DialogPrimitive } from "@base-ui/react/dialog" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { XIcon } from "lucide-react" + +function Dialog({ ...props }: DialogPrimitive.Root.Props) { + return +} + +function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) { + return +} + +function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) { + return +} + +function DialogClose({ ...props }: DialogPrimitive.Close.Props) { + return +} + +function DialogOverlay({ + className, + ...props +}: DialogPrimitive.Backdrop.Props) { + return ( + + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: DialogPrimitive.Popup.Props & { + showCloseButton?: boolean +}) { + return ( + + + + {children} + {showCloseButton && ( + + } + > + + Close + + )} + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ + className, + showCloseButton = false, + children, + ...props +}: React.ComponentProps<"div"> & { + showCloseButton?: boolean +}) { + return ( +
+ {children} + {showCloseButton && ( + }> + Close + + )} +
+ ) +} + +function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: DialogPrimitive.Description.Props) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/frontend/athena/components/ui/input.tsx b/frontend/athena/components/ui/input.tsx new file mode 100644 index 0000000..7d21bab --- /dev/null +++ b/frontend/athena/components/ui/input.tsx @@ -0,0 +1,20 @@ +import * as React from "react" +import { Input as InputPrimitive } from "@base-ui/react/input" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/frontend/athena/components/ui/label.tsx b/frontend/athena/components/ui/label.tsx new file mode 100644 index 0000000..74da65c --- /dev/null +++ b/frontend/athena/components/ui/label.tsx @@ -0,0 +1,20 @@ +"use client" + +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Label({ className, ...props }: React.ComponentProps<"label">) { + return ( +