+
My documents
{error &&
}
{!error && documents.length === 0 ? (
-
-
Keine eigenen Dokumente vorhanden
+
+
There are no documents.
) : (
-
- {documents.map((doc) => (
-
handleCardClick(doc.noteId)}
- >
-
-
{doc.title}
-
-
-
- Dokument-ID:
- {doc.noteId}
-
-
-
- Ansehen →
-
-
- ))}
-
+
)}
>
diff --git a/projekt/frontend/src/index.css b/projekt/frontend/src/index.css
index a461c50..fb88b19 100644
--- a/projekt/frontend/src/index.css
+++ b/projekt/frontend/src/index.css
@@ -1 +1,68 @@
-@import "tailwindcss";
\ No newline at end of file
+@import "tailwindcss";
+
+@theme {
+ --animate-loading: loading 1.4s ease-in-out infinite;
+
+ @keyframes loading {
+ 0% { transform: translateX(-100%); }
+ 100% { transform: translateX(400%); }
+ }
+
+}
+
+@layer components {
+ .page-container {
+ @apply max-w-4xl mx-auto px-4 md:px-2 py-8;
+ }
+ .auth-form-wrapper {
+ @apply max-w-xl mx-auto mt-8 px-4;
+ }
+
+ .form-group {
+ @apply mb-6;
+ }
+ .form-group label {
+ @apply block mb-2 font-semibold;
+ }
+ .form-group input {
+ @apply w-full px-3 py-3 text-base border border-gray-300 rounded-md;
+ }
+
+ .primary-button {
+ @apply w-full py-3 text-base bg-blue-500 text-white border-none rounded-md cursor-pointer font-semibold;
+ }
+ .primary-button.disabled {
+ @apply bg-gray-300 cursor-not-allowed;
+ }
+
+ .password-strength-bar {
+ @apply w-full h-3 rounded-full appearance-none mt-2 mb-1 bg-gray-200 overflow-hidden;
+ }
+
+ .strength-0::-webkit-progress-value {
+ background-color: #e5e7eb;
+ }
+ .strength-1::-webkit-progress-value {
+ background-color: #ef4444;
+ }
+ .strength-2::-webkit-progress-value {
+ background-color: #f97316;
+ }
+ .strength-3::-webkit-progress-value {
+ background-color: #eab308;
+ }
+ .strength-4::-webkit-progress-value {
+ background-color: #22c55e;
+ }
+
+ .password-strength-bar::-moz-progress-bar {
+ background-color: inherit;
+ }
+
+ .strength-0::-moz-progress-bar { background-color: #e5e7eb; }
+ .strength-1::-moz-progress-bar { background-color: #ef4444; }
+ .strength-2::-moz-progress-bar { background-color: #f97316; }
+ .strength-3::-moz-progress-bar { background-color: #eab308; }
+ .strength-4::-moz-progress-bar { background-color: #22c55e; }
+
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/styling/CreateDocuments.css b/projekt/frontend/src/styling/CreateDocuments.css
deleted file mode 100644
index 11f601e..0000000
--- a/projekt/frontend/src/styling/CreateDocuments.css
+++ /dev/null
@@ -1,62 +0,0 @@
-.create-document-container {
- max-width: 800px;
- margin: 2rem auto;
- padding: 0 1rem;
-}
-
-.create-document-form {
- width: 100%;
-}
-
-.form-group {
- margin-bottom: 1.5rem;
-}
-
-.form-label {
- display: block;
- margin-bottom: 0.5rem;
- font-weight: 600;
-}
-
-.form-input,
-.form-textarea {
- width: 100%;
- padding: 0.75rem;
- font-size: 1rem;
- border: 1px solid #ddd;
- border-radius: 6px;
-}
-
-.form-textarea {
- font-family: 'Courier New', monospace;
- resize: vertical;
-}
-
-.checkbox-label {
- display: inline-flex;
- align-items: center;
- gap: 0.5rem;
- cursor: pointer;
- font-weight: 600;
-}
-
-.checkbox-label input[type="checkbox"] {
- width: 16px;
- height: 16px;
- margin-right: 0.25rem;
-}
-
-
-.submit-button {
- padding: 0.75rem 1.5rem;
- background-color: #2196f3;
- color: white;
- border: none;
- border-radius: 6px;
- cursor: pointer;
-}
-
-.submit-button:disabled {
- background-color: #ccc;
- cursor: not-allowed;
-}
diff --git a/projekt/frontend/src/styling/Navbar.css b/projekt/frontend/src/styling/Navbar.css
deleted file mode 100644
index 3192a91..0000000
--- a/projekt/frontend/src/styling/Navbar.css
+++ /dev/null
@@ -1,75 +0,0 @@
-.navbar {
- background-color: #ffffff;
- border-bottom: 1px solid #e0e0e0;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
- position: sticky;
- top: 0;
- z-index: 1000;
-}
-
-.navbar-container {
- max-width: 1400px;
- margin: 0 auto;
- padding: 1rem 2rem;
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
-
-.navbar-brand a {
- font-size: 1.5rem;
- font-weight: 700;
- color: #1a1a1a;
- text-decoration: none;
- transition: color 0.2s ease;
-}
-
-.navbar-brand a:hover {
- color: #2196f3;
-}
-
-.navbar-links {
- display: flex;
- gap: 0.5rem;
- align-items: center;
-}
-
-.nav-link {
- padding: 0.5rem 1rem;
- color: #666;
- text-decoration: none;
- font-weight: 500;
- border-radius: 6px;
- transition: all 0.2s ease;
- border: 1px solid transparent;
-}
-
-.nav-link:hover {
- background-color: #f5f5f5;
- color: #1a1a1a;
-}
-
-/* ✅ React Router fügt automatisch .active Klasse hinzu */
-.nav-link.active {
- background-color: #e3f2fd;
- color: #2196f3;
- border-color: #2196f3;
-}
-
-.nav-link-primary {
- background-color: #2196f3;
- color: white !important;
- border-color: #2196f3;
-}
-
-.nav-link-primary:hover {
- background-color: #1976d2;
- border-color: #1976d2;
-}
-
-
-.nav-link-primary.active {
- background-color: #1565c0;
- border-color: #1565c0;
-}
-
diff --git a/projekt/frontend/src/styling/PublicDocumentsPage.css b/projekt/frontend/src/styling/PublicDocumentsPage.css
deleted file mode 100644
index 0577396..0000000
--- a/projekt/frontend/src/styling/PublicDocumentsPage.css
+++ /dev/null
@@ -1,160 +0,0 @@
-.public-documents-container {
- max-width: 1400px;
- margin: 0 auto;
- padding: 2rem 1rem;
-}
-
-.page-title {
- font-size: 2rem;
- font-weight: 700;
- color: #1a1a1a;
- margin-bottom: 2rem;
- text-align: center;
-}
-
-.loading,
-.error-message,
-.empty-state {
- text-align: center;
- padding: 3rem 1rem;
- font-size: 1.1rem;
- color: #666;
-}
-
-.error-message {
- color: #d32f2f;
- background-color: #ffebee;
- border-radius: 8px;
- padding: 1.5rem;
- margin: 2rem 0;
-}
-
-.empty-state {
- background-color: #f5f5f5;
- border-radius: 8px;
- padding: 3rem 1rem;
-}
-
-/* Grid Layout */
-.documents-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
- gap: 1.5rem;
- margin-top: 2rem;
-}
-
-/* Card Styles */
-.document-card {
- background: white;
- border: 1px solid #e0e0e0;
- border-radius: 12px;
- padding: 1.5rem;
- cursor: pointer;
- transition: all 0.3s ease;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
-}
-
-.document-card:hover {
- transform: translateY(-4px);
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
- border-color: #2196f3;
-}
-
-.card-header {
- margin-bottom: 1rem;
- padding-bottom: 1rem;
- border-bottom: 2px solid #f0f0f0;
-}
-
-.card-title {
- font-size: 1.25rem;
- font-weight: 600;
- color: #1a1a1a;
- margin: 0;
- line-height: 1.4;
- word-break: break-word;
-}
-
-.card-body {
- display: flex;
- flex-direction: column;
- gap: 0.75rem;
- margin-bottom: 1rem;
-}
-
-.card-info {
- display: flex;
- flex-direction: column;
- gap: 0.25rem;
-}
-
-.info-label {
- font-size: 0.75rem;
- font-weight: 600;
- text-transform: uppercase;
- color: #757575;
- letter-spacing: 0.5px;
-}
-
-.info-value {
- font-size: 0.9rem;
- color: #424242;
- word-break: break-all;
- font-family: 'Courier New', monospace;
-}
-
-.card-footer {
- margin-top: 1rem;
- padding-top: 1rem;
- border-top: 1px solid #f0f0f0;
-}
-
-.view-link {
- color: #2196f3;
- font-weight: 600;
- font-size: 0.9rem;
- transition: color 0.2s ease;
-}
-
-.document-card:hover .view-link {
- color: #1976d2;
-}
-
-/* Responsive Design */
-@media (max-width: 768px) {
- .public-documents-container {
- padding: 1rem 0.5rem;
- }
-
- .page-title {
- font-size: 1.5rem;
- margin-bottom: 1.5rem;
- }
-
- .documents-grid {
- grid-template-columns: 1fr;
- gap: 1rem;
- }
-
- .document-card {
- padding: 1.25rem;
- }
-
- .card-title {
- font-size: 1.1rem;
- }
-}
-
-@media (max-width: 480px) {
- .page-title {
- font-size: 1.25rem;
- }
-
- .document-card {
- padding: 1rem;
- }
-
- .info-value {
- font-size: 0.85rem;
- }
-}
\ No newline at end of file
diff --git a/projekt/frontend/src/styling/RegisterPage.css b/projekt/frontend/src/styling/RegisterPage.css
deleted file mode 100644
index 04385c6..0000000
--- a/projekt/frontend/src/styling/RegisterPage.css
+++ /dev/null
@@ -1,117 +0,0 @@
-/* Wrapper */
-.register-wrapper {
- max-width: 500px;
- margin: 2rem auto;
- padding: 0 1rem;
-}
-
-/* Form */
-.form-group {
- margin-bottom: 1.5rem;
-}
-
-.form-group label {
- display: block;
- margin-bottom: 0.5rem;
- font-weight: 600;
-}
-
-.form-group input {
- width: 100%;
- padding: 0.75rem;
- font-size: 1rem;
- border: 1px solid #ddd;
- border-radius: 6px;
-}
-
-/* Buttons */
-.primary-button {
- width: 100%;
- padding: 0.75rem;
- font-size: 1rem;
- background-color: #2196f3;
- color: white;
- border: none;
- border-radius: 6px;
- cursor: pointer;
- font-weight: 600;
-}
-
-.primary-button.disabled {
- background-color: #ccc;
- cursor: not-allowed;
-}
-
-/* Login Hint */
-.login-hint {
- text-align: center;
- margin-top: 1.5rem;
- color: #666;
-}
-
-.login-hint span {
- color: #2196f3;
- cursor: pointer;
- text-decoration: underline;
-}
-
-/* Success */
-.register-success-wrapper {
- max-width: 600px;
- margin: 3rem auto;
- padding: 2rem;
- text-align: center;
-}
-
-.register-success-box {
- background-color: #e8f5e9;
- border: 2px solid #4caf50;
- border-radius: 12px;
- padding: 2rem;
- margin-bottom: 2rem;
-}
-
-.register-success-box h1 {
- color: #2e7d32;
- margin-bottom: 1rem;
-}
-
-.register-success-box p {
- font-size: 1.1rem;
- color: #333;
- line-height: 1.6;
-}
-
-.register-success-box .hint {
- font-size: 1rem;
- color: #666;
- margin-top: 1rem;
-}
-
-/* Passwort-Progressbar */
-.password-strength-bar {
- width: 100%;
- height: 12px;
- border-radius: 6px;
- appearance: none;
- margin-top: 0.5rem;
- margin-bottom: 0.25rem;
- background-color: #eee;
- overflow: hidden;
-}
-
-.password-feedback {
- background: #ececec;
- margin-top: 0.25rem;
- font-size: 0.875rem;
-}
-
-.password-feedback .warning {
- color: #e53935;
- font-weight: 600;
-}
-
-.password-feedback .suggestion {
- color: #dca721;
- margin-left: 0.5rem;
-}
diff --git a/projekt/frontend/src/styling/Searchbar.css b/projekt/frontend/src/styling/Searchbar.css
deleted file mode 100644
index 0e03079..0000000
--- a/projekt/frontend/src/styling/Searchbar.css
+++ /dev/null
@@ -1,65 +0,0 @@
-/* SearchBar Container */
-.search-bar {
- max-width: 900px;
- margin: 1.5rem auto 2rem auto;
- padding: 1rem 1.25rem;
- display: flex;
- gap: 0.75rem;
- align-items: center;
-
- background-color: #ffffff;
- border: 1px solid #e0e0e0;
- border-radius: 12px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
-}
-
-/* Input */
-.search-input {
- flex: 1;
- padding: 0.65rem 0.85rem;
- font-size: 1rem;
- color: #1a1a1a;
-
- border: 1px solid #ccc;
- border-radius: 8px;
- outline: none;
-
- transition: border-color 0.2s ease, box-shadow 0.2s ease;
-}
-
-.search-input::placeholder {
- color: #999;
-}
-
-.search-input:focus {
- border-color: #2196f3;
- box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.15);
-}
-
-/* Button */
-.search-button {
- padding: 0.65rem 1.25rem;
- font-size: 0.95rem;
- font-weight: 600;
-
- background-color: #2196f3;
- color: white;
- border: none;
- border-radius: 8px;
-
- cursor: pointer;
- transition: background-color 0.2s ease, transform 0.1s ease;
-}
-
-.search-button:hover {
- background-color: #1976d2;
-}
-
-.search-button:active {
- transform: translateY(1px);
-}
-
-.search-button:disabled {
- background-color: #b0bec5;
- cursor: not-allowed;
-}
diff --git a/projekt/frontend/src/tests/Navbar.test.tsx b/projekt/frontend/src/tests/Navbar.test.tsx
index 8645e65..d018a1f 100644
--- a/projekt/frontend/src/tests/Navbar.test.tsx
+++ b/projekt/frontend/src/tests/Navbar.test.tsx
@@ -4,12 +4,11 @@ import Navbar from "../components/Navbar.tsx";
import { vi, describe, it, expect } from "vitest";
import { useAuth } from "../utils/useAuth.ts";
-// useAuth mocken
vi.mock("../utils/useAuth");
const mockedUseAuth = vi.mocked(useAuth);
describe("Navbar", () => {
- it("zeigt Benutzer-Navigation wenn eingeloggt", () => {
+ it("shows User Navigation when authenticated", () => {
mockedUseAuth.mockReturnValue({
isAuthenticated: true,
token: "some-token",
@@ -17,21 +16,19 @@ describe("Navbar", () => {
logout: vi.fn(),
refreshAccessToken: vi.fn(),
});
-
render(
);
-
- expect(screen.getByText("Erstellen")).toBeInTheDocument();
- expect(screen.getByText("Meine Dokumente")).toBeInTheDocument();
- expect(screen.getByText("Profil")).toBeInTheDocument();
+ expect(screen.getByText("Create")).toBeInTheDocument();
+ expect(screen.getByText("My Documents")).toBeInTheDocument();
+ expect(screen.getByText("Profile")).toBeInTheDocument();
expect(screen.queryByText("Login")).not.toBeInTheDocument();
- expect(screen.queryByText("Registrieren")).not.toBeInTheDocument();
+ expect(screen.queryByText("Register")).not.toBeInTheDocument();
});
- it("zeigt Login & Registrieren wenn NICHT eingeloggt", () => {
+ it("shows Login and Registration when NOT authenticated", () => {
mockedUseAuth.mockReturnValue({
isAuthenticated: false,
token: null,
@@ -39,16 +36,14 @@ describe("Navbar", () => {
logout: vi.fn(),
refreshAccessToken: vi.fn(),
});
-
render(
);
-
expect(screen.getByText("Login")).toBeInTheDocument();
- expect(screen.getByText("Registrieren")).toBeInTheDocument();
- expect(screen.queryByText("Erstellen")).not.toBeInTheDocument();
- expect(screen.queryByText("Meine Dokumente")).not.toBeInTheDocument();
+ expect(screen.getByText("Register")).toBeInTheDocument();
+ expect(screen.queryByText("Create")).not.toBeInTheDocument();
+ expect(screen.queryByText("My Documents")).not.toBeInTheDocument();
});
-});
+});
\ No newline at end of file
diff --git a/projekt/frontend/src/tests/SearchBar.test.tsx b/projekt/frontend/src/tests/SearchBar.test.tsx
deleted file mode 100644
index 1d20585..0000000
--- a/projekt/frontend/src/tests/SearchBar.test.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-// @vitest-environment jsdom
-import { describe, it, expect, vi } from "vitest";
-import { render, screen } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import SearchBar from "../components/Searchbar.tsx";
-import "@testing-library/jest-dom";
-
-describe("SearchBar", () => {
- it("rendert Input und Button", () => {
- render(
-
{}} onSubmit={() => {}} />
- );
-
- expect(screen.getByPlaceholderText("Eingabe...")).toBeInTheDocument();
- expect(screen.getByRole("button", { name: /suchen/i })).toBeInTheDocument();
- });
-
- it("ruft onChange auf, wenn getippt wird", async () => {
- const user = userEvent.setup();
- const onChange = vi.fn();
-
- render(
- {}} />
- );
-
- const input = screen.getByPlaceholderText("Eingabe...");
- await user.type(input, "test");
-
- // wird für jedes Zeichen aufgerufen
- expect(onChange).toHaveBeenCalled();
- });
-
- it("ruft onSubmit auf bei gültiger Eingabe", async () => {
- const user = userEvent.setup();
- const onSubmit = vi.fn();
-
- render(
- {}} onSubmit={onSubmit} />
- );
-
- const button = screen.getByRole("button", { name: /suchen/i });
- await user.click(button);
-
- expect(onSubmit).toHaveBeenCalledTimes(1);
- });
-
-});
diff --git a/projekt/frontend/src/types/AuthContextType.ts b/projekt/frontend/src/types/AuthContextType.ts
new file mode 100644
index 0000000..80d0933
--- /dev/null
+++ b/projekt/frontend/src/types/AuthContextType.ts
@@ -0,0 +1,8 @@
+// Typen definieren
+export interface AuthContextType {
+ token: string | null;
+ login: (newToken: string) => void;
+ logout: () => void;
+ refreshAccessToken: () => Promise;
+ isAuthenticated: boolean;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/DocumentInput.ts b/projekt/frontend/src/types/DocumentInput.ts
new file mode 100644
index 0000000..2b9f92c
--- /dev/null
+++ b/projekt/frontend/src/types/DocumentInput.ts
@@ -0,0 +1,5 @@
+export interface DocumentInput {
+ title: string;
+ mdContent: string;
+ isPrivate: boolean;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/DocumentType.ts b/projekt/frontend/src/types/DocumentType.ts
new file mode 100644
index 0000000..0753321
--- /dev/null
+++ b/projekt/frontend/src/types/DocumentType.ts
@@ -0,0 +1,8 @@
+import type {DocumentInput} from "./DocumentInput.ts";
+
+export interface DocumentType extends DocumentInput {
+ noteId: string;
+ userId: string;
+ created_at: string;
+ updated_at: string;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ErrorMessageProps.ts b/projekt/frontend/src/types/ErrorMessageProps.ts
new file mode 100644
index 0000000..8b993b2
--- /dev/null
+++ b/projekt/frontend/src/types/ErrorMessageProps.ts
@@ -0,0 +1,14 @@
+/**
+ * Interface for error message properties.
+ */
+export interface ErrorMessageProps {
+ /** The error message to display. If empty, nothing will be rendered. */
+ message?: string;
+ /**
+ * Visual style variant.
+ * - `'field'`: inline below form fields
+ * - `'general'`: prominent block with warning icon
+ * @default 'field'
+ */
+ type?: 'field' | 'general';
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ErrorType.ts b/projekt/frontend/src/types/ErrorType.ts
deleted file mode 100644
index cd2c589..0000000
--- a/projekt/frontend/src/types/ErrorType.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * ErrorType defines the structure of an error object
- * It is oriented on the structure of the ProblemDetail object defined in the backend
- */
-export type ErrorType = {
- status: number; // HTTP status code
- title: string; // Short, human-readable summary of the problem
- detail?: string; // Detailed description of the problem (optional)
- path? : string; // The request path that caused the error (optional)
-}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/FormErrorType.ts b/projekt/frontend/src/types/FormErrorType.ts
index b0ce5db..9d14973 100644
--- a/projekt/frontend/src/types/FormErrorType.ts
+++ b/projekt/frontend/src/types/FormErrorType.ts
@@ -1,6 +1,6 @@
-import type {ErrorType} from "./ErrorType.ts";
+import type {ApiErrorType} from "./ProblemDetail/ApiErrorType.ts";
export type FormErrorType = {
- api?: Partial
+ api?: ApiErrorType
general?: string;
}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/OverviewPageProps.ts b/projekt/frontend/src/types/OverviewPageProps.ts
new file mode 100644
index 0000000..1ed6cc8
--- /dev/null
+++ b/projekt/frontend/src/types/OverviewPageProps.ts
@@ -0,0 +1,6 @@
+import type {PublicDocument} from "./PublicDocument.ts";
+
+export interface OverviewPageProps {
+ documents: PublicDocument[];
+ onCardClick: (noteId: string) => void;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ProblemDetail/ApiErrorType.ts b/projekt/frontend/src/types/ProblemDetail/ApiErrorType.ts
new file mode 100644
index 0000000..3954ec2
--- /dev/null
+++ b/projekt/frontend/src/types/ProblemDetail/ApiErrorType.ts
@@ -0,0 +1,7 @@
+import type {ValidationError} from "./ValidationError.ts";
+import type {DetailError} from "./DetailError.ts";
+
+/**
+ * The ApiErrorType is a union type that represents either a ValidationError or a DetailError.
+ */
+export type ApiErrorType = ValidationError | DetailError;
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ProblemDetail/DetailError.ts b/projekt/frontend/src/types/ProblemDetail/DetailError.ts
new file mode 100644
index 0000000..db8f0a7
--- /dev/null
+++ b/projekt/frontend/src/types/ProblemDetail/DetailError.ts
@@ -0,0 +1,10 @@
+import type {ProblemDetailBaseType} from "./ProblemDetailBaseType.ts";
+
+/**
+ * The DetailError interface represents a specific type of problem detail response that includes a detailed error message.
+ * It extends the ProblemDetailBaseType interface, inheriting its common fields (instance, status, title) and adding a new field called detail,
+ * which contains a string describing the specific error in more detail.
+ */
+export interface DetailError extends ProblemDetailBaseType {
+ detail: string;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ProblemDetail/IsErrorTypeGuards.ts b/projekt/frontend/src/types/ProblemDetail/IsErrorTypeGuards.ts
new file mode 100644
index 0000000..f1e79f3
--- /dev/null
+++ b/projekt/frontend/src/types/ProblemDetail/IsErrorTypeGuards.ts
@@ -0,0 +1,11 @@
+import type {ValidationError} from "./ValidationError.ts";
+import type {ApiErrorType} from "./ApiErrorType.ts";
+import type {DetailError} from "./DetailError.ts";
+
+export function isValidationError(error: ApiErrorType): error is ValidationError {
+ return "errors" in error && Array.isArray(error.errors);
+}
+
+export function isDetailError(error: ApiErrorType): error is DetailError {
+ return "detail" in error;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ProblemDetail/ProblemDetailBaseType.ts b/projekt/frontend/src/types/ProblemDetail/ProblemDetailBaseType.ts
new file mode 100644
index 0000000..576a595
--- /dev/null
+++ b/projekt/frontend/src/types/ProblemDetail/ProblemDetailBaseType.ts
@@ -0,0 +1,10 @@
+/**
+ * The ProblemDetailBaseType interface represents the base structure of a problem detail response
+ * after the RFC 7807 standard. It includes the common fields that are present in all problem detail responses, such as instance, status, and title.
+ * This interface can be extended by other specific problem detail types, such as DetailError and ValidationError, to include additional fields relevant to those specific error cases.
+ */
+export interface ProblemDetailBaseType {
+ instance: string;
+ status: number;
+ title: string;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ProblemDetail/ValidationError.ts b/projekt/frontend/src/types/ProblemDetail/ValidationError.ts
new file mode 100644
index 0000000..4d2bfe7
--- /dev/null
+++ b/projekt/frontend/src/types/ProblemDetail/ValidationError.ts
@@ -0,0 +1,12 @@
+import type {ValidationFieldError} from "./ValidationFieldError.ts";
+import type {ProblemDetailBaseType} from "./ProblemDetailBaseType.ts";
+
+/**
+ * The ValidationError interface represents a specific type of problem detail response that includes validation errors for specific fields.
+ * It extends the ProblemDetailBaseType interface, inheriting its common fields (instance, status, title) and adding a new field called errors,
+ * which is an array of ValidationFieldError objects. Each ValidationFieldError object contains information about a specific field that failed validation,
+ * including the field name and the corresponding error message.
+ */
+export interface ValidationError extends ProblemDetailBaseType {
+ errors: ValidationFieldError[];
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/ProblemDetail/ValidationFieldError.ts b/projekt/frontend/src/types/ProblemDetail/ValidationFieldError.ts
new file mode 100644
index 0000000..c9a5bdc
--- /dev/null
+++ b/projekt/frontend/src/types/ProblemDetail/ValidationFieldError.ts
@@ -0,0 +1,7 @@
+/**
+ * Represents a validation error for a specific field in a problem detail response.
+ */
+export interface ValidationFieldError {
+ field: string;
+ message: string;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/types/PublicDocument.ts b/projekt/frontend/src/types/PublicDocument.ts
new file mode 100644
index 0000000..17b3b25
--- /dev/null
+++ b/projekt/frontend/src/types/PublicDocument.ts
@@ -0,0 +1,5 @@
+export interface PublicDocument {
+ noteId: string;
+ title: string;
+ userId: string;
+}
\ No newline at end of file
diff --git a/projekt/frontend/src/utils/apiFetch.ts b/projekt/frontend/src/utils/apiFetch.ts
index dbd0f6f..90e5f9f 100644
--- a/projekt/frontend/src/utils/apiFetch.ts
+++ b/projekt/frontend/src/utils/apiFetch.ts
@@ -1,5 +1,6 @@
import { getCookie } from "./cookies";
-import type {AuthContextType} from "../components/AuthContext.tsx";
+import type {AuthContextType} from "../types/AuthContextType.ts";
+
const CSRF_HEADER = "X-XSRF-TOKEN";
const CSRF_COOKIE = "XSRF-TOKEN";
@@ -41,7 +42,7 @@ export async function apiFetch(
// Access Token refreshen
const newToken = await auth.refreshAccessToken();
if (!newToken) {
- throw new Error("Sie müssen angemeldet sein, um diese Aktion durchzuführen.");
+ throw new Error("You have to be authenticated to perform this action. Please log in.");
}
// Retry mit neuem Token + CSRF
diff --git a/projekt/frontend/src/utils/useAuth.ts b/projekt/frontend/src/utils/useAuth.ts
index b73563d..c4e8637 100644
--- a/projekt/frontend/src/utils/useAuth.ts
+++ b/projekt/frontend/src/utils/useAuth.ts
@@ -1,6 +1,7 @@
// hooks/useAuth.ts
import { useContext } from 'react';
-import {AuthContext, type AuthContextType} from '../components/AuthContext.tsx';
+import {AuthContext} from '../components/AuthContext.tsx';
+import type {AuthContextType} from "../types/AuthContextType.ts";
export function useAuth(): AuthContextType {
const context = useContext(AuthContext);
diff --git a/projekt/frontend/vite.config.ts b/projekt/frontend/vite.config.ts
index 0eebf60..a15338e 100644
--- a/projekt/frontend/vite.config.ts
+++ b/projekt/frontend/vite.config.ts
@@ -5,6 +5,19 @@ import tailwindcss from "@tailwindcss/vite";
// https://vite.dev/config/
export default defineConfig({
+ server: {
+ host: '0.0.0.0',
+ port: 8080,
+ watch : {
+ usePolling: true
+ },
+ proxy: {
+ '/api': {
+ target: 'http://backend:8080',
+ changeOrigin: true
+ }
+ }
+ },
plugins: [react(), tailwindcss()],
resolve: {
alias: {