Compare commits

...

26 commits

Author SHA1 Message Date
40b14747fc
docs: Update project documentation and titles
All checks were successful
CI / Get Changed Files (pull_request) Successful in 11s
CI / eslint (pull_request) Has been skipped
CI / Backend Tests (pull_request) Has been skipped
Label PRs based on size / Check PR size (pull_request) Successful in 11s
CI / oxlint (pull_request) Has been skipped
CI / Checkstyle Main (pull_request) Has been skipped
CI / prettier (pull_request) Has been skipped
CI / Docker frontend validation (pull_request) Has been skipped
CI / test-build (pull_request) Has been skipped
Pull Request Labeler / labeler (pull_request_target) Successful in 14s
CI / Docker backend validation (pull_request) Has been skipped
Claude PR Review / claude-code (pull_request) Successful in 26s
2025-06-05 10:02:46 +02:00
37f0ec4ae2
docs: update diagrams formatting in project documentation 2025-06-05 10:02:46 +02:00
64cd36536e
Merge branch 'main' into add-project-documentation
All checks were successful
CI / Get Changed Files (pull_request) Successful in 8s
CI / eslint (pull_request) Has been skipped
CI / oxlint (pull_request) Has been skipped
Pull Request Labeler / labeler (pull_request_target) Successful in 4s
CI / Backend Tests (pull_request) Has been skipped
CI / prettier (pull_request) Has been skipped
Label PRs based on size / Check PR size (pull_request) Successful in 13s
CI / Checkstyle Main (pull_request) Has been skipped
CI / test-build (pull_request) Has been skipped
CI / Docker frontend validation (pull_request) Has been skipped
CI / Docker backend validation (pull_request) Has been skipped
Claude PR Review / claude-code (pull_request) Successful in 25s
2025-06-04 12:43:43 +00:00
a296ae147b
Merge pull request 'feat: adjust landing page styles' (!290) from restyle-landing-page into main
Some checks failed
Build docs / build-docs (push) Failing after 16s
Release / Release (push) Successful in 1m18s
Release / Build Backend Image (push) Successful in 31s
Release / Build Frontend Image (push) Successful in 35s
Reviewed-on: #290
Reviewed-by: Jan K9f <jan@kjan.email>
2025-06-04 12:41:31 +00:00
be9dcc41a8
docs: Update project documentation with architecture details
All checks were successful
CI / Get Changed Files (pull_request) Successful in 11s
CI / eslint (pull_request) Has been skipped
CI / oxlint (pull_request) Has been skipped
CI / prettier (pull_request) Has been skipped
CI / test-build (pull_request) Has been skipped
Claude PR Review / claude-code (pull_request) Successful in 26s
Pull Request Labeler / labeler (pull_request_target) Successful in 6s
CI / Backend Tests (pull_request) Has been skipped
Label PRs based on size / Check PR size (pull_request) Successful in 12s
CI / Checkstyle Main (pull_request) Has been skipped
CI / Docker frontend validation (pull_request) Has been skipped
CI / Docker backend validation (pull_request) Has been skipped
2025-06-04 14:39:40 +02:00
Phan Huy Tran
ed83097b6b feat: open login form when accessing restricted links
All checks were successful
CI / Get Changed Files (pull_request) Successful in 10s
Label PRs based on size / Check PR size (pull_request) Successful in 19s
Claude PR Review / claude-code (pull_request) Successful in 35s
CI / Backend Tests (pull_request) Has been skipped
CI / Checkstyle Main (pull_request) Has been skipped
Pull Request Labeler / labeler (pull_request_target) Successful in 14s
CI / Docker backend validation (pull_request) Has been skipped
CI / oxlint (pull_request) Successful in 44s
CI / prettier (pull_request) Successful in 52s
CI / eslint (pull_request) Successful in 1m6s
CI / test-build (pull_request) Successful in 1m31s
CI / Docker frontend validation (pull_request) Successful in 1m15s
2025-06-04 14:27:11 +02:00
Phan Huy Tran
68306f3893 feat: adjust register and login buttons 2025-06-04 13:35:43 +02:00
20076d8fe0
Merge pull request 'chore(deps): update https://git.kjan.de/actions/upload-artifact action to v4' (!289) from renovate/major-github-artifact-actions into main
Some checks failed
Build docs / build-docs (push) Failing after 30s
Reviewed-on: #289
Reviewed-by: Jan-Marlon Leibl <jleibl@proton.me>
2025-06-04 11:08:23 +00:00
0c2a21c218
Merge pull request 'chore(deps): update actions/checkout action to v4' (!288) from renovate/actions-checkout-4.x into main
Some checks failed
Build docs / build-docs (push) Has been cancelled
Reviewed-on: #288
Reviewed-by: Jan-Marlon Leibl <jleibl@proton.me>
2025-06-04 11:07:40 +00:00
Phan Huy Tran
65471d1666 feat: adjust game grid slider 2025-06-04 13:04:45 +02:00
801c60b793
chore(deps): update https://git.kjan.de/actions/upload-artifact action to v4
All checks were successful
CI / Get Changed Files (pull_request) Successful in 13s
Pull Request Labeler / labeler (pull_request_target) Successful in 10s
Label PRs based on size / Check PR size (pull_request) Successful in 22s
Claude PR Review / claude-code (pull_request) Successful in 36s
CI / oxlint (pull_request) Successful in 35s
CI / eslint (pull_request) Successful in 56s
CI / prettier (pull_request) Successful in 47s
CI / Docker frontend validation (pull_request) Successful in 18s
CI / test-build (pull_request) Successful in 1m24s
CI / Docker backend validation (pull_request) Successful in 20s
CI / Checkstyle Main (pull_request) Successful in 1m55s
CI / Backend Tests (pull_request) Successful in 4m25s
2025-06-04 11:02:47 +00:00
feb40e3f79
chore(deps): update actions/checkout action to v4
All checks were successful
CI / Get Changed Files (pull_request) Successful in 11s
Label PRs based on size / Check PR size (pull_request) Successful in 11s
Pull Request Labeler / labeler (pull_request_target) Successful in 5s
Claude PR Review / claude-code (pull_request) Successful in 24s
CI / oxlint (pull_request) Successful in 20s
CI / eslint (pull_request) Successful in 43s
CI / prettier (pull_request) Successful in 47s
CI / Docker frontend validation (pull_request) Successful in 29s
CI / Docker backend validation (pull_request) Successful in 36s
CI / test-build (pull_request) Successful in 1m40s
CI / Checkstyle Main (pull_request) Successful in 2m5s
CI / Backend Tests (pull_request) Successful in 4m22s
2025-06-04 11:02:44 +00:00
761a527ef0
Merge pull request 'docs: add project documentation for Casino Gaming Platform' (!285) from add-project-documentation into main
All checks were successful
Build docs / build-docs (push) Successful in 17s
Reviewed-on: #285
Reviewed-by: Jan K9f <jan@kjan.email>
2025-06-04 10:48:00 +00:00
e02f7357f0
Merge pull request 'feat: adjust spacings in the navbar' (!287) from noonereadsthisanywayitodesntreallymatter into main
All checks were successful
Release / Release (push) Successful in 1m22s
Release / Build Backend Image (push) Successful in 26s
Release / Build Frontend Image (push) Successful in 32s
Reviewed-on: #287
Reviewed-by: Jan K9f <jan@kjan.email>
2025-06-04 10:41:54 +00:00
Phan Huy Tran
db93ec790e feat: adjust spacings in the navbar
All checks were successful
CI / Get Changed Files (pull_request) Successful in 8s
CI / Backend Tests (pull_request) Has been skipped
Pull Request Labeler / labeler (pull_request_target) Successful in 7s
CI / Checkstyle Main (pull_request) Has been skipped
Label PRs based on size / Check PR size (pull_request) Successful in 11s
CI / Docker backend validation (pull_request) Has been skipped
Claude PR Review / claude-code (pull_request) Successful in 27s
CI / oxlint (pull_request) Successful in 23s
CI / eslint (pull_request) Successful in 32s
CI / prettier (pull_request) Successful in 31s
CI / Docker frontend validation (pull_request) Successful in 43s
CI / test-build (pull_request) Successful in 45s
2025-06-04 12:39:34 +02:00
4644e5cecc
Merge pull request 'does it even matter?' (!286) from number-omit into main
All checks were successful
Release / Release (push) Successful in 1m2s
Release / Build Backend Image (push) Successful in 38s
Release / Build Frontend Image (push) Successful in 47s
Reviewed-on: #286
Reviewed-by: Jan K9f <jan@kjan.email>
2025-06-04 10:35:09 +00:00
Phan Huy Tran
e35a30a606 refactor: remove unnecessary code
All checks were successful
CI / Get Changed Files (pull_request) Successful in 21s
Label PRs based on size / Check PR size (pull_request) Successful in 22s
Claude PR Review / claude-code (pull_request) Successful in 36s
CI / Backend Tests (pull_request) Has been skipped
CI / Checkstyle Main (pull_request) Has been skipped
Pull Request Labeler / labeler (pull_request_target) Successful in 10s
CI / Docker backend validation (pull_request) Has been skipped
CI / oxlint (pull_request) Successful in 38s
CI / eslint (pull_request) Successful in 58s
CI / prettier (pull_request) Successful in 47s
CI / Docker frontend validation (pull_request) Successful in 58s
CI / test-build (pull_request) Successful in 57s
2025-06-04 12:32:44 +02:00
Phan Huy Tran
81ec7122ea fix: display real number 2025-06-04 12:31:05 +02:00
17774fcdb2 chore: Add pipeline
All checks were successful
CI / Get Changed Files (pull_request) Successful in 10s
Pull Request Labeler / labeler (pull_request_target) Successful in 6s
Label PRs based on size / Check PR size (pull_request) Successful in 13s
Claude PR Review / claude-code (pull_request) Successful in 33s
CI / oxlint (pull_request) Successful in 32s
CI / eslint (pull_request) Successful in 47s
CI / prettier (pull_request) Successful in 48s
CI / Docker frontend validation (pull_request) Successful in 29s
CI / Docker backend validation (pull_request) Successful in 17s
CI / test-build (pull_request) Successful in 1m21s
CI / Checkstyle Main (pull_request) Successful in 1m32s
CI / Backend Tests (pull_request) Successful in 2m18s
2025-06-04 12:28:12 +02:00
43e321c0d6
Merge pull request 'feat: restyle navbar' (!284) from homepage into main
All checks were successful
Release / Release (push) Successful in 1m6s
Release / Build Backend Image (push) Successful in 28s
Release / Build Frontend Image (push) Successful in 34s
Reviewed-on: #284
Reviewed-by: Jan K9f <jan@kjan.email>
2025-06-04 10:23:17 +00:00
Phan Huy Tran
23710a0553 style: fix pipelines
All checks were successful
CI / Get Changed Files (pull_request) Successful in 15s
Label PRs based on size / Check PR size (pull_request) Successful in 13s
Pull Request Labeler / labeler (pull_request_target) Successful in 11s
Claude PR Review / claude-code (pull_request) Successful in 36s
CI / Backend Tests (pull_request) Has been skipped
CI / Checkstyle Main (pull_request) Has been skipped
CI / oxlint (pull_request) Successful in 30s
CI / eslint (pull_request) Successful in 37s
CI / prettier (pull_request) Successful in 36s
CI / Docker backend validation (pull_request) Has been skipped
CI / test-build (pull_request) Successful in 51s
CI / Docker frontend validation (pull_request) Successful in 42s
2025-06-04 12:15:24 +02:00
Phan Huy Tran
4331484d48 feat: adjust icons
Some checks failed
CI / Get Changed Files (pull_request) Successful in 14s
CI / oxlint (pull_request) Successful in 33s
Claude PR Review / claude-code (pull_request) Successful in 41s
Label PRs based on size / Check PR size (pull_request) Successful in 18s
CI / prettier (pull_request) Failing after 45s
Pull Request Labeler / labeler (pull_request_target) Successful in 10s
CI / eslint (pull_request) Successful in 54s
CI / test-build (pull_request) Successful in 1m11s
CI / Backend Tests (pull_request) Has been skipped
CI / Checkstyle Main (pull_request) Has been skipped
CI / Docker backend validation (pull_request) Has been skipped
CI / Docker frontend validation (pull_request) Successful in 50s
2025-06-04 12:15:07 +02:00
Phan Huy Tran
1e77beb7b6 style: fix pipelines
All checks were successful
CI / Get Changed Files (pull_request) Successful in 17s
Pull Request Labeler / labeler (pull_request_target) Successful in 6s
Label PRs based on size / Check PR size (pull_request) Successful in 19s
CI / oxlint (pull_request) Successful in 26s
CI / eslint (pull_request) Successful in 36s
CI / prettier (pull_request) Successful in 35s
Claude PR Review / claude-code (pull_request) Successful in 1m13s
CI / test-build (pull_request) Successful in 37s
CI / Backend Tests (pull_request) Has been skipped
CI / Checkstyle Main (pull_request) Has been skipped
CI / Docker backend validation (pull_request) Has been skipped
CI / Docker frontend validation (pull_request) Successful in 24s
2025-06-04 11:53:41 +02:00
Phan Huy Tran
d4fd5e068d feat: restyle navbar 2025-06-04 11:53:41 +02:00
Phan Huy Tran
06f2ae5d86 feat: improve game grid 2025-06-04 11:53:41 +02:00
Phan Huy Tran
b2f80dedf2 feat: adjust game grid 2025-06-04 11:53:41 +02:00
14 changed files with 646 additions and 220 deletions

28
.gitea/workflows/docs.yml Normal file
View file

@ -0,0 +1,28 @@
name: Build docs
on:
push:
branches: [main]
jobs:
build-docs:
runs-on: ubuntu-latest
container:
image: git.kjan.de/actions/runner-latex:latest
env:
# Edit here with the names of your latex file and directory (can use ".")
DIR: docs
FILE: projektdokumentation.tex
steps:
- name: Checkout
uses: actions/checkout@v4
- name: LaTeX compile
working-directory: ${{ env.DIR }}
run: latexmk -pdf -xelatex ${{ env.FILE }}
- name: Upload artifacts
uses: https://git.kjan.de/actions/upload-artifact@v4
with:
name: Doku
path: docs/projektdokumentation.pdf

View file

@ -19,6 +19,9 @@
\usepackage{enumitem}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, positioning}
\usepackage{pdflscape}
\usepackage{afterpage}
\usepackage{needspace}
% Seitenränder
\geometry{
@ -77,9 +80,9 @@
\centering
\vspace*{2cm}
{\Huge\bfseries Casino Gaming Platform\par}
{\Huge\bfseries Trustworthy Casino\par}
\vspace{1.5cm}
{\Large\itshape Projektdokumentation für die IHK-Abschlussprüfung\par}
{\Large\itshape Projektdokumentation für das Mittelstufenprojekt\par}
\vspace{2cm}
{\large\bfseries Fachinformatiker für Anwendungsentwicklung\par}
@ -87,12 +90,10 @@
{\large
\begin{tabular}{ll}
\textbf{Prüfling:} & [Name des Prüflings] \\
\textbf{Prüflingsnummer:} & [Prüflingsnummer] \\
\textbf{Ausbildungsbetrieb:} & Hitec GmbH \\
\textbf{Projektbetreuer:} & [Betreuer] \\
\textbf{Projektdauer:} & 70 Stunden \\
\textbf{Abgabedatum:} & \today \\
\textbf{Projektbetreuer:} & Herr Heidemann / Frau Deeken \\
\textbf{Projektdauer:} & 10 Wochen \\
\textbf{Abgabedatum:} & 12.06.2025 \\
\end{tabular}
\par}
@ -109,17 +110,16 @@
\chapter{Beschreibung}
\section{Umfeld}
Das Projekt wurde im Rahmen der Abschlussprüfung zum Fachinformatiker für Anwendungsentwicklung bei der Hitec GmbH entwickelt.
Das Projekt wurde im Rahmen des Mittelstufenprojektes entwickelt.
\subsection{Produktportfolio der Hitec GmbH}
Die Hitec GmbH ist ein innovatives IT-Unternehmen, das sich auf die Entwicklung moderner Webanwendungen und digitaler Lösungen spezialisiert hat. Das Unternehmen deckt ein breites Spektrum an Technologien ab, von klassischen Enterprise-Anwendungen bis hin zu modernen Cloud-nativen Lösungen.
Die Hitec GmbH ist ein innovatives IT-Unternehmen, das sich auf die Entwicklung moderner Webanwendungen und digitaler Lösungen spezialisiert hat. Das Unternehmen deckt ein breites Spektrum an Technologien ab.
\subsection{Weg zur Produktidee}
Die Casino Gaming Platform entstand als LF08 Projekt mit dem Ziel, eine vollständige Online-Casino-Plattform zu entwickeln. Die Produktidee basiert auf:
\begin{itemize}
\item \textbf{Gamification-Trend:} Wachsende Nachfrage nach Online-Gaming-Plattformen
\item \textbf{Technologie-Demonstration:} Showcase moderner Full-Stack-Entwicklung (Angular 20 + Spring Boot 3.5)
\item \textbf{Bildungsziel:} Praktische Anwendung von Enterprise-Patterns und modernen Web-Technologien
\item \textbf{Marktforschung:} Integration aktueller Standards (OAuth2, Stripe, responsive Design)
\end{itemize}
@ -182,14 +182,14 @@ Detaillierte Beschreibung des Problems oder der Marktlücke, welche unser Produk
\chapter{Durchführung}
\section{Vorgehensweise}
Für die Projektdurchführung wurde Scrum als Vorgehensmodell gewählt. Dadurch wird am Anfang des Projektes und eines Sprints festgelegt, was gemacht werden soll, und jeder ist auf dem gleichen Wissensstand. Des Weiteren gibt es tägliche Updates vom Fortschritt, Probleme können schnell erkannt und angesprochen werden. Dazu kann man auch neue Anforderungen flexibel mit einbringen.
Für die Projektdurchführung wurde Scrum als Vorgehensmodell gewählt bzw. vorgeschrieben. Dadurch wird am Anfang des Projektes und eines Sprints festgelegt, was gemacht werden soll, und jeder ist auf dem gleichen Wissensstand. Des Weiteren gibt es tägliche Updates vom Fortschritt, Probleme können schnell erkannt und angesprochen werden. Dazu kann man auch neue Anforderungen flexibel mit einbringen.
\subsection{Verwendete Tools und Methoden}
\begin{itemize}
\item \textbf{Scrum:} Agile Projektmanagement-Methodik
\item \textbf{Jira:} Ticketing und Sprint-Planning
\item \textbf{Git:} Versionskontrolle mit Feature-Branch-Workflow
\item \textbf{CI/CD:} Automatisierte Build- und Deployment-Pipelines
\item \textbf{CI/CD:} Automatisierte Build- und Deployment-Pipelines welche gleichzeitig auch die Qualitätssicherung übernimmt
\end{itemize}
\section{Umsetzung}
@ -266,33 +266,203 @@ Guards → Components → Services → HTTP Interceptors → Backend APIs
Angular Router → Lazy Loading → Feature Modules
\end{verbatim}
\subsection{Systemarchitektur}
Die Casino Gaming Platform basiert auf einer modernen Microservice-Architektur mit klarer Trennung zwischen Frontend und Backend.
\subsection{Systemarchitektur nach C4-Modell}
Die Casino Gaming Platform basiert auf einer modernen, schichtbasierten Architektur mit klarer Trennung zwischen Frontend und Backend. Die Darstellung erfolgt mittels des C4-Architekturmodells (Context, Container, Component, Code), welches eine hierarchische Sichtweise auf die Softwarearchitektur ermöglicht.
\subsubsection{Gesamtarchitektur}
\clearpage
\begin{landscape}
\subsubsection{Ebene 1: Systemkontext-Diagramm}
Das Systemkontext-Diagramm stellt die Casino Gaming Platform in ihrem geschäftlichen Umfeld dar und zeigt die Beziehungen zu externen Akteuren und Systemen. Es verdeutlicht, wer das System nutzt und mit welchen externen Systemen es interagiert.
\vspace{0.5cm}
\begin{tikzpicture}[
box/.style={rectangle, draw, minimum width=3cm, minimum height=1.5cm, align=center},
->, >=Stealth, node distance=2cm and 1.5cm
person/.style={rectangle, draw=blue!80, fill=blue!15, minimum width=3cm, minimum height=1.8cm, align=center, rounded corners=3pt, thick},
system/.style={rectangle, draw=blue!80, fill=blue!60, minimum width=5cm, minimum height=2.5cm, align=center, text=white, rounded corners=5pt, thick},
external/.style={rectangle, draw=gray!80, fill=gray!20, minimum width=3.5cm, minimum height=1.8cm, align=center, rounded corners=3pt, thick},
->, >=Stealth, node distance=4cm, thick
]
% Top row
\node[box] (angular) {Angular\\Frontend};
\node[box, right=of angular] (spring) {Spring Boot\\Backend};
\node[box, right=of spring] (postgres) {PostgreSQL\\Database};
% Akteure
\node[person] (player) at (-8, 3) {\textbf{Casino-Spieler}\\Nutzt die Plattform\\für Online-Gaming};
\node[person] (admin) at (-8, -3) {\textbf{System-Administrator}\\Verwaltet Plattform\\und Benutzer};
% Bottom row
\node[box, below=of angular] (keycloak) {Keycloak\\Auth Server};
\node[box, below=of spring] (stripe) {Stripe API\\Payment};
% Hauptsystem
\node[system] (casino) at (0, 0) {\textbf{Casino Gaming Platform}\\Bietet Online-Casino-Spiele\\mit virtueller Währung und\\Zahlungsabwicklung};
% Arrows top row
\draw (angular) -- (spring);
\draw (spring) -- (postgres);
% Externe Systeme
\node[external] (stripe) at (8, 3) {\textbf{Stripe API}\\Zahlungsabwicklung};
\node[external] (email) at (8, 0) {\textbf{E-Mail-Service}\\Verifizierung \&\\Benachrichtigungen};
\node[external] (oauth) at (8, -3) {\textbf{OAuth-Provider}\\GitHub, Google};
% Arrows to bottom row
\draw (angular.south) -- (keycloak.north);
\draw (spring.south) -- (stripe.north);
% Beziehungen
\draw (player) -- (casino) node[midway, above] {Spielt Spiele, tätigt Einzahlungen};
\draw (admin) -- (casino) node[midway, below] {Administriert System};
\draw (casino) -- (stripe) node[midway, above] {Verarbeitet Zahlungen};
\draw (casino) -- (email) node[midway, above] {Sendet E-Mails};
\draw (casino) -- (oauth) node[midway, below] {Authentifiziert Benutzer};
\end{tikzpicture}
\end{landscape}
\clearpage
\begin{landscape}
\subsubsection{Ebene 2: Container-Diagramm}
Das Container-Diagramm zeigt die High-Level-Architektur der Software und stellt die verschiedenen ausführbaren Einheiten (Container) sowie deren Kommunikation dar. Jeder Container repräsentiert eine separate deploybare/ausführbare Einheit wie eine Webanwendung, Datenbank oder Microservice.
\vspace{0.5cm}
\begin{tikzpicture}[
webapp/.style={rectangle, draw=blue!80, fill=blue!25, minimum width=4cm, minimum height=2.5cm, align=center, rounded corners=4pt, thick},
api/.style={rectangle, draw=green!80, fill=green!25, minimum width=4cm, minimum height=2.5cm, align=center, rounded corners=4pt, thick},
database/.style={rectangle, draw=red!80, fill=red!25, minimum width=3.5cm, minimum height=2.2cm, align=center, rounded corners=4pt, thick},
external/.style={rectangle, draw=gray!80, fill=gray!20, minimum width=3.2cm, minimum height=1.8cm, align=center, rounded corners=3pt, thick},
->, >=Stealth, node distance=4cm, thick
]
% Akteur
\node[webapp] (person) at (-10, 5) {\textbf{Casino-Spieler}\\Person\\Nutzt Webbrowser\\oder Mobile App};
% Frontend-Container
\node[webapp] (frontend) at (-10, 1) {\textbf{Webanwendung}\\Angular 20, TypeScript\\Liefert statische Inhalte\\und Casino-Spiele-UI};
% Backend-Container
\node[api] (backend) at (0, 1) {\textbf{API-Anwendung}\\Spring Boot, Java\\Stellt Spiellogik,\\Benutzerverwaltung und\\Geschäftsregeln bereit};
% Datenbank
\node[database] (database) at (0, -4) {\textbf{Datenbank}\\PostgreSQL\\Speichert Benutzerkonten,\\Spielhistorie und\\Transaktionen};
% Externe Systeme
\node[external] (stripe) at (9, 4) {\textbf{Stripe API}\\Zahlungssystem};
\node[external] (email) at (9, 1) {\textbf{Mailpit}\\E-Mail-Service};
\node[external] (oauth) at (9, -2) {\textbf{OAuth-Provider}\\Authentifizierungsdienste};
% Beziehungen
\draw (person) -- (frontend) node[midway, right] {HTTPS};
\draw (frontend) -- (backend) node[midway, above] {REST API\\JSON/HTTPS};
\draw (backend) -- (database) node[midway, left] {JDBC\\SQL};
\draw (backend) -- (stripe) node[midway, above] {HTTPS\\Webhooks};
\draw (backend) -- (email) node[midway, above] {SMTP};
\draw (backend) -- (oauth) node[midway, below] {OAuth2\\HTTPS};
\end{tikzpicture}
\end{landscape}
\clearpage
\begin{landscape}
\subsubsection{Ebene 3: Komponenten-Diagramm - Backend API}
Das Komponenten-Diagramm zeigt die interne Struktur des Backend API-Containers und stellt die wichtigsten Softwarekomponenten sowie deren Abhängigkeiten dar. Es verdeutlicht die Architektur nach dem MVC-Pattern mit Controller-, Service- und Repository-Schichten.
\vspace{0.5cm}
\begin{tikzpicture}[
component/.style={rectangle, draw=yellow!80, fill=yellow!15, minimum width=3.2cm, minimum height=1.8cm, align=center, rounded corners=3pt, thick},
controller/.style={rectangle, draw=blue!80, fill=blue!20, minimum width=3.2cm, minimum height=1.8cm, align=center, rounded corners=3pt, thick},
service/.style={rectangle, draw=green!80, fill=green!20, minimum width=3.2cm, minimum height=1.6cm, align=center, rounded corners=3pt, thick},
repository/.style={rectangle, draw=orange!80, fill=orange!20, minimum width=3.2cm, minimum height=1.4cm, align=center, rounded corners=3pt, thick},
->, >=Stealth, node distance=3cm, thick
]
% Controller (API-Schicht)
\node[controller] (auth-ctrl) at (-8, 5) {\textbf{Auth Controller}\\Anmeldung, Registrierung,\\OAuth};
\node[controller] (game-ctrl) at (-3, 5) {\textbf{Spiele-Controller}\\Blackjack, Slots,\\Würfel, etc.};
\node[controller] (deposit-ctrl) at (2, 5) {\textbf{Einzahlungs-Controller}\\Zahlungs-\\abwicklung};
\node[controller] (user-ctrl) at (7, 5) {\textbf{Benutzer-Controller}\\Profil-\\verwaltung};
% Services (Geschäftslogik-Schicht)
\node[service] (auth-svc) at (-8, 2.5) {\textbf{Auth Service}\\Authentifizierungs-\\logik};
\node[service] (game-svc) at (-3, 2.5) {\textbf{Spiele-Services}\\Spiellogik und\\Regeln};
\node[service] (balance-svc) at (2, 2.5) {\textbf{Guthaben-Service}\\Transaktions-\\verwaltung};
\node[service] (user-svc) at (7, 2.5) {\textbf{Benutzer-Service}\\Benutzer-\\verwaltung};
% Repositories (Datenschicht)
\node[repository] (user-repo) at (-5.5, 0) {\textbf{Benutzer-Repository}\\JPA/Hibernate};
\node[repository] (game-repo) at (-0.5, 0) {\textbf{Spiele-Repository}\\JPA/Hibernate};
\node[repository] (transaction-repo) at (4.5, 0) {\textbf{Transaktions-Repo}\\JPA/Hibernate};
% Sicherheitskomponenten
\node[component] (security) at (-13, 2.5) {\textbf{Security Config}\\JWT-Filter,\\CORS, OAuth2};
% Relationships
\draw (auth-ctrl) -- (auth-svc);
\draw (game-ctrl) -- (game-svc);
\draw (deposit-ctrl) -- (balance-svc);
\draw (user-ctrl) -- (user-svc);
\draw (auth-svc) -- (user-repo);
\draw (game-svc) -- (game-repo);
\draw (balance-svc) -- (transaction-repo);
\draw (user-svc) -- (user-repo);
\draw (security) -- (auth-svc);
\end{tikzpicture}
\end{landscape}
\clearpage
\begin{landscape}
\subsubsection{Ebene 4: Code-Diagramm - Blackjack Service Implementierung}
Das Code-Diagramm zeigt exemplarisch die Implementierungsdetails des Blackjack Game Service auf Klassenebene. Es stellt die wichtigsten Klassen, Interfaces und deren Beziehungen dar, die für die Blackjack-Spiellogik verantwortlich sind.
\vspace{0.5cm}
\begin{tikzpicture}[
class/.style={rectangle, draw=cyan!80, fill=cyan!15, minimum width=3.5cm, minimum height=3cm, align=center, rounded corners=4pt, thick},
interface/.style={rectangle, draw=yellow!80, fill=yellow!15, minimum width=3cm, minimum height=2cm, align=center, rounded corners=4pt, thick},
->, >=Stealth, node distance=4.5cm, thick
]
% Interface - mit mehr Abstand
\node[interface] (game-interface) at (0, 6) {\textbf{<<Interface>>}\\GameService\\+spielStarten()\\+zugVerarbeiten()\\+ergebnisBerechnen()};
% Haupt-Service-Klasse
\node[class] (blackjack-service) at (0, 1) {\textbf{BlackjackService}\\implementiert GameService\\- kartendeck: Deck\\- spielRepository: Repository\\+ neuesSpielStarten(einsatz)\\+ ziehen(spielId)\\+ halten(spielId)\\+ verdoppeln(spielId)};
% Unterstützende Klassen - mit größerem Abstand
\node[class] (card-deck) at (-8, 1) {\textbf{Kartendeck}\\- karten: List<Karte>\\+ mischen()\\+ karteGeben()\\+ zurücksetzen()};
\node[class] (game-entity) at (8, 1) {\textbf{BlackjackSpielEntity}\\- id: Long\\- spielerKarten: List<Karte>\\- dealerKarten: List<Karte>\\- einsatzBetrag: BigDecimal\\- spielZustand: SpielZustand};
\node[class] (balance-service) at (0, -3.5) {\textbf{GuthabenService}\\+ guthabenAktualisieren()\\+ ausreichendGuthaben()\\+ transaktionErstellen()};
% Beziehungen
\draw (blackjack-service) -- (game-interface) node[midway, right] {implementiert};
\draw (blackjack-service) -- (card-deck) node[midway, above] {verwendet};
\draw (blackjack-service) -- (game-entity) node[midway, above] {verwaltet};
\draw (blackjack-service) -- (balance-service) node[midway, right] {abhängig von};
\end{tikzpicture}
\end{landscape}
\clearpage
\begin{landscape}
\subsubsection{Deployment-Diagramm}
Das Deployment-Diagramm zeigt die Verteilung der Software-Container auf die physische/virtuelle Infrastruktur und stellt die Laufzeitumgebung der Anwendung dar. Es verdeutlicht, wie die verschiedenen Komponenten in der Produktionsumgebung deployed werden.
\vspace{0.5cm}
\begin{tikzpicture}[
node/.style={rectangle, draw=gray!80, fill=gray!15, minimum width=5cm, minimum height=4cm, align=center, rounded corners=5pt, thick},
container/.style={rectangle, draw=blue!80, fill=blue!20, minimum width=3cm, minimum height=2cm, align=center, rounded corners=4pt, thick},
->, >=Stealth, node distance=6cm, thick
]
% Infrastruktur-Knoten
\node[node] (docker-host) at (0, 0) {\textbf{Docker-Host}\\Ubuntu Linux\\Docker Engine 24.x};
% Container innerhalb des Docker-Hosts - mit mehr Abstand
\node[container] (nginx) at (-7, 3.5) {\textbf{nginx}\\Webserver\\Port 80/443};
\node[container] (angular) at (-2, 3.5) {\textbf{Frontend}\\Angular-App\\Statische Dateien};
\node[container] (spring) at (2, 3.5) {\textbf{Backend}\\Spring Boot\\Port 8080};
\node[container] (postgres) at (7, 3.5) {\textbf{Datenbank}\\PostgreSQL\\Port 5432};
\node[container] (mailpit) at (2, -3.5) {\textbf{Mailpit}\\E-Mail-Service\\Port 1025/8025};
% Externe Dienste - mit mehr Abstand
\node[node] (cloud) at (9, 0) {\textbf{Cloud-Dienste}\\Stripe API\\OAuth-Provider};
% Beziehungen
\draw (nginx) -- (angular);
\draw (spring) -- (postgres);
\draw (spring) -- (mailpit);
\draw (spring) -- (cloud);
\end{tikzpicture}
\end{landscape}
\subsubsection{Backend-Paketstruktur}
Das Backend folgt dem Domain-Driven Design (DDD) Ansatz:
@ -335,8 +505,73 @@ Das Backend folgt dem Domain-Driven Design (DDD) Ansatz:
.3 UserService.java.
}
\subsubsection{Frontend-Modulstruktur}
Das Frontend ist modular aufgebaut mit Feature-Modulen:
\clearpage
\begin{landscape}
\subsubsection{Ebene 3: Komponenten-Diagramm - Frontend-Anwendung}
Das Komponenten-Diagramm zeigt die interne Struktur der Angular Frontend-Anwendung und stellt die modulare Architektur mit Feature-Modulen, Services und Guards dar. Es verdeutlicht die Anwendung des Angular-Framework-Patterns.
\vspace{0.5cm}
\begin{tikzpicture}[
module/.style={rectangle, draw=purple!80, fill=purple!20, minimum width=3.5cm, minimum height=2.2cm, align=center, rounded corners=4pt, thick},
component/.style={rectangle, draw=blue!80, fill=blue!20, minimum width=3cm, minimum height=1.6cm, align=center, rounded corners=3pt, thick},
service/.style={rectangle, draw=green!80, fill=green!20, minimum width=3cm, minimum height=1.6cm, align=center, rounded corners=3pt, thick},
guard/.style={rectangle, draw=orange!80, fill=orange!20, minimum width=3cm, minimum height=1.6cm, align=center, rounded corners=3pt, thick},
->, >=Stealth, node distance=3.5cm, thick
]
% Kern-Module
\node[module] (app) at (4, 6) {\textbf{App}\\Haupt-Modul};
\node[module] (shared) at (-6, 2) {\textbf{Shared}\\Gemeinsame Komponenten};
% Feature-Module
\node[module] (auth) at (-1, 2) {\textbf{Auth}\\Anmeldung, OAuth2};
\node[module] (games) at (3.75, 2) {\textbf{Spiele}\\Blackjack, Slots, Würfel};
\node[module] (deposit) at (8.25, 2) {\textbf{Einzahlung}\\Stripe};
\node[module] (home) at (12.25, 2){\textbf{Home}\\Dashboard};
% Services
\node[service] (auth-svc) at (-1, -1.5) {\textbf{Auth}\\JWT, Benutzerstatus};
\node[service] (user-svc) at (12.25, -1.5) {\textbf{User}\\Profil, Guthaben};
\node[service] (game-svc) at (3.75, -1.5) {\textbf{Spiele}\\API, Logik};
\node[service] (payment-svc) at (8.25, -1.5) {\textbf{Payment}\\Stripe};
% Guards
\node[guard] (auth-guard) at (-1.5, -4.5) {\textbf{Auth}\\Routen-Schutz};
\node[guard] (http-interceptor) at (2, -4.5) {\textbf{HTTP}\\JWT, CORS};
% Relationships
\draw (app) -- (shared);
\draw (app) -- (auth);
\draw (app) -- (games);
\draw (app) -- (deposit);
\draw (app) -- (home);
\draw (auth) -- (auth-svc);
\draw (games) -- (game-svc);
\draw (deposit) -- (payment-svc);
\draw (home) -- (user-svc);
\draw (auth-guard) -- (auth-svc);
\draw (http-interceptor) -- (auth-svc);
\end{tikzpicture}
\end{landscape}
\subsubsection{Architektur-Zusammenfassung}
Die Casino Gaming Platform implementiert eine moderne, mehrschichtige Architektur, die folgende Prinzipien befolgt:
\begin{itemize}
\item \textbf{Separation of Concerns:} Klare Trennung zwischen Präsentations-, Geschäftslogik- und Datenschicht
\item \textbf{Modularität:} Feature-basierte Modularisierung sowohl im Frontend als auch Backend
\item \textbf{Skalierbarkeit:} Container-basierte Architektur ermöglicht horizontale Skalierung
\item \textbf{Sicherheit:} Umfassende Sicherheitskonzepte mit JWT, OAuth2 und CORS-Schutz
\item \textbf{Wartbarkeit:} Standardisierte Patterns (MVC, Repository, Service Layer) erleichtern die Wartung
\end{itemize}
Die C4-Darstellung zeigt die Architektur auf verschiedenen Abstraktionsebenen und ermöglicht es unterschiedlichen Stakeholdern, die für sie relevanten Aspekte zu verstehen - von der Geschäftslogik bis hin zu technischen Implementierungsdetails.
\subsubsection{Frontend-Paketstruktur (Detailansicht)}
Die detaillierte Paketstruktur des Frontend zeigt die modulare Organisation:
\dirtree{%
.1 app/feature/.

View file

@ -21,7 +21,8 @@
{
"glob": "**/*",
"input": "public"
}
},
"src/assets"
],
"styles": [
"src/styles.css"

View file

@ -61,6 +61,9 @@ export class AnimatedNumberComponent implements OnChanges, AfterViewInit {
this.countUp = new CountUp(this.numberElement.nativeElement, this.value, {
startVal: this.previousValue,
duration: this.duration,
decimalPlaces: 2,
useEasing: true,
useGrouping: false,
easingFn: (t, b, c, d) => {
if (this.ease === 'power1.out') {
return c * (1 - Math.pow(1 - t / d, 1)) + b;

View file

@ -4,7 +4,7 @@
</div>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
<div class="lg:col-span-3">
<div class="lg:col-span-4">
<div class="flex justify-between items-center mb-6">
<h3 class="section-heading text-2xl">Alle Spiele</h3>
<div class="flex space-x-2">
@ -18,24 +18,54 @@
</div>
<div class="slider-container">
<div class="slider-grid">
<div class="card group" *ngFor="let game of featuredGames">
<div class="relative overflow-hidden rounded-lg">
<img
[src]="game.image"
[alt]="game.name"
class="w-full aspect-[4/3] object-cover transition-transform duration-300 group-hover:scale-105"
/>
<div
class="absolute inset-0 bg-gradient-to-t from-deep-blue/95 via-deep-blue/50 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 ease-in-out"
>
<div class="min-w-full space-y-4">
<!-- Top row with 3 games -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="card group" *ngFor="let game of featuredGames.slice(0, 3)">
<div class="relative overflow-hidden rounded-lg">
<img
[src]="game.image"
[alt]="game.name"
class="w-full aspect-[4/3] object-cover transition-transform duration-300 group-hover:scale-105"
/>
<div
class="absolute bottom-4 left-4 right-4 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300"
class="absolute inset-0 bg-gradient-to-t from-deep-blue/95 via-deep-blue/50 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 ease-in-out"
>
<h4 class="game-heading">{{ game.name }}</h4>
<button class="button-primary w-full py-2" (click)="navigateToGame(game.route)">
Jetzt Spielen
</button>
<div
class="absolute bottom-4 left-4 right-4 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300"
>
<h4 class="game-heading">{{ game.name }}</h4>
<button class="button-primary w-full py-2" (click)="navigateToGame(game.route)">
Jetzt Spielen
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Bottom row with 2 games centered -->
<div
class="grid grid-cols-1 sm:grid-cols-2 gap-4 max-w-2xl mx-auto xl:max-w-3xl xl:gap-6"
>
<div class="card group" *ngFor="let game of featuredGames.slice(3, 5)">
<div class="relative overflow-hidden rounded-lg">
<img
[src]="game.image"
[alt]="game.name"
class="w-full aspect-[4/3] object-cover transition-transform duration-300 group-hover:scale-105"
/>
<div
class="absolute inset-0 bg-gradient-to-t from-deep-blue/95 via-deep-blue/50 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 ease-in-out"
>
<div
class="absolute bottom-4 left-4 right-4 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300"
>
<h4 class="game-heading">{{ game.name }}</h4>
<button class="button-primary w-full py-2" (click)="navigateToGame(game.route)">
Jetzt Spielen
</button>
</div>
</div>
</div>
</div>
@ -43,53 +73,5 @@
</div>
</div>
</div>
<div class="lg:col-span-1 space-y-6">
<div class="card p-4">
<h3 class="section-heading text-xl mb-4">Konto</h3>
<div class="space-y-4">
<button class="button-primary w-full py-2" (click)="openDepositModal()">Einzahlen</button>
<app-deposit
[isOpen]="isDepositModalOpen"
(closeModalEmitter)="closeDepositModal()"
></app-deposit>
<button
class="bg-deep-blue-light hover:bg-deep-blue-contrast w-full py-2 rounded"
(click)="openTransactionModal()"
>
Transaktionen
</button>
<app-transaction-history
[isOpen]="isTransactionModalOpen"
(closeEventEmitter)="closeTransactionModal()"
/>
</div>
</div>
<app-confirmation
[successful]="isDepositSuccessful"
(closeConfirmation)="closeDepositConfirmationModal()"
></app-confirmation>
<div class="card p-4">
<h3 class="section-heading text-xl mb-4">Letzte Transaktionen</h3>
<div class="space-y-3">
<div
class="flex justify-between items-center"
*ngFor="let transaction of (recentTransactionData | async)?.transactions"
>
<div>
<p class="text-sm font-medium">{{ transaction.status }}</p>
<p class="text-xs text-text-secondary">
{{ transaction.createdAt | date: 'd.m.y H:m' }}
</p>
</div>
<span [class]="transaction.amount > 0 ? 'text-emerald' : 'text-accent-red'">
{{ transaction.amount | currency: 'EUR' }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,34 +1,18 @@
import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core';
import { AsyncPipe, CurrencyPipe, DatePipe, NgFor } from '@angular/common';
import { DepositComponent } from '../deposit/deposit.component';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { NgFor } from '@angular/common';
import { ActivatedRoute, Router } from '@angular/router';
import { ConfirmationComponent } from '@shared/components/confirmation/confirmation.component';
import { Game } from 'app/model/Game';
import { Observable } from 'rxjs';
import { TransactionService } from '@service/transaction.service';
import format from 'ajv/dist/vocabularies/format';
import { TransactionHistoryComponent } from '../transaction-history/transaction-history.component';
import { TransactionData } from '../../model/TransactionData';
@Component({
selector: 'app-homepage',
standalone: true,
imports: [
CurrencyPipe,
NgFor,
DepositComponent,
ConfirmationComponent,
AsyncPipe,
DatePipe,
TransactionHistoryComponent,
],
imports: [NgFor],
templateUrl: './home.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export default class HomeComponent implements OnInit {
isDepositModalOpen = false;
isDepositSuccessful = false;
isTransactionModalOpen = false;
constructor(
public route: ActivatedRoute,
@ -78,35 +62,10 @@ export default class HomeComponent implements OnInit {
},
];
allGames: Game[] = [...this.featuredGames];
recentTransactionData: Observable<TransactionData> =
inject(TransactionService).getUsersTransactions(5);
openDepositModal() {
this.isDepositModalOpen = true;
}
closeDepositModal() {
this.isDepositModalOpen = false;
}
openDepositConfirmationModal() {
this.isDepositSuccessful = true;
}
openTransactionModal() {
this.isTransactionModalOpen = true;
}
closeDepositConfirmationModal() {
this.isDepositSuccessful = false;
}
closeTransactionModal() {
this.isTransactionModalOpen = false;
}
navigateToGame(route: string) {
this.router.navigate([route]);
}

View file

@ -21,13 +21,7 @@
(click)="showRegisterForm()"
class="w-full sm:w-auto button-primary px-6 sm:px-8 py-3 shadow-lg"
>
Konto erstellen
</button>
<button
(click)="showLoginForm()"
class="w-full sm:w-auto bg-slate-700 text-white hover:bg-slate-600 px-6 sm:px-8 py-3 shadow-lg rounded"
>
Anmelden
Jetzt registrieren
</button>
}
</div>
@ -46,33 +40,63 @@
<div class="game-card-content">
<h3 class="game-heading-sm">Slots</h3>
<p class="game-text">Klassische Spielautomaten</p>
<a
routerLink="game/slots"
class="button-primary w-full py-2 inline-block text-center"
>Jetzt Spielen</a
>
@if (isLoggedIn()) {
<a
routerLink="game/slots"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</a>
} @else {
<button
(click)="showLoginForm()"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</button>
}
</div>
</div>
<div class="hidden lg:block card">
<div class="game-card-content">
<h3 class="game-heading-sm">Blackjack</h3>
<p class="game-text">Klassisches Kartenspiel</p>
<a
routerLink="game/blackjack"
class="button-primary w-full py-2 inline-block text-center"
>Jetzt Spielen</a
>
@if (isLoggedIn()) {
<a
routerLink="game/blackjack"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</a>
} @else {
<button
(click)="showLoginForm()"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</button>
}
</div>
</div>
<div class="hidden lg:block card">
<div class="game-card-content">
<h3 class="game-heading-sm">Coinflip</h3>
<p class="game-text">Münzwurf</p>
<a
routerLink="game/blackjack"
class="button-primary w-full py-2 inline-block text-center"
>Jetzt Spielen</a
>
@if (isLoggedIn()) {
<a
routerLink="game/coinflip"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</a>
} @else {
<button
(click)="showLoginForm()"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</button>
}
</div>
</div>
</div>
@ -82,22 +106,42 @@
<div class="game-card-content">
<h3 class="game-heading-sm">Dice</h3>
<p class="game-text">Würfelspiel</p>
<a
routerLink="/game/dice"
class="button-primary w-full py-2 inline-block text-center"
>Jetzt Spielen</a
>
@if (isLoggedIn()) {
<a
routerLink="game/dice"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</a>
} @else {
<button
(click)="showLoginForm()"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</button>
}
</div>
</div>
<div class="hidden lg:block card">
<div class="game-card-content">
<h3 class="game-heading-sm">Lootboxen</h3>
<p class="game-text">Überraschungskisten</p>
<a
routerLink="game/lootboxes"
class="button-primary w-full py-2 inline-block text-center"
>Jetzt Spielen</a
>
@if (isLoggedIn()) {
<a
routerLink="game/lootboxes"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</a>
} @else {
<button
(click)="showLoginForm()"
class="button-primary w-full py-2 inline-block text-center"
>
Jetzt Spielen
</button>
}
</div>
</div>
</div>

View file

@ -23,15 +23,14 @@ import RecoverPasswordComponent from '../auth/recover-password/recover-password.
})
export class LandingComponent implements OnInit, OnDestroy {
currentSlide = 0;
private autoplayInterval: ReturnType<typeof setInterval> | undefined;
authService: AuthService = inject(AuthService);
route: ActivatedRoute = inject(ActivatedRoute);
showLogin = signal(false);
showRegister = signal(false);
showRecoverPassword = signal(false);
isLoggedIn = signal(this.authService.isLoggedIn());
ngOnInit() {
this.startAutoplay();
document.body.style.overflow = 'auto';
if (this.route.snapshot.queryParamMap.get('login') === 'true') {
this.showLoginForm();
@ -39,7 +38,6 @@ export class LandingComponent implements OnInit, OnDestroy {
}
ngOnDestroy() {
this.stopAutoplay();
document.body.style.overflow = 'auto';
}
@ -73,33 +71,13 @@ export class LandingComponent implements OnInit, OnDestroy {
prevSlide() {
this.currentSlide = this.currentSlide === 0 ? 1 : 0;
this.resetAutoplay();
}
nextSlide() {
this.currentSlide = this.currentSlide === 1 ? 0 : 1;
this.resetAutoplay();
}
goToSlide(index: number) {
this.currentSlide = index;
this.resetAutoplay();
}
private startAutoplay() {
this.autoplayInterval = setInterval(() => {
this.nextSlide();
}, 5000);
}
private stopAutoplay() {
if (this.autoplayInterval) {
clearInterval(this.autoplayInterval);
}
}
private resetAutoplay() {
this.stopAutoplay();
this.startAutoplay();
}
}

View file

@ -1,40 +1,120 @@
<nav class="bg-deep-blue border-b border-deep-blue-contrast">
<div class="max-w-full mx-auto px-4">
<div class="flex justify-between items-center h-14">
<div class="flex items-center space-x-6">
<a routerLink="/" class="nav-brand">
<span>Trustworthy Casino</span>
<nav class="bg-deep-blue-light border-b border-emerald-500/30 shadow-lg">
<div class="max-w-full mx-auto px-6">
<div class="flex justify-between items-center h-16">
<div class="flex items-center space-x-2">
<a routerLink="/" class="flex items-center space-x-3 group">
<div class="flex flex-col">
<span class="text-xl font-bold text-white"> Trustworthy Casino </span>
<span class="text-xs text-emerald-400 font-medium">Trust. Play. Win.</span>
</div>
</a>
<div class="hidden md:flex items-center space-x-1">
<a routerLink="/home" class="nav-link">Spiele</a>
@if (isLoggedIn()) {
<a
routerLink="/home"
class="flex items-center px-4 py-2 text-white/90 hover:text-white font-medium rounded-lg hover:bg-white/10 transition-colors duration-200"
>
<img class="mr-2 w-4 h-4" src="assets/games.svg" alt="gamess" />
Spiele
</a>
} @else {
<button
(click)="showLogin.emit()"
class="flex items-center px-4 py-2 text-white/90 hover:text-white font-medium rounded-lg hover:bg-white/10 transition-colors duration-200"
>
<img class="mr-2 w-4 h-4" src="assets/games.svg" alt="gamess" />
Spiele
</button>
}
</div>
</div>
<div class="hidden md:flex items-center space-x-4">
<div class="hidden md:flex items-center space-x-1">
@if (!isLoggedIn()) {
<button (click)="showLogin.emit()" class="button-primary px-4 py-1.5">Anmelden</button>
<button
(click)="showLogin.emit()"
class="flex items-center px-4 py-2 text-white font-medium border border-emerald-500 rounded-lg hover:bg-emerald-500/10 transition-colors duration-200"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"
/>
</svg>
Anmelden
</button>
<button
(click)="showRegister.emit()"
class="bg-emerald-700 text-white hover:bg-emerald-600 px-4 py-1.5 rounded"
class="flex items-center px-4 py-2 bg-emerald-600 text-white font-medium rounded-lg hover:bg-emerald-500 transition-colors duration-200"
>
Registrieren
Jetzt registrieren
</button>
}
@if (isLoggedIn()) {
<div
class="text-white font-bold bg-deep-blue-contrast rounded-full px-4 py-2 text-sm hover:bg-deep-blue-contrast/80 hover:cursor-pointer hover:scale-105 transition-all active:scale-95 select-none duration-300"
routerLink="/home"
class="flex items-center px-4 py-2 mr-2 bg-slate-700 border border-emerald-500/30 rounded-lg font-medium"
>
<span [class]="balance() < 0 ? 'text-accent-red' : ''">
<span class="text-emerald-400 text-sm mr-2">Guthaben:</span>
<span
[class]="balance() < 0 ? 'text-red-400 font-bold' : 'text-white font-bold'"
class="text-sm"
>
<app-animated-number [value]="balance()" [duration]="0.5"></app-animated-number>
</span>
</div>
<button (click)="logout()" class="button-primary px-4 py-1.5">Abmelden</button>
<button
class="flex items-center px-4 py-2 bg-emerald-600 text-white font-medium rounded-lg hover:bg-emerald-500 transition-colors duration-200"
(click)="openDepositModal()"
>
<img class="mr-2 w-3 h-3" src="assets/deposit.svg" alt="deposits" />
Einzahlen
</button>
<app-deposit
[isOpen]="isDepositModalOpen"
(closeModalEmitter)="closeDepositModal()"
></app-deposit>
<button
class="flex items-center px-4 py-2 bg-slate-700 text-white font-medium rounded-lg hover:bg-slate-600 border border-slate-600 transition-colors duration-200"
(click)="openTransactionModal()"
>
<img class="mr-2 w-4 h-4" src="assets/transaction.svg" alt="transactions" />
Transaktionen
</button>
<app-transaction-history
[isOpen]="isTransactionModalOpen"
(closeEventEmitter)="closeTransactionModal()"
/>
<button
(click)="logout()"
class="flex items-center px-4 py-2 text-red-400 font-medium border border-red-500/50 rounded-lg hover:bg-red-500/10 transition-colors duration-200"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
/>
</svg>
Abmelden
</button>
}
</div>
<div class="md:hidden">
<button (click)="toggleMenu()" class="nav-toggle">
<button
(click)="toggleMenu()"
class="p-2 text-white hover:text-emerald-400 rounded-lg transition-colors duration-200"
>
<svg
class="h-6 w-6"
[class.hidden]="isMenuOpen"
@ -68,25 +148,95 @@
</div>
<div [class]="isMenuOpen ? 'block' : 'hidden'" class="md:hidden">
<div class="nav-mobile-menu">
<a routerLink="/games" class="nav-mobile-link">Spiele</a>
<div class="pt-2 space-y-2">
<div class="px-2 pt-2 pb-4 space-y-3 bg-slate-700 rounded-lg mt-2">
<a
routerLink="/home"
class="flex items-center px-4 py-3 text-white/90 hover:text-white hover:bg-white/10 rounded-lg transition-colors duration-200"
>
<img class="mr-2 w-4 h-4" src="assets/games.svg" alt="gamess" />
Spiele
</a>
<div class="border-t border-slate-600 pt-3 space-y-3">
@if (!isLoggedIn()) {
<button
(click)="showLogin.emit()"
class="button-primary w-full py-1.5 block text-center"
class="w-full flex items-center justify-center px-4 py-3 text-white font-medium border border-emerald-500 rounded-lg hover:bg-emerald-500/10 transition-colors duration-200"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"
/>
</svg>
Anmelden
</button>
<button
(click)="showRegister.emit()"
class="bg-emerald-700 text-white hover:bg-emerald-600 w-full py-1.5 rounded block text-center"
class="w-full flex items-center justify-center px-4 py-3 bg-emerald-600 text-white font-medium rounded-lg hover:bg-emerald-500 transition-colors duration-200"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"
/>
</svg>
Registrieren
</button>
}
@if (isLoggedIn()) {
<button (click)="logout()" class="button-primary w-full py-1.5">Abmelden</button>
<div
class="flex items-center justify-center px-4 py-3 bg-slate-700 border border-emerald-500/30 rounded-lg"
>
<span class="text-emerald-400 text-sm font-medium mr-2">Guthaben:</span>
<span
[class]="balance() < 0 ? 'text-red-400 font-bold' : 'text-white font-bold'"
class="text-sm"
>
<app-animated-number [value]="balance()" [duration]="0.5"></app-animated-number>
</span>
</div>
<button
(click)="openDepositModal()"
class="w-full flex items-center justify-center px-4 py-3 bg-emerald-600 text-white font-medium rounded-lg hover:bg-emerald-500 transition-colors duration-200"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
Einzahlen
</button>
<button
(click)="openTransactionModal()"
class="w-full flex items-center justify-center px-4 py-3 bg-slate-700 text-white font-medium rounded-lg hover:bg-slate-600 border border-slate-600 transition-colors duration-200"
>
Transaktionen
</button>
<button
(click)="logout()"
class="w-full flex items-center justify-center px-4 py-3 text-red-400 font-medium border border-red-500/50 rounded-lg hover:bg-red-500/10 transition-colors duration-200"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
/>
</svg>
Abmelden
</button>
}
</div>
</div>

View file

@ -12,16 +12,20 @@ import { RouterModule } from '@angular/router';
import { AuthService } from '@service/auth.service';
import { Subscription } from 'rxjs';
import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component';
import { DepositComponent } from '../../../feature/deposit/deposit.component';
import { TransactionHistoryComponent } from '../../../feature/transaction-history/transaction-history.component';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
standalone: true,
imports: [RouterModule, AnimatedNumberComponent],
imports: [RouterModule, AnimatedNumberComponent, DepositComponent, TransactionHistoryComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavbarComponent implements OnInit, OnDestroy {
isMenuOpen = false;
isDepositModalOpen = false;
isTransactionModalOpen = false;
private authService: AuthService = inject(AuthService);
isLoggedIn = signal(this.authService.isLoggedIn());
@ -51,4 +55,20 @@ export class NavbarComponent implements OnInit, OnDestroy {
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
openDepositModal() {
this.isDepositModalOpen = true;
}
closeDepositModal() {
this.isDepositModalOpen = false;
}
openTransactionModal() {
this.isTransactionModalOpen = true;
}
closeTransactionModal() {
this.isTransactionModalOpen = false;
}
}

View file

@ -0,0 +1,4 @@
<svg style="color: white;" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
<path fill="currentColor" d="m8 16l-2-3h1v-2h2v2h1zm7-15v8H1V1zm1-1H0v10h16z"/>
<path fill="currentColor" d="M8 2a3 3 0 1 1 0 6h5V7h1V3h-1V2zM5 5a3 3 0 0 1 3-3H3v1H2v4h1v1h5a3 3 0 0 1-3-3"/>
</svg>

After

Width:  |  Height:  |  Size: 314 B

View file

@ -0,0 +1,6 @@
<svg style="color: white;" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
<path fill="currentColor"
d="M495.24 267.592L445.066 41.083A32.04 32.04 0 0 0 406.9 16.76L180.393 66.934a32 32 0 0 0-24.322 38.166l21.021 94.9H48a32.036 32.036 0 0 0-32 32v232a32.036 32.036 0 0 0 32 32h232a32.036 32.036 0 0 0 32-32V340.957l158.917-35.2a32.04 32.04 0 0 0 24.323-38.165M280 464H48V232h136.181l22.063 99.606a32.03 32.03 0 0 0 31.18 25.092a32.3 32.3 0 0 0 6.984-.769l35.6-7.886L280.02 464Zm184-189.487l-226.513 50.173l-50.173-226.51L413.824 48l50.193 226.505Z"/>
<path fill="currentColor"
d="M80 264h40v40H80zm0 128h40v40H80zm128 0h40v40h-40zm-64-64h40v40h-40zm81.456-205.433l39.054-8.644l8.644 39.055l-39.054 8.644zm152.672 97.223l39.054-8.65l8.65 39.054l-39.054 8.65zm-76.324-48.649l39.053-8.65l8.65 39.053l-39.052 8.65z"/>
</svg>

After

Width:  |  Height:  |  Size: 882 B

View file

@ -0,0 +1,12 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
style="color: white;"
>
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" color="currentColor">
<path d="M4.58 8.607L2 8.454C3.849 3.704 9.158 1 14.333 2.344c5.513 1.433 8.788 6.918 7.314 12.25c-1.219 4.411-5.304 7.337-9.8 7.406"/>
<path d="M12 22C6.5 22 2 17 2 11m11.604-1.278c-.352-.37-1.213-1.237-2.575-.62c-1.361.615-1.577 2.596.482 2.807c.93.095 1.537-.11 2.093.47c.556.582.659 2.198-.761 2.634s-2.341-.284-2.588-.509m1.653-6.484v.79m0 6.337v.873"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 639 B

View file

@ -71,6 +71,10 @@ a {
@apply font-bold text-text-primary text-sm mb-2;
}
.game-heading {
@apply font-bold text-text-primary text-lg mb-2;
}
.game-heading-xl {
@apply font-bold text-text-primary text-xl mb-2;
}