nextjs improvements, auth dummies, auth guard
This commit is contained in:
		
							parent
							
								
									872ad76f59
								
							
						
					
					
						commit
						e06753fb24
					
				
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -13,12 +13,6 @@ node_modules/
 | 
				
			|||||||
.DS_Store
 | 
					.DS_Store
 | 
				
			||||||
Thumbs.db
 | 
					Thumbs.db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# IDE spezifische Ignorierungen
 | 
					 | 
				
			||||||
.idea/
 | 
					 | 
				
			||||||
.vscode/
 | 
					 | 
				
			||||||
*.swp
 | 
					 | 
				
			||||||
*~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Weitere Build-Verzeichnisse
 | 
					# Weitere Build-Verzeichnisse
 | 
				
			||||||
**/dist/
 | 
					**/dist/
 | 
				
			||||||
**/build/
 | 
					**/build/
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@ -1,4 +1,9 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"editor.formatOnSave": true,
 | 
						"editor.formatOnSave": true,
 | 
				
			||||||
	"cSpell.language": "en,de-DE"
 | 
						"cSpell.language": "en,de-DE",
 | 
				
			||||||
 | 
						"files.autoSave": "afterDelay",
 | 
				
			||||||
 | 
						"editor.formatOnPaste": false,
 | 
				
			||||||
 | 
						"deno.disablePaths": [
 | 
				
			||||||
 | 
							"frontend-react"
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								backend-dart/run.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								backend-dart/run.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					dart run bin/backend_dart.dart # Backend on port 8080
 | 
				
			||||||
							
								
								
									
										3
									
								
								frontend-react/deno.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								frontend-react/deno.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "unstable": ["unsafe-proto"]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								frontend-react/import_map.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								frontend-react/import_map.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "imports": {
 | 
				
			||||||
 | 
					    "@/components/": "./components/"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -19,5 +19,12 @@
 | 
				
			|||||||
  "NotFoundPage": {
 | 
					  "NotFoundPage": {
 | 
				
			||||||
    "description": "Bitte überprüfe die Addressleiste deines Browsers oder verwende die Navigation um zu einer bekannten Seite zu wechseln.",
 | 
					    "description": "Bitte überprüfe die Addressleiste deines Browsers oder verwende die Navigation um zu einer bekannten Seite zu wechseln.",
 | 
				
			||||||
    "title": "Seite nicht gefunden"
 | 
					    "title": "Seite nicht gefunden"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "Auth": {
 | 
				
			||||||
 | 
					    "login": "Anmelden",
 | 
				
			||||||
 | 
					    "logout": "Abmelden",
 | 
				
			||||||
 | 
					    "profile": "Profil",
 | 
				
			||||||
 | 
					    "register": "Registrieren",
 | 
				
			||||||
 | 
					    "resetPassword": "Passwort zurücksetzen"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,4 +1,6 @@
 | 
				
			|||||||
"use client";
 | 
					"use client";
 | 
				
			||||||
 | 
					import BackButton from "@/components/Buttons/BackButton";
 | 
				
			||||||
 | 
					import StartpageButton from "@/components/Buttons/StartpageButton";
 | 
				
			||||||
import Link from "next/link";
 | 
					import Link from "next/link";
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
import { useState } from "react";
 | 
					import { useState } from "react";
 | 
				
			||||||
@ -20,7 +22,8 @@ const LoginPage = () => {
 | 
				
			|||||||
				<div className="mb-4">
 | 
									<div className="mb-4">
 | 
				
			||||||
					<label
 | 
										<label
 | 
				
			||||||
						htmlFor="email"
 | 
											htmlFor="email"
 | 
				
			||||||
						className="block text-gray-700 font-medium mb-2">
 | 
											className="block text-gray-700 font-medium mb-2"
 | 
				
			||||||
 | 
										>
 | 
				
			||||||
						Email
 | 
											Email
 | 
				
			||||||
					</label>
 | 
										</label>
 | 
				
			||||||
					<input
 | 
										<input
 | 
				
			||||||
@ -36,7 +39,8 @@ const LoginPage = () => {
 | 
				
			|||||||
				<div className="mb-4">
 | 
									<div className="mb-4">
 | 
				
			||||||
					<label
 | 
										<label
 | 
				
			||||||
						htmlFor="password"
 | 
											htmlFor="password"
 | 
				
			||||||
						className="block text-gray-700 font-medium mb-2">
 | 
											className="block text-gray-700 font-medium mb-2"
 | 
				
			||||||
 | 
										>
 | 
				
			||||||
						Password
 | 
											Password
 | 
				
			||||||
					</label>
 | 
										</label>
 | 
				
			||||||
					<input
 | 
										<input
 | 
				
			||||||
@ -51,12 +55,17 @@ const LoginPage = () => {
 | 
				
			|||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<button
 | 
									<button
 | 
				
			||||||
					type="submit"
 | 
										type="submit"
 | 
				
			||||||
					className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition duration-200">
 | 
										className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition duration-200"
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
					Login
 | 
										Login
 | 
				
			||||||
				</button>
 | 
									</button>
 | 
				
			||||||
				<Link className="block text-center mt-4 text-blue-500" href="register">
 | 
									<Link className="block text-center mt-4 text-blue-500" href="register">
 | 
				
			||||||
					Create an account
 | 
										Create an account
 | 
				
			||||||
				</Link>
 | 
									</Link>
 | 
				
			||||||
 | 
									<div className="flex justify-between mt-8">
 | 
				
			||||||
 | 
										<BackButton />
 | 
				
			||||||
 | 
										<StartpageButton />
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								frontend-react/src/app/[locale]/(auth)/register/layout.tsx
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										9
									
								
								frontend-react/src/app/[locale]/(auth)/register/layout.tsx
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					const LoginLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="min-h-screen flex items-center justify-center bg-gray-100">
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default LoginLayout;
 | 
				
			||||||
							
								
								
									
										108
									
								
								frontend-react/src/app/[locale]/(auth)/register/page.tsx
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										108
									
								
								frontend-react/src/app/[locale]/(auth)/register/page.tsx
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					import Link from "next/link";
 | 
				
			||||||
 | 
					import React, { FormEvent, useState } from "react";
 | 
				
			||||||
 | 
					import BackButton from "@/components/Buttons/BackButton";
 | 
				
			||||||
 | 
					import StartpageButton from "@/components/Buttons/StartpageButton";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const RegisterPage = () => {
 | 
				
			||||||
 | 
					  const [email, setEmail] = useState("");
 | 
				
			||||||
 | 
					  const [password, setPassword] = useState("");
 | 
				
			||||||
 | 
					  const [confirmPassword, setConfirmPassword] = useState("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  interface RegisterFormState {
 | 
				
			||||||
 | 
					    email: string;
 | 
				
			||||||
 | 
					    password: string;
 | 
				
			||||||
 | 
					    confirmPassword: string;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSubmit = (e: FormEvent) => {
 | 
				
			||||||
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					    if (password !== confirmPassword) {
 | 
				
			||||||
 | 
					      alert("Passwords do not match");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const formData: RegisterFormState = {
 | 
				
			||||||
 | 
					      email,
 | 
				
			||||||
 | 
					      password,
 | 
				
			||||||
 | 
					      confirmPassword,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    // Registrierungshandhabung hier hinzufügen
 | 
				
			||||||
 | 
					    console.log("Registering user with data", formData);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="flex items-center justify-center min-h-screen bg-gray-100">
 | 
				
			||||||
 | 
					      <div className="w-full max-w-md bg-white rounded-lg shadow-md p-8">
 | 
				
			||||||
 | 
					        <h2 className="text-2xl font-bold mb-6 text-center">Registrieren</h2>
 | 
				
			||||||
 | 
					        <form onSubmit={handleSubmit}>
 | 
				
			||||||
 | 
					          <div className="mb-4">
 | 
				
			||||||
 | 
					            <label
 | 
				
			||||||
 | 
					              htmlFor="email"
 | 
				
			||||||
 | 
					              className="block text-gray-700 font-medium mb-2"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Email
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					              type="email"
 | 
				
			||||||
 | 
					              id="email"
 | 
				
			||||||
 | 
					              value={email}
 | 
				
			||||||
 | 
					              onChange={(e) => setEmail(e.target.value)}
 | 
				
			||||||
 | 
					              className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
 | 
				
			||||||
 | 
					              placeholder="Geben Sie Ihre Email ein"
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="mb-4">
 | 
				
			||||||
 | 
					            <label
 | 
				
			||||||
 | 
					              htmlFor="password"
 | 
				
			||||||
 | 
					              className="block text-gray-700 font-medium mb-2"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Passwort
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					              type="password"
 | 
				
			||||||
 | 
					              id="password"
 | 
				
			||||||
 | 
					              value={password}
 | 
				
			||||||
 | 
					              onChange={(e) => setPassword(e.target.value)}
 | 
				
			||||||
 | 
					              className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
 | 
				
			||||||
 | 
					              placeholder="Geben Sie Ihr Passwort ein"
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div className="mb-4">
 | 
				
			||||||
 | 
					            <label
 | 
				
			||||||
 | 
					              htmlFor="confirmPassword"
 | 
				
			||||||
 | 
					              className="block text-gray-700 font-medium mb-2"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Passwort bestätigen
 | 
				
			||||||
 | 
					            </label>
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					              type="password"
 | 
				
			||||||
 | 
					              id="confirmPassword"
 | 
				
			||||||
 | 
					              value={confirmPassword}
 | 
				
			||||||
 | 
					              onChange={(e) => setConfirmPassword(e.target.value)}
 | 
				
			||||||
 | 
					              className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
 | 
				
			||||||
 | 
					              placeholder="Bestätigen Sie Ihr Passwort"
 | 
				
			||||||
 | 
					              required
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <button
 | 
				
			||||||
 | 
					            type="submit"
 | 
				
			||||||
 | 
					            className="w-full bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600 transition duration-200"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Registrieren
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					          <Link className="block text-center mt-4 text-blue-500" href="/login">
 | 
				
			||||||
 | 
					            Bereits ein Konto? Anmelden
 | 
				
			||||||
 | 
					          </Link>
 | 
				
			||||||
 | 
					          <div className="flex justify-between mt-8">
 | 
				
			||||||
 | 
					            <BackButton />
 | 
				
			||||||
 | 
					            <StartpageButton />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default RegisterPage;
 | 
				
			||||||
							
								
								
									
										16
									
								
								frontend-react/src/app/[locale]/(info)/about/layout.tsx
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								frontend-react/src/app/[locale]/(info)/about/layout.tsx
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					import { ReactNode } from "react";
 | 
				
			||||||
 | 
					import BaseLayout from "@/components/Layout/BaseLayout";
 | 
				
			||||||
 | 
					import { routing } from "@/i18n/routing";
 | 
				
			||||||
 | 
					import UnauthenticatedLayout from "@/components/Layout/UnauthenticatedLayout";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
						children: ReactNode;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function RootLayout({ children }: Props) {
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<BaseLayout locale={routing.defaultLocale}>
 | 
				
			||||||
 | 
								<UnauthenticatedLayout>{children}</UnauthenticatedLayout>
 | 
				
			||||||
 | 
							</BaseLayout>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								frontend-react/src/app/[locale]/(info)/about/page.tsx
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										48
									
								
								frontend-react/src/app/[locale]/(info)/about/page.tsx
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function About() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <main className="flex items-center justify-center">
 | 
				
			||||||
 | 
					      <div className="max-w-2xl pt-5 sm:pt-10 lg:pt-20">
 | 
				
			||||||
 | 
					        <div className="text-center">
 | 
				
			||||||
 | 
					          <h1 className="text-balance text-5xl font-semibold tracking-tight text-gray-900 sm:text-7xl">
 | 
				
			||||||
 | 
					            Über das Projekt
 | 
				
			||||||
 | 
					          </h1>
 | 
				
			||||||
 | 
					          <p className="mt-8 text-pretty text-lg font-medium text-gray-500 sm:text-xl/8">
 | 
				
			||||||
 | 
					            Entwickler: Jean Jacques Avril
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
 | 
					          <p className="mt-2 text-pretty text-lg font-medium text-gray-500 sm:text-xl/8">
 | 
				
			||||||
 | 
					            Webseite:{" "}
 | 
				
			||||||
 | 
					            <a
 | 
				
			||||||
 | 
					              href="https://jeanavril.com"
 | 
				
			||||||
 | 
					              className="text-indigo-600 hover:underline"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              jeanavril.com
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
 | 
					          <p className="mt-2 text-pretty text-lg font-medium text-gray-500 sm:text-xl/8">
 | 
				
			||||||
 | 
					            Technologien: Next.js, Go, Docker, Deno, Dart
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
 | 
					          <p className="mt-8 text-pretty text-lg font-medium text-gray-500 sm:text-xl/8">
 | 
				
			||||||
 | 
					            Mit ActaTempus können Sie Ihre Arbeitszeiten mühelos erfassen und
 | 
				
			||||||
 | 
					            Einblicke in Ihre Arbeitsgewohnheiten gewinnen. Beginnen Sie noch
 | 
				
			||||||
 | 
					            heute und steigern Sie Ihre Produktivität.
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
 | 
					          <div className="mt-10 flex items-center justify-center gap-x-6">
 | 
				
			||||||
 | 
					            <a
 | 
				
			||||||
 | 
					              href="/register"
 | 
				
			||||||
 | 
					              className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Registrieren
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					            <a href="#" className="text-sm/6 font-semibold text-gray-900">
 | 
				
			||||||
 | 
					              Mehr erfahren <span aria-hidden="true">→</span>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </main>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,4 +1,7 @@
 | 
				
			|||||||
"use client";
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Link from "next/link";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Home() {
 | 
					export default function Home() {
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<main className="flex items-center justify-center">
 | 
							<main className="flex items-center justify-center">
 | 
				
			||||||
@ -14,13 +17,17 @@ export default function Home() {
 | 
				
			|||||||
					</p>
 | 
										</p>
 | 
				
			||||||
					<div className="mt-10 flex items-center justify-center gap-x-6">
 | 
										<div className="mt-10 flex items-center justify-center gap-x-6">
 | 
				
			||||||
						<a
 | 
											<a
 | 
				
			||||||
							href="#"
 | 
												href="/register"
 | 
				
			||||||
							className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
 | 
												className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
							Register
 | 
												Register
 | 
				
			||||||
						</a>
 | 
											</a>
 | 
				
			||||||
						<a href="#" className="text-sm/6 font-semibold text-gray-900">
 | 
											<a href="#" className="text-sm/6 font-semibold text-gray-900">
 | 
				
			||||||
							Learn more <span aria-hidden="true">→</span>
 | 
												Learn more <span aria-hidden="true">→</span>
 | 
				
			||||||
						</a>
 | 
											</a>
 | 
				
			||||||
 | 
											<Link href={"/dashboard"}>
 | 
				
			||||||
 | 
												Go to Dashboard
 | 
				
			||||||
 | 
											</Link>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										19
									
								
								frontend-react/src/app/[locale]/dashboard/layout.tsx
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										19
									
								
								frontend-react/src/app/[locale]/dashboard/layout.tsx
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { ReactNode } from "react";
 | 
				
			||||||
 | 
					import BaseLayout from "@/components/Layout/BaseLayout";
 | 
				
			||||||
 | 
					import { routing } from "@/i18n/routing";
 | 
				
			||||||
 | 
					import UnauthenticatedLayout from "@/components/Layout/UnauthenticatedLayout";
 | 
				
			||||||
 | 
					import AuthGuard from "@/components/guards/AuthGuard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
						children: ReactNode;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function RootLayout({ children }: Props) {
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<BaseLayout locale={routing.defaultLocale}>
 | 
				
			||||||
 | 
								<AuthGuard>
 | 
				
			||||||
 | 
									<UnauthenticatedLayout>{children}</UnauthenticatedLayout>
 | 
				
			||||||
 | 
								</AuthGuard>
 | 
				
			||||||
 | 
							</BaseLayout>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										97
									
								
								frontend-react/src/app/[locale]/dashboard/page.tsx
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										97
									
								
								frontend-react/src/app/[locale]/dashboard/page.tsx
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					import React, { FormEvent, useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Dashboard = () => {
 | 
				
			||||||
 | 
						const [newEntry, setNewEntry] = useState({ task: "", duration: "" });
 | 
				
			||||||
 | 
						const [entries, setEntries] = useState([
 | 
				
			||||||
 | 
							{ id: 1, task: "Meeting", duration: "1h 30m", date: "2024-12-30" },
 | 
				
			||||||
 | 
							{ id: 2, task: "Development", duration: "3h 45m", date: "2024-12-29" },
 | 
				
			||||||
 | 
							{ id: 3, task: "Code Review", duration: "2h", date: "2024-12-28" },
 | 
				
			||||||
 | 
						]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleInputChange = (e: FormEvent) => {
 | 
				
			||||||
 | 
							const { name, value } = e.target as HTMLInputElement;
 | 
				
			||||||
 | 
							setNewEntry({ ...newEntry, [name]: value });
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleAddEntry = () => {
 | 
				
			||||||
 | 
							if (!newEntry.task || !newEntry.duration) return;
 | 
				
			||||||
 | 
							const newId = entries.length ? entries[entries.length - 1].id + 1 : 1;
 | 
				
			||||||
 | 
							setEntries([
 | 
				
			||||||
 | 
								...entries,
 | 
				
			||||||
 | 
								{ id: newId, ...newEntry, date: new Date().toISOString().split("T")[0] },
 | 
				
			||||||
 | 
							]);
 | 
				
			||||||
 | 
							setNewEntry({ task: "", duration: "" });
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<main className="min-h-screen bg-gray-100 py-10 px-4">
 | 
				
			||||||
 | 
								<div className="max-w-4xl mx-auto bg-white rounded-lg shadow-lg p-6">
 | 
				
			||||||
 | 
									{/* Timer Section */}
 | 
				
			||||||
 | 
									<section className="mb-8">
 | 
				
			||||||
 | 
										<h2 className="text-2xl font-bold text-gray-800 mb-4">New Entry</h2>
 | 
				
			||||||
 | 
										<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
 | 
				
			||||||
 | 
											<input
 | 
				
			||||||
 | 
												type="text"
 | 
				
			||||||
 | 
												name="task"
 | 
				
			||||||
 | 
												value={newEntry.task}
 | 
				
			||||||
 | 
												onChange={handleInputChange}
 | 
				
			||||||
 | 
												placeholder="Task Name"
 | 
				
			||||||
 | 
												className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none"
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
											<input
 | 
				
			||||||
 | 
												type="text"
 | 
				
			||||||
 | 
												name="duration"
 | 
				
			||||||
 | 
												value={newEntry.duration}
 | 
				
			||||||
 | 
												onChange={handleInputChange}
 | 
				
			||||||
 | 
												placeholder="Duration (e.g., 1h 30m)"
 | 
				
			||||||
 | 
												className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none"
 | 
				
			||||||
 | 
											/>
 | 
				
			||||||
 | 
											<button
 | 
				
			||||||
 | 
												onClick={handleAddEntry}
 | 
				
			||||||
 | 
												className="w-full bg-indigo-600 text-white py-2 rounded-lg hover:bg-indigo-500 transition duration-200"
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
 | 
												Add Entry
 | 
				
			||||||
 | 
											</button>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									{/* Past Entries Section */}
 | 
				
			||||||
 | 
									<section>
 | 
				
			||||||
 | 
										<h2 className="text-2xl font-bold text-gray-800 mb-4">
 | 
				
			||||||
 | 
											Past Entries
 | 
				
			||||||
 | 
										</h2>
 | 
				
			||||||
 | 
										<div className="bg-gray-50 p-4 rounded-lg shadow-inner">
 | 
				
			||||||
 | 
											{entries.length
 | 
				
			||||||
 | 
												? (
 | 
				
			||||||
 | 
													<ul className="divide-y divide-gray-300">
 | 
				
			||||||
 | 
														{entries.map((entry) => (
 | 
				
			||||||
 | 
															<li
 | 
				
			||||||
 | 
																key={entry.id}
 | 
				
			||||||
 | 
																className="py-3 flex justify-between items-center"
 | 
				
			||||||
 | 
															>
 | 
				
			||||||
 | 
																<div>
 | 
				
			||||||
 | 
																	<p className="text-lg font-medium text-gray-700">
 | 
				
			||||||
 | 
																		{entry.task}
 | 
				
			||||||
 | 
																	</p>
 | 
				
			||||||
 | 
																	<p className="text-sm text-gray-500">
 | 
				
			||||||
 | 
																		{entry.date} - {entry.duration}
 | 
				
			||||||
 | 
																	</p>
 | 
				
			||||||
 | 
																</div>
 | 
				
			||||||
 | 
															</li>
 | 
				
			||||||
 | 
														))}
 | 
				
			||||||
 | 
													</ul>
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
												: (
 | 
				
			||||||
 | 
													<p className="text-gray-500">
 | 
				
			||||||
 | 
														No entries yet. Start adding some!
 | 
				
			||||||
 | 
													</p>
 | 
				
			||||||
 | 
												)}
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</section>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</main>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Dashboard;
 | 
				
			||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
import {ReactNode} from 'react';
 | 
					import { UserProvider } from "@/context/UserContext";
 | 
				
			||||||
 | 
					import { ReactNode } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
  children: ReactNode;
 | 
					  children: ReactNode;
 | 
				
			||||||
@ -7,5 +8,9 @@ type Props = {
 | 
				
			|||||||
// Since we have a `not-found.tsx` page on the root, a layout file
 | 
					// Since we have a `not-found.tsx` page on the root, a layout file
 | 
				
			||||||
// is required, even if it's just passing children through.
 | 
					// is required, even if it's just passing children through.
 | 
				
			||||||
export default function RootLayout({ children }: Props) {
 | 
					export default function RootLayout({ children }: Props) {
 | 
				
			||||||
  return children;
 | 
					  return (
 | 
				
			||||||
 | 
					    <UserProvider>
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					    </UserProvider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								frontend-react/src/components/Buttons/BackButton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								frontend-react/src/components/Buttons/BackButton.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					import { ChevronLeft } from "lucide-react";
 | 
				
			||||||
 | 
					import { useRouter } from "next/navigation";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const BackButton = () => {
 | 
				
			||||||
 | 
					  const router = useRouter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleBack = () => {
 | 
				
			||||||
 | 
					    if (window.history.length > 1) {
 | 
				
			||||||
 | 
					      router.back();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      router.push("/");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <button
 | 
				
			||||||
 | 
					      onClick={handleBack}
 | 
				
			||||||
 | 
					      className="text-blue-500 hover:underline focus:outline-none flex items-center"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <ChevronLeft className="inline-block w-4 h-4 mr-2" />
 | 
				
			||||||
 | 
					      Zurück
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default BackButton;
 | 
				
			||||||
							
								
								
									
										16
									
								
								frontend-react/src/components/Buttons/StartpageButton.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								frontend-react/src/components/Buttons/StartpageButton.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					import { HomeIcon } from "lucide-react";
 | 
				
			||||||
 | 
					import Link from "next/link";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StartpageButton = () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Link
 | 
				
			||||||
 | 
					      className="text-blue-500 hover:underline focus:outline-none flex items-center"
 | 
				
			||||||
 | 
					      href="/"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <HomeIcon className="inline-block w-4 h-4 mr-2" />
 | 
				
			||||||
 | 
					      Startseite
 | 
				
			||||||
 | 
					    </Link>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default StartpageButton;
 | 
				
			||||||
							
								
								
									
										23
									
								
								frontend-react/src/components/guards/AuthGuard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								frontend-react/src/components/guards/AuthGuard.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					import { useRouter } from "next/navigation";
 | 
				
			||||||
 | 
					import { useUser } from "@/context/UserContext";
 | 
				
			||||||
 | 
					import React, { useEffect } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AuthGuard = ({ children }: { children: React.ReactNode }) => {
 | 
				
			||||||
 | 
					  const router = useRouter();
 | 
				
			||||||
 | 
					  const { user } = useUser(); // Benutzer aus dem globalen Zustand abrufen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!user) {
 | 
				
			||||||
 | 
					      router.push("/login"); // Weiterleitung zur Login-Seite
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [user, router]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!user) {
 | 
				
			||||||
 | 
					    return <p>Lade...</p>; // Anzeige während des Ladens oder Weiterleitens
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return <>{children}</>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AuthGuard;
 | 
				
			||||||
							
								
								
									
										33
									
								
								frontend-react/src/context/UserContext.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								frontend-react/src/context/UserContext.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					import React, { createContext, ReactNode, useContext, useState } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type User = {
 | 
				
			||||||
 | 
					  id: string;
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  email: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UserContextType = {
 | 
				
			||||||
 | 
					  user: User | null;
 | 
				
			||||||
 | 
					  setUser: (user: User | null) => void;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const UserContext = createContext<UserContextType | undefined>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const UserProvider = ({ children }: { children: ReactNode }) => {
 | 
				
			||||||
 | 
					  const [user, setUser] = useState<User | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <UserContext.Provider value={{ user, setUser }}>
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					    </UserContext.Provider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useUser = (): UserContextType => {
 | 
				
			||||||
 | 
					  const context = useContext(UserContext);
 | 
				
			||||||
 | 
					  if (!context) {
 | 
				
			||||||
 | 
					    throw new Error("useUser must be used within a UserProvider");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return context;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user