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 (
-
-
-
-
-
- To get started, edit the page.tsx file.
-
-
- Looking for a starting point or more instructions? Head over to{" "}
-
- Templates
- {" "}
- or the{" "}
-
- Learning
- {" "}
- center.
-
-
-
-
-
+ <>
+
+
+ } />
+
+
+
+
+
+
+
+
+ >
);
-}
+}
\ 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 (
+
+ )
+}
+
+export { Label }
diff --git a/frontend/athena/components/users/UserDialog.tsx b/frontend/athena/components/users/UserDialog.tsx
new file mode 100644
index 0000000..a76cd9f
--- /dev/null
+++ b/frontend/athena/components/users/UserDialog.tsx
@@ -0,0 +1,133 @@
+"use client";
+
+import { useState } from "react";
+
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from "@/components/ui/dialog";
+
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+
+import { api } from "@/lib/api";
+
+export default function UserDialog() {
+
+ const [open, setOpen] = useState(false);
+
+ const [username, setUsername] = useState("");
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+
+ async function saveUser() {
+
+ console.log("Speichern geklickt");
+
+ try {
+
+ const response = await api.post("/users/", {
+ username,
+ email,
+ password,
+ });
+
+ console.log(response.data);
+
+ setOpen(false);
+
+ window.location.reload();
+
+ } catch (error) {
+
+ console.error(error);
+
+ }
+
+}
+
+ return (
+
+ <>
+
+
+
+
+
+ >
+
+ );
+
+}
\ No newline at end of file
diff --git a/frontend/athena/components/users/UserForm.tsx b/frontend/athena/components/users/UserForm.tsx
new file mode 100644
index 0000000..d9af0df
--- /dev/null
+++ b/frontend/athena/components/users/UserForm.tsx
@@ -0,0 +1,7 @@
+export default function UserForm() {
+ return (
+
+ UserForm
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/athena/components/users/UserTable.tsx b/frontend/athena/components/users/UserTable.tsx
new file mode 100644
index 0000000..bf83418
--- /dev/null
+++ b/frontend/athena/components/users/UserTable.tsx
@@ -0,0 +1,63 @@
+"use client";
+
+import { User } from "@/types/user";
+
+type Props = {
+ users: User[];
+};
+
+export default function UserTable({ users }: Props) {
+ return (
+
+
+
+
+
+
+
+
+ | ID |
+ Benutzername |
+ E-Mail |
+ Aktionen |
+
+
+
+
+
+
+
+ {users.map((user) => (
+
+
+
+ | {user.id} |
+ {user.username} |
+ {user.email} |
+
+
+
+
+
+
+
+ |
+
+
+
+ ))}
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/athena/components/utils.ts b/frontend/athena/components/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/frontend/athena/components/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/frontend/athena/lib/api.ts b/frontend/athena/lib/api.ts
new file mode 100644
index 0000000..51f8d07
--- /dev/null
+++ b/frontend/athena/lib/api.ts
@@ -0,0 +1,5 @@
+import axios from "axios";
+
+export const api = axios.create({
+ baseURL: "http://127.0.0.1:8000",
+});
\ No newline at end of file
diff --git a/frontend/athena/types/user.ts b/frontend/athena/types/user.ts
new file mode 100644
index 0000000..e09fd9a
--- /dev/null
+++ b/frontend/athena/types/user.ts
@@ -0,0 +1,5 @@
+export interface User {
+ id: number;
+ username: string;
+ email: string;
+}
\ No newline at end of file