diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
new file mode 100644
index 0000000..b5e505f
--- /dev/null
+++ b/.github/workflows/docker-image.yml
@@ -0,0 +1,26 @@
+name: Docker Compose Build
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Install docker-compose (if needed)
+ run: sudo apt-get install docker-compose
+
+ - name: Build and push Docker images
+ run: |
+ cd src/Docker
+ docker-compose build
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
new file mode 100644
index 0000000..5ef5ef3
--- /dev/null
+++ b/.github/workflows/gradle.yml
@@ -0,0 +1,67 @@
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
+
+name: Java CI with Gradle
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 24
+ uses: actions/setup-java@v4
+ with:
+ java-version: '24'
+ distribution: 'temurin'
+
+ # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
+ # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
+
+ - name: Build with Gradle Wrapper
+ run: ./gradlew build
+
+ # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).
+ # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.
+ #
+ # - name: Setup Gradle
+ # uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
+ # with:
+ # gradle-version: '8.9'
+ #
+ # - name: Build with Gradle 8.9
+ # run: gradle build
+
+ dependency-submission:
+
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 24
+ uses: actions/setup-java@v4
+ with:
+ java-version: '24'
+ distribution: 'temurin'
+
+ # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.
+ # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md
+ - name: Generate and submit dependency graph
+ uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
diff --git a/.gitignore b/.gitignore
index 0fd53fb..81d5aae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,6 @@ out/
/logs/
+/.idea/
+
+/uploads/
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index fdc392f..f5a0c5d 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -16,5 +16,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Docker/Dockerfile b/Dockerfile
similarity index 78%
rename from src/Docker/Dockerfile
rename to Dockerfile
index 766fe68..fb86206 100644
--- a/src/Docker/Dockerfile
+++ b/Dockerfile
@@ -9,7 +9,7 @@
FROM openjdk:24
WORKDIR /jambotron/
-COPY './build/libs/jambotron-0.0.1-SNAPSHOT.jar' '/app/jambotron.jar'
+COPY '/build/libs/jambotron-0.0.1-SNAPSHOT.jar' '/app/jambotron.jar'
#COPY --from=BUILD_IMAGE '/jambotron/build/libs/jambotron-0.0.1-SNAPSHOT.jar' '/app/jambotron.jar'
EXPOSE 8080
-CMD ["java","-jar","/app/jambotron.jar"]
\ No newline at end of file
+CMD ["java","-jar","/app/jambotron.jar"]
diff --git a/HELP.md b/HELP.md
index 75481ee..47eb320 100644
--- a/HELP.md
+++ b/HELP.md
@@ -108,6 +108,42 @@ docker-compose -f src/Docker/docker-compose.yml up -d
```
- For connection from pgadmin use
hostename: **host.docker.internal**
+
+# How to run the application in Docker with HTTPS support
+
+To run the application in Docker with HTTPS support, follow these steps:
+
+1. **Cerbot**
+
+ - Run this command below to get certificate and store it to volume certs.
+
+ - Expanded(readable) command example:
+
+ docker run -d \
+ --name certbot \
+ -v certs:/etc/letsencrypt \
+ -v certs-data:/var/lib/letsencrypt \
+ -p 80:80 \
+ -p 443:443 \
+ certbot/certbot \
+ certonly --standalone --preferred-challenges http --email youremail@gmail.com -d yourdomain.com --agree-tos
+
+ - Command to run certbot with specific domain and email:
+ ```bash
+ docker run -d --name certbot -v certs:/etc/letsencrypt -v certs-data:/var/lib/letsencrypt -p 8081:80 -p 8443:443 certbot/certbot certonly --standalone --preferred-challenges http --email liosha84@gmail.com -d jambotron.run.place --agree-tos
+ ```
+ - Tip: You can generate certificate to local machine used next command:
+ ```bash
+ docker run -it --rm -p 8081:80 --name certbot -v "C:\certbot\etc\letsencrypt:/etc/letsencrypt" -v "C:\certbot\var\lib\letsencrypt:/var/lib/letsencrypt" certbot/certbot certonly --standalone -d jambotron.run.place
+ ```
+ certificate files will be stored in `C:\certbot\etc\letsencrypt` and `C:\certbot\var\lib\letsencrypt` directories.
+
+ - If you have same trouble before running certbot, run this project
+ `Helper_projects/spring-boot-https-main` insted of jambotron project.
+
+
+ - TODO: Add cron job to renew certificate automatically.
+
### Reference Documentation
For further reference, please consider the following sections:
diff --git a/Helper_projects/spring-boot-https-main/.gitignore b/Helper_projects/spring-boot-https-main/.gitignore
new file mode 100644
index 0000000..c2065bc
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/.gitignore
@@ -0,0 +1,37 @@
+HELP.md
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
diff --git a/Helper_projects/spring-boot-https-main/Dockerfile b/Helper_projects/spring-boot-https-main/Dockerfile
new file mode 100644
index 0000000..25cbb22
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/Dockerfile
@@ -0,0 +1,10 @@
+FROM openjdk:20 as build
+WORKDIR /app
+COPY . ./
+RUN microdnf install findutils
+RUN ./gradlew build -x test
+
+FROM openjdk:20-jdk-slim
+WORKDIR /app
+COPY --from=build /app/build/libs/spring-boot-https-0.0.1.jar .
+CMD ["java", "-jar", "spring-boot-https-0.0.1.jar"]
\ No newline at end of file
diff --git a/Helper_projects/spring-boot-https-main/README.md b/Helper_projects/spring-boot-https-main/README.md
new file mode 100644
index 0000000..02cca74
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/README.md
@@ -0,0 +1 @@
+https://dev.to/beksultandev/how-to-add-https-support-to-your-spring-boot-app-2h53
\ No newline at end of file
diff --git a/Helper_projects/spring-boot-https-main/build.gradle b/Helper_projects/spring-boot-https-main/build.gradle
new file mode 100644
index 0000000..9f60ac2
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/build.gradle
@@ -0,0 +1,29 @@
+plugins {
+ id 'java'
+ id 'org.springframework.boot' version '3.1.5'
+ id 'io.spring.dependency-management' version '1.1.3'
+}
+
+group = 'dev.beksultan'
+version = '0.0.1'
+
+java {
+ sourceCompatibility = '17'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation 'org.springframework.boot:spring-boot-starter-web'
+ testImplementation 'org.springframework.boot:spring-boot-starter-test'
+}
+
+tasks.named('bootBuildImage') {
+ builder = 'paketobuildpacks/builder-jammy-base:latest'
+}
+
+tasks.named('test') {
+ useJUnitPlatform()
+}
diff --git a/Helper_projects/spring-boot-https-main/compose.yml b/Helper_projects/spring-boot-https-main/compose.yml
new file mode 100644
index 0000000..64e3042
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/compose.yml
@@ -0,0 +1,17 @@
+services:
+ spring-boot-https:
+ container_name: spring-boot-https
+ image: beksultancs/spring-boot-https:1
+ restart: unless-stopped
+ ports:
+ - "8081:80"
+ - "8443:443"
+ volumes:
+ - certs:/certs
+ environment:
+ - SERVER_PORT=443
+ - FULLCHAINPEM=/certs/live/jambotron.run.place/fullchain.pem
+ - PRIVKEYPEM=/certs/live/jambotron.run.place/privkey.pem
+volumes:
+ certs:
+ external: true
diff --git a/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.jar b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..7f93135
Binary files /dev/null and b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.properties b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..3fa8f86
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/Helper_projects/spring-boot-https-main/gradlew b/Helper_projects/spring-boot-https-main/gradlew
new file mode 100644
index 0000000..1aa94a4
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/gradlew
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/Helper_projects/spring-boot-https-main/gradlew.bat b/Helper_projects/spring-boot-https-main/gradlew.bat
new file mode 100644
index 0000000..93e3f59
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Helper_projects/spring-boot-https-main/settings.gradle b/Helper_projects/spring-boot-https-main/settings.gradle
new file mode 100644
index 0000000..66302e0
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'spring-boot-https'
diff --git a/Helper_projects/spring-boot-https-main/src/main/java/https/SpringBootHttpsApplication.java b/Helper_projects/spring-boot-https-main/src/main/java/https/SpringBootHttpsApplication.java
new file mode 100644
index 0000000..1bec660
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/src/main/java/https/SpringBootHttpsApplication.java
@@ -0,0 +1,20 @@
+package https;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@SpringBootApplication
+@RestController
+public class SpringBootHttpsApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootHttpsApplication.class, args);
+ }
+
+ @GetMapping("/hello")
+ public String hello() {
+ return "Hello World! 🚀";
+ }
+}
diff --git a/Helper_projects/spring-boot-https-main/src/main/resources/application.properties b/Helper_projects/spring-boot-https-main/src/main/resources/application.properties
new file mode 100644
index 0000000..da29fa2
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/src/main/resources/application.properties
@@ -0,0 +1,3 @@
+server.ssl.enabled=true
+server.ssl.certificate=${FULLCHAINPEM}
+server.ssl.certificate-private-key=${PRIVKEYPEM}
\ No newline at end of file
diff --git a/Helper_projects/spring-boot-https-main/src/main/resources/application.yml b/Helper_projects/spring-boot-https-main/src/main/resources/application.yml
new file mode 100644
index 0000000..ad1eab0
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/src/main/resources/application.yml
@@ -0,0 +1,5 @@
+server:
+ ssl:
+ enabled: true
+ certificate: ${FULLCHAINPEM}
+ certificate-private-key: ${PRIVKEYPEM}
diff --git a/Helper_projects/spring-boot-https-main/src/test/java/https/SpringBootHttpsApplicationTests.java b/Helper_projects/spring-boot-https-main/src/test/java/https/SpringBootHttpsApplicationTests.java
new file mode 100644
index 0000000..515002c
--- /dev/null
+++ b/Helper_projects/spring-boot-https-main/src/test/java/https/SpringBootHttpsApplicationTests.java
@@ -0,0 +1,13 @@
+package https;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class SpringBootHttpsApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/build.gradle b/build.gradle
index f6dc56e..abbfed1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,6 +15,7 @@ java {
}
repositories {
+
mavenCentral()
}
@@ -36,17 +37,7 @@ dependencies {
implementation("org.flywaydb:flyway-database-postgresql:10.4.1")
implementation 'org.springframework.ai:spring-ai-zhipuai-spring-boot-starter:1.0.0-M6'
-
- //implementation 'org.springframework.ai:spring-ai-starter-model-zhipuai'
- //implementation 'org.springframework.ai:spring-ai-zhipuai-spring-boot-starter'
- //implementation platform("org.springframework.ai:spring-ai-bom:1.0.0-SNAPSHOT")
- // Replace the following with the starter dependencies of specific modules you wish to use
- //implementation 'org.springframework.ai:spring-ai-zhipuai'
- //implementation 'org.springframework.boot:spring-boot-starter-webflux'
- //implementation("org.springframework.ai:spring-ai-spring-boot-autoconfigure:1.0.0-M6")
- // https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-retry
- //implementation("org.springframework.ai:spring-ai-retry:1.0.0")
- //implementation group: 'org.springframework.ai', name: 'spring-ai-spring-boot-autoconfigure', version: '1.0.0-M6'
+ implementation 'org.springframework:spring-mock:2.0.8'
}
apply plugin: 'io.spring.dependency-management'
@@ -56,46 +47,78 @@ apply plugin: 'io.spring.dependency-management'
}
}
-// copyResouces( type:Copy
-
-//tasks copyResouces( type:Copy){
-//
-// def buildDir = "${project.buildDir}"
-//
-// from "{$buildDir}/jambotron-ui/dist/jambotron-ui/browser" // Source directory
-// into "{$buildDir}/build/resources" // Target directory
-// include '**/*'
-//}
-
apply plugin: 'java'
-/*tasks.register('copyResources', Copy) {
- from 'jambotron-ui/dist/jambotron-ui/browser'
- into 'src/main/resources/public'
-}*/
-//processResources.dependsOn(copyResources)
-//compileJava.dependsOn(copyResources)
-//bootRun.dependsOn("flywayMigrate")
-
-//flyway {
-// user='admin'
-// password= 'postgrespw'
-// url = 'jdbc:postgresql://localhost:5432/jambotronDB'
-// driver = 'org.postgresql.Driver'
-// baselineOnMigrate = true
-// locations = ['filesystem:src/main/resources/db.migration/']
-//}
-
-/*sourceSets {
- main {
- resources {
- srcDir 'jambotron-ui/dist/jambotron-ui/browser'
+tasks.register("bootRun_Dev") {
+ group = "_jambotron_build"
+ description = "Runs the Spring Boot application with the dev profile"
+ doFirst {
+ tasks.bootRun.configure {
+ systemProperty("spring.profiles.active", "dev")
}
}
-}*/
+ finalizedBy("bootRun")
+}
-tasks.register('copyAngularBuild', Copy) {
- //dependsOn buildAngular
+
+
+tasks.register('buildAngular_dev', Exec) {
+ group = "_jambotron_build"
+ description = "Builds the Angular application in development mode"
+
+ workingDir './jambotron-ui' // Path to your Angular project (e.g., './angular-app')
+ executable 'npm.cmd' // Use 'npm' for Unix-like systems or 'npm.cmd' for Windows
+
+ // Your Angular build command
+ //do not forget build_dev used port 8082 with proxy for connect to backend rest api
+ args = ['run', 'build_dev']
+
+}
+
+tasks.register('buildAngular_prod', Exec) {
+ group = "_jambotron_build"
+
+ workingDir './jambotron-ui' // Path to your Angular project (e.g., './angular-app')
+ executable 'npm.cmd' // Use 'npm' for Unix-like systems or 'npm.cmd' for Windows
+
+ // Your Angular build command
+ args = ['run', 'build']
+
+}
+
+
+tasks.register('deleteStaticFolder_prod', Delete) {
+ group = "_jambotron_build"
+
+ dependsOn buildAngular_prod
+ def dirName = "src/main/resources/static"
+ file(dirName).list().each {
+ f ->
+ delete "${dirName}/${f}"
+ }
+}
+
+tasks.register('deleteStaticFolder_dev', Delete) {
+ group = "_jambotron_build"
+
+ dependsOn buildAngular_dev
+ def dirName = "src/main/resources/static"
+ file(dirName).list().each {
+ f ->
+ delete "${dirName}/${f}"
+ }
+}
+
+tasks.register('copyAngularBuild_prod', Copy) {
+ group = "_jambotron_build"
+
+ dependsOn deleteStaticFolder_prod
+ from 'jambotron-ui/dist/jambotron-ui' // Path to your Angular dist folder
+ into "src/main/resources/static" // Path INSIDE the WAR
+}
+tasks.register('copyAngularBuild_dev', Copy) {
+ group = "_jambotron_build"
+ dependsOn deleteStaticFolder_dev
from 'jambotron-ui/dist/jambotron-ui' // Path to your Angular dist folder
into "src/main/resources/static" // Path INSIDE the WAR
}
diff --git a/build/libs/jambotron-0.0.1-SNAPSHOT.jar b/build/libs/jambotron-0.0.1-SNAPSHOT.jar
deleted file mode 100644
index a7b3f99..0000000
Binary files a/build/libs/jambotron-0.0.1-SNAPSHOT.jar and /dev/null differ
diff --git a/jambotron-ui/angular.json b/jambotron-ui/angular.json
index 8624583..341a2c7 100644
--- a/jambotron-ui/angular.json
+++ b/jambotron-ui/angular.json
@@ -20,7 +20,6 @@
"outputPath": "dist/jambotron-ui",
"index": "src/index.html",
"main": "src/main.ts",
-
"polyfills": [
"zone.js"
],
@@ -39,15 +38,45 @@
],
"styles": [
"@angular/material/prebuilt-themes/azure-blue.css",
- "src/styles.scss"
+ "src/styles.scss",
+ "node_modules/prismjs/themes/prism-okaidia.css",
+ "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css",
+ "node_modules/prismjs/plugins/line-highlight/prism-line-highlight.css",
+ "node_modules/prismjs/plugins/command-line/prism-command-line.css",
+
+ "node_modules/bootstrap/dist/css/bootstrap.css",
+ "node_modules/bootstrap-markdown/css/bootstrap-markdown.min.css",
+ "node_modules/font-awesome/css/font-awesome.css"
+
+
],
"scripts": [
- "node_modules/viewerjs/dist/viewer.min.js"
+ "node_modules/viewerjs/dist/viewer.min.js",
+ "node_modules/prismjs/prism.js",
+ "node_modules/prismjs/components/prism-csharp.min.js",
+ "node_modules/prismjs/components/prism-css.min.js",
+ "node_modules/prismjs/components/prism-javascript.min.js",
+ "node_modules/prismjs/components/prism-typescript.min.js",
+ "node_modules/prismjs/components/prism-java.js",
+ "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js",
+ "node_modules/prismjs/plugins/line-highlight/prism-line-highlight.js",
+ "node_modules/prismjs/plugins/command-line/prism-command-line.js",
+
+
+ "node_modules/jquery/dist/jquery.js",
+ "node_modules/bootstrap-markdown/js/bootstrap-markdown.js"
+
]
},
"configurations": {
"production": {
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.prod.ts"
+ }
+ ],
"budgets": [
{
"type": "initial",
@@ -65,14 +94,21 @@
"development": {
"optimization": false,
"extractLicenses": false,
- "sourceMap": true
+ "sourceMap": true,
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.development.ts"
+ }
+ ],
+ "index": "src/index_dev.html"
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
- "configurations": {
+ "configurations": {
"production": {
"buildTarget": "jambotron-ui:build:production"
},
diff --git a/jambotron-ui/package-lock.json b/jambotron-ui/package-lock.json
index 2224499..fba3123 100644
--- a/jambotron-ui/package-lock.json
+++ b/jambotron-ui/package-lock.json
@@ -24,8 +24,12 @@
"@ng-bootstrap/ng-bootstrap": "19.0.0",
"@popperjs/core": "2.11.8",
"@primeng/themes": "^19.1.3",
+ "angular-markdown-editor": "^3.1.1",
"bootstrap": "5.3.6",
+ "cropperjs": "^2.0.1",
"express": "^5.1.0",
+ "file-saver": "^2.0.5",
+ "ngx-filesaver": "^20.0.0",
"ngx-markdown": "^20.0.0",
"ngx-scrollbar": "18.0.0",
"primeng": "^19.1.3",
@@ -40,6 +44,7 @@
"@angular/cli": "^20.0.2",
"@angular/compiler-cli": "^20.0.0",
"@types/express": "^5.0.1",
+ "@types/file-saver": "^2.0.7",
"@types/jasmine": "~5.1.0",
"@types/node": "^20.17.19",
"jasmine-core": "~5.7.0",
@@ -2683,6 +2688,126 @@
"node": ">=0.1.90"
}
},
+ "node_modules/@cropper/element": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.0.1.tgz",
+ "integrity": "sha512-Jn1hR7XWzWQM/QfXRGMGzdkJ2gG/UcLdQPZQ7OKs0JiFfRzKpzu4u/nYrXHeH3MM2iOslLqh2kqYju6mjZLMJQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-canvas": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.0.1.tgz",
+ "integrity": "sha512-OKxq/O0HL9W2JegOsc2zh1NRpERZcLM5+M8aQ/eXdmMcfi1lzosPftag3Irp6pTsVpwV6B6ypIxKESzJ4ci9Fw==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-crosshair": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-crosshair/-/element-crosshair-2.0.1.tgz",
+ "integrity": "sha512-bS5msU9cTU/jf1/kDw+QJmEM9/rw8IgOdpolR85iMVUCR8sRcLa0wgom42MBHcpBYB6hvL5YfiOeXZ7lHIYMpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-grid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-grid/-/element-grid-2.0.1.tgz",
+ "integrity": "sha512-ayqCvYQJ+GVT31HhFpttzHabW1T/LsIwLJY5PLTMG0cEZLw/E8ihg8mxctjZbo852D7oEePbz6/2SeuCb1018Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-handle": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-handle/-/element-handle-2.0.1.tgz",
+ "integrity": "sha512-fdifyyPIaR9S2eQ7qPHuM8fX8uToAfBsi8vQlR9EM+oJkDNil0uO4rWyArLWEtlr0/q7U0OvsufcuJ7ffqfmpg==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-image": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-image/-/element-image-2.0.1.tgz",
+ "integrity": "sha512-gPj5Sl2T8Cno198Cz3F3TDfcYoALW3yJ3fV6PHXmhMnX8sBkL7J441do7Vwkg0mEd2CogCtTLAf+p7ljdV0kgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/element-canvas": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-selection": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-selection/-/element-selection-2.0.1.tgz",
+ "integrity": "sha512-atv+Aeq2N2eWawelIRPGh1kYFdNrpb0QkUPPheGxz1ImfxpLdcHO9gb9T5noQijizUW2G0pNvts4ZaITQ0I71Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/element-canvas": "^2.0.1",
+ "@cropper/element-image": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-shade": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-shade/-/element-shade-2.0.1.tgz",
+ "integrity": "sha512-YIYgJ690NdFQ6wJLRFh/EySNVxGFKArncQ4FrsJ3yHU+ShgtOKz4FpjFLpqJRJB9swoVbD3WKTimGyzXrwjZrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/element-canvas": "^2.0.1",
+ "@cropper/element-selection": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/element-viewer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/element-viewer/-/element-viewer-2.0.1.tgz",
+ "integrity": "sha512-HDj25l08pWi/AO6El/OqfQHBpBC4Lh5NEnQN1SOldsmxEwt27Ubv6ndDsF8LkTK7XPwjjZRpyQPyfig4w8L2JQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/element-canvas": "^2.0.1",
+ "@cropper/element-image": "^2.0.1",
+ "@cropper/element-selection": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/elements/-/elements-2.0.1.tgz",
+ "integrity": "sha512-paFbBLXTKXNngn1yDi2ZIf+FO1pIEQXyBntmqOjuxqtG73KuEKv633wsJPFpj958bgcfSakgBbF80j+3nHbPug==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/element": "^2.0.1",
+ "@cropper/element-canvas": "^2.0.1",
+ "@cropper/element-crosshair": "^2.0.1",
+ "@cropper/element-grid": "^2.0.1",
+ "@cropper/element-handle": "^2.0.1",
+ "@cropper/element-image": "^2.0.1",
+ "@cropper/element-selection": "^2.0.1",
+ "@cropper/element-shade": "^2.0.1",
+ "@cropper/element-viewer": "^2.0.1"
+ }
+ },
+ "node_modules/@cropper/utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@cropper/utils/-/utils-2.0.1.tgz",
+ "integrity": "sha512-A9RnAFmgNF5aZk5q2VZnFnHtXWu1kPyEN0LVsX8wJ2LBRu2nyETKwz+ZXVsVWliktToCaYojHKrS+6/HODyEZA==",
+ "license": "MIT"
+ },
"node_modules/@discoveryjs/json-ext": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz",
@@ -5822,6 +5947,13 @@
"@types/send": "*"
}
},
+ "node_modules/@types/file-saver": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
+ "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
@@ -6286,6 +6418,27 @@
"ajv": "^8.8.2"
}
},
+ "node_modules/angular-markdown-editor": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/angular-markdown-editor/-/angular-markdown-editor-3.1.1.tgz",
+ "integrity": "sha512-0Ho8HZaR95guBwZHDPw9k4Ac2zKW4hr1kl676uaoBsMZ82YHcl3VNb2TTwwui5qtcUWYmdXA++2DmZInj2GIlA==",
+ "license": "MIT",
+ "dependencies": {
+ "bootstrap": ">=4.6.2",
+ "bootstrap-markdown": "github:refactory-id/bootstrap-markdown",
+ "font-awesome": "^4.7.0",
+ "jquery": "^3.7.0",
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=14.17.0",
+ "npm": ">=6.14.13"
+ },
+ "funding": {
+ "type": "ko_fi",
+ "url": "https://ko-fi.com/ghiscoding"
+ }
+ },
"node_modules/ansi-colors": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
@@ -6623,6 +6776,11 @@
"@popperjs/core": "^2.11.8"
}
},
+ "node_modules/bootstrap-markdown": {
+ "version": "2.10.0",
+ "resolved": "git+ssh://git@github.com/refactory-id/bootstrap-markdown.git#a496d34b9bd34451c8315a850472f794c8df7d53",
+ "license": "Apache-2.0"
+ },
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -7558,6 +7716,16 @@
}
}
},
+ "node_modules/cropperjs": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.0.1.tgz",
+ "integrity": "sha512-hiJwk2SCPZqxMA7aR3byzLpYUqOrQo+ihMk8k/WRm/xe/LX8wNzAIzMwEB/NEGJYA6sbewxW9TUlrRUYi/2Ipg==",
+ "license": "MIT",
+ "dependencies": {
+ "@cropper/elements": "^2.0.1",
+ "@cropper/utils": "^2.0.1"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -9091,6 +9259,12 @@
}
}
},
+ "node_modules/file-saver": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
+ "license": "MIT"
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -9176,6 +9350,15 @@
}
}
},
+ "node_modules/font-awesome": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
+ "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
+ "license": "(OFL-1.1 AND MIT)",
+ "engines": {
+ "node": ">=0.10.3"
+ }
+ },
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@@ -10307,6 +10490,12 @@
"jiti": "bin/jiti.js"
}
},
+ "node_modules/jquery": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
+ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
+ "license": "MIT"
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -12015,6 +12204,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/ngx-filesaver": {
+ "version": "20.0.0",
+ "resolved": "https://registry.npmjs.org/ngx-filesaver/-/ngx-filesaver-20.0.0.tgz",
+ "integrity": "sha512-84vRFGko1BmpyerAmLLggjx5ZiHvaWadJKlb2n0hOZlnzlTNZeI3zHKY9vqtIRkxCX70vWHTB8JwV5VvvkB2uQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@types/file-saver": "^2.0.0",
+ "file-saver": "^2.0.0"
+ }
+ },
"node_modules/ngx-markdown": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-20.0.0.tgz",
diff --git a/jambotron-ui/package.json b/jambotron-ui/package.json
index 27d5ca8..4e9a8bc 100644
--- a/jambotron-ui/package.json
+++ b/jambotron-ui/package.json
@@ -3,9 +3,11 @@
"version": "0.0.0",
"scripts": {
"ng": "ng",
- "start": "ng serve",
+ "start": "ng serve --proxy-config src/proxy.conf.json",
+ "start_prod": "ng serve --configuration production",
"build": "ng build",
"watch": "ng build --watch --configuration development",
+ "build_dev": "ng build --configuration development",
"test": "ng test",
"serve:ssr:jambotron-ui": "node dist/jambotron-ui/server/server.mjs"
},
@@ -22,16 +24,14 @@
"@angular/platform-server": "^20.0.0",
"@angular/router": "^20.0.0",
"@angular/ssr": "^20.0.2",
- "@ant-design/icons-angular": "19.0.0",
- "@hreimer/angular-image-viewer": "^0.14.1",
"@ng-bootstrap/ng-bootstrap": "19.0.0",
"@popperjs/core": "2.11.8",
"@primeng/themes": "^19.1.3",
+ "angular-markdown-editor": "^3.1.1",
"bootstrap": "5.3.6",
+ "cropperjs": "^2.0.1",
"express": "^5.1.0",
"ngx-markdown": "^20.0.0",
- "ngx-scrollbar": "18.0.0",
- "primeng": "^19.1.3",
"prismjs": "^1.30.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
@@ -43,6 +43,7 @@
"@angular/cli": "^20.0.2",
"@angular/compiler-cli": "^20.0.0",
"@types/express": "^5.0.1",
+ "@types/file-saver": "^2.0.7",
"@types/jasmine": "~5.1.0",
"@types/node": "^20.17.19",
"jasmine-core": "~5.7.0",
diff --git a/jambotron-ui/src/app/admin-module/admin.component/admin.component.html b/jambotron-ui/src/app/admin-module/admin.component/admin.component.html
deleted file mode 100644
index 3876019..0000000
--- a/jambotron-ui/src/app/admin-module/admin.component/admin.component.html
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/jambotron-ui/src/app/admin-module/admin.component/admin.component.scss b/jambotron-ui/src/app/admin-module/admin.component/admin.component.scss
deleted file mode 100644
index 3cde704..0000000
--- a/jambotron-ui/src/app/admin-module/admin.component/admin.component.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-.example-container {
- width: 500px;
- height: 300px;
- border: 1px solid rgba(0, 0, 0, 0.5);
-}
-
-.example-sidenav-content {
- display: flex;
- height: 100%;
- align-items: center;
- justify-content: center;
-}
-
-.example-sidenav {
- padding: 20px;
-}
-
-.pc-sidebar{
- top: 120px;
-}
-
-
diff --git a/jambotron-ui/src/app/app.component.html b/jambotron-ui/src/app/app.component.html
deleted file mode 100644
index 85204c4..0000000
--- a/jambotron-ui/src/app/app.component.html
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
- @if (showAdminBoard) {
-
- }
-
- @if (showAdminBoard) {
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/jambotron-ui/src/app/app.component.scss b/jambotron-ui/src/app/app.component.scss
deleted file mode 100644
index fee240c..0000000
--- a/jambotron-ui/src/app/app.component.scss
+++ /dev/null
@@ -1,23 +0,0 @@
-//#header_panel{
-// background-color: #181d1f;
-//}
-.example-spacer {
- flex: 1 1 auto;
-}
-
-mat-toolbar{
- background-color: rgba(153,153,153,0.16);
- backdrop-filter: blur(8px);
-}
-
-.fixed-top {
- position: fixed;
- top: 0;
- right: 0;
- left: 0;
- z-index: 1000; // Ensure toolbar stays above other content
-}
-.router_outlet_padding{
- padding-top: 60px;
- scroll-padding-inline: 40%;
-}
diff --git a/jambotron-ui/src/app/app.component.spec.ts b/jambotron-ui/src/app/app.component.spec.ts
deleted file mode 100644
index 98d898f..0000000
--- a/jambotron-ui/src/app/app.component.spec.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-import { AppComponent } from './app.component';
-
-describe('AppComponent', () => {
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- imports: [
- RouterTestingModule
- ],
- declarations: [
- AppComponent
- ],
- }).compileComponents();
- });
-
- it('should create the app', () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app = fixture.componentInstance;
- expect(app).toBeTruthy();
- });
-
- // it(`should have as title 'spring-angular-ui'`, () => {
- // const fixture = TestBed.createComponent(AppComponent);
- // const app = fixture.componentInstance;
- // expect(app.title).toEqual('spring-angular-ui');
- // });
-
- it('should render title', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled = fixture.nativeElement;
- expect(compiled.querySelector('.content span').textContent).toContain('spring-angular-ui app is running!');
- });
-});
diff --git a/jambotron-ui/src/app/app.component.ts b/jambotron-ui/src/app/app.component.ts
deleted file mode 100644
index b51a698..0000000
--- a/jambotron-ui/src/app/app.component.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
-import { TokenStorageService } from './services/token-storage.service';
-import {MatDialog} from "@angular/material/dialog";
-import {DialogComponent} from "./components/dialog/dialog.component";
-import {AuthService} from "./services/auth.service";
-import {Subscription} from "rxjs";
-import {EventBusService} from "./_shared/event-bus.service";
-import {RouterLink, RouterOutlet} from '@angular/router';
-import {MatButton} from '@angular/material/button';
-import {MatToolbar} from '@angular/material/toolbar';
-import {MatIcon} from '@angular/material/icon';
-
-@Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
- imports: [
- RouterOutlet,
- MatIcon,
- MatButton,
- RouterLink,
- MatToolbar
- ],
- styleUrls: ['./app.component.scss']
-})
-export class AppComponent implements OnInit {
- readonly dialog = inject(MatDialog);
- //private authService = new AuthService(provideHttpClient())
- public dialogData : DialogData = {password: "", username: ""};
-
- private roles: string[] = [];
- isLoggedIn = false;
- showAdminBoard = false;
- showModeratorBoard = false;
- username?: string;
-
- eventBusSub?: Subscription;
- private errorMessage: any;
- private isLoginFailed: boolean = false;
-
- constructor(
- private storageService: TokenStorageService,
- private authService: AuthService,
- private eventBusService: EventBusService
- ) {}
-
- ngOnInit(): void {
- this.isLoggedIn = this.storageService.isLoggedIn();
-
- if (this.isLoggedIn) {
- const user = this.storageService.getUser();
- this.roles = user.roles;
-
- this.showAdminBoard = true;//this.roles.includes('ROLE_ADMIN');
- this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR');
-
- this.username = user.username;
- }
-
- this.eventBusSub = this.eventBusService.on('logout', () => {
- this.logout();
- });
- }
-
- logout(): void {
- this.authService.logout().subscribe({
- next: res => {
- console.log(res);
- this.storageService.clean();
-
- window.location.reload();
- },
- error: err => {
- console.log(err);
- }
- });
- }
-
- openDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
-
- let dialogRef = this.dialog.open(DialogComponent, {
- width: '350px',
- enterAnimationDuration,
- exitAnimationDuration,
- data: {username: this.dialogData.username, password: this.dialogData.password}
- });
-
- dialogRef.afterClosed().subscribe(result => {
- console.log('The dialog was closed');
- if(result== null){
- return;
- }
- this.dialogData = result;
-
- this.authService.login(this.dialogData.username, this.dialogData.password).subscribe(
- data => {
- this.storageService.saveToken(data.accessToken);
- this.storageService.saveUser(data);
-
- this.isLoginFailed = false;
- this.isLoggedIn = true;
- let user = this.storageService.getUser();
- this.roles = user.roles;
- //this.username = user.username;
- this.reloadPage();
- },
- err => {
- this.errorMessage = err.error.message;
- this.isLoginFailed = true;
- }
- );
- });
-
- }
- reloadPage(): void {
- window.location.reload();
- }
-
-}
-export interface DialogData {
- username: string;
- password: string;
-}
-
diff --git a/jambotron-ui/src/app/app.config.server.ts b/jambotron-ui/src/app/app.config.server.ts
deleted file mode 100644
index 41031f1..0000000
--- a/jambotron-ui/src/app/app.config.server.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
-import { provideServerRendering, withRoutes } from '@angular/ssr';
-import { appConfig } from './app.config';
-import { serverRoutes } from './app.routes.server';
-
-const serverConfig: ApplicationConfig = {
- providers: [
- provideServerRendering(withRoutes(serverRoutes))
- ]
-};
-
-export const config = mergeApplicationConfig(appConfig, serverConfig);
diff --git a/jambotron-ui/src/app/app.config.ts b/jambotron-ui/src/app/app.config.ts
index 6373f43..b527fc1 100644
--- a/jambotron-ui/src/app/app.config.ts
+++ b/jambotron-ui/src/app/app.config.ts
@@ -14,13 +14,11 @@ import Aura from '@primeng/themes/aura';
import {provideAnimations} from '@angular/platform-browser/animations';
import {provideMarkdown} from 'ngx-markdown';
-import 'prismjs';
-import 'prismjs/components/prism-typescript.min.js';
-import 'prismjs/plugins/line-numbers/prism-line-numbers.js';
-import 'prismjs/plugins/line-highlight/prism-line-highlight.js';
+import {authInterceptorProviders} from './helpers/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
+ authInterceptorProviders,
provideBrowserGlobalErrorListeners(),
provideAnimations(),
provideZoneChangeDetection({ eventCoalescing: true }),
diff --git a/jambotron-ui/src/app/app.html b/jambotron-ui/src/app/app.html
index 0680b43..c6957c2 100644
--- a/jambotron-ui/src/app/app.html
+++ b/jambotron-ui/src/app/app.html
@@ -1 +1,2 @@
+
diff --git a/jambotron-ui/src/app/app.module.ts b/jambotron-ui/src/app/app.module.ts
index 08f57c9..fb4aa21 100644
--- a/jambotron-ui/src/app/app.module.ts
+++ b/jambotron-ui/src/app/app.module.ts
@@ -7,15 +7,17 @@ import { BrowserAnimationsModule }
import { MatIconModule } from '@angular/material/icon';
import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
-import {MainComponent} from './main-module/main.component/main.component';
+import {MainComponent} from './modules/main-module/main.component/main.component';
import {authInterceptorProviders} from './helpers/auth.interceptor';
-import {AdminComponent} from './admin-module/admin.component/admin.component';
-import {AdminWelcomeComponent} from './admin-module/admin-welcome.component/admin-welcome.component';
-import {SettingsComponent} from './admin-module/settings.component/settings.component';
-import {SystemComponent} from './admin-module/system.component/system.component';
+import {AdminComponent} from './modules/admin-module/admin.component/admin.component';
+import {AdminWelcomeComponent} from './modules/admin-module/admin-welcome.component/admin-welcome.component';
+import {SettingsComponent} from './modules/admin-module/settings.component/settings.component';
+import {SystemComponent} from './modules/admin-module/system.component/system.component';
import {AppRoutingModule} from './app.routes';
import {App} from './app';
import {CustomHttpInterceptor} from './helpers/custom-http-interceptor';
+import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
+import {MatFormFieldModule} from '@angular/material/form-field';
@NgModule({
@@ -32,7 +34,8 @@ import {CustomHttpInterceptor} from './helpers/custom-http-interceptor';
AdminWelcomeComponent,
SettingsComponent,
SystemComponent,
-
+ AngularMarkdownEditorModule.forRoot({ iconlibrary: 'fa' }),
+ MatFormFieldModule
],
providers: [
authInterceptorProviders,provideHttpClient(withInterceptorsFromDi()),{
diff --git a/jambotron-ui/src/app/app.routes.server.ts b/jambotron-ui/src/app/app.routes.server.ts
deleted file mode 100644
index ffd37b1..0000000
--- a/jambotron-ui/src/app/app.routes.server.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { RenderMode, ServerRoute } from '@angular/ssr';
-
-export const serverRoutes: ServerRoute[] = [
- {
- path: '**',
- renderMode: RenderMode.Prerender
- }
-];
diff --git a/jambotron-ui/src/app/app.routes.ts b/jambotron-ui/src/app/app.routes.ts
index a1a9723..8f9c71e 100644
--- a/jambotron-ui/src/app/app.routes.ts
+++ b/jambotron-ui/src/app/app.routes.ts
@@ -1,23 +1,21 @@
import {RouterModule, Routes} from '@angular/router';
-import {AdminComponent} from './layouts/admin-layout/admin-layout.component';
-import {MainComponent} from './main-module/main.component/main.component';
import {NgModule} from '@angular/core';
+import {environment} from '../environments/environment';
export const routes: Routes = [
{
path: '',
- redirectTo: 'main/generate-image',
+ redirectTo: environment.default_page,
pathMatch: 'full'
},
{
path: 'main',
loadChildren: () =>
- import('./main-module/main.module').then((m) => m.MainModule),
+ import('./modules/main-module/main.module').then((m) => m.MainModule),
}
-
];
@NgModule({
- imports: [RouterModule.forRoot(routes)],//, { useHash: true }
+ imports: [RouterModule.forRoot(routes, { useHash: true}) ],
exports: [RouterModule],
})
export class AppRoutingModule {}
diff --git a/jambotron-ui/src/app/app.ts b/jambotron-ui/src/app/app.ts
index 649c4a1..571584f 100644
--- a/jambotron-ui/src/app/app.ts
+++ b/jambotron-ui/src/app/app.ts
@@ -1,16 +1,21 @@
-import { Component } from '@angular/core';
+import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import { RouterOutlet } from '@angular/router';
+import {SpinnerComponent} from './components/spinner/spinner.component';
@Component({
selector: 'app-root',
templateUrl: './app.html',
imports: [
- RouterOutlet
+ RouterOutlet,
+ SpinnerComponent
],
- styleUrl: './app.scss'
+ styleUrl: './app.scss',
+ schemas:[CUSTOM_ELEMENTS_SCHEMA]
})
export class App {
protected title = 'jambotron-ui';
- constructor() { }
+ constructor() {
+
+ }
}
diff --git a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.html b/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.html
deleted file mode 100644
index 2942a01..0000000
--- a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.html
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Add tutorial
-
-
-
- Title
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Tutorial was submitted successfully!
-
-
-
-
diff --git a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.spec.ts b/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.spec.ts
deleted file mode 100644
index b6a07c8..0000000
--- a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AddTutorialComponent } from './add-tutorial.component';
-
-describe('AddTutorialComponent', () => {
- let component: AddTutorialComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ AddTutorialComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(AddTutorialComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.ts b/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.ts
deleted file mode 100644
index 3fe0470..0000000
--- a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit} from '@angular/core';
-import {Tutorial} from '../../models/tutorial.model';
-import {TutorialService} from '../../services/tutorial.service';
-import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
-import {FormsModule} from '@angular/forms';
-import {MatButton} from '@angular/material/button';
-import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
-import {NgIf} from '@angular/common';
-import {MatTab, MatTabGroup} from '@angular/material/tabs';
-import {MarkdownComponent} from 'ngx-markdown';
-
-@Component({
- selector: 'app-add-tutorial',
- templateUrl: './add-tutorial.component.html',
- styleUrls: ['./add-tutorial.component.scss'],
- imports: [
- MatCardActions,
- FormsModule,
- MatCard,
- MatCardHeader,
- MatCardContent,
- MatFormField,
- MatLabel,
-
- MatButton,
- MatInput,
- NgIf,
- MatLabel,
- MatTabGroup,
- MatTab,
- MarkdownComponent,
-
- ],
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
-})
-export class AddTutorialComponent implements OnInit {
-
- tutorial: Tutorial = {
- title: '',
- description: '',
- published: false
- };
- submitted = false
-
- markdown = `## Markdown __rulez__!
----
-
-### Syntax highlight
-\`\`\`typescript
-const language = 'typescript';
-\`\`\`
-
-### Lists
-1. Ordered list
-2. Another bullet point
- - Unordered list
- - Another unordered bullet
-
-### Blockquote
-> Blockquote to the max`;
-
-
-
- constructor(private tutorialService: TutorialService) {
- this.tutorial.description = this.markdown;
- }
-
- ngOnInit(): void {
- }
-
- saveTutorial(): void {
- const data = {
- title: this.tutorial.title,
- description: this.tutorial.description
- };
-
- this.tutorialService.create(data)
- .subscribe(
- response => {
- console.log(response);
- this.submitted = true;
- },
- error => {
- console.log(error);
- });
- }
-
- newTutorial(): void {
- this.submitted = false;
- this.tutorial = {
- title: '',
- description: '',
- published: false
- };
- }
-}
diff --git a/jambotron-ui/src/app/components/article-comments/article-comments.html b/jambotron-ui/src/app/components/article-comments/article-comments.html
deleted file mode 100644
index dfba77e..0000000
--- a/jambotron-ui/src/app/components/article-comments/article-comments.html
+++ /dev/null
@@ -1,10 +0,0 @@
-Comments
-
-
-
diff --git a/jambotron-ui/src/app/components/article-comments/article-comments.scss b/jambotron-ui/src/app/components/article-comments/article-comments.scss
deleted file mode 100644
index efc2191..0000000
--- a/jambotron-ui/src/app/components/article-comments/article-comments.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.comment {
- padding: 15px;
- margin-left: 30px;
- background-color: paleturquoise;
- border-radius: 20px;
-}
diff --git a/jambotron-ui/src/app/components/article-comments/article-comments.ts b/jambotron-ui/src/app/components/article-comments/article-comments.ts
deleted file mode 100644
index 958bf9e..0000000
--- a/jambotron-ui/src/app/components/article-comments/article-comments.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-article-comments',
- imports: [],
- templateUrl: './article-comments.html',
- styleUrl: './article-comments.scss'
-})
-export class ArticleComments {
-
-}
diff --git a/jambotron-ui/src/app/components/board-admin/board-admin.component.html b/jambotron-ui/src/app/components/board-admin/board-admin.component.html
deleted file mode 100644
index c986337..0000000
--- a/jambotron-ui/src/app/components/board-admin/board-admin.component.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
Users List
-
- -
- {{ row.username }}
-
- {{ row.email}}
- {{ row.password}}
-
-
-
-
-
-
-
-
diff --git a/jambotron-ui/src/app/components/board-admin/board-admin.component.ts b/jambotron-ui/src/app/components/board-admin/board-admin.component.ts
deleted file mode 100644
index 1fc3f98..0000000
--- a/jambotron-ui/src/app/components/board-admin/board-admin.component.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-import {Component, computed, CUSTOM_ELEMENTS_SCHEMA, model, OnInit} from '@angular/core';
-
-import {User} from "../../models/user.model";
-import {MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRow} from "@angular/material/chips";
-import {
- MatAutocomplete,
- MatAutocompleteSelectedEvent,
- MatAutocompleteTrigger,
- MatOption
-} from "@angular/material/autocomplete";
-import {COMMA, ENTER} from "@angular/cdk/keycodes";
-import {AsyncPipe, NgForOf} from "@angular/common";
-import {FormControl, ReactiveFormsModule} from "@angular/forms";
-import {Observable, startWith} from "rxjs";
-import {map} from "rxjs/operators";
-import {UserService} from '../../services/user.service';
-import {RolesService} from '../../services/roles.service';
-import {Role} from '../../models/role';
-import {MatIcon} from '@angular/material/icon';
-import {MatFormField} from '@angular/material/form-field';
-
-
-
-@Component({
- selector: 'app-board-admin',
- templateUrl: './board-admin.component.html',
- styleUrls: ['./board-admin.component.scss'],
-
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
- imports: [
- MatAutocomplete,
- ReactiveFormsModule,
- MatOption,
- NgForOf,
- MatFormField,
- MatChipGrid,
-
- MatChipRow,
- MatIcon,
- MatChipInput,
- MatAutocompleteTrigger
- ]
-})
-export class BoardAdminComponent implements OnInit {
- private gridApi: any;
-
- readonly separatorKeysCodes: number[] = [ENTER, COMMA];
- rowData?: User[];
- allRoles: any;
-
- readonly currentRole = model('');
-
- protected myControl = new FormControl('');
- protected ac = new FormControl('');
-
-
- constructor(private userService: UserService, private roleService: RolesService ) {
- this.getAllRoles();
- }
-
- ngOnInit(): void {
-
- //this.myControl.valueChanges.pipe(
- // startWith(''),
- // map(value => this._filter(value || '')),
- //);
-
- this.userService.getAdminBoard().subscribe(
- (data : any) => {
- this.rowData = data;
- },
- (err : any)=> {
- this.rowData = JSON.parse(err.error).message;
- }
-
- );
- }
-
- filteredRoles(roles : Role[]):any {
- return this.allRoles.filter(
- (r:Role) => !roles.some((item) => item.id === r.id),
- );
-
- }
-
- // private _filter(value: string): string[] {
- // const filterValue = value.toLowerCase();
- // this.ac.
- // return this.options.filter(option => option.toLowerCase().includes(filterValue));
- //}
-
- getAllRoles():any{
- this.roleService.getAllRoles().subscribe(
- (data : any) => {
- this.allRoles = data;
- },
- (err : any)=> {
- this.allRoles = JSON.parse(err.error).message;
- }
- );
- }
-
- remove(role: string): void {
- // this.fruits.update(fruits => {
- // const index = fruits.indexOf(fruit);
- // if (index < 0) {
- // return fruits;
- // }
-
- // fruits.splice(index, 1);
- // this.announcer.announce(`Removed ${fruit}`);
- // return [...fruits];
-
- }
-
-
-
- add(event: MatChipInputEvent): void {
- const value = (event.value || '').trim();
-
- // Add our fruit
- if (value) {
- // this.fruits.update(fruits => [...fruits, value]);
- }
-
- // Clear the input value
- this.currentRole.set('');
- }
-
- selected(roles : Role[], event: MatAutocompleteSelectedEvent): void {
-
- roles.push(event.option.value);
- this.currentRole.set('');
- event.option.deselect();
- }
-
-
- change($event: Event, roles: Role[]) {
-
- roles.filter(
- (r:Role) => !roles.some((item) => item.name?.toLowerCase() === r.id),
- )
- }
-}
diff --git a/jambotron-ui/src/app/components/board-moderator/board-moderator.component.html b/jambotron-ui/src/app/components/board-moderator/board-moderator.component.html
deleted file mode 100644
index 16287a2..0000000
--- a/jambotron-ui/src/app/components/board-moderator/board-moderator.component.html
+++ /dev/null
@@ -1 +0,0 @@
-board-moderator works!
diff --git a/jambotron-ui/src/app/components/board-moderator/board-moderator.component.spec.ts b/jambotron-ui/src/app/components/board-moderator/board-moderator.component.spec.ts
deleted file mode 100644
index dff7faa..0000000
--- a/jambotron-ui/src/app/components/board-moderator/board-moderator.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { BoardModeratorComponent } from './board-moderator.component';
-
-describe('BoardModeratorComponent', () => {
- let component: BoardModeratorComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ BoardModeratorComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(BoardModeratorComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/components/board-moderator/board-moderator.component.ts b/jambotron-ui/src/app/components/board-moderator/board-moderator.component.ts
deleted file mode 100644
index daad28f..0000000
--- a/jambotron-ui/src/app/components/board-moderator/board-moderator.component.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-board-moderator',
- templateUrl: './board-moderator.component.html',
- styleUrls: ['./board-moderator.component.scss'],
- standalone: false
-})
-export class BoardModeratorComponent implements OnInit {
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/jambotron-ui/src/app/components/board-user/board-user.component.html b/jambotron-ui/src/app/components/board-user/board-user.component.html
deleted file mode 100644
index 76c5fa7..0000000
--- a/jambotron-ui/src/app/components/board-user/board-user.component.html
+++ /dev/null
@@ -1 +0,0 @@
-board-user works!
diff --git a/jambotron-ui/src/app/components/board-user/board-user.component.spec.ts b/jambotron-ui/src/app/components/board-user/board-user.component.spec.ts
deleted file mode 100644
index 23555bb..0000000
--- a/jambotron-ui/src/app/components/board-user/board-user.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { BoardUserComponent } from './board-user.component';
-
-describe('BoardUserComponent', () => {
- let component: BoardUserComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ BoardUserComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(BoardUserComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/components/board-user/board-user.component.ts b/jambotron-ui/src/app/components/board-user/board-user.component.ts
deleted file mode 100644
index 487eddf..0000000
--- a/jambotron-ui/src/app/components/board-user/board-user.component.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-board-user',
- templateUrl: './board-user.component.html',
- styleUrls: ['./board-user.component.scss'],
- standalone: false
-})
-export class BoardUserComponent implements OnInit {
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.html b/jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.html
deleted file mode 100644
index 77f0cb0..0000000
--- a/jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.html
+++ /dev/null
@@ -1,43 +0,0 @@
-@for (breadcrumb of navigationList; track breadcrumb; let last = $last) {
- @if (last && breadcrumb.breadcrumbs !== false) {
-
- }
-}
diff --git a/jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.ts b/jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.ts
deleted file mode 100644
index d119640..0000000
--- a/jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-// Angular Import
-import { Component, Input, inject, input } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { NavigationEnd, Router, RouterModule, Event } from '@angular/router';
-import { Title } from '@angular/platform-browser';
-
-// project import
-//import { NavigationItem, NavigationItems } from 'src/app/theme/layouts/admin-layout/navigation/navigation';
-
-// icons
-import { IconService } from '@ant-design/icons-angular';
-import { GlobalOutline, NodeExpandOutline } from '@ant-design/icons-angular/icons';
-import {NavigationItem, NavigationItems} from '../../layouts/admin-layout/navigation/navigation';
-
-interface titleType {
- // eslint-disable-next-line
- url: any;
- title: string;
- breadcrumbs: unknown;
- type: string;
- link?: string | undefined;
- description?: string | undefined;
- path?: string | undefined;
-}
-
-@Component({
- selector: 'app-breadcrumb',
- imports: [CommonModule, RouterModule],
- templateUrl: './breadcrumb.component.html',
- styleUrls: ['./breadcrumb.component.scss']
-})
-export class BreadcrumbComponent {
- private route = inject(Router);
- private titleService = inject(Title);
- private iconService = inject(IconService);
-
- // public props
- @Input() type: string;
- dashboard = input(true);
- Component = input(false);
-
- navigations: NavigationItem[];
- ComponentNavigations: NavigationItem[] = [];
- breadcrumbList: Array = [];
- navigationList!: titleType[];
- componentList!: titleType[];
-
- // constructor
- constructor() {
- this.navigations = NavigationItems;
- this.type = 'theme1';
- this.setBreadcrumb();
- this.iconService.addIcon(...[GlobalOutline, NodeExpandOutline]);
- }
-
- // public method
- setBreadcrumb() {
- this.route.events.subscribe((router: Event) => {
- if (router instanceof NavigationEnd) {
- const activeLink = router.url;
- const breadcrumbList = this.filterNavigation(this.navigations, activeLink);
-
- this.navigationList = breadcrumbList;//breadcrumbList.slice(breadcrumbList.length - 1, breadcrumbList.length);
- const title = breadcrumbList[breadcrumbList.length - 1]?.title || 'Welcome';
- this.titleService.setTitle(title );
- }
- });
- }
-
- filterNavigation(navItems: NavigationItem[], activeLink: string): titleType[] {
- for (const navItem of navItems) {
- if (navItem.type === 'item' && 'url' in navItem && navItem.url === activeLink) {
- return [
- {
- url: 'url' in navItem ? navItem.url : false,
- title: navItem.title,
- link: navItem.link,
- description: navItem.description,
- path: navItem.path,
- breadcrumbs: 'breadcrumbs' in navItem ? navItem.breadcrumbs : true,
- type: navItem.type
- }
- ];
- }
- if ((navItem.type === 'group' || navItem.type === 'collapse') && 'children' in navItem) {
- const breadcrumbList = this.filterNavigation(navItem.children!, activeLink);
- if (breadcrumbList.length > 0) {
- breadcrumbList.unshift({
- url: 'url' in navItem ? navItem.url : false,
- title: navItem.title,
- link: navItem.link,
- path: navItem.path,
- description: navItem.description,
- breadcrumbs: 'breadcrumbs' in navItem ? navItem.breadcrumbs : true,
- type: navItem.type
- });
- return breadcrumbList;
- }
- }
- }
- return [];
- }
-}
diff --git a/jambotron-ui/src/app/components/card/card.component.html b/jambotron-ui/src/app/components/card/card.component.html
deleted file mode 100644
index 2716fa8..0000000
--- a/jambotron-ui/src/app/components/card/card.component.html
+++ /dev/null
@@ -1,16 +0,0 @@
-
- @if (showHeader()) {
-
- }
- @if (showContent()) {
-
-
-
- }
-
diff --git a/jambotron-ui/src/app/components/card/card.component.ts b/jambotron-ui/src/app/components/card/card.component.ts
deleted file mode 100644
index 85f8cd3..0000000
--- a/jambotron-ui/src/app/components/card/card.component.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-// Angular import
-import { Component, ContentChild, ElementRef, TemplateRef, input } from '@angular/core';
-import { CommonModule } from '@angular/common';
-
-@Component({
- selector: 'app-card',
- standalone: true,
- imports: [CommonModule],
- templateUrl: './card.component.html',
- styleUrls: ['./card.component.scss']
-})
-export class CardComponent {
- // public props
- /**
- * Title of card. It will be visible at left side of card header
- */
- cardTitle = input();
-
- /**
- * Class to be applied at card level
- */
- cardClass = input();
-
- /**
- * To hide content from card
- */
- showContent = input(true);
-
- /**
- * Class to be applied at card content.
- */
- blockClass = input();
-
- /**
- * Class to be applied on card header
- */
- headerClass = input();
-
- /**
- * To hide header from card
- */
- showHeader = input(true);
-
- /**
- * padding around card content. default in px
- */
- padding = input(20); // set default to 24 px
-
- /**
- * Template reference of header actions on custom header
- */
- @ContentChild('headerOptionsTemplate') headerOptionsTemplate!: TemplateRef;
-
- /**
- * Template reference of header actions besides title at left
- */
- @ContentChild('headerTitleTemplate') headerTitleTemplate!: TemplateRef;
-}
diff --git a/jambotron-ui/src/app/components/dialog/dialog.component.ts b/jambotron-ui/src/app/components/dialog/dialog.component.ts
deleted file mode 100644
index c9f8128..0000000
--- a/jambotron-ui/src/app/components/dialog/dialog.component.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import {ChangeDetectionStrategy, Component, EventEmitter, Inject, inject, Output, signal} from '@angular/core';
-import {MatButtonModule} from "@angular/material/button";
-import {
- MAT_DIALOG_DATA,
- MatDialogActions,
- MatDialogClose,
- MatDialogContent,
- MatDialogRef,
- MatDialogTitle
-} from "@angular/material/dialog";
-
-
-import {MatFormField, MatLabel, MatSuffix} from "@angular/material/form-field";
-import {MatInput} from "@angular/material/input";
-import {FormsModule} from "@angular/forms";
-import {MatIcon} from "@angular/material/icon";
-import {DialogLoginData} from '../../main-module/main.component/main.component';
-import {MatSnackBar} from '@angular/material/snack-bar';
-
-
-
-
-@Component({
- selector: 'app-dialog',
- imports: [MatButtonModule, MatLabel, MatDialogActions, MatDialogClose, MatDialogTitle, MatDialogContent, MatFormField, MatInput, FormsModule, MatIcon, MatSuffix],
-
- templateUrl: './dialog.component.html',
- styleUrl: './dialog.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class DialogComponent {
- readonly dialogRef = inject(MatDialogRef);
- hide = signal(true);
-
- @Output() loginClicked = new EventEmitter();
- @Output() signupClicked = new EventEmitter();
-
- private _snackBar = inject(MatSnackBar);
- durationInSeconds = 5;
-
-
- constructor(
- @Inject(MAT_DIALOG_DATA) public data:DialogLoginData) {
-
- }
-
- openLoginFailedSnackBar(errorMessage : string = "Login failed.") {
- this._snackBar.open(errorMessage , "", {
- duration: this.durationInSeconds * 1000,
- });
- }
-
- onNoClick() {
- this.dialogRef.close();
- }
-
-
- clickEvent(event: MouseEvent) {
- this.hide.set(!this.hide());
- event.stopPropagation();
- }
-
- login() {
- this.loginClicked.emit(this.data);
-
- }
-
- signup() {
- this.signupClicked.emit();
- }
-}
-
-
-
diff --git a/jambotron-ui/src/app/components/file-upload.component/file-upload.component.html b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.html
new file mode 100644
index 0000000..18201d4
--- /dev/null
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.html
@@ -0,0 +1,40 @@
+
+
+@if (progress) {
+
+
+ {{ progress }}%
+
+}
+
+@if (message) {
+
+ {{ message }}
+
+}
+
+@if (fileInfo){
+
+}
diff --git a/jambotron-ui/src/app/components/file-upload.component/file-upload.component.scss b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.scss
new file mode 100644
index 0000000..2052132
--- /dev/null
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.scss
@@ -0,0 +1,47 @@
+.progress-bar {
+ padding: 0;
+}
+
+.progress {
+ width: 50px;
+}
+
+#fileInput {
+ position: absolute;
+ cursor: pointer;
+ z-index: 10;
+ opacity: 0;
+ height: 100%;
+ left: 0px;
+ top: 0px;
+}
+
+.mat-toolbar-single-row {
+ height: auto !important;
+ background: transparent;
+ padding: 0;
+}
+
+.mat-toolbar-single-row button {
+ width: 100px;
+}
+
+.mat-form-field {
+ width: 100%;
+}
+
+.mat-mdc-form-field {
+ display: block;
+}
+
+.message {
+ background-color: #ddd;
+ padding: 15px;
+ color: #333;
+ border: #aaa solid 1px;
+ border-radius: 4px;
+ margin-bottom: 10px;
+}
+img{
+ max-width: -webkit-fill-available;
+}
diff --git a/jambotron-ui/src/app/components/login/login.component.spec.ts b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.spec.ts
similarity index 52%
rename from jambotron-ui/src/app/components/login/login.component.spec.ts
rename to jambotron-ui/src/app/components/file-upload.component/file-upload.component.spec.ts
index d2c0e6c..dd2958d 100644
--- a/jambotron-ui/src/app/components/login/login.component.spec.ts
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.spec.ts
@@ -1,20 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { LoginComponent } from './login.component';
+import { FileUploadComponent } from './file-upload.component';
-describe('LoginComponent', () => {
- let component: LoginComponent;
- let fixture: ComponentFixture;
+describe('FileUploadComponent', () => {
+ let component: FileUploadComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ LoginComponent ]
+ imports: [FileUploadComponent]
})
.compileComponents();
- });
- beforeEach(() => {
- fixture = TestBed.createComponent(LoginComponent);
+ fixture = TestBed.createComponent(FileUploadComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/jambotron-ui/src/app/components/file-upload.component/file-upload.component.ts b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.ts
new file mode 100644
index 0000000..5ef375e
--- /dev/null
+++ b/jambotron-ui/src/app/components/file-upload.component/file-upload.component.ts
@@ -0,0 +1,80 @@
+import {Component, EventEmitter, OnInit, Output} from '@angular/core';
+import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
+import {MatList, MatListItem} from '@angular/material/list';
+import {AsyncPipe} from '@angular/common';
+import {MatToolbar} from '@angular/material/toolbar';
+import {MatProgressBar} from '@angular/material/progress-bar';
+import {MatFormField, MatInput} from '@angular/material/input';
+import {Observable} from 'rxjs';
+import {HttpEventType, HttpResponse} from '@angular/common/http';
+import {MatButton} from '@angular/material/button';
+import {UserApiService} from '../../modules/user-module/user-api.service';
+import {FileInfo} from '../../models/file-info';
+
+@Component({
+ selector: 'app-file-upload',
+ imports: [
+ MatToolbar,
+ MatProgressBar,
+ MatFormField,
+ MatButton,
+ MatInput
+ ],
+ templateUrl: './file-upload.component.html',
+ styleUrl: './file-upload.component.scss'
+})
+export class FileUploadComponent {
+ currentFile?: File;
+ progress = 0;
+ message = '';
+
+ fileName = 'Select File';
+
+ @Output() fileInfo: FileInfo | undefined;
+ @Output() onImageUploaded = new EventEmitter();
+ constructor(private userApiService: UserApiService) {}
+
+ selectFile(event: any): void {
+ this.progress = 0;
+ this.message = '';
+
+ if (event.target.files && event.target.files[0]) {
+ const file: File = event.target.files[0];
+ this.currentFile = file;
+ this.fileName = this.currentFile.name;
+ } else {
+ this.fileName = 'Select File';
+ }
+ }
+
+ upload(): void {
+ if (this.currentFile) {
+ this.userApiService.upload(this.currentFile).subscribe({
+ next: (event: any) => {
+ if (event.type === HttpEventType.UploadProgress) {
+ this.progress = Math.round((100 * event.loaded) / event.total);
+ } else if (event instanceof HttpResponse) {
+ this.message = event.body.message;
+ this.fileInfo =event.body.fileInfo;
+ this.onImageUploaded.emit(this.fileInfo);
+ console.log(event.body);
+ }
+ },
+ error: (err: any) => {
+ console.log(err);
+ this.progress = 0;
+
+ if (err.error && err.error.message) {
+ this.message = err.error.message;
+ } else {
+ this.message = 'Could not upload the file!';
+ this.onImageUploaded.emit(undefined);
+ }
+ },
+ complete: () => {
+ this.currentFile = undefined;
+ },
+ });
+ }
+ }
+}
diff --git a/jambotron-ui/src/app/components/generate-image.component/generate-image.component.ts b/jambotron-ui/src/app/components/generate-image.component/generate-image.component.ts
index 53e58dc..2ad49b4 100644
--- a/jambotron-ui/src/app/components/generate-image.component/generate-image.component.ts
+++ b/jambotron-ui/src/app/components/generate-image.component/generate-image.component.ts
@@ -1,11 +1,11 @@
-import {Component, CUSTOM_ELEMENTS_SCHEMA, Input, OnInit, Renderer2} from '@angular/core';
+import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, Renderer2} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MatButton} from '@angular/material/button';
import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
-import {AsyncPipe, NgIf, NgOptimizedImage} from '@angular/common';
+import {AsyncPipe} from '@angular/common';
import {Image} from '../../models/image';
-import {ZhipuaiImageService} from '../../services/zhipuai-image.service';
+import {ZhipuaiImageService} from '../../modules/ai-module/zhipuai-image.service';
import {MatProgressSpinner} from '@angular/material/progress-spinner';
import {SpinnerService} from '../../services/spinner.service';
@@ -25,9 +25,7 @@ import Viewer from 'viewerjs';
MatInput,
MatLabel,
MatProgressSpinner,
- AsyncPipe,
-
-
+ AsyncPipe
],
templateUrl: './generate-image.component.html',
styleUrl: './generate-image.component.scss',
@@ -52,11 +50,6 @@ export class GenerateImageComponent implements OnInit {
ngOnInit(): void {
- const img = this.renderer.selectRootElement('img');
- this.viewer = new Viewer(img, {
- inline: true,
- });
- this.viewer.zoomTo(1);
}
@@ -79,11 +72,4 @@ export class GenerateImageComponent implements OnInit {
this.viewer.update();
})
}
-
- zoomPlus(){
- this.viewer.zoomTo(5);
- }
- zoomMinus(){
- this.viewer.zoomTo(-5);
- }
}
diff --git a/jambotron-ui/src/app/components/home.component/home.component.html b/jambotron-ui/src/app/components/home.component/home.component.html
deleted file mode 100644
index f3f751c..0000000
--- a/jambotron-ui/src/app/components/home.component/home.component.html
+++ /dev/null
@@ -1,50 +0,0 @@
-dfsdgdsgsdg
-
-dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
-dfsdgdsgsdg
-
-
-
-
-
-
-
diff --git a/jambotron-ui/src/app/components/home.component/home.component.scss b/jambotron-ui/src/app/components/home.component/home.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/components/home.component/home.component.ts b/jambotron-ui/src/app/components/home.component/home.component.ts
deleted file mode 100644
index 9b001f7..0000000
--- a/jambotron-ui/src/app/components/home.component/home.component.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
-import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
-import {AngularImageViewerModule, CustomImageEvent, ImageViewerConfig} from '@hreimer/angular-image-viewer';
-
-import {ZhipuaiImageService} from '../../services/zhipuai-image.service';
-import {MatButton} from '@angular/material/button';
-
-
-import {FormsModule} from '@angular/forms';
-import {CommonModule} from '@angular/common';
-import {BrowserModule} from '@angular/platform-browser';
-import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
-import {Image} from '../../models/image';
-
-@Component({
- selector: 'app-home.component',
- imports: [
-
- AngularImageViewerModule,
- FormsModule
- ],
- templateUrl: './home.component.html',
- styleUrl: './home.component.scss',
- schemas :[CUSTOM_ELEMENTS_SCHEMA]
-})
-
-export class HomeComponent {
-
- query : string = '';
-
- images = [];
- image: Image = {
- url: ''
- };
- imageIndexOne = 0;
-
- config: ImageViewerConfig = { customBtns: [{ name: 'print', icon: {
- classes: 'fas fa-paperclip',
- text: 'link'
- } }, { name: 'link', icon: {
- classes: 'fas fa-paperclip',
- text: 'link'
- } }] };
-
- submitted: boolean = false;
-
- constructor(private zhipuaiImageService: ZhipuaiImageService) {
-
- }
-
- handleEvent(event: CustomImageEvent) {
- console.log(`${event.name} has been click on img ${event.imageIndex + 1}`);
-
- switch (event.name) {
- case 'print':
- console.log('run print logic');
- break;
- }
- }
- generateImage(){
- this.zhipuaiImageService.generate(this.query).subscribe(
- data => {
- this.image = data;
- // @ts-ignore
- this.images = [this.image.url];
-
-
- console.log(data);
- },
- error => {
- console.log(error);
- }
- )
- }
-
-}
diff --git a/jambotron-ui/src/app/components/login/login.component.html b/jambotron-ui/src/app/components/login/login.component.html
deleted file mode 100644
index a4d4501..0000000
--- a/jambotron-ui/src/app/components/login/login.component.html
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-

-
-
-
- Logged in as {{ roles }}.
-
-
-
diff --git a/jambotron-ui/src/app/components/login/login.component.scss b/jambotron-ui/src/app/components/login/login.component.scss
deleted file mode 100644
index 8b13789..0000000
--- a/jambotron-ui/src/app/components/login/login.component.scss
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/jambotron-ui/src/app/components/login/login.component.ts b/jambotron-ui/src/app/components/login/login.component.ts
deleted file mode 100644
index b9518e7..0000000
--- a/jambotron-ui/src/app/components/login/login.component.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-import {FormsModule} from "@angular/forms";
-import {NgIf} from "@angular/common";
-import {AuthService} from '../../services/auth.service';
-import {TokenStorageService} from '../../services/token-storage.service';
-
-@Component({
- selector: 'app-login',
- templateUrl: './login.component.html',
- imports: [
- FormsModule,
- NgIf
- ],
- styleUrls: ['./login.component.scss']
-})
-export class LoginComponent implements OnInit {
-
- form: any = {
- username: null,
- password: null
- };
- isLoggedIn = false;
- isLoginFailed = false;
- errorMessage = '';
- roles: string[] = [];
-
- constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }
-
- ngOnInit(): void {
- if (this.tokenStorage.getToken()) {
- this.isLoggedIn = true;
- this.roles = this.tokenStorage.getUser().roles;
- }
- }
-
- onSubmit(): void {
- const { username, password } = this.form;
-
- this.authService.login(username, password).subscribe(
- data => {
- this.tokenStorage.saveToken(data.accessToken);
- this.tokenStorage.saveUser(data);
-
- this.isLoginFailed = false;
- this.isLoggedIn = true;
- this.roles = this.tokenStorage.getUser().roles;
- this.reloadPage();
- },
- err => {
- this.errorMessage = err.error.message;
- this.isLoginFailed = true;
- }
- );
- }
-
- reloadPage(): void {
- window.location.reload();
- }
-}
diff --git a/jambotron-ui/src/app/components/profile/profile.component.html b/jambotron-ui/src/app/components/profile/profile.component.html
deleted file mode 100644
index d6c4144..0000000
--- a/jambotron-ui/src/app/components/profile/profile.component.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- {{ currentUser.username }} Profile
-
-
-
- Token:
- {{ currentUser.accessToken.substring(0, 20) }} ...
- {{ currentUser.accessToken.substr(currentUser.accessToken.length - 20) }}
-
-
- Email:
- {{ currentUser.email }}
-
-
Roles:
-
-
-
-
- Please login.
-
\ No newline at end of file
diff --git a/jambotron-ui/src/app/components/profile/profile.component.scss b/jambotron-ui/src/app/components/profile/profile.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/components/profile/profile.component.spec.ts b/jambotron-ui/src/app/components/profile/profile.component.spec.ts
deleted file mode 100644
index e88012e..0000000
--- a/jambotron-ui/src/app/components/profile/profile.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ProfileComponent } from './profile.component';
-
-describe('ProfileComponent', () => {
- let component: ProfileComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ ProfileComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(ProfileComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/components/profile/profile.component.ts b/jambotron-ui/src/app/components/profile/profile.component.ts
deleted file mode 100644
index 8cad428..0000000
--- a/jambotron-ui/src/app/components/profile/profile.component.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {TokenStorageService} from '../../services/token-storage.service';
-import {NgForOf, NgIf} from '@angular/common';
-
-
-@Component({
- selector: 'app-profile',
- templateUrl: './profile.component.html',
- imports: [
- NgForOf,
- NgIf
- ],
- styleUrls: ['./profile.component.scss']
-})
-export class ProfileComponent implements OnInit {
- currentUser: any;
-
- constructor(private token: TokenStorageService) { }
-
- ngOnInit(): void {
- this.currentUser = this.token.getUser();
- }
-}
diff --git a/jambotron-ui/src/app/components/register/register.component.html b/jambotron-ui/src/app/components/register/register.component.html
deleted file mode 100644
index 59c36ee..0000000
--- a/jambotron-ui/src/app/components/register/register.component.html
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-

-
-
-
- Your registration is successful!
-
-
-
diff --git a/jambotron-ui/src/app/components/register/register.component.scss b/jambotron-ui/src/app/components/register/register.component.scss
deleted file mode 100644
index 8b13789..0000000
--- a/jambotron-ui/src/app/components/register/register.component.scss
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/jambotron-ui/src/app/components/register/register.component.spec.ts b/jambotron-ui/src/app/components/register/register.component.spec.ts
deleted file mode 100644
index f6db869..0000000
--- a/jambotron-ui/src/app/components/register/register.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { RegisterComponent } from './register.component';
-
-describe('RegisterComponent', () => {
- let component: RegisterComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ RegisterComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(RegisterComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/components/register/register.component.ts b/jambotron-ui/src/app/components/register/register.component.ts
deleted file mode 100644
index 65d3c7e..0000000
--- a/jambotron-ui/src/app/components/register/register.component.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {AuthService} from '../../services/auth.service';
-import {FormsModule} from '@angular/forms';
-import {NgIf} from '@angular/common';
-
-
-@Component({
- selector: 'app-register',
- templateUrl: './register.component.html',
- imports: [
- FormsModule,
- NgIf
- ],
- styleUrls: ['./register.component.scss']
-})
-export class RegisterComponent implements OnInit {
- form: any = {
- username: null,
- email: null,
- password: null
- };
- isSuccessful = false;
- isSignUpFailed = false;
- errorMessage = '';
-
- constructor(private authService: AuthService) { }
-
- ngOnInit(): void {
- }
-
- onSubmit(): void {
- const { username, email, password } = this.form;
-
- this.authService.register(username, email, password).subscribe(
- data => {
- console.log(data);
- this.isSuccessful = true;
- this.isSignUpFailed = false;
- },
- err => {
- this.errorMessage = err.error.message;
- this.isSignUpFailed = true;
- }
- );
- }
-}
diff --git a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.html b/jambotron-ui/src/app/components/side-bar.component/side-bar.component.html
deleted file mode 100644
index 00d2c21..0000000
--- a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.html
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
diff --git a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.scss b/jambotron-ui/src/app/components/side-bar.component/side-bar.component.scss
deleted file mode 100644
index 52e31bf..0000000
--- a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-.entry{
- display: flex;
- align-items: center;
- gap: 1rem;
- padding:0.75rem;
-}
-
-
-
-.example-action-buttons {
- padding-bottom: 20px;
-}
-
-.example-headers-align .mat-expansion-panel-header-description {
- justify-content: space-between;
- align-items: center;
-}
-
-.example-headers-align .mat-mdc-form-field + .mat-mdc-form-field {
- margin-left: 8px;
-}
diff --git a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.ts b/jambotron-ui/src/app/components/side-bar.component/side-bar.component.ts
deleted file mode 100644
index ec8cf0b..0000000
--- a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import {ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, viewChild} from '@angular/core';
-import {MatTree, MatTreeNode} from '@angular/material/tree';
-import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
-import {MatDivider, MatListItem, MatNavList} from '@angular/material/list';
-import {MatIcon} from '@angular/material/icon';
-import {RouterLink} from '@angular/router';
-import {
- MatAccordion, MatExpansionModule,
- MatExpansionPanel,
- MatExpansionPanelDescription,
- MatExpansionPanelTitle
-} from '@angular/material/expansion';
-import {MatDatepicker, MatDatepickerInput} from '@angular/material/datepicker';
-import {provideNativeDateAdapter} from '@angular/material/core';
-import {MatButton} from '@angular/material/button';
-
-@Component({
- selector: 'app-side-bar',
- imports: [
- MatTree,
- MatTreeNode,
- MatLabel,
- MatNavList,
- MatListItem,
- MatIcon,
- RouterLink,
- MatExpansionPanel,
- MatExpansionPanelTitle,
- MatExpansionPanelDescription,
- MatFormField,
- MatInput,
- MatDatepickerInput,
- MatDatepicker,
- MatDivider,
- MatAccordion,
- MatButton,
- MatExpansionModule
- ],
- templateUrl: './side-bar.component.html',
- styleUrl: './side-bar.component.scss',
- schemas:[CUSTOM_ELEMENTS_SCHEMA],
- changeDetection: ChangeDetectionStrategy.OnPush,
- providers: [provideNativeDateAdapter()],
-})
-
-
-
-export class SideBarComponent {
- isCollapsed = false;
-
- accordion = viewChild.required(MatAccordion);
-}
-
-const TREE_DATA: TreeNode[] = [
- {
- name: 'Fruit',
- children: [{name: 'Apple'}, {name: 'Banana'}, {name: 'Fruit loops'}],
- },
- {
- name: 'Vegetables',
- children: [
- {
- name: 'Green',
- children: [{name: 'Broccoli'}, {name: 'Brussels sprouts'}],
- },
- {
- name: 'Orange',
- children: [{name: 'Pumpkins'}, {name: 'Carrots'}],
- },
- ],
- },
-];
-
-interface TreeNode {
- name: string;
- children?: TreeNode[];
-}
-
-
diff --git a/jambotron-ui/src/app/components/system.component/system.component.html b/jambotron-ui/src/app/components/system.component/system.component.html
deleted file mode 100644
index e42981e..0000000
--- a/jambotron-ui/src/app/components/system.component/system.component.html
+++ /dev/null
@@ -1,110 +0,0 @@
-
-
-
- System
-
-
-
-
-
- Content 1
-
- @for (column of displayedColumns; track column) {
-
- | {{column}} |
- {{element[column]}} |
-
- }
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
- Name: {{element.name}}
- Type: {{element.type}}
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
- Content 2
-
- @for (column of displayedColumns; track column) {
-
- | {{column}} |
- {{element[column]}} |
-
- }
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
- Name: {{element.name}}
- Type: {{element.type}}
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
diff --git a/jambotron-ui/src/app/components/system.component/system.component.scss b/jambotron-ui/src/app/components/system.component/system.component.scss
deleted file mode 100644
index b4c141e..0000000
--- a/jambotron-ui/src/app/components/system.component/system.component.scss
+++ /dev/null
@@ -1,113 +0,0 @@
-mat-card{
- margin: 20px;
- background-color: rgba(240, 248, 255, 0.7);
- backdrop-filter: blur(8px);
-}
-
-mat-card-title{
- color: cyan;
-}
-
-mat-card-subtitle{
- color: #009dff;
-}
-
-mat-tab{
- color:black;
-}
-
-
-table {
- width: 100%;
- margin-top: 20px;
- color:lightgrey;
-}
-
-
-mat-list{
- //background-color: #1389d3;
- color:black;
- width: 100%;
-}
-
-mat-list-item{
- margin: 10px 10px 10px 10px;
- background-color: #447694;
-
- color: #009dff;
-}
-
-tr.example-detail-row {
- height: 0;
- background: #fffdfd;
-}
-
-tr.example-element-row {
- cursor: pointer;
- color: #1389d3;
- background: #fffdfd;
-}
-
-tr.example-element-row:not(.example-expanded-row):hover {
- background: whitesmoke;
-}
-
-tr.example-element-row:not(.example-expanded-row):active {
- background: #efefef;
-}
-
-.example-element-row td {
- border-bottom-width: 0;
-}
-
-.example-element-detail-wrapper {
- overflow: hidden;
- display: grid;
- grid-template-rows: 0fr;
- grid-template-columns: 100%;
- transition: grid-template-rows 225ms cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-.example-element-detail-wrapper-expanded {
- grid-template-rows: 1fr;
-
-}
-
-.example-element-detail {
- display: flex;
- min-height: 0;
-}
-
-.example-element-diagram {
- min-width: 80px;
- border: 2px solid black;
- padding: 8px;
- font-weight: lighter;
- margin: 8px 0;
- height: 104px;
-}
-
-.example-element-symbol {
- font-weight: bold;
- font-size: 40px;
- line-height: normal;
-}
-
-.example-element-description {
- padding: 16px;
-}
-
-.example-element-description-attribution {
- opacity: 0.5;
-}
-
-.example-toggle-button {
- transition: transform 225ms cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-.example-toggle-button-expanded {
- transform: rotate(180deg);
-}
-
-
-
diff --git a/jambotron-ui/src/app/components/system.component/system.component.ts b/jambotron-ui/src/app/components/system.component/system.component.ts
deleted file mode 100644
index f5f4699..0000000
--- a/jambotron-ui/src/app/components/system.component/system.component.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import { Component } from '@angular/core';
-import {Bean} from '../../models/Bean';
-import {SystemService} from '../../services/system.service';
-import {
- MatCard,
- MatCardContent,
- MatCardHeader,
- MatCardSubtitle,
- MatCardTitle,
- MatCardTitleGroup
-} from '@angular/material/card';
-import {MatDivider} from '@angular/material/divider';
-import {
- MatCell,
- MatCellDef,
- MatColumnDef,
- MatHeaderCell,
- MatHeaderCellDef, MatHeaderRow,
- MatHeaderRowDef, MatRow, MatRowDef,
- MatTable
-} from '@angular/material/table';
-import {MatIconButton} from '@angular/material/button';
-import {MatIcon} from '@angular/material/icon';
-import {MatList, MatListItem} from '@angular/material/list';
-import {MatTab, MatTabGroup} from '@angular/material/tabs';
-
-@Component({
- selector: 'app-system.component',
- imports: [
- MatCard,
- MatCardSubtitle,
- MatCardContent,
- MatCardHeader,
- MatCardTitle,
- MatDivider,
- MatTable,
- MatColumnDef,
- MatHeaderCell,
- MatCell,
- MatIconButton,
- MatIcon,
- MatHeaderCellDef,
- MatCellDef,
- MatHeaderRowDef,
- MatRowDef,
- MatRow,
- MatHeaderRow,
- MatList,
- MatListItem,
- MatTabGroup,
- MatTab
- ],
- templateUrl: './system.component.html',
- styleUrl: './system.component.scss'
-})
-export class SystemComponent {
-
- // @ViewChild(MatPaginator) paginator: MatPaginator;
- // @ViewChild(MatSort) sort: MatSort;
-
- customBeans:Bean[] = [] ;
- allBeans:Bean[] = [] ;
- displayedColumns: string[] = ['shortName', 'typeShortName', 'scope'];
-
- columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
-
- expandedElement: Bean | null = null;
-
- resultsLength = 0;
- isLoadingResults = true;
- isRateLimitReached = false;
-
- constructor(private systemService: SystemService) { }
-
- ngOnInit(): void {
- this.retrieveBeans();
- }
-
- retrieveBeans(): void {
- this.systemService.getCustomBeans()
- .subscribe(
- (data: Bean[]) => {
- this.customBeans = data;
- console.log(data);
- },
- (error: any) => {
- console.log(error);
- });
- this.systemService.getAllBeans()
- .subscribe(
- (data: Bean[]) => {
- this.allBeans = data;
- console.log(data);
- },
- (error: any) => {
- console.log(error);
- });
- }
-
- /** Checks whether an element is expanded. */
- isExpanded(element: Bean) {
- return this.expandedElement === element;
- }
-
- /** Toggles the expanded state of an element. */
- toggle(element: Bean) {
- this.expandedElement = this.isExpanded(element) ? null : element;
- }
-
-
-
- ngAfterViewInit() {
- //this.exampleDatabase = new ExampleHttpDatabase(this._httpClient);
-
- // If the user changes the sort order, reset back to the first page.
- /* this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0));
-
- merge(this.sort.sortChange, this.paginator.page)
- .pipe(
- startWith({}),
- switchMap(() => {
- this.isLoadingResults = true;
- return this.exampleDatabase!.getRepoIssues(
- this.sort.active,
- this.sort.direction,
- this.paginator.pageIndex,
- ).pipe(catchError(() => observableOf(null)));
- }),
- map(data => {
- // Flip flag to show that loading has finished.
- this.isLoadingResults = false;
- this.isRateLimitReached = data === null;
-
- if (data === null) {
- return [];
- }
-
- // Only refresh the result length if there is new data. In case of rate
- // limit errors, we do not want to reset the paginator to zero, as that
- // would prevent users from re-triggering requests.
- this.resultsLength = data.total_count;
- return data.items;
- }),
- )
- .subscribe(data => (this.data = data));*/
- }
-
-}
diff --git a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.html b/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.html
deleted file mode 100644
index ef9cf6b..0000000
--- a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.html
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
Cannot access this Tutorial...
-
-
\ No newline at end of file
diff --git a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.scss b/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.scss
deleted file mode 100644
index 0f56416..0000000
--- a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-.edit-form {
- max-width: 400px;
- margin: auto;
-}
\ No newline at end of file
diff --git a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.spec.ts b/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.spec.ts
deleted file mode 100644
index 7bc4afd..0000000
--- a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { TutorialDetailsComponent } from './tutorial-details.component';
-
-describe('TutorialDetailsComponent', () => {
- let component: TutorialDetailsComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ TutorialDetailsComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(TutorialDetailsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.ts b/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.ts
deleted file mode 100644
index 96a88d3..0000000
--- a/jambotron-ui/src/app/components/tutorial-details/tutorial-details.component.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-import { ActivatedRoute, Router } from '@angular/router';
-import {Tutorial} from '../../models/tutorial.model';
-import {TutorialService} from '../../services/tutorial.service';
-import {FormsModule} from '@angular/forms';
-import {NgIf} from '@angular/common';
-
-
-@Component({
- selector: 'app-tutorial-details',
- templateUrl: './tutorial-details.component.html',
- imports: [
- FormsModule,
- NgIf
- ],
- styleUrls: ['./tutorial-details.component.scss']
-})
-export class TutorialDetailsComponent implements OnInit {
-
- currentTutorial: Tutorial = {
- title: '',
- description: '',
- published: false
- };
- message = '';
-
- constructor(
- private tutorialService: TutorialService,
- private route: ActivatedRoute,
- private router: Router) { }
-
- ngOnInit(): void {
- this.message = '';
- this.getTutorial(this.route.snapshot.params['id']);
- }
-
- getTutorial(id: string): void {
- this.tutorialService.get(id)
- .subscribe(
- data => {
- this.currentTutorial = data;
- console.log(data);
- },
- error => {
- console.log(error);
- });
- }
-
- updatePublished(status: boolean): void {
- const data = {
- title: this.currentTutorial.title,
- description: this.currentTutorial.description,
- published: status
- };
-
- this.message = '';
-
- this.tutorialService.update(this.currentTutorial.id, data)
- .subscribe(
- response => {
- this.currentTutorial.published = status;
- console.log(response);
- this.message = response.message ? response.message : 'The status was updated successfully!';
- },
- error => {
- console.log(error);
- });
- }
-
- updateTutorial(): void {
- this.message = '';
-
- this.tutorialService.update(this.currentTutorial.id, this.currentTutorial)
- .subscribe(
- response => {
- console.log(response);
- this.message = response.message ? response.message : 'This tutorial was updated successfully!';
- },
- error => {
- console.log(error);
- });
- }
-
- deleteTutorial(): void {
- this.tutorialService.delete(this.currentTutorial.id)
- .subscribe(
- response => {
- console.log(response);
- this.router.navigate(['/tutorials']);
- },
- error => {
- console.log(error);
- });
- }
-}
diff --git a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.html b/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.html
deleted file mode 100644
index a83a76a..0000000
--- a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
Tutorials List
-
- -
- {{ tutorial.title }}
-
-
-
-
-
-
-
-
Tutorial
-
- {{ currentTutorial.title }}
-
-
-
- {{ currentTutorial.description }}
-
-
-
- {{ currentTutorial.published ? "Published" : "Pending" }}
-
-
-
- Edit
-
-
-
-
-
-
Please click on a Tutorial...
-
-
-
\ No newline at end of file
diff --git a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.scss b/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.scss
deleted file mode 100644
index 9dc0d33..0000000
--- a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-.list {
- text-align: left;
- max-width: 750px;
- margin: auto;
-}
-
diff --git a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.ts b/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.ts
deleted file mode 100644
index f1cdb3f..0000000
--- a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-
-import {Tutorial} from '../../models/tutorial.model';
-import {TutorialService} from '../../services/tutorial.service';
-import {FormsModule} from '@angular/forms';
-import {RouterLink} from '@angular/router';
-import {NgIf} from '@angular/common';
-
-@Component({
- selector: 'app-tutorials-list',
- templateUrl: './tutorials-list.component.html',
- imports: [
- FormsModule,
- RouterLink,
- NgIf
- ],
- styleUrls: ['./tutorials-list.component.scss']
-})
-export class TutorialsListComponent implements OnInit {
-
- tutorials?: Tutorial[];
- currentTutorial: Tutorial = {};
- currentIndex = -1;
- title = '';
-
- constructor(private tutorialService: TutorialService) { }
-
- ngOnInit(): void {
- this.retrieveTutorials();
- }
-
- retrieveTutorials(): void {
- this.tutorialService.getAll()
- .subscribe(
- data => {
- this.tutorials = data;
- console.log(data);
- },
- error => {
- console.log(error);
- });
- }
-
- refreshList(): void {
- this.retrieveTutorials();
- this.currentTutorial = {};
- this.currentIndex = -1;
- }
-
- setActiveTutorial(tutorial: Tutorial, index: number): void {
- this.currentTutorial = tutorial;
- this.currentIndex = index;
- }
-
- removeAllTutorials(): void {
- this.tutorialService.deleteAll()
- .subscribe(
- response => {
- console.log(response);
- this.refreshList();
- },
- error => {
- console.log(error);
- });
- }
-
- searchTitle(): void {
- this.currentTutorial = {};
- this.currentIndex = -1;
-
- this.tutorialService.findByTitle(this.title)
- .subscribe(
- data => {
- this.tutorials = data;
- console.log(data);
- },
- error => {
- console.log(error);
- });
- }
-}
diff --git a/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts b/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts
index 11913d6..34ad1a1 100644
--- a/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts
+++ b/jambotron-ui/src/app/components/tutorials.component/tutorials.component.ts
@@ -1,8 +1,11 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {Tutorial} from '../../models/tutorial.model';
-import {TutorialService} from '../../services/tutorial.service';
+
import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
import {MarkdownComponent} from 'ngx-markdown';
+import {authInterceptorProviders} from '../../helpers/auth.interceptor';
+import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
+import {CustomHttpInterceptor} from '../../helpers/custom-http-interceptor';
@Component({
selector: 'app-tutorials.component',
@@ -14,13 +17,18 @@ import {MarkdownComponent} from 'ngx-markdown';
],
templateUrl: './tutorials.component.html',
styleUrl: './tutorials.component.scss',
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ providers: [authInterceptorProviders,{
+ provide: HTTP_INTERCEPTORS,
+ useClass: CustomHttpInterceptor,
+ multi: true
+ }],
})
export class TutorialsComponent {
tutorials?: Tutorial[];
-
+/*
constructor(private tutorialService: TutorialService) {
- this.retrieveTutorials();
+ // this.retrieveTutorials();
}
// ngOnInit(): void {
@@ -28,14 +36,14 @@ export class TutorialsComponent {
// }
retrieveTutorials(): void {
- this.tutorialService.getAll()
- .subscribe(
- data => {
- this.tutorials = data;
- console.log(data);
- },
- error => {
- console.log(error);
- });
- }
+ this.tutorialService.getAllPublic().subscribe(
+ (data : Tutorial[]) =>{
+ this.tutorials = data;
+ console.log(data);
+ },
+ error => {
+ console.log(error);
+ }
+ );
+ }*/
}
diff --git a/jambotron-ui/src/app/directives/column-resize.directive.spec.ts b/jambotron-ui/src/app/directives/column-resize.directive.spec.ts
deleted file mode 100644
index 181e843..0000000
--- a/jambotron-ui/src/app/directives/column-resize.directive.spec.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { ColumnResizeDirective } from './column-resize.directive';
-
-describe('ColumnResizeDirective', () => {
- it('should create an instance', () => {
- const directive = new ColumnResizeDirective();
- expect(directive).toBeTruthy();
- });
-});
diff --git a/jambotron-ui/src/app/directives/column-resize.directive.ts b/jambotron-ui/src/app/directives/column-resize.directive.ts
deleted file mode 100644
index c842abf..0000000
--- a/jambotron-ui/src/app/directives/column-resize.directive.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import {
- Directive,
- ElementRef,
- OnDestroy,
- OnInit,
- Renderer2,
- NgZone,
- Input,
-} from '@angular/core';
-import { Subject, fromEvent, takeUntil } from 'rxjs';
-
-@Directive({
- selector: '[appColumnResize]',
-})
-export class ColumnResizeDirective implements OnInit, OnDestroy {
- @Input() resizableTable: HTMLElement | null = null;
-
- private isResizing = false;
- private startX!: number;
- private startWidth!: number;
- private column: HTMLElement;
- private table: HTMLElement | null = null;
- private resizer!: HTMLElement;
- private destroy$ = new Subject();
-
- constructor(
- private el: ElementRef,
- private renderer: Renderer2,
- private zone: NgZone
- ) {
- this.column = this.el.nativeElement;
- }
-
- ngOnInit() {
- this.table = this.resizableTable || this.findParentTable(this.column);
-
- if (!this.table) {
- console.error(
- 'Parent table not found. Make sure the directive is applied to a th element within a table.'
- );
- return;
- }
-
- this.createResizer();
- this.initializeResizeListener();
- }
-
- private createResizer() {
- this.resizer = this.renderer.createElement('div');
- this.renderer.addClass(this.resizer, 'column-resizer');
- this.renderer.setStyle(this.resizer, 'position', 'absolute');
- this.renderer.setStyle(this.resizer, 'right', '0');
- this.renderer.setStyle(this.resizer, 'top', '0');
- this.renderer.setStyle(this.resizer, 'height', '100%');
- this.renderer.setStyle(this.resizer, 'width', '5px');
- this.renderer.setStyle(this.resizer, 'cursor', 'col-resize');
- this.renderer.appendChild(this.column, this.resizer);
- }
-
- private initializeResizeListener() {
- this.zone.runOutsideAngular(() => {
- fromEvent(this.resizer, 'mousedown')
- .pipe(takeUntil(this.destroy$))
- .subscribe((e: Event) => this.onMouseDown(e as MouseEvent));
-
- fromEvent(document, 'mousemove')
- .pipe(takeUntil(this.destroy$))
- .subscribe((e: Event) => this.onMouseMove(e as MouseEvent));
-
- fromEvent(document, 'mouseup')
- .pipe(takeUntil(this.destroy$))
- .subscribe(() => this.onMouseUp());
- });
- }
-
- private onMouseDown(e: MouseEvent) {
- e.preventDefault();
- this.isResizing = true;
- this.startX = e.pageX;
- this.startWidth = this.column.offsetWidth;
- this.renderer.addClass(this.column, 'resizing');
- if (this.table) {
- this.renderer.addClass(this.table, 'resizing');
- }
- }
-
- private onMouseMove(e: MouseEvent) {
- if (!this.isResizing) return;
- const width = this.startWidth + (e.pageX - this.startX);
- this.renderer.setStyle(this.column, 'width', `${width}px`);
- }
-
- private onMouseUp() {
- if (!this.isResizing) return;
- this.isResizing = false;
- this.renderer.removeClass(this.column, 'resizing');
- if (this.table) {
- this.renderer.removeClass(this.table, 'resizing');
- }
- }
-
- private findParentTable(element: HTMLElement): HTMLElement | null {
- while (element && element.tagName !== 'TABLE') {
- element = element.parentElement as HTMLElement;
- if (!element) return null;
- }
- return element;
- }
-
- ngOnDestroy() {
- this.destroy$.next();
- this.destroy$.complete();
- }
-}
diff --git a/jambotron-ui/src/app/global-constants.spec.ts b/jambotron-ui/src/app/global-constants.spec.ts
new file mode 100644
index 0000000..e3b64b4
--- /dev/null
+++ b/jambotron-ui/src/app/global-constants.spec.ts
@@ -0,0 +1,7 @@
+import { GlobalConstants } from './global-constants';
+
+describe('GlobalConstants', () => {
+ it('should create an instance', () => {
+ expect(new GlobalConstants()).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/global-constants.ts b/jambotron-ui/src/app/global-constants.ts
new file mode 100644
index 0000000..22b2bd4
--- /dev/null
+++ b/jambotron-ui/src/app/global-constants.ts
@@ -0,0 +1,24 @@
+import {environment} from '../environments/environment';
+
+export class GlobalConstants {
+
+ public static readonly TITLE = (() => {
+ return environment.title;
+ })();
+
+ public static readonly DEFAULT_PAGE = (() => {
+ return environment.default_page;
+ })();
+
+ public static readonly API_URL = (() => {
+ // ... calculate the value and return it
+
+ if(environment.production) {
+ return `${environment.host_name}/api`;
+ }else {
+ //return `${environment.host_name}:${environment.port}/api`;
+ return `/api`; // in serve mode used proxy (proxy.conf.json)
+ }
+
+ })();
+}
diff --git a/jambotron-ui/src/app/helpers/auth.interceptor.ts b/jambotron-ui/src/app/helpers/auth.interceptor.ts
index 47204e7..7769749 100644
--- a/jambotron-ui/src/app/helpers/auth.interceptor.ts
+++ b/jambotron-ui/src/app/helpers/auth.interceptor.ts
@@ -1,26 +1,56 @@
-import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http';
+import {HTTP_INTERCEPTORS, HttpErrorResponse, HttpEvent} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { TokenStorageService } from '../services/token-storage.service';
-import { Observable } from 'rxjs';
+import {catchError, Observable, throwError} from 'rxjs';
+import {EventBusService} from '../_shared/event-bus.service';
+import {EventData} from '../_shared/event.class';
+import {environment} from '../../environments/environment';
const TOKEN_HEADER_KEY = 'Authorization'; // for Spring Boot back-end
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
- constructor(private token: TokenStorageService) { }
+ private isRefreshing = false;
+ enviorment = environment;
+
+ constructor(private tokenStorageService: TokenStorageService, private eventBusService: EventBusService) { }
intercept(req: HttpRequest, next: HttpHandler): Observable> {
- let authReq = req;
- const token = this.token.getToken();
- if (token != null) {
- authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
+
+ // req = req.clone({
+ // withCredentials: true,
+ // });
+
+ return next.handle(req).pipe(
+ catchError((error) => {
+ if (
+ error instanceof HttpErrorResponse &&
+ !req.url.includes('auth/signin') &&
+ (error.status === 401) /// must be only 401 without 500 and 0
+ ) {
+ return this.handle401Error(req, next);
+ }
+
+ return throwError(() => error);
+ })
+ );
+ }
+
+ private handle401Error(request: HttpRequest, next: HttpHandler) {
+ if (!this.isRefreshing) {
+ this.isRefreshing = true;
+
+ if (this.tokenStorageService.isLoggedIn()) {
+ this.eventBusService.emit(new EventData('logout', null));
+ }
}
- return next.handle(authReq);
+
+ return next.handle(request);
}
}
export const authInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
-];
\ No newline at end of file
+];
diff --git a/jambotron-ui/src/app/helpers/custom-http-interceptor.ts b/jambotron-ui/src/app/helpers/custom-http-interceptor.ts
index df18c65..04222ec 100644
--- a/jambotron-ui/src/app/helpers/custom-http-interceptor.ts
+++ b/jambotron-ui/src/app/helpers/custom-http-interceptor.ts
@@ -1,4 +1,4 @@
-import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
+import {HttpEvent, HttpEventType, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
import {Observable, tap} from 'rxjs';
import {SpinnerService} from '../services/spinner.service';
import {Injectable} from '@angular/core';
@@ -19,6 +19,7 @@ export class CustomHttpInterceptor implements HttpInterceptor {
}
}, (error) => {
this.spinnerService.hide();
- }));
+ }))
+ ;
}
}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.html b/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.html
deleted file mode 100644
index e36c8f4..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.html
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
diff --git a/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.scss b/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.ts b/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.ts
deleted file mode 100644
index 597a5b6..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/admin-layout.component.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-// Angular import
-import { Component } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { RouterModule } from '@angular/router';
-
-// Project import
-
-import { NavBarComponent } from './nav-bar/nav-bar.component';
-import { NavigationComponent } from './navigation/navigation.component';
-import { ConfigurationComponent } from './configuration/configuration.component';
-import {BreadcrumbComponent} from '../../components/breadcrumb/breadcrumb.component';
-//import { BreadcrumbComponent } from 'src/app/theme/shared/components/breadcrumb/breadcrumb.component';
-
-@Component({
- selector: 'app-admin',
- imports: [CommonModule, BreadcrumbComponent, NavigationComponent, NavBarComponent, RouterModule, ConfigurationComponent, NavigationComponent],
- templateUrl: './admin-layout.component.html',
- styleUrls: ['./admin-layout.component.scss']
-})
-export class AdminComponent {
- // public props
- navCollapsed: boolean = true;
- navCollapsedMob: boolean = true;
-
- // public method
- navMobClick() {
- if (this.navCollapsedMob && !document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
- this.navCollapsedMob = !this.navCollapsedMob;
- setTimeout(() => {
- this.navCollapsedMob = !this.navCollapsedMob;
- }, 100);
- } else {
- this.navCollapsedMob = !this.navCollapsedMob;
- }
- if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('navbar-collapsed')) {
- document.querySelector('app-navigation.pc-sidebar')?.classList.remove('navbar-collapsed');
- }
- }
-
- handleKeyDown(event: KeyboardEvent): void {
- if (event.key === 'Escape') {
- this.closeMenu();
- }
- }
-
- closeMenu() {
- if (document.querySelector('app-navigation.pc-sidebar')?.classList.contains('mob-open')) {
- document.querySelector('app-navigation.pc-sidebar')?.classList.remove('mob-open');
- }
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/configuration/configuration.component.html b/jambotron-ui/src/app/layouts/admin-layout/configuration/configuration.component.html
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/configuration/configuration.component.scss b/jambotron-ui/src/app/layouts/admin-layout/configuration/configuration.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/configuration/configuration.component.ts b/jambotron-ui/src/app/layouts/admin-layout/configuration/configuration.component.ts
deleted file mode 100644
index abfc887..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/configuration/configuration.component.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-configuration',
- imports: [],
- templateUrl: './configuration.component.html',
- styleUrl: './configuration.component.scss'
-})
-export class ConfigurationComponent {}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.html b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.html
deleted file mode 100644
index 99f375e..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.scss b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.scss
deleted file mode 100644
index e4f45fd..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.pc-header{
- left: 0;
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.ts b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.ts
deleted file mode 100644
index 7c7ebaf..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-bar.component.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-// angular import
-import {Component, inject, OnInit, output} from '@angular/core';
-
-// project import
-
-import { NavLeftComponent } from './nav-left/nav-left.component';
-import { NavRightComponent } from './nav-right/nav-right.component';
-import {DOCUMENT} from '@angular/common';
-
-@Component({
- selector: 'app-nav-bar',
- imports: [NavLeftComponent, NavRightComponent],
- templateUrl: './nav-bar.component.html',
- styleUrls: ['./nav-bar.component.scss']
-})
-export class NavBarComponent implements OnInit{
- private document = inject(DOCUMENT);
- // public props
- NavCollapse = output();
- NavCollapsedMob = output();
-
- navCollapsed: boolean = false;
- windowWidth: number | undefined = 1000;
- navCollapsedMob: boolean;
-
- // Constructor
- constructor() {
-
- this.navCollapsedMob = false;
- }
-
- ngOnInit(): void {
- this.windowWidth = this.document.defaultView?.innerWidth;
- }
-
- // public method
- navCollapse() {
- if (!!this.windowWidth && this.windowWidth >= 1025) {
- this.navCollapsed = !this.navCollapsed;
- this.NavCollapse.emit();
- }
- }
-
- navCollapseMob() {
- if (!!this.windowWidth && this.windowWidth < 1025) {
- this.NavCollapsedMob.emit();
- }
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.html b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.html
deleted file mode 100644
index d7991d8..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.scss b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.ts b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.ts
deleted file mode 100644
index 4dfb288..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-left/nav-left.component.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-// Angular import
-import {CommonModule, DOCUMENT} from '@angular/common';
-import { Component, inject, input, output } from '@angular/core';
-
-// project import
-
-// icons
-import { IconService, IconDirective } from '@ant-design/icons-angular';
-import { MenuUnfoldOutline, MenuFoldOutline, SearchOutline } from '@ant-design/icons-angular/icons';
-
-@Component({
- selector: 'app-nav-left',
- imports: [IconDirective, CommonModule],
- templateUrl: './nav-left.component.html',
- styleUrls: ['./nav-left.component.scss']
-})
-export class NavLeftComponent {
- private iconService = inject(IconService);
- private document = inject(DOCUMENT);
-
- // public props
- navCollapsed = input.required();
- NavCollapse = output();
- NavCollapsedMob = output();
- windowWidth: number | undefined = 1000;
-
- // Constructor
- constructor() {
- this.windowWidth = this.document.defaultView?.innerWidth;
- this.iconService.addIcon(...[MenuUnfoldOutline, MenuFoldOutline, SearchOutline]);
- }
-
- // public method
- navCollapse() {
- this.NavCollapse.emit();
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.html b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.html
deleted file mode 100644
index 5d6e4a5..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.html
+++ /dev/null
@@ -1,147 +0,0 @@
-
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.scss b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.ts b/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.ts
deleted file mode 100644
index baf2a8c..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/nav-bar/nav-right/nav-right.component.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-// angular import
-import {Component, inject, input, OnInit, output} from '@angular/core';
-import { RouterModule } from '@angular/router';
-
-// project import
-
-// icon
-import { IconService, IconDirective } from '@ant-design/icons-angular';
-import {
- BellOutline,
- SettingOutline,
- GiftOutline,
- MessageOutline,
- PhoneOutline,
- CheckCircleOutline,
- LogoutOutline,
- EditOutline,
- UserOutline,
- ProfileOutline,
- WalletOutline,
- QuestionCircleOutline,
- LockOutline,
- CommentOutline,
- UnorderedListOutline,
- ArrowRightOutline,
- GithubOutline
-} from '@ant-design/icons-angular/icons';
-import { NgbDropdownModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
-import { NgScrollbarModule } from 'ngx-scrollbar';
-import {DOCUMENT} from '@angular/common';
-
-@Component({
- selector: 'app-nav-right',
- imports: [IconDirective, RouterModule, NgScrollbarModule, NgbNavModule, NgbDropdownModule],
- templateUrl: './nav-right.component.html',
- styleUrls: ['./nav-right.component.scss']
-})
-export class NavRightComponent implements OnInit{
- private iconService = inject(IconService);
- private document = inject(DOCUMENT);
- //private window = inject(Window);
- styleSelectorToggle = input();
- Customize = output();
- windowWidth: number | undefined = 1000;
- screenFull: boolean = true;
-
- constructor() {
-
- this.iconService.addIcon(
- ...[
- CheckCircleOutline,
- GiftOutline,
- MessageOutline,
- SettingOutline,
- PhoneOutline,
- LogoutOutline,
- UserOutline,
- EditOutline,
- ProfileOutline,
- QuestionCircleOutline,
- LockOutline,
- CommentOutline,
- UnorderedListOutline,
- ArrowRightOutline,
- BellOutline,
- GithubOutline,
- WalletOutline
- ]
- );
- }
-
- ngOnInit(): void {
- this.windowWidth = this.document.defaultView?.innerWidth;
- }
-
- profile = [
- {
- icon: 'edit',
- title: 'Edit Profile'
- },
- {
- icon: 'user',
- title: 'View Profile'
- },
- {
- icon: 'profile',
- title: 'Social Profile'
- },
- {
- icon: 'wallet',
- title: 'Billing'
- },
- {
- icon: 'logout',
- title: 'Logout'
- }
- ];
-
- setting = [
- {
- icon: 'question-circle',
- title: 'Support'
- },
- {
- icon: 'user',
- title: 'Account Settings'
- },
- {
- icon: 'lock',
- title: 'Privacy Center'
- },
- {
- icon: 'comment',
- title: 'Feedback'
- },
- {
- icon: 'unordered-list',
- title: 'History'
- }
- ];
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.html b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.html
deleted file mode 100644
index 522253f..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.scss b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.ts b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.ts
deleted file mode 100644
index 5623fc2..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-collapse/nav-collapse.component.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-// Angular import
-import {Component, inject, Input, OnInit, output} from '@angular/core';
-import { animate, style, transition, trigger } from '@angular/animations';
-import {CommonModule, DOCUMENT} from '@angular/common';
-import { RouterModule } from '@angular/router';
-
-// project import
-import { NavigationItem } from '../../navigation';
-
-import { NavItemComponent } from '../nav-item/nav-item.component';
-import { IconDirective } from '@ant-design/icons-angular';
-
-@Component({
- selector: 'app-nav-collapse',
- imports: [CommonModule, IconDirective, RouterModule, NavItemComponent],
- templateUrl: './nav-collapse.component.html',
- styleUrls: ['./nav-collapse.component.scss'],
- animations: [
- trigger('slideInOut', [
- transition(':enter', [
- style({ transform: 'translateY(-100%)', display: 'block' }),
- animate('250ms ease-in', style({ transform: 'translateY(0%)' }))
- ]),
- transition(':leave', [animate('250ms ease-in', style({ transform: 'translateY(-100%)' }))])
- ])
- ]
-})
-export class NavCollapseComponent implements OnInit{
-
- private document = inject(DOCUMENT);
-
- // public props
-
- // Compact Menu in use For Sub Child Open in sidebar menu
- showCollapseItem = output();
-
- // all Version Get Item(Component Name Take)
- @Input() item!: NavigationItem;
-
- windowWidth: number | undefined = 1000;
-
- // Constructor
- constructor() {
- //this.windowWidth = window.innerWidth;
- }
-
- ngOnInit(): void {
- this.windowWidth = this.document.defaultView?.innerWidth;throw new Error('Method not implemented.');
- }
-
- // public method
- navCollapse(e: MouseEvent) {
- let parent = e.target as HTMLElement;
-
- if (parent?.tagName === 'SPAN') {
- parent = parent.parentElement!;
- }
-
- parent = (parent as HTMLElement).parentElement as HTMLElement;
-
- const sections = document.querySelectorAll('.coded-hasmenu');
- for (let i = 0; i < sections.length; i++) {
- if (sections[i] !== parent) {
- sections[i].classList.remove('coded-trigger');
- }
- }
-
- let first_parent = parent.parentElement;
- let pre_parent = ((parent as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
- if (first_parent?.classList.contains('coded-hasmenu')) {
- do {
- first_parent?.classList.add('coded-trigger');
- first_parent = ((first_parent as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
- } while (first_parent.classList.contains('coded-hasmenu'));
- } else if (pre_parent.classList.contains('coded-submenu')) {
- do {
- pre_parent?.parentElement?.classList.add('coded-trigger');
- pre_parent = (((pre_parent as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
- } while (pre_parent.classList.contains('coded-submenu'));
- }
- parent.classList.toggle('coded-trigger');
- }
-
- // for Compact Menu
- subMenuCollapse(item: void) {
- this.showCollapseItem.emit(item);
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.html b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.html
deleted file mode 100644
index d1cdf42..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
- @for (item of navigations; track item) {
- @if (item.type === 'group') {
-
- }
- }
-
-
-
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.scss b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.ts b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.ts
deleted file mode 100644
index 38a475e..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-content.component.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-// Angular import
-import { Component, OnInit, inject, output } from '@angular/core';
-import {CommonModule, DOCUMENT, Location, LocationStrategy} from '@angular/common';
-import { RouterModule } from '@angular/router';
-
-// project import
-import { NavigationItem, NavigationItems } from '../navigation';
-//import { environment } from 'src/environments/environment';
-
-import { NavGroupComponent } from './nav-group/nav-group.component';
-
-// icon
-import { IconService } from '@ant-design/icons-angular';
-import {
- DashboardOutline,
- CreditCardOutline,
- LoginOutline,
- QuestionOutline,
- ChromeOutline,
- FontSizeOutline,
- ProfileOutline,
- BgColorsOutline,
- AntDesignOutline
-} from '@ant-design/icons-angular/icons';
-import { NgScrollbarModule } from 'ngx-scrollbar';
-
-@Component({
- selector: 'app-nav-content',
- imports: [CommonModule, RouterModule, NavGroupComponent, NgScrollbarModule],
- templateUrl: './nav-content.component.html',
- styleUrls: ['./nav-content.component.scss']
-})
-export class NavContentComponent implements OnInit {
- private location = inject(Location);
- private locationStrategy = inject(LocationStrategy);
- private iconService = inject(IconService)
- private document = inject(DOCUMENT);
-
- // public props
- NavCollapsedMob = output();
-
- navigations: NavigationItem[];
-
- // version
- title = 'Demo application for version numbering';
- //currentApplicationVersion = environment.appVersion;
-
- navigation = NavigationItems;
- windowWidth: number | undefined = 1000;
-
- // Constructor
- constructor() {
- this.iconService.addIcon(
- ...[
- DashboardOutline,
- CreditCardOutline,
- FontSizeOutline,
- LoginOutline,
- ProfileOutline,
- BgColorsOutline,
- AntDesignOutline,
- ChromeOutline,
- QuestionOutline
- ]
- );
- this.navigations = NavigationItems;
- }
-
- // Life cycle events
- ngOnInit() {
- if (!!this.windowWidth && this.windowWidth < 1025) {
- (this.document.querySelector('.coded-navbar') as HTMLDivElement)?.classList.add('menupos-static');
- }
- }
-
- fireOutClick() {
- let current_url = this.location.path();
- const baseHref = this.locationStrategy.getBaseHref();
- if (baseHref) {
- current_url = baseHref + this.location.path();
- }
- const link = "a.nav-link[ href='" + current_url + "' ]";
- const ele = document?.querySelector(link);
- if (ele !== null && ele !== undefined) {
- const parent = ele.parentElement;
- const up_parent = parent?.parentElement?.parentElement;
- const last_parent = up_parent?.parentElement;
- if (parent?.classList.contains('coded-hasmenu')) {
- parent.classList.add('coded-trigger');
- parent.classList.add('active');
- } else if (up_parent?.classList.contains('coded-hasmenu')) {
- up_parent.classList.add('coded-trigger');
- up_parent.classList.add('active');
- } else if (last_parent?.classList.contains('coded-hasmenu')) {
- last_parent.classList.add('coded-trigger');
- last_parent.classList.add('active');
- }
- }
- }
-
- navMob() {
- if (!!this.windowWidth && this.windowWidth < 1025 && document.querySelector('app-navigation.coded-navbar')?.classList.contains('mob-open')) {
- this.NavCollapsedMob.emit();
- }
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.html b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.html
deleted file mode 100644
index 98289c9..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-@for (item of item().children; track item) {
- @if (item.type === 'collapse') {
-
- } @else if (item.type === 'item') {
-
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.scss b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.ts b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.ts
deleted file mode 100644
index 7a7edcf..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-group/nav-group.component.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-// Angular import
-import { Component, OnInit, inject, input } from '@angular/core';
-import {CommonModule, DOCUMENT, Location} from '@angular/common';
-
-// project import
-import { NavigationItem } from '../../navigation';
-
-import { NavCollapseComponent } from '../nav-collapse/nav-collapse.component';
-import { NavItemComponent } from '../nav-item/nav-item.component';
-
-@Component({
- selector: 'app-nav-group',
- imports: [CommonModule, NavCollapseComponent, NavItemComponent],
- templateUrl: './nav-group.component.html',
- styleUrls: ['./nav-group.component.scss']
-})
-export class NavGroupComponent implements OnInit {
- private location = inject(Location);
- private document = inject(DOCUMENT);
- // public props
-
- // All Version in Group Name
- item = input.required();
-
- // Life cycle events
- ngOnInit() {
- // at reload time active and trigger link
- let current_url: string | any = this.location.path();
- // eslint-disable-next-line
- // @ts-ignore
- if (this.location['_baseHref']) {
- // eslint-disable-next-line
-
-
- // @ts-ignore
- current_url = this.location['_baseHref'] + this.location.path();
- }
-
- const link = "a.nav-link[ href='" + current_url + "' ]";
- //if(this.document === undefined) return;
- const ele = this.document?.querySelector(link);
- if (ele !== null && ele !== undefined) {
- const parent = ele.parentElement;
- const up_parent = parent?.parentElement?.parentElement;
- const pre_parent = up_parent?.parentElement;
- const last_parent = up_parent?.parentElement?.parentElement?.parentElement?.parentElement;
- if (parent?.classList.contains('coded-hasmenu')) {
- parent?.classList.add('coded-trigger');
- parent?.classList.add('active');
- } else if (up_parent?.classList.contains('coded-hasmenu')) {
- up_parent?.classList.add('coded-trigger');
- up_parent?.classList.add('active');
- } else if (pre_parent?.classList.contains('coded-hasmenu')) {
- pre_parent?.classList.add('coded-trigger');
- pre_parent?.classList.add('active');
- }
-
- if (last_parent?.classList.contains('coded-hasmenu')) {
- last_parent?.classList.add('coded-trigger');
- if (pre_parent?.classList.contains('coded-hasmenu')) {
- pre_parent?.classList.add('coded-trigger');
- }
- }
- last_parent?.classList.add('active');
- }
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.html b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.html
deleted file mode 100644
index f60f5f7..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.html
+++ /dev/null
@@ -1,32 +0,0 @@
-@if (item.url && !item.external) {
-
-
- @if (item.icon) {
-
-
-
- }
- @if (item.icon) {
- {{ item.title }}
- } @else {
- {{ item.title }}
- }
-
-
-}
-@if (item.url && item.external) {
-
-
- @if (item.icon) {
-
-
-
- }
- @if (item.icon) {
- {{ item.title }}
- } @else {
- {{ item.title }}
- }
-
-
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.scss b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.ts b/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.ts
deleted file mode 100644
index 6347047..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/nav-content/nav-item/nav-item.component.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-// Angular import
-import {Component, inject, Input} from '@angular/core';
-import {CommonModule, DOCUMENT} from '@angular/common';
-import { RouterModule } from '@angular/router';
-
-// Project import
-import { NavigationItem } from '../../navigation';
-
-import { IconDirective } from '@ant-design/icons-angular';
-
-@Component({
- selector: 'app-nav-item',
- imports: [CommonModule, IconDirective, RouterModule],
- templateUrl: './nav-item.component.html',
- styleUrls: ['./nav-item.component.scss']
-})
-export class NavItemComponent {
- private document = inject(DOCUMENT);
-
- // public props
- @Input() item!: NavigationItem;
-
- // public method
- closeOtherMenu(event: MouseEvent) {
- const ele = event.target as HTMLElement;
- if (ele !== null && ele !== undefined) {
- const parent = ele.parentElement as HTMLElement;
- const up_parent = ((parent.parentElement as HTMLElement).parentElement as HTMLElement).parentElement as HTMLElement;
- const last_parent = up_parent.parentElement;
- const sections = this.document.querySelectorAll('.coded-hasmenu');
- for (let i = 0; i < sections.length; i++) {
- sections[i].classList.remove('active');
- sections[i].classList.remove('coded-trigger');
- }
-
- if (parent.classList.contains('coded-hasmenu')) {
- parent.classList.add('coded-trigger');
- parent.classList.add('active');
- } else if (up_parent.classList.contains('coded-hasmenu')) {
- up_parent.classList.add('coded-trigger');
- up_parent.classList.add('active');
- } else if (last_parent?.classList.contains('coded-hasmenu')) {
- last_parent.classList.add('coded-trigger');
- last_parent.classList.add('active');
- }
- }
- if ((document.querySelector('app-navigation.pc-sidebar') as HTMLDivElement).classList.contains('mob-open')) {
- (document.querySelector('app-navigation.pc-sidebar') as HTMLDivElement).classList.remove('mob-open');
- }
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.html b/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.html
deleted file mode 100644
index 8d9ddf6..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.scss b/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.ts b/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.ts
deleted file mode 100644
index 3821a53..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.component.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-// Angular import
-import {Component, EventEmitter, inject, OnInit, Output, output} from '@angular/core';
-import {CommonModule, DOCUMENT} from '@angular/common';
-import {NavContentComponent} from './nav-content/nav-content.component';
-
-// project import
-
-//import { NavContentComponent } from './nav-content/nav-content.component';
-
-@Component({
- selector: 'app-navigation',
- // imports: [ CommonModule],
- templateUrl: './navigation.component.html',
- imports: [
- NavContentComponent
- ],
- styleUrls: ['./navigation.component.scss']
-})
-export class NavigationComponent implements OnInit{
-
- private document = inject(DOCUMENT);
-
- // media 1025 After Use Menu Open
- NavCollapsedMob = output();
-
- navCollapsedMob:boolean;
- windowWidth: number | undefined = 1000;
-
- // Constructor
- constructor() {
- //window.
- // this.windowWidth = !document ? 0 : !document.defaultView ? 0 : document.defaultView?.innerWidth;
- //this.windowWidth = document.defaultView !== null? document.defaultView.innerWidth : 1000;//window.innerWidth;
- this.navCollapsedMob = false;
- }
-
- ngOnInit(): void {
- this.windowWidth = this.document.defaultView?.innerWidth;
- }
-
- // public method
- // @Output() NavCollapse = new EventEmitter();
- @Output() NavCollapse = new EventEmitter();
- navCollapseMob() {
- if (!!this.windowWidth && this.windowWidth < 1025) {
- this.NavCollapsedMob.emit();
- }
- }
-}
diff --git a/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.ts b/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.ts
deleted file mode 100644
index 1673226..0000000
--- a/jambotron-ui/src/app/layouts/admin-layout/navigation/navigation.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-export interface NavigationItem {
- id: string;
- title: string;
- type: 'item' | 'collapse' | 'group';
- translate?: string;
- icon?: string;
- hidden?: boolean;
- url?: string;
- classes?: string;
- groupClasses?: string;
- exactMatch?: boolean;
- external?: boolean;
- target?: boolean;
- breadcrumbs?: boolean;
- children?: NavigationItem[];
- link?: string;
- description?: string;
- path?: string;
-}
-
-export const NavigationItems: NavigationItem[] = [
- {
- id: 'admin',
- title: 'Admin',
- type: 'group',
- icon: 'icon-navigation',
- url: '/main/admin/welcome',
- target: true,
- children: [
- {
- id: 'settings',
- title: 'Settings',
- type: 'item',
- classes: 'nav-item',
- url: '/main/admin/settings',
- icon: 'dashboard',
- // breadcrumbs: false
- },
- {
- id: 'system',
- title: 'System',
- type: 'item',
- classes: 'nav-item',
- url: '/main/admin/system',
- icon: 'dashboard',
- // breadcrumbs: false
- }
- ]
- }/*,
- {
- id: 'authentication',
- title: 'Authentication',
- type: 'group',
- icon: 'icon-navigation',
- children: [
- {
- id: 'login',
- title: 'Login',
- type: 'item',
- classes: 'nav-item',
- url: '/login',
- icon: 'login',
- target: true,
- breadcrumbs: false
- },
- {
- id: 'register',
- title: 'Register',
- type: 'item',
- classes: 'nav-item',
- url: '/register',
- icon: 'profile',
- target: true,
- breadcrumbs: false
- }*/
- // ]
- // }
- /*,
- {
- id: 'utilities',
- title: 'UI Components',
- type: 'group',
- icon: 'icon-navigation',
- children: [
- {
- id: 'typography',
- title: 'Typography',
- type: 'item',
- classes: 'nav-item',
- url: '/typography',
- icon: 'font-size'
- },
- {
- id: 'color',
- title: 'Colors',
- type: 'item',
- classes: 'nav-item',
- url: '/color',
- icon: 'bg-colors'
- },
- {
- id: 'ant-icons',
- title: 'Ant Icons',
- type: 'item',
- classes: 'nav-item',
- url: 'https://ant.design/components/icon',
- icon: 'ant-design',
- target: true,
- external: true
- }
- ]
- },
-
- {
- id: 'other',
- title: 'Other',
- type: 'group',
- icon: 'icon-navigation',
- children: [
- {
- id: 'sample-page',
- title: 'Sample Page',
- type: 'item',
- url: '/sample-page',
- classes: 'nav-item',
- icon: 'chrome'
- },
- {
- id: 'document',
- title: 'Document',
- type: 'item',
- classes: 'nav-item',
- url: 'https://codedthemes.gitbook.io/mantis-angular/',
- icon: 'question',
- target: true,
- external: true
- }
- ]
- }*/
-];
diff --git a/jambotron-ui/src/app/layouts/guest-layout/guest-layout.component.scss b/jambotron-ui/src/app/layouts/guest-layout/guest-layout.component.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/jambotron-ui/src/app/layouts/guest-layout/guest-layout.component.ts b/jambotron-ui/src/app/layouts/guest-layout/guest-layout.component.ts
deleted file mode 100644
index 7582ad4..0000000
--- a/jambotron-ui/src/app/layouts/guest-layout/guest-layout.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Component } from '@angular/core';
-import { RouterModule } from '@angular/router';
-
-@Component({
- selector: 'app-guest-layout',
- imports: [RouterModule],
- templateUrl: './guest-layout.component.html',
- styleUrl: './guest-layout.component.scss'
-})
-export class GuestLayoutComponent {}
diff --git a/jambotron-ui/src/app/main-module/main.module.ts b/jambotron-ui/src/app/main-module/main.module.ts
deleted file mode 100644
index c5b199f..0000000
--- a/jambotron-ui/src/app/main-module/main.module.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import {RouterModule} from '@angular/router';
-import {MainComponent} from './main.component/main.component';
-import {mainRouting} from './main.routing';
-import {NgModule} from '@angular/core';
-import {CommonModule} from '@angular/common';
-
-import {HTTP_INTERCEPTORS} from '@angular/common/http';
-import {CustomHttpInterceptor} from '../helpers/custom-http-interceptor';
-import {AngularImageViewerModule} from '@hreimer/angular-image-viewer';
-import {BrowserModule} from '@angular/platform-browser';
-import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
-
-@NgModule({
- declarations: [
-
- ],
- imports: [
- mainRouting,
- MainComponent,
-
-
-
- ],
- exports: [RouterModule],
- providers: [{
- provide: HTTP_INTERCEPTORS,
- useClass: CustomHttpInterceptor,
- multi: true
- }],
-})
-export class MainModule {}
diff --git a/jambotron-ui/src/app/main-module/main.routing.ts b/jambotron-ui/src/app/main-module/main.routing.ts
deleted file mode 100644
index 9059cc4..0000000
--- a/jambotron-ui/src/app/main-module/main.routing.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import {RouterModule, Routes} from '@angular/router';
-import {HomeComponent} from '../components/home.component/home.component';
-import {MainComponent} from './main.component/main.component';
-import {TutorialsListComponent} from '../components/tutorials-list/tutorials-list.component';
-import {TutorialsComponent} from '../components/tutorials.component/tutorials.component';
-import {AddTutorialComponent} from '../components/add-tutorial/add-tutorial.component';
-import {RegisterComponent} from '../components/register/register.component';
-import {ProfileComponent} from '../components/profile/profile.component';
-import {GenerateImageComponent} from '../components/generate-image.component/generate-image.component';
-
-const MAIN_ROUTES: Routes =[
- {
- path: '',
- component: MainComponent,
- children: [
- {
- path: 'admin',
- loadChildren: () =>
- import('../admin-module/admin.module').then((m) => m.AdminModule),
- },
- {
- path: 'user',
- loadChildren: () =>
- import('../user-module/user.module').then((m) => m.UserModule),
- },
- {
- path: 'home',
- component: HomeComponent
- },
- {
- path: 'generate-image',
- component: GenerateImageComponent
- },
- {
- path: 'tutorials',
- component: TutorialsComponent
- },
- {
- path: 'add-tutorial',
- component: AddTutorialComponent
- },
- {
- path: 'register',
- component : RegisterComponent
- },
- {
- path: 'profile',
- component : ProfileComponent
- }
- ],
- }
-];
-
-export const mainRouting = RouterModule.forChild(MAIN_ROUTES);
diff --git a/jambotron-ui/src/app/models/file-info.spec.ts b/jambotron-ui/src/app/models/file-info.spec.ts
new file mode 100644
index 0000000..0a4e521
--- /dev/null
+++ b/jambotron-ui/src/app/models/file-info.spec.ts
@@ -0,0 +1,7 @@
+import { FileInfo } from './file-info';
+
+describe('FileInfo', () => {
+ it('should create an instance', () => {
+ expect(new FileInfo()).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/models/file-info.ts b/jambotron-ui/src/app/models/file-info.ts
new file mode 100644
index 0000000..fc55c33
--- /dev/null
+++ b/jambotron-ui/src/app/models/file-info.ts
@@ -0,0 +1,4 @@
+export class FileInfo {
+ name: string | undefined;
+ url: string | undefined;
+}
diff --git a/jambotron-ui/src/app/models/image.ts b/jambotron-ui/src/app/models/image.ts
index cf7f79b..fc813b8 100644
--- a/jambotron-ui/src/app/models/image.ts
+++ b/jambotron-ui/src/app/models/image.ts
@@ -1,5 +1,5 @@
export class Image {
url?:string;
- b64Json?:boolean;
+ b64string?:string;
}
diff --git a/jambotron-ui/src/app/models/tutorial.model.ts b/jambotron-ui/src/app/models/tutorial.model.ts
index 66c6ce3..e41afe1 100644
--- a/jambotron-ui/src/app/models/tutorial.model.ts
+++ b/jambotron-ui/src/app/models/tutorial.model.ts
@@ -1,6 +1,15 @@
export class Tutorial {
+ isSelected?: boolean;
id?: any;
title?: string;
description?: string;
published?: boolean;
+ created?: Date;
+ modified?: Date;
+ tobepublished?: boolean;
+ isEdit?: boolean;
+ titleimage?: string;
+ body?: string;
}
+
+
diff --git a/jambotron-ui/src/app/modules/admin-module/admin-module.service.spec.ts b/jambotron-ui/src/app/modules/admin-module/admin-module.service.spec.ts
new file mode 100644
index 0000000..1dfcd33
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/admin-module.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AdminModuleService } from './admin-module.service';
+
+describe('AdminModuleService', () => {
+ let service: AdminModuleService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(AdminModuleService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/admin-module/admin-module.service.ts b/jambotron-ui/src/app/modules/admin-module/admin-module.service.ts
new file mode 100644
index 0000000..ee2b26b
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/admin-module.service.ts
@@ -0,0 +1,20 @@
+import { Injectable } from '@angular/core';
+import {Observable} from 'rxjs';
+import {HttpClient} from '@angular/common/http';
+import {User} from '../../models/user.model';
+import {GlobalConstants} from '../../global-constants';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AdminModuleService {
+ baseUrl = GlobalConstants.API_URL;
+
+ constructor(private http: HttpClient) {
+
+}
+
+ updateUserRoles(id: any,data: User): Observable {
+ return this.http.put(`${this.baseUrl}/users/${id}`, data);
+ }
+}
diff --git a/jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.html b/jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.html
similarity index 100%
rename from jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.html
rename to jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.html
diff --git a/jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.scss b/jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.scss
similarity index 100%
rename from jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.scss
rename to jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.scss
diff --git a/jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.spec.ts b/jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.spec.ts
rename to jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.spec.ts
diff --git a/jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.ts b/jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.ts
similarity index 100%
rename from jambotron-ui/src/app/admin-module/admin-welcome.component/admin-welcome.component.ts
rename to jambotron-ui/src/app/modules/admin-module/admin-welcome.component/admin-welcome.component.ts
diff --git a/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.html b/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.html
new file mode 100644
index 0000000..ef736f1
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.scss b/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.scss
new file mode 100644
index 0000000..9a8a04f
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.scss
@@ -0,0 +1,16 @@
+//---------------
+.pc-sidebar{
+ top: 65px;
+ overflow-y: auto;
+ background-color: rgba(153, 153, 153, 0.16);
+
+ backdrop-filter: blur(8px);
+}
+.pc-container{
+ top: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+
+
diff --git a/jambotron-ui/src/app/admin-module/admin.component/admin.component.spec.ts b/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/admin-module/admin.component/admin.component.spec.ts
rename to jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.spec.ts
diff --git a/jambotron-ui/src/app/admin-module/admin.component/admin.component.ts b/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.ts
similarity index 76%
rename from jambotron-ui/src/app/admin-module/admin.component/admin.component.ts
rename to jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.ts
index c7496dc..bd93db9 100644
--- a/jambotron-ui/src/app/admin-module/admin.component/admin.component.ts
+++ b/jambotron-ui/src/app/modules/admin-module/admin.component/admin.component.ts
@@ -1,30 +1,15 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
-import {BreadcrumbComponent} from "../../components/breadcrumb/breadcrumb.component";
-import {NavBarComponent} from "../../layouts/admin-layout/nav-bar/nav-bar.component";
-import {NavigationComponent} from "../../layouts/admin-layout/navigation/navigation.component";
-import {Router, RouterLink, RouterOutlet} from "@angular/router";
-import {DOCUMENT, NgClass, NgStyle} from '@angular/common';
-import {MatDrawerContainer, MatSidenavModule} from '@angular/material/sidenav';
-import {MatButton} from '@angular/material/button';
+import {Router, RouterOutlet} from "@angular/router";
+import {DOCUMENT} from '@angular/common';
import { ScrollPanelModule } from 'primeng/scrollpanel';
import {MenuItem} from 'primeng/api';
-import {PanelMenu} from 'primeng/panelmenu';
+import {SideBarAdminComponent} from '../side-bar-admin.component/side-bar-admin.component';
@Component({
selector: 'app-admin.component',
imports: [
- BreadcrumbComponent,
- NavBarComponent,
- NavigationComponent,
RouterOutlet,
- NgClass,
- MatDrawerContainer,
- MatSidenavModule,
ScrollPanelModule,
- MatButton,
- RouterLink,
- PanelMenu,
- NgStyle,
-
+ SideBarAdminComponent
],
schemas:[CUSTOM_ELEMENTS_SCHEMA],
templateUrl: './admin.component.html',
@@ -36,11 +21,9 @@ export class AdminComponent implements OnInit {
items: MenuItem[] | undefined;
-
constructor(private router: Router) {
this.height = 0
-
}
// public props
diff --git a/jambotron-ui/src/app/admin-module/admin.module.ts b/jambotron-ui/src/app/modules/admin-module/admin.module.ts
similarity index 100%
rename from jambotron-ui/src/app/admin-module/admin.module.ts
rename to jambotron-ui/src/app/modules/admin-module/admin.module.ts
diff --git a/jambotron-ui/src/app/admin-module/admin.routing.ts b/jambotron-ui/src/app/modules/admin-module/admin.routing.ts
similarity index 79%
rename from jambotron-ui/src/app/admin-module/admin.routing.ts
rename to jambotron-ui/src/app/modules/admin-module/admin.routing.ts
index 246ca73..eb1ff4f 100644
--- a/jambotron-ui/src/app/admin-module/admin.routing.ts
+++ b/jambotron-ui/src/app/modules/admin-module/admin.routing.ts
@@ -8,7 +8,7 @@ const ADMIN_ROUTES: Routes = [
children: [
{
- path: 'welcome',
+ path: 'admin-welcome',
loadComponent: () => import('../admin-module/admin-welcome.component/admin-welcome.component').then((c) => c.AdminWelcomeComponent),
},
{
@@ -17,9 +17,12 @@ const ADMIN_ROUTES: Routes = [
},
{
path: 'system',
-
loadComponent: () => import('../admin-module/system.component/system.component').then((c) => c.SystemComponent)
- }
+ },
+ {
+ path: 'users',
+ loadComponent: () => import('../admin-module/users.component/users.component').then((c) => c.UsersComponent)
+ }
]
}
];
diff --git a/jambotron-ui/src/app/admin-module/settings.component/settings.component.html b/jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.html
similarity index 100%
rename from jambotron-ui/src/app/admin-module/settings.component/settings.component.html
rename to jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.html
diff --git a/jambotron-ui/src/app/admin-module/settings.component/settings.component.scss b/jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.scss
similarity index 100%
rename from jambotron-ui/src/app/admin-module/settings.component/settings.component.scss
rename to jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.scss
diff --git a/jambotron-ui/src/app/admin-module/settings.component/settings.component.spec.ts b/jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/admin-module/settings.component/settings.component.spec.ts
rename to jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.spec.ts
diff --git a/jambotron-ui/src/app/admin-module/settings.component/settings.component.ts b/jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.ts
similarity index 82%
rename from jambotron-ui/src/app/admin-module/settings.component/settings.component.ts
rename to jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.ts
index 1209c58..b728cab 100644
--- a/jambotron-ui/src/app/admin-module/settings.component/settings.component.ts
+++ b/jambotron-ui/src/app/modules/admin-module/settings.component/settings.component.ts
@@ -11,11 +11,8 @@ import {
MatHeaderRowDef,
MatRow, MatRowDef, MatTable
} from '@angular/material/table';
-import {MatIconButton} from '@angular/material/button';
-import {MatList, MatListItem} from '@angular/material/list';
-import {Bean} from '../../models/Bean';
-import {SystemService} from '../../services/system.service';
-import {NameValueItem} from '../../models/name-value-item';
+import {SystemService} from '../../../services/system.service';
+import {NameValueItem} from '../../../models/name-value-item';
@Component({
selector: 'app-settings.component',
diff --git a/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.html b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.html
new file mode 100644
index 0000000..3d40615
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.html
@@ -0,0 +1,35 @@
+
+
+
+ house
+ @if (!isCollapsed) {
+ Dashboard
+ }
+
+
+
+
+ newspaper
+ @if (!isCollapsed) {
+ Users
+ }
+
+
+
+
+ newspaper
+ @if (!isCollapsed) {
+ Settings
+ }
+
+
+
+
+ newspaper
+ @if (!isCollapsed) {
+ System
+ }
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.scss b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.scss
new file mode 100644
index 0000000..62a5b67
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.scss
@@ -0,0 +1,14 @@
+.entry{
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding:0.75rem;
+ color: rgba(24, 255, 255, 0.96);
+
+}
+
+a.mdc-list-item
+{
+
+ background-color: rgba(24,255,255,0.04);
+}
diff --git a/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.spec.ts b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.spec.ts
new file mode 100644
index 0000000..b6f9f21
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SideBarAdminComponent } from './side-bar-admin.component';
+
+describe('SideBarAdminComponent', () => {
+ let component: SideBarAdminComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [SideBarAdminComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(SideBarAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.ts b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.ts
new file mode 100644
index 0000000..0940b6f
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/side-bar-admin.component/side-bar-admin.component.ts
@@ -0,0 +1,20 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {MatListItem, MatNavList} from '@angular/material/list';
+import {RouterLink} from '@angular/router';
+import {MatIcon} from '@angular/material/icon';
+
+@Component({
+ selector: 'app-side-bar-admin',
+ imports: [
+ MatIcon,
+ MatListItem,
+ MatNavList,
+ RouterLink
+ ],
+ templateUrl: './side-bar-admin.component.html',
+ styleUrl: './side-bar-admin.component.scss',
+ schemas:[CUSTOM_ELEMENTS_SCHEMA]
+})
+export class SideBarAdminComponent {
+ isCollapsed = false;
+}
diff --git a/jambotron-ui/src/app/admin-module/system.component/system.component.html b/jambotron-ui/src/app/modules/admin-module/system.component/system.component.html
similarity index 100%
rename from jambotron-ui/src/app/admin-module/system.component/system.component.html
rename to jambotron-ui/src/app/modules/admin-module/system.component/system.component.html
diff --git a/jambotron-ui/src/app/admin-module/system.component/system.component.scss b/jambotron-ui/src/app/modules/admin-module/system.component/system.component.scss
similarity index 100%
rename from jambotron-ui/src/app/admin-module/system.component/system.component.scss
rename to jambotron-ui/src/app/modules/admin-module/system.component/system.component.scss
diff --git a/jambotron-ui/src/app/admin-module/system.component/system.component.spec.ts b/jambotron-ui/src/app/modules/admin-module/system.component/system.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/admin-module/system.component/system.component.spec.ts
rename to jambotron-ui/src/app/modules/admin-module/system.component/system.component.spec.ts
diff --git a/jambotron-ui/src/app/admin-module/system.component/system.component.ts b/jambotron-ui/src/app/modules/admin-module/system.component/system.component.ts
similarity index 94%
rename from jambotron-ui/src/app/admin-module/system.component/system.component.ts
rename to jambotron-ui/src/app/modules/admin-module/system.component/system.component.ts
index 115c92d..22b2009 100644
--- a/jambotron-ui/src/app/admin-module/system.component/system.component.ts
+++ b/jambotron-ui/src/app/modules/admin-module/system.component/system.component.ts
@@ -1,5 +1,5 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
-import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
+import {MatCard, MatCardContent} from '@angular/material/card';
import {MatDivider} from '@angular/material/divider';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
import {
@@ -14,15 +14,14 @@ import {
} from '@angular/material/table';
import {MatList, MatListItem} from '@angular/material/list';
import {MatIconButton} from '@angular/material/button';
-import {SystemService} from '../../services/system.service';
-import {Bean} from '../../models/Bean';
+import {SystemService} from '../../../services/system.service';
+import {Bean} from '../../../models/Bean';
import {MatIcon} from '@angular/material/icon';
@Component({
selector: 'app-system.component',
imports: [
MatCard,
- MatCardHeader,
MatCardContent,
MatDivider,
MatTabGroup,
diff --git a/jambotron-ui/src/app/modules/admin-module/users.component/users.component.html b/jambotron-ui/src/app/modules/admin-module/users.component/users.component.html
new file mode 100644
index 0000000..4634e66
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/users.component/users.component.html
@@ -0,0 +1,56 @@
+
diff --git a/jambotron-ui/src/app/components/board-admin/board-admin.component.scss b/jambotron-ui/src/app/modules/admin-module/users.component/users.component.scss
similarity index 100%
rename from jambotron-ui/src/app/components/board-admin/board-admin.component.scss
rename to jambotron-ui/src/app/modules/admin-module/users.component/users.component.scss
diff --git a/jambotron-ui/src/app/components/home.component/home.component.spec.ts b/jambotron-ui/src/app/modules/admin-module/users.component/users.component.spec.ts
similarity index 56%
rename from jambotron-ui/src/app/components/home.component/home.component.spec.ts
rename to jambotron-ui/src/app/modules/admin-module/users.component/users.component.spec.ts
index 1191557..241dbac 100644
--- a/jambotron-ui/src/app/components/home.component/home.component.spec.ts
+++ b/jambotron-ui/src/app/modules/admin-module/users.component/users.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { HomeComponent } from './home.component';
+import { UsersComponent } from './users.component';
-describe('HomeComponent', () => {
- let component: HomeComponent;
- let fixture: ComponentFixture;
+describe('UsersComponent', () => {
+ let component: UsersComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [HomeComponent]
+ imports: [UsersComponent]
})
.compileComponents();
- fixture = TestBed.createComponent(HomeComponent);
+ fixture = TestBed.createComponent(UsersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/jambotron-ui/src/app/modules/admin-module/users.component/users.component.ts b/jambotron-ui/src/app/modules/admin-module/users.component/users.component.ts
new file mode 100644
index 0000000..8725efb
--- /dev/null
+++ b/jambotron-ui/src/app/modules/admin-module/users.component/users.component.ts
@@ -0,0 +1,182 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA, model} from '@angular/core';
+import {UserService} from '../../../services/user.service';
+import {RolesService} from '../../../services/roles.service';
+import {Role} from '../../../models/role';
+import {User} from '../../../models/user.model';
+import {
+ MatCell,
+ MatCellDef, MatColumnDef,
+ MatHeaderCell, MatHeaderCellDef,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef, MatTable
+} from '@angular/material/table';
+import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {
+ MatAutocomplete,
+ MatAutocompleteSelectedEvent,
+ MatAutocompleteTrigger,
+ MatOption
+} from '@angular/material/autocomplete';
+import {
+ MatChip,
+ MatChipGrid,
+ MatChipInput,
+ MatChipInputEvent,
+ MatChipRow,
+ MatChipsModule
+} from '@angular/material/chips';
+import {MatIcon} from '@angular/material/icon';
+import {COMMA, ENTER} from '@angular/cdk/keycodes';
+import {MatFormField} from '@angular/material/input';
+import {MatSelect, MatSelectTrigger} from '@angular/material/select';
+import {AdminModuleService} from '../admin-module.service';
+
+@Component({
+ selector: 'app-users.component',
+ imports: [
+ MatCell,
+ MatCellDef,
+ MatHeaderCell,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef,
+ MatTable,
+ MatColumnDef,
+ MatHeaderCellDef,
+ FormsModule,
+ ReactiveFormsModule,
+ MatChipGrid,
+ MatChipRow,
+ MatIcon,
+ MatAutocompleteTrigger,
+ MatChipInput,
+ MatAutocomplete,
+ MatOption,
+ MatFormField,
+ MatChipsModule
+ ],
+ templateUrl: './users.component.html',
+ styleUrl: './users.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+export class UsersComponent {
+
+ readonly separatorKeysCodes: number[] = [ENTER, COMMA];
+ allRoles:Role[] = [];
+ users: User[] = [];
+
+ displayedColumns: string[] = UserColumns.map((col) => col.key)
+ columnsSchema: any = UserColumns
+
+ readonly currentRole = model('');
+
+ rolesControl = new FormControl([]);
+ protected ac = new FormControl('');
+
+ constructor(private userService: UserService,
+ private roleService: RolesService,
+ private adminService:AdminModuleService) {
+ this.getAllRoles();
+ this.getUsers();
+ }
+
+ getAllRoles():any{
+ this.roleService.getAllRoles().subscribe(
+ (data : any) => {
+ this.allRoles = data;
+ console.log(data);
+ },
+ (err : any)=> {
+ this.allRoles = JSON.parse(err.error).message;
+ }
+ );
+ }
+
+ getUsers(){
+ this.userService.getAdminBoard().subscribe(
+ (data : any) => {
+ this.users = data;
+ console.log(data);
+ },
+ (err : any)=> {
+ console.log(JSON.parse(err.error).message);
+ }
+ );
+ }
+
+ filteredRoles(roles : Role[]):any {
+ return this.allRoles.filter(
+ (r:Role) => !roles.some((item) => item.id === r.id),
+ );
+ }
+
+ change($event: Event, roles: Role[]) {
+ roles.filter(
+ (r:Role) => !roles.some((item) => item.name?.toLowerCase() === r.name?.toLowerCase()),
+ )
+ }
+
+ selected(user: User, $event: MatAutocompleteSelectedEvent) {
+ user.roles.push($event.option.value);
+ this.saveUserRoles(user);
+ this.currentRole.set('');
+ $event.option.deselect();
+ }
+
+ add($event: MatChipInputEvent, user:User) {
+ /*const value = ($event.value || '').trim();
+
+ const role = this.allRoles.find(value1 => value1.name === value);
+ if(role){
+ user.roles.push(value);
+ this.saveUserRoles(user);
+ }*/
+ // Clear the input value
+ this.currentRole.set('');
+ }
+
+ remove(role: any, user: User) {
+ const updatedRoles: Role[] = user.roles.filter(
+ (r: Role) => r.id !== role.id // Simple comparison with the role to remove
+ );
+
+ if (updatedRoles && updatedRoles.length >= 1) {
+ user.roles = updatedRoles;
+ this.saveUserRoles(user);
+ }
+ }
+
+ saveUserRoles(user:User){
+ this.adminService.updateUserRoles(user.id, user).subscribe(
+ (data:any) => {
+ console.log(data);
+ },
+ (err:any) => {
+ console.log(err);
+ }
+ )
+ }
+}
+
+export const UserColumns = [
+ {
+ key: 'username',
+ type: 'text',
+ label: 'Name',
+ required: true,
+ },
+ {
+ key: 'email',
+ type: 'text',
+ label: 'Email',
+ },
+ {
+ key: 'roles',
+ type: 'list',
+ label: 'Roles',
+ required: true,
+ }
+];
diff --git a/jambotron-ui/src/app/layouts/guest-layout/guest-layout.component.html b/jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.html
similarity index 100%
rename from jambotron-ui/src/app/layouts/guest-layout/guest-layout.component.html
rename to jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.html
diff --git a/jambotron-ui/src/app/components/board-moderator/board-moderator.component.scss b/jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.scss
similarity index 100%
rename from jambotron-ui/src/app/components/board-moderator/board-moderator.component.scss
rename to jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.scss
diff --git a/jambotron-ui/src/app/components/system.component/system.component.spec.ts b/jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.spec.ts
similarity index 55%
rename from jambotron-ui/src/app/components/system.component/system.component.spec.ts
rename to jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.spec.ts
index bae84c7..8757d60 100644
--- a/jambotron-ui/src/app/components/system.component/system.component.spec.ts
+++ b/jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { SystemComponent } from './system.component';
+import { AiMainComponent } from './ai-main.component';
-describe('SystemComponent', () => {
- let component: SystemComponent;
- let fixture: ComponentFixture;
+describe('AiMainComponent', () => {
+ let component: AiMainComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [SystemComponent]
+ imports: [AiMainComponent]
})
.compileComponents();
- fixture = TestBed.createComponent(SystemComponent);
+ fixture = TestBed.createComponent(AiMainComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.ts b/jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.ts
new file mode 100644
index 0000000..7e7559a
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/ai-main.component/ai-main.component.ts
@@ -0,0 +1,14 @@
+import { Component } from '@angular/core';
+import {RouterOutlet} from '@angular/router';
+
+@Component({
+ selector: 'app-ai-main.component',
+ imports: [
+ RouterOutlet
+ ],
+ templateUrl: './ai-main.component.html',
+ styleUrl: './ai-main.component.scss'
+})
+export class AiMainComponent {
+
+}
diff --git a/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.html b/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.html
new file mode 100644
index 0000000..2eb2466
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.html
@@ -0,0 +1 @@
+ai-welcome.component works!
diff --git a/jambotron-ui/src/app/components/board-user/board-user.component.scss b/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.scss
similarity index 100%
rename from jambotron-ui/src/app/components/board-user/board-user.component.scss
rename to jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.scss
diff --git a/jambotron-ui/src/app/components/article-comments/article-comments.spec.ts b/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.spec.ts
similarity index 53%
rename from jambotron-ui/src/app/components/article-comments/article-comments.spec.ts
rename to jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.spec.ts
index 35dbfc8..b90e25f 100644
--- a/jambotron-ui/src/app/components/article-comments/article-comments.spec.ts
+++ b/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ArticleComments } from './article-comments';
+import { AiWelcomeComponent } from './ai-welcome.component';
-describe('ArticleComments', () => {
- let component: ArticleComments;
- let fixture: ComponentFixture;
+describe('AiWelcomeComponent', () => {
+ let component: AiWelcomeComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [ArticleComments]
+ imports: [AiWelcomeComponent]
})
.compileComponents();
- fixture = TestBed.createComponent(ArticleComments);
+ fixture = TestBed.createComponent(AiWelcomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.ts b/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.ts
new file mode 100644
index 0000000..f152488
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/ai-welcome.component/ai-welcome.component.ts
@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-ai-welcome.component',
+ imports: [],
+ templateUrl: './ai-welcome.component.html',
+ styleUrl: './ai-welcome.component.scss'
+})
+export class AiWelcomeComponent {
+
+}
diff --git a/jambotron-ui/src/app/modules/ai-module/ai.module.ts b/jambotron-ui/src/app/modules/ai-module/ai.module.ts
new file mode 100644
index 0000000..a72727b
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/ai.module.ts
@@ -0,0 +1,21 @@
+import {NgModule} from '@angular/core';
+import {moderatorRouting} from '../moderator-module/moderator.routing';
+import {ModeratorComponent} from '../moderator-module/moderator.component/moderator.component';
+import {CommonModule} from '@angular/common';
+import {AiWelcomeComponent} from './ai-welcome.component/ai-welcome.component';
+import {AiMainComponent} from './ai-main.component/ai-main.component';
+import {aiRouting} from './ai.routing';
+
+import {MatDialogModule} from "@angular/material/dialog";
+
+@NgModule({
+ declarations: [],
+ imports: [
+ aiRouting,
+ AiMainComponent,
+ CommonModule,
+
+ MatDialogModule
+ ]
+})
+export class AiModule { }
diff --git a/jambotron-ui/src/app/modules/ai-module/ai.routing.ts b/jambotron-ui/src/app/modules/ai-module/ai.routing.ts
new file mode 100644
index 0000000..44ee87b
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/ai.routing.ts
@@ -0,0 +1,22 @@
+import {RouterModule, Routes} from '@angular/router';
+import {AiMainComponent} from './ai-main.component/ai-main.component';
+
+const AI_ROUTES: Routes = [
+ {
+ path: '',
+ component: AiMainComponent,
+
+ children: [
+ {
+ path: 'ai-welcome',
+ loadComponent: () => import('../ai-module/ai-welcome.component/ai-welcome.component').then((c) => c.AiWelcomeComponent),
+ },
+ {
+ path: 'generate-image',
+ loadComponent: () => import('../ai-module/generate-image.component/generate-image.component').then((c) => c.GenerateImageComponent)
+ }
+ ]
+ }
+];
+
+export const aiRouting = RouterModule.forChild(AI_ROUTES);
diff --git a/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.html b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.html
new file mode 100644
index 0000000..1fc5881
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.html
@@ -0,0 +1,49 @@
+
+
+
+ Generate image
+
+
+
+ Query
+
+
+
+
+
+ @if (spinnerService.visibility | async) {
+
+ } @else {
+
+ }
+
+
+
+@for (image of images; track image) {
+
+
+
+

+
+
+
+
+ @if (isLoggedIn) {
+
+ }
+
+
+
+
+
+
+
+}
+
+
diff --git a/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.scss b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.scss
new file mode 100644
index 0000000..6004a05
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.scss
@@ -0,0 +1,42 @@
+.generate-image-view {
+ margin-right: 12px;
+ padding: 20px 22px 18px 22px;
+ margin-top: 81px;
+ margin-left: 12px;
+ background-color: #e7edf3c2;
+ backdrop-filter: blur(8px);
+}
+
+.image-container{
+ width: 100%;
+ height: 100%;
+
+}
+.image-container .image-toolbar
+{
+ background-color: rgba(153,153,153,0);
+
+ color: white;
+ position: absolute;
+ left : 50%;
+ top: 5%;
+ transform: translate(-50%, -50%);
+ -ms-transform: translate(-50%, -50%);
+ padding: 0px 2px;
+ border: none;
+ border-radius: 5px;
+}
+.image-container .menu-spacer {
+ flex: 1 1 auto;
+}
+
+.image-container .image-file-name {
+ padding-left: 10px;
+}
+
+.mat-mdc-mini-fab{
+ margin: 5px;
+}
+.example-full-width {
+ width: 100%;
+}
diff --git a/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.spec.ts b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.spec.ts
new file mode 100644
index 0000000..255f5e0
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GenerateImageComponent } from './generate-image.component';
+
+describe('GenerateImageComponent', () => {
+ let component: GenerateImageComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [GenerateImageComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(GenerateImageComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.ts b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.ts
new file mode 100644
index 0000000..70df3d9
--- /dev/null
+++ b/jambotron-ui/src/app/modules/ai-module/generate-image.component/generate-image.component.ts
@@ -0,0 +1,94 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core';
+import {AsyncPipe} from "@angular/common";
+import {FormsModule} from "@angular/forms";
+import {MatButton, MatMiniFabButton} from "@angular/material/button";
+import {
+ MatCard,
+ MatCardActions,
+ MatCardContent,
+ MatCardHeader
+} from "@angular/material/card";
+import {MatFormField, MatInput, MatLabel} from "@angular/material/input";
+import {Image} from '../../../models/image';
+import {ZhipuaiImageService} from '../zhipuai-image.service';
+import {SpinnerService} from '../../../services/spinner.service';
+import {MatProgressSpinner} from '@angular/material/progress-spinner';
+import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
+import {MatIcon} from '@angular/material/icon';
+import {MatTooltip} from '@angular/material/tooltip';
+import {TokenStorageService} from '../../../services/token-storage.service';
+import {UserApiService} from '../../user-module/user-api.service';
+
+@Component({
+ selector: 'app-generate-image.component',
+ imports: [
+ AsyncPipe,
+ FormsModule,
+ MatButton,
+ MatCard,
+ MatCardActions,
+ MatCardContent,
+ MatCardHeader,
+ MatFormField,
+ MatInput,
+ MatLabel,
+ MatProgressSpinner,
+ MatToolbarRow,
+ MatToolbar,
+ MatIcon,
+ MatMiniFabButton,
+ MatTooltip,
+ ],
+ templateUrl: './generate-image.component.html',
+ styleUrl: './generate-image.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+export class GenerateImageComponent {
+ query : string = '';
+
+ images: Image[] = [];
+ image: Image = {
+ url: ''
+ };
+
+ isLoggedIn = false;
+
+ private storageService: TokenStorageService = inject(TokenStorageService);
+
+ constructor(
+ private zhipuaiImageService: ZhipuaiImageService,
+ public spinnerService: SpinnerService,
+ private userApiService:UserApiService
+ ) {
+ this.isLoggedIn = this.storageService.isLoggedIn();
+ }
+
+ generateImage(){
+ this.zhipuaiImageService.generate(this.query).subscribe(
+ data => {
+
+ this.image = data;
+
+ this.images.unshift(this.image);
+
+ // this.output.file = data.;
+ console.log(data);
+ },
+ error => {
+ console.log(error);
+ }
+ )
+ }
+
+ save(url: string | undefined) {
+ let requestUrl:string = url?url:"";
+ console.log(requestUrl);
+ this.userApiService.saveZhipuaiImage(requestUrl).subscribe(data =>{
+ console.log(data);
+ },error =>{
+ console.log(error);
+ });
+ }
+}
+
+
diff --git a/jambotron-ui/src/app/services/zhipuai-image.service.spec.ts b/jambotron-ui/src/app/modules/ai-module/zhipuai-image.service.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/services/zhipuai-image.service.spec.ts
rename to jambotron-ui/src/app/modules/ai-module/zhipuai-image.service.spec.ts
diff --git a/jambotron-ui/src/app/services/zhipuai-image.service.ts b/jambotron-ui/src/app/modules/ai-module/zhipuai-image.service.ts
similarity index 59%
rename from jambotron-ui/src/app/services/zhipuai-image.service.ts
rename to jambotron-ui/src/app/modules/ai-module/zhipuai-image.service.ts
index ab843e2..7bf9cff 100644
--- a/jambotron-ui/src/app/services/zhipuai-image.service.ts
+++ b/jambotron-ui/src/app/modules/ai-module/zhipuai-image.service.ts
@@ -1,9 +1,10 @@
import { Injectable } from '@angular/core';
import {Observable} from 'rxjs';
-import {Tutorial} from '../models/tutorial.model';
+import {Tutorial} from '../../models/tutorial.model';
import {HttpClient} from '@angular/common/http';
-import {Image} from '../models/image';
-import {SpinnerService} from './spinner.service';
+import {Image} from '../../models/image';
+import {SpinnerService} from '../../services/spinner.service';
+import {GlobalConstants} from '../../global-constants';
@@ -13,7 +14,7 @@ import {SpinnerService} from './spinner.service';
export class ZhipuaiImageService {
- baseUrl : string = 'http://localhost:8080/api/zhipuai';
+ baseUrl : string = `${GlobalConstants.API_URL}/public/zhipuai`;
constructor(private http: HttpClient,public spinnerService: SpinnerService) {
diff --git a/jambotron-ui/src/app/components/dialog/dialog.component.html b/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.html
similarity index 100%
rename from jambotron-ui/src/app/components/dialog/dialog.component.html
rename to jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.html
diff --git a/jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.scss b/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.scss
similarity index 100%
rename from jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.scss
rename to jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.scss
diff --git a/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.spec.ts b/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.spec.ts
new file mode 100644
index 0000000..af9ec3b
--- /dev/null
+++ b/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DialogLoginComponent } from './dialog-login.component';
+
+describe('DialogLoginComponent', () => {
+ let component: DialogLoginComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DialogLoginComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(DialogLoginComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.ts b/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.ts
new file mode 100644
index 0000000..229151c
--- /dev/null
+++ b/jambotron-ui/src/app/modules/main-module/main.component/dialog-login.component/dialog-login.component.ts
@@ -0,0 +1,86 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ CUSTOM_ELEMENTS_SCHEMA,
+ EventEmitter, Inject,
+ inject,
+ Output,
+ signal
+} from '@angular/core';
+import {FormsModule} from '@angular/forms';
+import {MatButton, MatIconButton} from '@angular/material/button';
+import {
+ MAT_DIALOG_DATA,
+ MatDialogActions,
+ MatDialogClose,
+ MatDialogContent,
+ MatDialogRef,
+ MatDialogTitle
+} from '@angular/material/dialog';
+import {MatFormField, MatInput, MatLabel, MatSuffix} from '@angular/material/input';
+import {MatIcon} from '@angular/material/icon';
+import {MatSnackBar} from '@angular/material/snack-bar';
+import {DialogLoginData} from '../main.component';
+
+@Component({
+ selector: 'app-dialog-login.component',
+ imports: [
+ FormsModule,
+ MatButton,
+ MatDialogActions,
+ MatDialogClose,
+ MatDialogContent,
+ MatDialogTitle,
+ MatFormField,
+ MatIcon,
+ MatIconButton,
+ MatInput,
+ MatLabel,
+ MatSuffix
+ ],
+ templateUrl: './dialog-login.component.html',
+ styleUrl: './dialog-login.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DialogLoginComponent {
+ readonly dialogRef = inject(MatDialogRef);
+ hide = signal(true);
+
+ @Output() loginClicked = new EventEmitter();
+ @Output() signupClicked = new EventEmitter();
+
+ private _snackBar = inject(MatSnackBar);
+ durationInSeconds = 5;
+
+
+ constructor(
+ @Inject(MAT_DIALOG_DATA) public data:DialogLoginData) {
+
+ }
+
+ openLoginFailedSnackBar(errorMessage : string = "Login failed.") {
+ this._snackBar.open(errorMessage , "", {
+ duration: this.durationInSeconds * 1000,
+ });
+ }
+
+ onNoClick() {
+ this.dialogRef.close();
+ }
+
+
+ clickEvent(event: MouseEvent) {
+ this.hide.set(!this.hide());
+ event.stopPropagation();
+ }
+
+ login() {
+ this.loginClicked.emit(this.data);
+
+ }
+
+ signup() {
+ this.signupClicked.emit();
+ }
+}
diff --git a/jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.html b/jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.html
similarity index 100%
rename from jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.html
rename to jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.html
diff --git a/jambotron-ui/src/app/components/dialog/dialog.component.scss b/jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.scss
similarity index 100%
rename from jambotron-ui/src/app/components/dialog/dialog.component.scss
rename to jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.scss
diff --git a/jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.spec.ts b/jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.spec.ts
rename to jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.spec.ts
diff --git a/jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.ts b/jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.ts
similarity index 97%
rename from jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.ts
rename to jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.ts
index 2d052e9..451be40 100644
--- a/jambotron-ui/src/app/components/dialog-signup.component/dialog-signup.component.ts
+++ b/jambotron-ui/src/app/modules/main-module/main.component/dialog-signup.component/dialog-signup.component.ts
@@ -22,7 +22,7 @@ import {
import {MatFormField, MatInput, MatLabel, MatSuffix} from '@angular/material/input';
import {MatIcon} from '@angular/material/icon';
import {MatSnackBar} from '@angular/material/snack-bar';
-import {DialogSignupData} from '../../main-module/main.component/main.component';
+import {DialogSignupData} from '../main.component';
import {ErrorStateMatcher} from '@angular/material/core';
@Component({
diff --git a/jambotron-ui/src/app/main-module/main.component/main.component.html b/jambotron-ui/src/app/modules/main-module/main.component/main.component.html
similarity index 59%
rename from jambotron-ui/src/app/main-module/main.component/main.component.html
rename to jambotron-ui/src/app/modules/main-module/main.component/main.component.html
index 19894b2..d4c3725 100644
--- a/jambotron-ui/src/app/main-module/main.component/main.component.html
+++ b/jambotron-ui/src/app/modules/main-module/main.component/main.component.html
@@ -1,15 +1,13 @@
-
+
+
diff --git a/jambotron-ui/src/app/main-module/main.component/main.component.scss b/jambotron-ui/src/app/modules/main-module/main.component/main.component.scss
similarity index 73%
rename from jambotron-ui/src/app/main-module/main.component/main.component.scss
rename to jambotron-ui/src/app/modules/main-module/main.component/main.component.scss
index fe0674f..bf054c9 100644
--- a/jambotron-ui/src/app/main-module/main.component/main.component.scss
+++ b/jambotron-ui/src/app/modules/main-module/main.component/main.component.scss
@@ -16,7 +16,11 @@ mat-toolbar{
}
.mat-mdc-raised-button:not(:disabled){
color: rgba(24, 255, 255, 0.96);
- background-color: rgba(24,255,255,0.04);
+ background-color: rgba(24, 255, 255, 0);
+}
+.mat-mdc-button:not(:disabled){
+ color: rgba(24, 255, 255, 0.96);
+ background-color: rgba(24, 255, 255, 0);
}
.mat-mdc-mini-fab{
margin: 5px;
diff --git a/jambotron-ui/src/app/main-module/main.component/main.component.spec.ts b/jambotron-ui/src/app/modules/main-module/main.component/main.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/main-module/main.component/main.component.spec.ts
rename to jambotron-ui/src/app/modules/main-module/main.component/main.component.spec.ts
diff --git a/jambotron-ui/src/app/main-module/main.component/main.component.ts b/jambotron-ui/src/app/modules/main-module/main.component/main.component.ts
similarity index 83%
rename from jambotron-ui/src/app/main-module/main.component/main.component.ts
rename to jambotron-ui/src/app/modules/main-module/main.component/main.component.ts
index 4ea4697..7d719d4 100644
--- a/jambotron-ui/src/app/main-module/main.component/main.component.ts
+++ b/jambotron-ui/src/app/modules/main-module/main.component/main.component.ts
@@ -1,32 +1,30 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
-//import {NavigationComponent} from '../navigation/navigation.component';
-
import {Router, RouterLink, RouterOutlet} from '@angular/router';
import {MatToolbar} from '@angular/material/toolbar';
import {MatButton, MatFabButton, MatMiniFabButton} from '@angular/material/button';
-import {IconDirective, IconService} from '@ant-design/icons-angular';
import {MatDialog} from '@angular/material/dialog';
import {Subscription} from 'rxjs';
-import {TokenStorageService} from '../../services/token-storage.service';
-import {AuthService} from '../../services/auth.service';
-import {EventBusService} from '../../_shared/event-bus.service';
-import {DialogComponent} from '../../components/dialog/dialog.component';
-
+import {TokenStorageService} from '../../../services/token-storage.service';
+import {AuthService} from '../../../services/auth.service';
+import {EventBusService} from '../../../_shared/event-bus.service';
import {CommonModule} from '@angular/common';
import {MatIcon} from '@angular/material/icon';
import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu';
import {MatTooltip} from '@angular/material/tooltip';
-import {DialogSignupComponent} from '../../components/dialog-signup.component/dialog-signup.component';
import {MatLabel} from '@angular/material/input';
+import {HttpClient} from '@angular/common/http';
+
+import { environment } from '../../../../environments/environment';
+import {DialogLoginComponent} from './dialog-login.component/dialog-login.component';
+import {DialogSignupComponent} from './dialog-signup.component/dialog-signup.component';
+import {GlobalConstants} from '../../../global-constants';
@Component({
selector: 'app-main.component',
imports: [
-
MatToolbar,
MatButton,
RouterLink,
- IconDirective,
RouterOutlet,
CommonModule,
MatIcon,
@@ -35,15 +33,15 @@ import {MatLabel} from '@angular/material/input';
MatMenu,
MatMenuItem,
MatTooltip,
- MatLabel,
- MatFabButton
-
+ MatLabel
],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ],
templateUrl: './main.component.html',
styleUrl: './main.component.scss'
})
export class MainComponent implements OnInit{
+ title = GlobalConstants.TITLE;
+
// public props
navCollapsed: boolean = false;
navCollapsedMob: boolean = false;
@@ -58,25 +56,22 @@ export class MainComponent implements OnInit{
isLoggedIn = false;
showAdminBoard = false;
showModeratorBoard = false;
+ showUserBoard = false;
username?: string;
eventBusSub?: Subscription;
+ private environment = environment;
private storageService: TokenStorageService = inject(TokenStorageService);
private authService: AuthService = inject(AuthService);
private eventBusService: EventBusService = inject(EventBusService);
- constructor(private router: Router) {
+ constructor(private router: Router,private http: HttpClient) {
}
- goToHome() {
- this.router.navigate(['main/home']);
- }
ngOnInit(): void {
- this.isLoggedIn = this.storageService.isLoggedIn();
-
this.refreshToolbar();
this.eventBusSub = this.eventBusService.on('logout', () => {
@@ -85,12 +80,14 @@ export class MainComponent implements OnInit{
}
refreshToolbar() {
+ this.isLoggedIn = this.storageService.isLoggedIn();
if (this.isLoggedIn) {
const user = this.storageService.getUser();
this.roles = user.roles;
- this.showAdminBoard = true;//this.roles.includes('ROLE_ADMIN');
+ this.showAdminBoard = this.roles.includes('ROLE_ADMIN');
this.showModeratorBoard = this.roles.includes('ROLE_MODERATOR');
+ this.showUserBoard = this.roles.includes('ROLE_USER');
this.username = user.username;
}
@@ -136,7 +133,11 @@ export class MainComponent implements OnInit{
console.log(res);
this.storageService.clean();
- window.location.reload();
+ //window.location.reload();
+ this.router.navigate([GlobalConstants.DEFAULT_PAGE]).then(() => {
+ window.location.reload();
+ });
+ //window.location.reload();
},
error: err => {
console.log(err);
@@ -146,7 +147,7 @@ export class MainComponent implements OnInit{
openLoginDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
- let dialogLoginRef = this.dialog.open(DialogComponent, {
+ let dialogLoginRef = this.dialog.open(DialogLoginComponent, {
width: '350px',
enterAnimationDuration,
exitAnimationDuration,
@@ -167,8 +168,9 @@ export class MainComponent implements OnInit{
this.dialogLoginData = result;
this.authService.login(this.dialogLoginData.username, this.dialogLoginData.password).subscribe(
- data => {
- this.storageService.saveToken(data.accessToken);
+ data => {
+
+
this.storageService.saveUser(data);
this.isLoggedIn = true;
@@ -177,15 +179,16 @@ export class MainComponent implements OnInit{
this.refreshToolbar();
dialogLoginRef.close()
- this.router.navigate(['main/user/user-welcome']);
+
+
+ this.router.navigate([GlobalConstants.DEFAULT_PAGE]);
},
err => {
dialogLoginRef.componentInstance.openLoginFailedSnackBar(err.error.message);
}
);
- // do something here with the data
- dialogSubmitSubscription.unsubscribe();
+
});
}
@@ -224,8 +227,7 @@ export class MainComponent implements OnInit{
);
// do something here with the data
- dialogloginSubscription.unsubscribe();
- dialogSubmitSubscription.unsubscribe();
+
});
}
}
diff --git a/jambotron-ui/src/app/modules/main-module/main.module.ts b/jambotron-ui/src/app/modules/main-module/main.module.ts
new file mode 100644
index 0000000..8393405
--- /dev/null
+++ b/jambotron-ui/src/app/modules/main-module/main.module.ts
@@ -0,0 +1,24 @@
+import {RouterModule} from '@angular/router';
+import {MainComponent} from './main.component/main.component';
+import {mainRouting} from './main.routing';
+import {NgModule} from '@angular/core';
+import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from '@angular/common/http';
+import {CustomHttpInterceptor} from '../../helpers/custom-http-interceptor';
+import {authInterceptorProviders} from '../../helpers/auth.interceptor';
+
+@NgModule({
+ declarations: [
+
+ ],
+ imports: [
+ mainRouting,
+ MainComponent
+ ],
+ exports: [RouterModule],
+ providers: [authInterceptorProviders,provideHttpClient(withInterceptorsFromDi()),{
+ provide: HTTP_INTERCEPTORS,
+ useClass: CustomHttpInterceptor,
+ multi: true
+ }],
+})
+export class MainModule {}
diff --git a/jambotron-ui/src/app/modules/main-module/main.routing.ts b/jambotron-ui/src/app/modules/main-module/main.routing.ts
new file mode 100644
index 0000000..6662926
--- /dev/null
+++ b/jambotron-ui/src/app/modules/main-module/main.routing.ts
@@ -0,0 +1,38 @@
+import {RouterModule, Routes} from '@angular/router';
+import {MainComponent} from './main.component/main.component';
+
+const MAIN_ROUTES: Routes =[
+ {
+ path: '',
+ component: MainComponent,
+ children: [
+ {
+ path: 'tutorials',
+ loadChildren: () =>
+ import('../tutorials-module/tutorials.module').then((m) => m.TutorialsModule),
+ },
+ {
+ path: 'ai',
+ loadChildren: () =>
+ import('../ai-module/ai.module').then((m) => m.AiModule),
+ },
+ {
+ path: 'admin',
+ loadChildren: () =>
+ import('../admin-module/admin.module').then((m) => m.AdminModule),
+ },
+ {
+ path: 'user',
+ loadChildren: () =>
+ import('../user-module/user.module').then((m) => m.UserModule),
+ },
+ {
+ path: 'moderator',
+ loadChildren: () =>
+ import('../moderator-module/moderator.module').then((m) => m.ModeratorModule),
+ }
+ ],
+ }
+];
+
+export const mainRouting = RouterModule.forChild(MAIN_ROUTES);
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator-api.service.spec.ts b/jambotron-ui/src/app/modules/moderator-module/moderator-api.service.spec.ts
new file mode 100644
index 0000000..29d53bc
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator-api.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { ModeratorApiService } from './moderator-api.service';
+
+describe('ModeratorApiService', () => {
+ let service: ModeratorApiService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(ModeratorApiService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator-api.service.ts b/jambotron-ui/src/app/modules/moderator-module/moderator-api.service.ts
new file mode 100644
index 0000000..26a95ed
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator-api.service.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@angular/core';
+import {HttpClient} from '@angular/common/http';
+import {Observable} from 'rxjs';
+import {Tutorial} from '../../models/tutorial.model';
+import {GlobalConstants} from '../../global-constants';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ModeratorApiService {
+ baseUrl = `${GlobalConstants.API_URL}/moderator`;
+
+
+ constructor(private http: HttpClient) {
+
+ }
+
+ getBePublishedTutorials(): Observable {
+ return this.http.get(`${this.baseUrl}/tutorials`);
+ }
+
+ getTutorial(id: string | null | undefined): Observable {
+ return this.http.get(`${this.baseUrl}/tutorial-get/${id}`);
+ }
+
+ publish(id: any,data: any): Observable {
+ return this.http.put(`${this.baseUrl}/tutorial-update/${id}`, data);
+ }
+
+}
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.html b/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.html
new file mode 100644
index 0000000..9dbfb19
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.html
@@ -0,0 +1 @@
+moderator-welcome.component works!
diff --git a/jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.scss b/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.scss
similarity index 100%
rename from jambotron-ui/src/app/components/breadcrumb/breadcrumb.component.scss
rename to jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.scss
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.spec.ts b/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.spec.ts
new file mode 100644
index 0000000..24729f7
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ModeratorWelcomeComponent } from './moderator-welcome.component';
+
+describe('ModeratorWelcomeComponent', () => {
+ let component: ModeratorWelcomeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ModeratorWelcomeComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(ModeratorWelcomeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.ts b/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.ts
new file mode 100644
index 0000000..c89ef04
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator-welcome.component/moderator-welcome.component.ts
@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-moderator-welcome.component',
+ imports: [],
+ templateUrl: './moderator-welcome.component.html',
+ styleUrl: './moderator-welcome.component.scss'
+})
+export class ModeratorWelcomeComponent {
+
+}
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.html b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.html
new file mode 100644
index 0000000..00b8184
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.scss b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.scss
new file mode 100644
index 0000000..016384e
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.scss
@@ -0,0 +1,13 @@
+//---------------
+.pc-sidebar{
+ top: 65px;
+ overflow-y: auto;
+ background-color: rgba(153, 153, 153, 0.16);
+
+ backdrop-filter: blur(8px);
+}
+.pc-container{
+ top: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
diff --git a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.spec.ts b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.spec.ts
similarity index 53%
rename from jambotron-ui/src/app/components/side-bar.component/side-bar.component.spec.ts
rename to jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.spec.ts
index b2bc9bf..21c79c0 100644
--- a/jambotron-ui/src/app/components/side-bar.component/side-bar.component.spec.ts
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { SideBarComponent } from './side-bar.component';
+import { ModeratorComponent } from './moderator.component';
-describe('SideBarComponent', () => {
- let component: SideBarComponent;
- let fixture: ComponentFixture;
+describe('ModeratorComponent', () => {
+ let component: ModeratorComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [SideBarComponent]
+ imports: [ModeratorComponent]
})
.compileComponents();
- fixture = TestBed.createComponent(SideBarComponent);
+ fixture = TestBed.createComponent(ModeratorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.ts b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.ts
new file mode 100644
index 0000000..c509dfd
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator.component/moderator.component.ts
@@ -0,0 +1,19 @@
+import { Component } from '@angular/core';
+import {RouterOutlet} from '@angular/router';
+import {SideBarModeratorComponent} from '../side-bar-moderator.component/side-bar-moderator.component';
+
+
+
+@Component({
+ selector: 'app-moderator.component',
+ imports: [
+ RouterOutlet,
+ SideBarModeratorComponent,
+
+ ],
+ templateUrl: './moderator.component.html',
+ styleUrl: './moderator.component.scss'
+})
+export class ModeratorComponent {
+
+}
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator.module.ts b/jambotron-ui/src/app/modules/moderator-module/moderator.module.ts
new file mode 100644
index 0000000..ed33648
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import {moderatorRouting} from './moderator.routing';
+import {ModeratorComponent} from './moderator.component/moderator.component';
+
+
+
+@NgModule({
+ declarations: [],
+ imports: [
+ moderatorRouting,
+ ModeratorComponent,
+ CommonModule
+ ]
+})
+export class ModeratorModule { }
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator.routing.spec.ts b/jambotron-ui/src/app/modules/moderator-module/moderator.routing.spec.ts
new file mode 100644
index 0000000..ad36ee8
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator.routing.spec.ts
@@ -0,0 +1,7 @@
+import { ModeratorRouting } from './moderator.routing';
+
+describe('ModeratorRouting', () => {
+ it('should create an instance', () => {
+ expect(new ModeratorRouting()).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/moderator-module/moderator.routing.ts b/jambotron-ui/src/app/modules/moderator-module/moderator.routing.ts
new file mode 100644
index 0000000..de5f280
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/moderator.routing.ts
@@ -0,0 +1,27 @@
+import {RouterModule, Routes} from '@angular/router';
+
+import {ModeratorComponent} from './moderator.component/moderator.component';
+
+const MODERATOR_ROUTES: Routes = [
+ {
+ path: '',
+ component: ModeratorComponent,
+
+ children: [
+ {
+ path: 'moderator-welcome',
+ loadComponent: () => import('../moderator-module/moderator-welcome.component/moderator-welcome.component').then((c) => c.ModeratorWelcomeComponent),
+ },
+ {
+ path: 'tutorials-list',
+ loadComponent: () => import('../moderator-module/tutorials-list.component/tutorials-list.component').then((c) => c.TutorialsListComponent)
+ },
+ {
+ path: 'tutorial-preview',
+ loadComponent: () => import('../moderator-module/tutorial-preview.component/tutorial-preview.component').then((c) => c.TutorialPreviewComponent)
+ }
+ ]
+ }
+];
+
+export const moderatorRouting = RouterModule.forChild(MODERATOR_ROUTES);
diff --git a/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.html b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.html
new file mode 100644
index 0000000..d4cb314
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.html
@@ -0,0 +1,18 @@
+
+
+
+ house
+ @if (!isCollapsed) {
+ Dashboard
+ }
+
+
+
+
+ newspaper
+ @if (!isCollapsed) {
+ Tutorials
+ }
+
+
+
diff --git a/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.scss b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.scss
new file mode 100644
index 0000000..62a5b67
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.scss
@@ -0,0 +1,14 @@
+.entry{
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding:0.75rem;
+ color: rgba(24, 255, 255, 0.96);
+
+}
+
+a.mdc-list-item
+{
+
+ background-color: rgba(24,255,255,0.04);
+}
diff --git a/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.spec.ts b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.spec.ts
new file mode 100644
index 0000000..35e9c23
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SideBarModeratorComponent } from './side-bar-moderator.component';
+
+describe('SideBarModeratorComponent', () => {
+ let component: SideBarModeratorComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [SideBarModeratorComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(SideBarModeratorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.ts b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.ts
new file mode 100644
index 0000000..1e9c3cc
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/side-bar-moderator.component/side-bar-moderator.component.ts
@@ -0,0 +1,20 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {MatIcon} from '@angular/material/icon';
+import {MatListItem, MatNavList} from '@angular/material/list';
+import {RouterLink} from '@angular/router';
+
+@Component({
+ selector: 'app-side-bar-moderator',
+ imports: [
+ MatIcon,
+ MatListItem,
+ MatNavList,
+ RouterLink
+ ],
+ templateUrl: './side-bar-moderator.component.html',
+ styleUrl: './side-bar-moderator.component.scss',
+ schemas:[CUSTOM_ELEMENTS_SCHEMA]
+})
+export class SideBarModeratorComponent {
+ isCollapsed = false;
+}
diff --git a/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.html b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.html
new file mode 100644
index 0000000..57f1cb0
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.html
@@ -0,0 +1,43 @@
+
+
+ Edit tutorial
+
+
+
+ Title
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Publicate
+ @if (hasError){
+
+ {{errorMessage}}
+
+ }
+ @if(submitted) {
+ Tutorial was submitted successfully!
+
+ Back to list
+ }
+
+
+
+
+
diff --git a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.scss b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.scss
similarity index 59%
rename from jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.scss
rename to jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.scss
index 2c829bf..183134c 100644
--- a/jambotron-ui/src/app/components/add-tutorial/add-tutorial.component.scss
+++ b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.scss
@@ -4,7 +4,7 @@
}
mat-card{
- margin: 20px;
+ //margin: 20px;
}
mat-card-title{
color: cyan;
@@ -16,6 +16,7 @@ mat-card-title{
width: 100%;
}
+
.example-full-width {
width: 100%;
}
@@ -45,8 +46,14 @@ mat-card-title{
}
.preview {
-/* display: block;
- float: right;*/
+ /* display: block;
+ float: right;*/
}
-
+.mat-mdc-card-outlined {
+ background-color: var(--mat-card-outlined-container-color, var(--mat-sys-surface));
+ border-radius:0;
+ border-width: var(--mat-card-outlined-outline-width, 1px);
+ border-color: var(--mat-card-outlined-outline-color, var(--mat-sys-outline-variant));
+ box-shadow: var(--mat-card-outlined-container-elevation, var(--mat-sys-level0));
+}
diff --git a/jambotron-ui/src/app/components/board-admin/board-admin.component.spec.ts b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.spec.ts
similarity index 50%
rename from jambotron-ui/src/app/components/board-admin/board-admin.component.spec.ts
rename to jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.spec.ts
index ebd5d54..0cfa641 100644
--- a/jambotron-ui/src/app/components/board-admin/board-admin.component.spec.ts
+++ b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.spec.ts
@@ -1,20 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { BoardAdminComponent } from './board-admin.component';
+import { TutorialPreviewComponent } from './tutorial-preview.component';
-describe('BoardAdminComponent', () => {
- let component: BoardAdminComponent;
- let fixture: ComponentFixture;
+describe('TutorialPreviewComponent', () => {
+ let component: TutorialPreviewComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ BoardAdminComponent ]
+ imports: [TutorialPreviewComponent]
})
.compileComponents();
- });
- beforeEach(() => {
- fixture = TestBed.createComponent(BoardAdminComponent);
+ fixture = TestBed.createComponent(TutorialPreviewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.ts b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.ts
new file mode 100644
index 0000000..5c075ae
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/tutorial-preview.component/tutorial-preview.component.ts
@@ -0,0 +1,93 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {FormsModule} from "@angular/forms";
+import {MarkdownComponent} from "ngx-markdown";
+import {MatButton} from "@angular/material/button";
+import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from "@angular/material/card";
+import {MatError, MatFormField, MatInput, MatLabel} from "@angular/material/input";
+import {MatTab, MatTabGroup} from "@angular/material/tabs";
+import {ActivatedRoute, RouterLink} from "@angular/router";
+import {Tutorial} from '../../../models/tutorial.model';
+import {ModeratorApiService} from '../moderator-api.service';
+
+@Component({
+ selector: 'app-tutorial-preview.component',
+ imports: [
+ FormsModule,
+ MarkdownComponent,
+ MatButton,
+ MatCard,
+ MatCardActions,
+ MatCardContent,
+ MatCardHeader,
+ MatError,
+ MatFormField,
+ MatInput,
+ MatLabel,
+ MatTab,
+ MatTabGroup,
+ RouterLink,
+ MatError,
+ MatFormField
+ ],
+ templateUrl: './tutorial-preview.component.html',
+ styleUrl: './tutorial-preview.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+export class TutorialPreviewComponent {
+ tutorial: Tutorial = new Tutorial();
+ submitted = false;
+ hasError = false;
+ errorMessage = '';
+ markdown = `## Markdown __rulez__!
+---
+
+### Syntax highlight
+\`\`\`typescript
+const language = 'typescript';
+\`\`\`
+
+### Lists
+1. Ordered list
+2. Another bullet point
+ - Unordered list
+ - Another unordered bullet
+
+### Blockquote
+> Blockquote to the max`;
+
+ private id: string | null | undefined;
+
+ constructor(private moderatorApiService: ModeratorApiService, private route: ActivatedRoute) {
+
+ this.route.queryParams
+ .subscribe(params => {
+ console.log(params);
+ this.id = params['id'];
+ console.log(this.id);
+ });
+
+ this.moderatorApiService.getTutorial(this.id).subscribe(
+ data=>{
+ this.tutorial = data;
+ }
+ );
+ }
+
+ publishTutorial(): void {
+ this.tutorial.published = true;
+ this.moderatorApiService.publish(this.id, this.tutorial)
+ .subscribe(
+ response => {
+ console.log(response);
+ this.submitted = true;
+ this.hasError = false;
+ },
+ error => {
+ console.log(error);
+
+ this.errorMessage = error.error.message;
+
+ this.hasError = true;
+ });
+ }
+}
diff --git a/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.html b/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.html
new file mode 100644
index 0000000..7d769b4
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.html
@@ -0,0 +1,44 @@
+
+ @for (column of columnsSchema; track column){
+
+ |
+ {{column.label}}
+ |
+
+ @switch (column.type) {
+
+ @case('isEdit') {
+
+
+ Preview
+
+
+
+ }
+ @case ('boolean') {
+
+
+
+ }
+ @case ('datetime') {
+ {{ element[column.key] | date: 'medium' }}
+ }
+ @default {
+ {{ element[column.key] }}
+ }
+ }
+
+ |
+
+
+ }
+
+
+
+
+
diff --git a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.scss b/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.scss
similarity index 100%
rename from jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.scss
rename to jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.scss
diff --git a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.spec.ts b/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.spec.ts
rename to jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.spec.ts
diff --git a/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.ts b/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.ts
new file mode 100644
index 0000000..9c51eb1
--- /dev/null
+++ b/jambotron-ui/src/app/modules/moderator-module/tutorials-list.component/tutorials-list.component.ts
@@ -0,0 +1,142 @@
+import { Component } from '@angular/core';
+import {DatePipe} from "@angular/common";
+import {MatButton} from "@angular/material/button";
+import {
+ MatCell,
+ MatCellDef, MatColumnDef,
+ MatHeaderCell, MatHeaderCellDef,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef, MatTable, MatTableDataSource
+} from "@angular/material/table";
+import {MatSlideToggle} from "@angular/material/slide-toggle";
+import {Router, RouterLink} from "@angular/router";
+import {Tutorial} from '../../../models/tutorial.model';
+import {EventData} from '../../../_shared/event.class';
+import {ModeratorApiService} from '../moderator-api.service';
+import {TokenStorageService} from '../../../services/token-storage.service';
+import {EventBusService} from '../../../_shared/event-bus.service';
+import {AuthService} from '../../../services/auth.service';
+
+@Component({
+ selector: 'app-tutorials-list.component',
+ imports: [
+ DatePipe,
+ MatButton,
+ MatCell,
+ MatCellDef,
+ MatHeaderCell,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef,
+ MatSlideToggle,
+ MatTable,
+ RouterLink,
+ MatColumnDef,
+ MatHeaderCellDef
+ ],
+ templateUrl: './tutorials-list.component.html',
+ styleUrl: './tutorials-list.component.scss'
+})
+export class TutorialsListComponent {
+ displayedColumns: string[] = TutorialColumns.map((col) => col.key)
+ columnsSchema: any = TutorialColumns
+ dataSource = new MatTableDataSource()
+
+
+
+ constructor(private moderatorApiService: ModeratorApiService,
+ private storageService: TokenStorageService,
+ private eventBusService: EventBusService,
+ private authService: AuthService,
+ private router: Router
+ ) {
+ this.getTutorials();
+ }
+ getTutorials(): void {
+ this.moderatorApiService.getBePublishedTutorials()
+ .subscribe(( data:Tutorial[] ) => {
+ this.dataSource.data = data;
+ console.log(data);
+ },
+ error => {
+ console.log(error);
+ if (
+ (
+ error.status === 401
+
+ )
+ && this.storageService.isLoggedIn()
+ ) {
+ this.eventBusService.emit(new EventData('logout', null));
+ }
+ });
+ }
+
+
+ publish(element: any, checked: boolean){
+ element.published = checked;
+ this.moderatorApiService.publish(element.id, element).subscribe(
+ response => {
+ console.log(response);
+ },
+ error => {
+ console.log(error);
+ });
+
+ }
+
+
+ logout(): void {
+ this.authService.logout().subscribe({
+ next: res => {
+ console.log(res);
+ this.storageService.clean();
+
+ //window.location.reload();
+ this.router.navigate(['main/generate-image']).then(() => {
+ //window.location.reload();
+ })
+
+ },
+ error: err => {
+ console.log(err);
+ }
+ });
+
+ }
+}
+
+export const TutorialColumns = [
+
+ {
+ key: 'title',
+ type: 'text',
+ label: 'Title',
+ required: true,
+ },
+ {
+ key: 'published',
+ type: 'boolean',
+ label: 'Is Published',
+ },
+ {
+ key: 'created',
+ type: 'datetime',
+ label: 'Created Date',
+ required: true,
+ },
+ {
+ key: 'modified',
+ type: 'datetime',
+ label: 'Modified Date',
+ required: true
+ },
+ {
+ key: 'isEdit',
+ type: 'isEdit',
+ label: '',
+ }
+];
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.html b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.html
new file mode 100644
index 0000000..f6a9b13
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.html
@@ -0,0 +1,5 @@
+
+

+
+
+
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.scss b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.scss
new file mode 100644
index 0000000..b1babdc
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.scss
@@ -0,0 +1,17 @@
+.tutorial-view {
+ margin-right: 12px;
+ padding: 20px 12px 12px 31px;
+ margin-top: 81px;
+ margin-left: 12px;
+ background-color: #e7edf3c2;
+ backdrop-filter: blur(8px);
+
+ display: flex;
+ flex-wrap: nowrap;
+ flex-direction: column;
+ align-items: center;
+}
+
+.tutorial-body{
+ width: 100%;
+}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.spec.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.spec.ts
new file mode 100644
index 0000000..0cadd22
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TutorialViewComponent } from './tutorial-view.component';
+
+describe('TutorialViewComponent', () => {
+ let component: TutorialViewComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TutorialViewComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(TutorialViewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.ts
new file mode 100644
index 0000000..bea5513
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorial-view.component/tutorial-view.component.ts
@@ -0,0 +1,35 @@
+import { Component } from '@angular/core';
+import {ModeratorApiService} from '../../moderator-module/moderator-api.service';
+import {ActivatedRoute} from '@angular/router';
+import {TutorialsApiService} from '../tutorials-api.service';
+import {Tutorial} from '../../../models/tutorial.model';
+import {MarkdownComponent} from 'ngx-markdown';
+
+@Component({
+ selector: 'app-tutorial-view.component',
+ imports: [
+ MarkdownComponent
+ ],
+ templateUrl: './tutorial-view.component.html',
+ styleUrl: './tutorial-view.component.scss'
+})
+export class TutorialViewComponent {
+ private id: string | null | undefined;
+ tutorial: Tutorial = new Tutorial();
+
+ constructor(private tutorialsApiService: TutorialsApiService, private route: ActivatedRoute) {
+
+ this.route.queryParams
+ .subscribe(params => {
+ console.log(params);
+ this.id = params['id'];
+ console.log(this.id);
+ });
+
+ this.tutorialsApiService.getTutorial(this.id).subscribe(
+ data => {
+ this.tutorial = data;
+ }
+ );
+ }
+}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-api.service.spec.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials-api.service.spec.ts
new file mode 100644
index 0000000..e752372
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-api.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { TutorialsApiService } from './tutorials-api.service';
+
+describe('TutorialsApiService', () => {
+ let service: TutorialsApiService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(TutorialsApiService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-api.service.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials-api.service.ts
new file mode 100644
index 0000000..1294704
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-api.service.ts
@@ -0,0 +1,22 @@
+import { Injectable } from '@angular/core';
+import {GlobalConstants} from '../../global-constants';
+import {HttpClient} from '@angular/common/http';
+import {Observable} from 'rxjs';
+import {Tutorial} from '../../models/tutorial.model';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TutorialsApiService {
+
+ baseUrl = `${GlobalConstants.API_URL}/public`;
+
+ constructor(private http: HttpClient) { }
+
+ getAllPublic(): Observable {
+ return this.http.get(`${this.baseUrl}/tutorials`);
+ }
+
+ getTutorial(id: string | null | undefined): Observable {
+ return this.http.get(`${this.baseUrl}/tutorial-get/${id}`);
+ }}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.html b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.html
new file mode 100644
index 0000000..555bb13
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.html
@@ -0,0 +1,14 @@
+
+ @for(tutorial of tutorials; track $index){
+
+
+
+
+ {{tutorial.title}}
+
+
+
+
+ }
+
+
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.scss b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.scss
new file mode 100644
index 0000000..7929a90
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.scss
@@ -0,0 +1,38 @@
+//mat-card{
+// width: 20%;
+//}
+
+.container-card-view{
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 24px;
+ width: 100%;
+ height: 200px;
+ object-fit: cover;
+ padding: 24px;
+ //width: 100%;
+ //height: 100%;
+ //padding-top: 20px;
+ //display: flex;
+ //flex-wrap: wrap;
+ //flex-direction: row;
+ //align-content: flex-start;
+ //justify-content: space-around;
+ //align-items: center;
+}
+
+.container {
+ padding: 24px;
+}
+
+img {
+ width: 100%;
+ height: 200px;
+ object-fit: cover;
+}
+
+.responsive-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 24px;
+}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.spec.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.spec.ts
new file mode 100644
index 0000000..7d850b2
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TutorialsCardViewComponent } from './tutorials-card-view.component';
+
+describe('TutorialsCardViewComponent', () => {
+ let component: TutorialsCardViewComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TutorialsCardViewComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(TutorialsCardViewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.ts
new file mode 100644
index 0000000..568f8f0
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-card-view.component/tutorials-card-view.component.ts
@@ -0,0 +1,38 @@
+import { Component } from '@angular/core';
+import {Tutorial} from '../../../models/tutorial.model';
+import {MatCard, MatCardActions, MatCardImage} from '@angular/material/card';
+import {RouterLink} from '@angular/router';
+import {MatButton} from '@angular/material/button';
+import {TutorialsApiService} from '../tutorials-api.service';
+
+@Component({
+ selector: 'app-tutorials-card-view.component',
+ imports: [
+ MatCard,
+ MatCardActions,
+ RouterLink,
+ MatButton,
+ MatCardImage
+ ],
+ templateUrl: './tutorials-card-view.component.html',
+ styleUrl: './tutorials-card-view.component.scss'
+})
+export class TutorialsCardViewComponent {
+ tutorials: Tutorial[] = [];
+
+ constructor(private tutorialsApiService: TutorialsApiService) {
+ this.retrieveTutorials();
+ }
+
+ retrieveTutorials(): void {
+ this.tutorialsApiService.getAllPublic().subscribe(
+ (data : Tutorial[]) =>{
+ this.tutorials = data;
+ console.log(data);
+ },
+ error => {
+ console.log(error);
+ }
+ );
+ }
+}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.html b/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.html
new file mode 100644
index 0000000..1e9583c
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.html
@@ -0,0 +1 @@
+tutorials-welcome.component works!
diff --git a/jambotron-ui/src/app/components/card/card.component.scss b/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.scss
similarity index 100%
rename from jambotron-ui/src/app/components/card/card.component.scss
rename to jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.scss
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.spec.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.spec.ts
new file mode 100644
index 0000000..47c5c99
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TutorialsWelcomeComponent } from './tutorials-welcome.component';
+
+describe('TutorialsWelcomeComponent', () => {
+ let component: TutorialsWelcomeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TutorialsWelcomeComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(TutorialsWelcomeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.ts
new file mode 100644
index 0000000..fffacac
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials-welcome.component/tutorials-welcome.component.ts
@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-tutorials-welcome.component',
+ imports: [],
+ templateUrl: './tutorials-welcome.component.html',
+ styleUrl: './tutorials-welcome.component.scss'
+})
+export class TutorialsWelcomeComponent {
+
+}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.html b/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.html
new file mode 100644
index 0000000..0680b43
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.html
@@ -0,0 +1 @@
+
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.scss b/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.scss
new file mode 100644
index 0000000..e88c9f5
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.scss
@@ -0,0 +1,9 @@
+
+.router_outlet{
+ width: 100%;
+ height: 100%;
+
+}
+
+
+
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.ts
new file mode 100644
index 0000000..3942b31
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials.component/tutorials.component.ts
@@ -0,0 +1,14 @@
+import { Component } from '@angular/core';
+import {RouterOutlet} from '@angular/router';
+
+@Component({
+ selector: 'app-tutorials.component',
+ imports: [
+ RouterOutlet
+ ],
+ templateUrl: './tutorials.component.html',
+ styleUrl: './tutorials.component.scss'
+})
+export class TutorialsComponent {
+
+}
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials.module.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials.module.ts
new file mode 100644
index 0000000..5d1f0a6
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import {tutorialsRouting} from './tutorials.routing';
+import {TutorialsComponent} from './tutorials.component/tutorials.component';
+import {MatFormFieldModule} from '@angular/material/form-field';
+
+@NgModule({
+ declarations: [],
+ imports: [
+ tutorialsRouting,
+ TutorialsComponent,
+ CommonModule,
+ MatFormFieldModule
+ ]
+})
+export class TutorialsModule { }
diff --git a/jambotron-ui/src/app/modules/tutorials-module/tutorials.routing.ts b/jambotron-ui/src/app/modules/tutorials-module/tutorials.routing.ts
new file mode 100644
index 0000000..adbdc38
--- /dev/null
+++ b/jambotron-ui/src/app/modules/tutorials-module/tutorials.routing.ts
@@ -0,0 +1,26 @@
+import {RouterModule, Routes} from '@angular/router';
+import {TutorialsComponent} from './tutorials.component/tutorials.component';
+
+const TUTORIALS_ROUTES: Routes = [
+ {
+ path: '',
+ component: TutorialsComponent,
+
+ children: [
+ {
+ path: 'tutorials-welcome',
+ loadComponent: () => import('../tutorials-module/tutorials-welcome.component/tutorials-welcome.component').then((c) => c.TutorialsWelcomeComponent),
+ },
+ {
+ path: 'tutorials-all',
+ loadComponent: () => import('../tutorials-module/tutorials-card-view.component/tutorials-card-view.component').then((c) => c.TutorialsCardViewComponent)
+ },
+ {
+ path: 'tutorial-view',
+ loadComponent: () => import('./tutorial-view.component/tutorial-view.component').then((c) => c.TutorialViewComponent)
+ }
+ ]
+ }
+];
+
+export const tutorialsRouting = RouterModule.forChild(TUTORIALS_ROUTES);
diff --git a/jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.html b/jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.html
similarity index 100%
rename from jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.html
rename to jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.html
diff --git a/jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.scss b/jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.scss
similarity index 100%
rename from jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.scss
rename to jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.scss
diff --git a/jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.spec.ts b/jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.spec.ts
rename to jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.spec.ts
diff --git a/jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.ts b/jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.ts
similarity index 100%
rename from jambotron-ui/src/app/user-module/ai-models.component/ai-models.component.ts
rename to jambotron-ui/src/app/modules/user-module/ai-models.component/ai-models.component.ts
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.html b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.html
new file mode 100644
index 0000000..4210267
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.html
@@ -0,0 +1,26 @@
+Hi
+
+
+
+ @for (fileInfo of fileInfos; track fileInfo){
+
+
+
+
+
+ {{fileInfo.name}}
+
+
+
+ }
+
+
+
+
+
+ Upload
+
+ No Thanks
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.scss b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.scss
new file mode 100644
index 0000000..6a02f8a
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.scss
@@ -0,0 +1,43 @@
+.menu-spacer {
+ flex: 1 1 auto;
+}
+
+.mat-mdc-list-item{
+
+}
+a.mdc-list-item
+{
+ cursor: grab;
+ height: 120px;
+ padding-bottom: 66px;
+ background-color: rgba(24,255,255,0.04);
+}
+/*.mat-dialog-content{
+ min-height: 300px;
+ min-width: 300px;
+
+ height: 75%;
+ width: 75%;
+}*/
+/*.mdc-dialog--open .mat-mdc-dialog-inner-container
+{
+ opacity: 1;
+ width: 600px;
+}*/
+
+/*.mat-mdc-dialog-container {
+ width: 600px;
+ height: 500px;
+ display: block;
+ box-sizing: border-box;
+ max-height: inherit;
+ min-height: inherit;
+ min-width: inherit;
+ max-width: inherit;
+ outline: 0;
+}*/
+/*
+.mat-mdc-dialog-content{
+ overflow: auto;
+}
+*/
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.spec.ts b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.spec.ts
new file mode 100644
index 0000000..6b51f49
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DialogSelectImageComponent } from './dialog-select-image.component';
+
+describe('DialogSelectImageComponent', () => {
+ let component: DialogSelectImageComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DialogSelectImageComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(DialogSelectImageComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.ts b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.ts
new file mode 100644
index 0000000..3dce61e
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-select-image.component/dialog-select-image.component.ts
@@ -0,0 +1,55 @@
+import {Component, EventEmitter, inject, Output} from '@angular/core';
+import {
+ MatDialogActions,
+ MatDialogClose,
+ MatDialogContent,
+ MatDialogRef,
+ MatDialogTitle
+} from '@angular/material/dialog';
+import {MatButton} from '@angular/material/button';
+import {MatListItem} from '@angular/material/list';
+import {FileInfo} from '../../../models/file-info';
+import {UserApiService} from '../user-api.service';
+
+@Component({
+ selector: 'app-dialog-select-image.component',
+ imports: [
+ MatDialogContent,
+ MatDialogTitle,
+ MatButton,
+ MatDialogActions,
+ MatDialogClose,
+ MatListItem
+ ],
+ templateUrl: './dialog-select-image.component.html',
+ styleUrl: './dialog-select-image.component.scss'
+})
+export class DialogSelectImageComponent {
+ fileInfos?: FileInfo[] = [];
+
+ @Output() uploadClicked = new EventEmitter();
+ @Output() selectClicked = new EventEmitter();
+
+ readonly dialogRef = inject(MatDialogRef);
+
+ constructor(private userApiService: UserApiService) {
+ this.userApiService.getImages().subscribe(data => {
+ this.fileInfos = data;
+ console.log(data);
+ });
+ }
+
+ //open upload dialog
+ upload() {
+ this.uploadClicked.emit();
+ }
+
+ onNoClick() {
+ this.dialogRef.close();
+ }
+
+ select(fileInfo:FileInfo) {
+ this.selectClicked.emit(fileInfo);
+ this.dialogRef.close();
+ }
+}
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.html b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.html
new file mode 100644
index 0000000..07b03c7
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.html
@@ -0,0 +1,14 @@
+dialog-upload-image.component works!
+
+
+
+
+
+
+ Select image
+
+ No Thanks
+
+
+ Ok
+
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.scss b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.scss
new file mode 100644
index 0000000..827c30d
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.scss
@@ -0,0 +1,6 @@
+.menu-spacer {
+ flex: 1 1 auto;
+}
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.spec.ts b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.spec.ts
new file mode 100644
index 0000000..7e0f85f
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DialogUploadImageComponent } from './dialog-upload-image.component';
+
+describe('DialogUploadImageComponent', () => {
+ let component: DialogUploadImageComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DialogUploadImageComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(DialogUploadImageComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
new file mode 100644
index 0000000..ae2879f
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/dialog-upload-image.component/dialog-upload-image.component.ts
@@ -0,0 +1,54 @@
+import {Component, EventEmitter, inject, Output, ViewChild} from '@angular/core';
+import {FileUploadComponent} from '../../../components/file-upload.component/file-upload.component';
+import {MatDialogActions, MatDialogClose, MatDialogContent, MatDialogRef} from '@angular/material/dialog';
+import {MatButton} from '@angular/material/button';
+import {FileInfo} from '../../../models/file-info';
+
+@Component({
+ selector: 'app-dialog-upload-image.component',
+ imports: [
+ FileUploadComponent,
+ MatDialogContent,
+ MatButton,
+ MatDialogActions,
+ MatDialogClose
+ ],
+ templateUrl: './dialog-upload-image.component.html',
+ styleUrl: './dialog-upload-image.component.scss'
+})
+export class DialogUploadImageComponent {
+ //select uploaded file
+ @Output() selectUploadedImageClicked = new EventEmitter();
+
+ //open select dialog
+ @Output() openSelectDialogClicked = new EventEmitter();
+
+ readonly dialogRef = inject(MatDialogRef);
+
+ @ViewChild('imageUpload') imageUpload: FileUploadComponent | undefined;
+
+ imageUploaded:boolean = false;
+
+ onNoClick() {
+ this.dialogRef.close();
+ }
+
+ selectUploadedImage() {
+ this.selectUploadedImageClicked.emit(this.imageUpload?.fileInfo);
+ this.dialogRef.close();
+ }
+
+ openSelectImageDialog() {
+ this.openSelectDialogClicked.emit();
+ this.dialogRef.close();
+ }
+
+ imageUpload_ImageUploaded($event: FileInfo | undefined) {
+ if($event){
+ this.imageUploaded = true;
+ }
+ else{
+ this.imageUploaded = false;
+ }
+ }
+}
diff --git a/jambotron-ui/src/app/modules/user-module/images.component/images.component.html b/jambotron-ui/src/app/modules/user-module/images.component/images.component.html
new file mode 100644
index 0000000..1f1aeef
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/images.component/images.component.html
@@ -0,0 +1,28 @@
+images.component works!
+
+
+
+
+ @for (fileInfo of fileInfos; track fileInfo) {
+
+
+
+

+
+
+
+
+
+ download
+
+
+
+
+
+
+
+
+
+ }
+
diff --git a/jambotron-ui/src/app/modules/user-module/images.component/images.component.scss b/jambotron-ui/src/app/modules/user-module/images.component/images.component.scss
new file mode 100644
index 0000000..0928c5a
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/images.component/images.component.scss
@@ -0,0 +1,31 @@
+
+.image-container{
+ width: 100%;
+ height: 100%;
+
+}
+.image-container .image-toolbar
+{
+ background-color: rgba(153,153,153,0);
+
+ color: white;
+ position: absolute;
+ left : 50%;
+ top: 5%;
+ transform: translate(-50%, -50%);
+ -ms-transform: translate(-50%, -50%);
+ padding: 0px 2px;
+ border: none;
+ border-radius: 5px;
+}
+.image-container .menu-spacer {
+ flex: 1 1 auto;
+}
+
+.image-container .image-file-name {
+ padding-left: 10px;
+}
+
+.mat-mdc-mini-fab{
+ margin: 5px;
+}
diff --git a/jambotron-ui/src/app/components/dialog/dialog.component.spec.ts b/jambotron-ui/src/app/modules/user-module/images.component/images.component.spec.ts
similarity index 55%
rename from jambotron-ui/src/app/components/dialog/dialog.component.spec.ts
rename to jambotron-ui/src/app/modules/user-module/images.component/images.component.spec.ts
index 8fd87c4..144618e 100644
--- a/jambotron-ui/src/app/components/dialog/dialog.component.spec.ts
+++ b/jambotron-ui/src/app/modules/user-module/images.component/images.component.spec.ts
@@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { DialogComponent } from './dialog.component';
+import { ImagesComponent } from './images.component';
-describe('DialogComponent', () => {
- let component: DialogComponent;
- let fixture: ComponentFixture;
+describe('ImagesComponent', () => {
+ let component: ImagesComponent;
+ let fixture: ComponentFixture;
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [DialogComponent]
+ imports: [ImagesComponent]
})
.compileComponents();
- fixture = TestBed.createComponent(DialogComponent);
+ fixture = TestBed.createComponent(ImagesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/jambotron-ui/src/app/modules/user-module/images.component/images.component.ts b/jambotron-ui/src/app/modules/user-module/images.component/images.component.ts
new file mode 100644
index 0000000..5d63db3
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/images.component/images.component.ts
@@ -0,0 +1,41 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core';
+import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card';
+import {MatMiniFabButton} from '@angular/material/button';
+import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
+import {MatTooltip} from '@angular/material/tooltip';
+import {MatIcon} from '@angular/material/icon';
+import {FileInfo} from '../../../models/file-info';
+import {UserApiService} from '../user-api.service';
+import {MatDialog} from '@angular/material/dialog';
+
+@Component({
+ selector: 'app-images.component',
+ imports: [
+ MatCard,
+ MatCardContent,
+ MatCardHeader,
+ MatIcon,
+ MatMiniFabButton,
+ MatToolbar,
+ MatToolbarRow,
+ MatTooltip
+ ],
+ templateUrl: './images.component.html',
+ styleUrl: './images.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+export class ImagesComponent {
+ fileInfos?: FileInfo[] = [];
+
+
+ constructor(private userApiService: UserApiService) {
+ this.userApiService.getImages().subscribe(data =>{
+ this.fileInfos = data;
+ console.log(data);
+ });
+ }
+
+ downloadImage(url: string | undefined) {
+
+ }
+}
diff --git a/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.html b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.html
new file mode 100644
index 0000000..d1ecbaf
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.html
@@ -0,0 +1,27 @@
+
+
+
+ house
+ @if (!isCollapsed) {
+ Dashboard
+ }
+
+
+
+
+ newspaper
+ @if (!isCollapsed) {
+ Tutorials
+ }
+
+
+
+
+ imagesmode
+ @if (!isCollapsed) {
+ Images
+ }
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.scss b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.scss
new file mode 100644
index 0000000..62a5b67
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.scss
@@ -0,0 +1,14 @@
+.entry{
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding:0.75rem;
+ color: rgba(24, 255, 255, 0.96);
+
+}
+
+a.mdc-list-item
+{
+
+ background-color: rgba(24,255,255,0.04);
+}
diff --git a/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.spec.ts b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.spec.ts
new file mode 100644
index 0000000..86d79b6
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SideBarUserComponent } from './side-bar-user.component';
+
+describe('SideBarUserComponent', () => {
+ let component: SideBarUserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [SideBarUserComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(SideBarUserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.ts b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.ts
new file mode 100644
index 0000000..7d71af6
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/side-bar-user.component/side-bar-user.component.ts
@@ -0,0 +1,20 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {MatListItem, MatNavList} from '@angular/material/list';
+import {RouterLink} from '@angular/router';
+import {MatIcon} from '@angular/material/icon';
+
+@Component({
+ selector: 'app-side-bar-user',
+ imports: [
+ MatIcon,
+ MatListItem,
+ MatNavList,
+ RouterLink
+ ],
+ templateUrl: './side-bar-user.component.html',
+ styleUrl: './side-bar-user.component.scss',
+ schemas:[CUSTOM_ELEMENTS_SCHEMA]
+})
+export class SideBarUserComponent {
+ isCollapsed = false;
+}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
new file mode 100644
index 0000000..571308f
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.html
@@ -0,0 +1,110 @@
+
+
+ Add tutorial
+
+
+
+ Title
+
+
+
+ Title image
+
+
+
+
+
Description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Body
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @if (submitted){
+
+
Tutorial was submitted successfully!
+ Add new tutorial
+
+ } @else {
+ Save
+ }
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.scss b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.scss
new file mode 100644
index 0000000..1ee9028
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.scss
@@ -0,0 +1,69 @@
+.submit-form {
+ max-width: 400px;
+ margin: auto;
+}
+
+.markdown-editor{
+ max-width: 300px;
+}
+.markdown-editor-container{
+ display: flex;
+}
+
+mat-card-title{
+ color: cyan;
+}
+
+.example-form {
+ min-width: 150px;
+ max-width: 500px;
+ width: 100%;
+}
+
+
+.example-full-width {
+ width: 100%;
+}
+
+
+.variable-binding,
+.variable-textarea {
+ width: 49%;
+}
+
+.variable-textarea {
+ border-radius: 5px;
+ box-shadow: 0 1px 2px rgba(0,0,0,.07);
+ min-height: 420px;
+ padding: 8px;
+ transition: all 300ms ease-out;
+}
+
+.variable-textarea:hover {
+ box-shadow: 0 6px 12px 3px rgba(0,0,0,.09),
+ 0 2px 3px 1px rgba(0,0,0,.06);
+}
+
+.variable-binding {
+ display: block;
+ float: right;
+}
+
+.preview {
+ /* display: block;
+ float: right;*/
+}
+
+.label-style{
+ font-size: -webkit-xxx-large;
+ font-weight: bold;
+}
+
+
+.mat-mdc-card-outlined {
+ background-color: var(--mat-card-outlined-container-color, var(--mat-sys-surface));
+ border-radius:0;
+ border-width: var(--mat-card-outlined-outline-width, 1px);
+ border-color: var(--mat-card-outlined-outline-color, var(--mat-sys-outline-variant));
+ box-shadow: var(--mat-card-outlined-container-elevation, var(--mat-sys-level0));
+}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.spec.ts b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.spec.ts
new file mode 100644
index 0000000..e7fb45d
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TutorialAddComponent } from './tutorial-add.component';
+
+describe('TutorialAddComponent', () => {
+ let component: TutorialAddComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TutorialAddComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(TutorialAddComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
new file mode 100644
index 0000000..adce58d
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-add.component/tutorial-add.component.ts
@@ -0,0 +1,226 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
+import {UserApiService} from '../user-api.service';
+import {TokenStorageService} from '../../../services/token-storage.service';
+import {EventBusService} from '../../../_shared/event-bus.service';
+import {Router} from '@angular/router';
+import {AuthService} from '../../../services/auth.service';
+import {Tutorial} from '../../../models/tutorial.model';
+import {MarkdownComponent, MarkdownService} from 'ngx-markdown';
+import {MatButton} from '@angular/material/button';
+import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
+import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
+import {MatTab, MatTabGroup} from '@angular/material/tabs';
+import {FormBuilder, FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {AngularMarkdownEditorModule, EditorInstance, EditorOption} from 'angular-markdown-editor';
+import {MatDialog} from '@angular/material/dialog';
+import {DialogSelectImageComponent} from '../dialog-select-image.component/dialog-select-image.component';
+import {DialogUploadImageComponent} from '../dialog-upload-image.component/dialog-upload-image.component';
+import {FileUploadComponent} from '../../../components/file-upload.component/file-upload.component';
+import {MatDivider} from '@angular/material/divider';
+
+@Component({
+ selector: 'app-tutorial-add.component',
+ imports: [
+ MarkdownComponent,
+ MatButton,
+ MatCard,
+ MatCardActions,
+ MatCardContent,
+ MatCardHeader,
+ MatFormField,
+ MatInput,
+ MatLabel,
+ MatTab,
+ MatTabGroup,
+ ReactiveFormsModule,
+ FormsModule,
+ AngularMarkdownEditorModule,
+ MatDivider
+ ],
+ templateUrl: './tutorial-add.component.html',
+ styleUrl: './tutorial-add.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+export class TutorialAddComponent implements OnInit{
+
+ tutorial: Tutorial = new Tutorial();
+ submitted = false;
+ markdownText = '';
+ showEditor = true;
+ bsEditorInstance!: EditorInstance;
+ tutorialForm!: FormGroup;
+ editorOptions!: EditorOption;
+
+ readonly dialog = inject(MatDialog);
+
+
+
+ markdown = `## Markdown __rulez__!
+---
+
+### Syntax highlight
+\`\`\`typescript
+const language = 'typescript';
+\`\`\`
+
+### Lists
+1. Ordered list
+2. Another bullet point
+ - Unordered list
+ - Another unordered bullet
+
+### Blockquote
+> Blockquote to the max`;
+
+
+ constructor(private fb: FormBuilder,
+ private markdownService: MarkdownService,
+ private userApiService: UserApiService,
+ ) {
+
+ this.tutorial.description = this.markdown;
+ this.tutorial.body = this.markdown;
+ }
+
+ ngOnInit(): void {
+ this.editorOptions = {
+ autofocus: false,
+ iconlibrary: 'fa',
+ height: 300,
+ savable: false,
+ onFullscreenExit: (e) => this.hidePreview(),
+ onShow: (e) => this.bsEditorInstance = e,
+ parser: (val) => this.parse(val)
+ };
+ this.buildForm(this.tutorial.description);
+
+ }
+
+ buildForm(markdownText: string | undefined) {
+ this.tutorialForm = this.fb.group({
+ body: [markdownText],
+ isPreview: [true]
+ });
+ }
+
+ /** highlight all code found, needs to be wrapped in timer to work properly */
+ highlight() {
+ setTimeout(() => {
+ this.markdownService.highlight();
+ });
+ }
+
+ hidePreview() {
+ if (this.bsEditorInstance && this.bsEditorInstance.hidePreview) {
+ this.bsEditorInstance.hidePreview();
+ }
+ }
+
+ showFullScreen(isFullScreen: boolean) {
+ if (this.bsEditorInstance && this.bsEditorInstance.setFullscreen) {
+ this.bsEditorInstance.showPreview();
+ this.bsEditorInstance.setFullscreen(isFullScreen);
+ }
+ }
+
+ parse(inputValue: string) {
+ const markedOutput = this.markdownService.parse(inputValue.trim());
+ this.highlight();
+
+ return markedOutput;
+ }
+
+ onFormChanges(): void {
+ this.tutorialForm.valueChanges.subscribe(formData => {
+ if (formData) {
+ this.markdownText = formData.body;
+ }
+ });
+ }
+
+ saveTutorial(): void {
+ const data = {
+ title: this.tutorial.title,
+ description: this.tutorial.description,
+ body: this.tutorial.body,
+ published: this.tutorial.published,
+ titleimage: this.tutorial.titleimage,
+ created: new Date(),
+ modified: new Date(),
+ tobepublished: false,
+ isEdit: false,
+ isAdmin: false,
+ isSuperAdmin: false,
+ isUser: false,
+ };
+
+ this.userApiService.create(data)
+ .subscribe(
+ response => {
+ console.log(response);
+ this.submitted = true;
+ },
+ error => {
+ console.log(error);
+ });
+ }
+ newTutorial(): void {
+ this.submitted = false;
+ this.tutorial = {
+ title: '',
+ description: '',
+ published: false
+ };
+ }
+
+ openSelectImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
+
+ let dialogSelectRef = this.dialog.open(DialogSelectImageComponent, {
+ height: '500px',
+ width: '600px',
+ enterAnimationDuration,
+ exitAnimationDuration,
+ // data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
+ });
+ dialogSelectRef.componentInstance.uploadClicked.subscribe(result => {
+ dialogSelectRef.close();
+ this.openUploadImageDialog(enterAnimationDuration, exitAnimationDuration);
+ })
+
+ const dialogSelectSubscription = dialogSelectRef.componentInstance.selectClicked
+ .subscribe(result => {
+ console.log('Got the data!', result);
+
+ if (result == null) {
+ return;
+ }
+ this.tutorial.titleimage = result.url;
+
+ });
+ }
+
+ openUploadImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
+ let dialogSelectRef = this.dialog.open(DialogUploadImageComponent, {
+ height: '500px',
+ width: '600px',
+ enterAnimationDuration,
+ exitAnimationDuration,
+ // data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
+ });
+ dialogSelectRef.componentInstance.openSelectDialogClicked.subscribe(result => {
+ dialogSelectRef.close();
+ this.openSelectImageDialog(enterAnimationDuration, exitAnimationDuration);
+ })
+
+ const dialogUploadSubscription = dialogSelectRef.componentInstance.selectUploadedImageClicked
+ .subscribe(result => {
+ console.log('Got the data!', result);
+
+ if (result == null) {
+ return;
+ }
+ this.tutorial.titleimage = result.url;
+
+ });
+ }
+}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.html b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.html
new file mode 100644
index 0000000..fbd0be4
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.html
@@ -0,0 +1,115 @@
+
+
+ Edit tutorial
+
+
+
+ Title
+
+
+
+ Title image
+
+
+
+
+
Description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Body
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Save
+ @if (hasError){
+
+ {{errorMessage}}
+
+ }
+ @if(submitted) {
+ Tutorial was submitted successfully!
+ Add new tutorial
+ Back to list
+ }
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.scss b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.scss
new file mode 100644
index 0000000..183134c
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.scss
@@ -0,0 +1,59 @@
+.submit-form {
+ max-width: 400px;
+ margin: auto;
+}
+
+mat-card{
+ //margin: 20px;
+}
+mat-card-title{
+ color: cyan;
+}
+
+.example-form {
+ min-width: 150px;
+ max-width: 500px;
+ width: 100%;
+}
+
+
+.example-full-width {
+ width: 100%;
+}
+
+
+.variable-binding,
+.variable-textarea {
+ width: 49%;
+}
+
+.variable-textarea {
+ border-radius: 5px;
+ box-shadow: 0 1px 2px rgba(0,0,0,.07);
+ min-height: 420px;
+ padding: 8px;
+ transition: all 300ms ease-out;
+}
+
+.variable-textarea:hover {
+ box-shadow: 0 6px 12px 3px rgba(0,0,0,.09),
+ 0 2px 3px 1px rgba(0,0,0,.06);
+}
+
+.variable-binding {
+ display: block;
+ float: right;
+}
+
+.preview {
+ /* display: block;
+ float: right;*/
+}
+
+.mat-mdc-card-outlined {
+ background-color: var(--mat-card-outlined-container-color, var(--mat-sys-surface));
+ border-radius:0;
+ border-width: var(--mat-card-outlined-outline-width, 1px);
+ border-color: var(--mat-card-outlined-outline-color, var(--mat-sys-outline-variant));
+ box-shadow: var(--mat-card-outlined-container-elevation, var(--mat-sys-level0));
+}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.spec.ts b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.spec.ts
new file mode 100644
index 0000000..e480036
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TutorialEditComponent } from './tutorial-edit.component';
+
+describe('TutorialEditComponent', () => {
+ let component: TutorialEditComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [TutorialEditComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(TutorialEditComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.ts b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.ts
new file mode 100644
index 0000000..0872c20
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorial-edit.component/tutorial-edit.component.ts
@@ -0,0 +1,217 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA, inject, OnInit} from '@angular/core';
+import {MarkdownComponent, MarkdownService} from 'ngx-markdown';
+import {MatButton} from '@angular/material/button';
+import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from '@angular/material/card';
+import {MatError, MatFormField, MatInput, MatLabel} from '@angular/material/input';
+import {MatTab, MatTabGroup} from '@angular/material/tabs';
+import {FormBuilder, FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {Tutorial} from '../../../models/tutorial.model';
+import {UserApiService} from '../user-api.service';
+import {ActivatedRoute, RouterLink} from '@angular/router';
+import {AngularMarkdownEditorModule, EditorInstance, EditorOption} from 'angular-markdown-editor';
+import {MatDivider} from '@angular/material/divider';
+import {MatDialog} from '@angular/material/dialog';
+import {DialogSelectImageComponent} from '../dialog-select-image.component/dialog-select-image.component';
+import {DialogUploadImageComponent} from '../dialog-upload-image.component/dialog-upload-image.component';
+
+@Component({
+ selector: 'app-tutorial-edit.component',
+ imports: [
+ MarkdownComponent,
+ MatButton,
+ MatCard,
+ MatCardActions,
+ MatCardContent,
+ MatCardHeader,
+ MatFormField,
+ MatInput,
+ MatLabel,
+ MatTab,
+ MatTabGroup,
+ ReactiveFormsModule,
+ FormsModule,
+ MatFormField,
+ RouterLink,
+ MatError,
+ AngularMarkdownEditorModule,
+ MatDivider
+ ],
+ templateUrl: './tutorial-edit.component.html',
+ styleUrl: './tutorial-edit.component.scss',
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+export class TutorialEditComponent implements OnInit{
+
+ tutorial: Tutorial = new Tutorial();
+ submitted = false;
+ hasError = false;
+ errorMessage = '';
+ markdownText="";
+ bsEditorInstance!: EditorInstance;
+ tutorialForm!: FormGroup;
+ editorOptions!: EditorOption;
+
+ readonly dialog = inject(MatDialog);
+
+
+ markdown = `## Markdown __rulez__!
+---
+
+### Syntax highlight
+\`\`\`typescript
+const language = 'typescript';
+\`\`\`
+
+### Lists
+1. Ordered list
+2. Another bullet point
+ - Unordered list
+ - Another unordered bullet
+
+### Blockquote
+> Blockquote to the max`;
+ private id: string | null | undefined;
+
+ constructor(private userApiService: UserApiService,
+ private route: ActivatedRoute,
+ private fb: FormBuilder,
+ private markdownService: MarkdownService
+ ) {
+
+ this.route.queryParams
+ .subscribe(params => {
+ console.log(params);
+ this.id = params['id'];
+ console.log(this.id);
+ });
+
+ this.userApiService.getTutorial(this.id).subscribe(
+ data=>{
+ this.tutorial = data;
+ }
+ );
+ }
+
+ ngOnInit(): void {
+ this.editorOptions = {
+ autofocus: false,
+ iconlibrary: 'fa',
+ height: 300,
+ savable: false,
+ onFullscreenExit: (e) => this.hidePreview(),
+ onShow: (e) => this.bsEditorInstance = e,
+ parser: (val) => this.parse(val)
+ };
+ this.buildForm(this.tutorial.description);
+
+ }
+
+ buildForm(markdownText: string | undefined) {
+ this.tutorialForm = this.fb.group({
+ body: [markdownText],
+ isPreview: [true]
+ });
+ }
+ /** highlight all code found, needs to be wrapped in timer to work properly */
+ highlight() {
+ setTimeout(() => {
+ this.markdownService.highlight();
+ });
+ }
+
+ hidePreview() {
+ if (this.bsEditorInstance && this.bsEditorInstance.hidePreview) {
+ this.bsEditorInstance.hidePreview();
+ }
+ }
+
+ showFullScreen(isFullScreen: boolean) {
+ if (this.bsEditorInstance && this.bsEditorInstance.setFullscreen) {
+ this.bsEditorInstance.showPreview();
+ this.bsEditorInstance.setFullscreen(isFullScreen);
+ }
+ }
+
+ parse(inputValue: string) {
+ const markedOutput = this.markdownService.parse(inputValue.trim());
+ this.highlight();
+
+ return markedOutput;
+ }
+
+ onFormChanges(): void {
+ this.tutorialForm.valueChanges.subscribe(formData => {
+ if (formData) {
+ this.markdownText = formData.body;
+ }
+ });
+ }
+
+ updateTutorial(): void {
+ this.userApiService.update(this.id, this.tutorial)
+ .subscribe(
+ response => {
+ console.log(response);
+ this.submitted = true;
+ this.hasError = false;
+ },
+ error => {
+ console.log(error);
+
+ this.errorMessage = error.error.message;
+
+ this.hasError = true;
+ });
+ }
+
+ openSelectImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
+
+ let dialogSelectRef = this.dialog.open(DialogSelectImageComponent, {
+ height: '500px',
+ width: '600px',
+ enterAnimationDuration,
+ exitAnimationDuration,
+ // data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
+ });
+ dialogSelectRef.componentInstance.uploadClicked.subscribe(result => {
+ dialogSelectRef.close();
+ this.openUploadImageDialog(enterAnimationDuration, exitAnimationDuration);
+ })
+
+ const dialogSelectSubscription = dialogSelectRef.componentInstance.selectClicked
+ .subscribe(result => {
+ console.log('Got the data!', result);
+
+ if (result == null) {
+ return;
+ }
+ this.tutorial.titleimage = result.url;
+
+ });
+ }
+
+ openUploadImageDialog(enterAnimationDuration: string, exitAnimationDuration: string) {
+ let dialogSelectRef = this.dialog.open(DialogUploadImageComponent, {
+ height: '500px',
+ width: '600px',
+ enterAnimationDuration,
+ exitAnimationDuration,
+ // data: {username: this.dialogLoginData.username, password: this.dialogLoginData.password}
+ });
+ dialogSelectRef.componentInstance.openSelectDialogClicked.subscribe(result => {
+ dialogSelectRef.close();
+ this.openSelectImageDialog(enterAnimationDuration, exitAnimationDuration);
+ })
+
+ const dialogUploadSubscription = dialogSelectRef.componentInstance.selectUploadedImageClicked
+ .subscribe(result => {
+ console.log('Got the data!', result);
+
+ if (result == null) {
+ return;
+ }
+ this.tutorial.titleimage = result.url;
+
+ });
+ }
+}
diff --git a/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.html b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.html
new file mode 100644
index 0000000..fb527b5
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.html
@@ -0,0 +1,147 @@
+
+
+ @for (column of columnsSchema; track column){
+
+ |
+ @switch (column.key) {
+ @case ('isSelected') {
+
+ }
+ @default {
+ {{ column.label }}
+ }
+ }
+ |
+
+
+ @switch (column.type) {
+ @case ('isSelected') {
+
+ }
+ @case('isEdit') {
+
+
+ Edit
+
+
+ Delete
+
+
+ }
+ @case ('boolean') {
+
+
+
+ }
+ @case ('datetime') {
+ {{ element[column.key] | date: 'medium' }}
+ }
+ @default {
+ {{ element[column.key] }}
+ }
+ }
+
+ |
+
+
+ }
+
+
+
+
+
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.scss b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.scss
new file mode 100644
index 0000000..e4d8bda
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.scss
@@ -0,0 +1,14 @@
+.table-header {
+ width: 90%;
+ margin: auto;
+ text-align: right;
+ margin-bottom: 10px;
+ padding-top: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.spacer{
+ flex: 1 1 auto;
+}
diff --git a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.spec.ts b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.spec.ts
similarity index 89%
rename from jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.spec.ts
rename to jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.spec.ts
index a757a41..c8453de 100644
--- a/jambotron-ui/src/app/components/tutorials-list/tutorials-list.component.spec.ts
+++ b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.spec.ts
@@ -8,12 +8,10 @@ describe('TutorialsListComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ TutorialsListComponent ]
+ imports: [TutorialsListComponent]
})
.compileComponents();
- });
- beforeEach(() => {
fixture = TestBed.createComponent(TutorialsListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
diff --git a/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.ts b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.ts
new file mode 100644
index 0000000..37e4ed5
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/tutorials-list.component/tutorials-list.component.ts
@@ -0,0 +1,210 @@
+import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit} from '@angular/core';
+import {Tutorial} from '../../../models/tutorial.model';
+import {UserApiService} from '../user-api.service';
+import {MatButton} from '@angular/material/button';
+import {TokenStorageService} from '../../../services/token-storage.service';
+import {EventBusService} from '../../../_shared/event-bus.service';
+import {EventData} from '../../../_shared/event.class';
+import {Router, RouterLink} from '@angular/router';
+import {AuthService} from '../../../services/auth.service';
+import {FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {
+ MatCell,
+ MatCellDef,
+ MatColumnDef, MatHeaderCell, MatHeaderCellDef,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef,
+ MatTable,
+ MatTableDataSource
+} from '@angular/material/table';
+import {DatePipe} from '@angular/common';
+import {MatCheckbox} from '@angular/material/checkbox';
+import {MatSlideToggle} from '@angular/material/slide-toggle';
+
+
+ @Component({
+ selector: 'app-tutorials-list.component',
+ imports: [
+ MatButton,
+ RouterLink,
+ ReactiveFormsModule,
+ MatTable,
+ MatHeaderRowDef,
+ MatHeaderRow,
+ MatRowDef,
+ MatRow,
+ FormsModule,
+ MatColumnDef,
+ MatHeaderCell,
+ MatHeaderCellDef,
+ MatCellDef,
+ MatCheckbox,
+ DatePipe,
+ MatCell,
+ MatSlideToggle
+ ],
+ schemas:[CUSTOM_ELEMENTS_SCHEMA],
+ templateUrl: './tutorials-list.component.html',
+ styleUrl: './tutorials-list.component.scss'
+ })
+ export class TutorialsListComponent implements OnInit {
+ //tutorials?: Tutorial[];
+
+ displayedColumns: string[] = TutorialColumns.map((col) => col.key)
+ columnsSchema: any = TutorialColumns
+ dataSource = new MatTableDataSource()
+
+
+ constructor(private userApiService: UserApiService,
+ private storageService: TokenStorageService,
+ private eventBusService: EventBusService,
+ private router: Router,
+ private authService: AuthService
+
+ ) {
+
+ }
+
+ ngOnInit(): void {
+ this.eventBusService.on('logout', () => {
+ this.logout();
+ })
+ this.getTutorials();
+ }
+
+ getTutorials(): void {
+ this.userApiService.getUserAllTutorials()
+ .subscribe(( data:Tutorial[] ) => {
+ this.dataSource.data = data;
+ console.log(data);
+ },
+ error => {
+ console.log(error);
+ if (
+ (
+ error.status === 401
+
+ )
+ && this.storageService.isLoggedIn()
+ ) {
+ this.eventBusService.emit(new EventData('logout', null));
+ }
+ });
+ }
+
+ selectAll(event: any) {
+ this.dataSource.data = this.dataSource.data.map((item) => ({
+ ...item,
+ isSelected: event.checked
+ }));
+ }
+
+ isAllSelected() {
+ return this.dataSource.data.every((item) => item.isSelected)
+ }
+
+ isAnySelected() {
+ return this.dataSource.data.some((item) => item.isSelected)
+ }
+
+ removeSelectedRows() {
+ const selectedTutorials = this.dataSource.data.filter((u: Tutorial) => u.isSelected)
+ /*this.dialog
+ .open(ConfirmDialogComponent)
+ .afterClosed()
+ .subscribe((confirm) => {*/
+ // if (confirm) {
+ this.userApiService.deleteTutorials(selectedTutorials).subscribe(() => {
+ this.dataSource.data = this.dataSource.data.filter(
+ (u: Tutorial) => !u.isSelected
+ )
+ })
+ // }
+ // })
+ }
+
+ removeRow(id: number) {
+ this.userApiService.deleteTutorial(id).subscribe(() => {
+ this.dataSource.data = this.dataSource.data.filter(
+ (u: Tutorial) => u.id !== id,
+ )
+ })
+ }
+
+ publish(element: any, checked: boolean){
+ element.tobepublished = checked;
+ this.userApiService.update(element.id, element).subscribe(
+ response => {
+ console.log(response);
+ },
+ error => {
+ console.log(error);
+ });
+
+ }
+
+ logout(): void {
+ this.authService.logout().subscribe({
+ next: res => {
+ console.log(res);
+ this.storageService.clean();
+
+ //window.location.reload();
+ this.router.navigate(['main/generate-image']).then(() => {
+ //window.location.reload();
+ })
+
+ },
+ error: err => {
+ console.log(err);
+ }
+ });
+
+ }
+}
+
+export const TutorialColumns = [
+ {
+ key: 'isSelected',
+ type: 'isSelected',
+ label: '',
+ },
+ {
+ key: 'title',
+ type: 'text',
+ label: 'Title',
+ required: true,
+ },
+ {
+ key: 'published',
+ type: 'boolean',
+ label: 'Is Published',
+ },
+ {
+ key: 'created',
+ type: 'datetime',
+ label: 'Created Date',
+ required: true,
+
+ },
+ {
+ key: 'modified',
+ type: 'datetime',
+ label: 'Modified Date',
+ required: true
+ },
+ {
+ key: 'tobepublished',
+ type: 'boolean',
+ label: 'To be published',
+ required: true
+
+ },
+ {
+ key: 'isEdit',
+ type: 'isEdit',
+ label: '',
+ }
+];
diff --git a/jambotron-ui/src/app/services/tutorial.service.spec.ts b/jambotron-ui/src/app/modules/user-module/user-api.service.spec.ts
similarity index 53%
rename from jambotron-ui/src/app/services/tutorial.service.spec.ts
rename to jambotron-ui/src/app/modules/user-module/user-api.service.spec.ts
index 2a5e71f..17ce238 100644
--- a/jambotron-ui/src/app/services/tutorial.service.spec.ts
+++ b/jambotron-ui/src/app/modules/user-module/user-api.service.spec.ts
@@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';
-import { TutorialService } from './tutorial.service';
+import { UserApiService } from './user-api.service';
-describe('TutorialService', () => {
- let service: TutorialService;
+describe('UserApiService', () => {
+ let service: UserApiService;
beforeEach(() => {
TestBed.configureTestingModule({});
- service = TestBed.inject(TutorialService);
+ service = TestBed.inject(UserApiService);
});
it('should be created', () => {
diff --git a/jambotron-ui/src/app/modules/user-module/user-api.service.ts b/jambotron-ui/src/app/modules/user-module/user-api.service.ts
new file mode 100644
index 0000000..8dfda62
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/user-api.service.ts
@@ -0,0 +1,67 @@
+import { Injectable } from '@angular/core';
+import {forkJoin, Observable} from 'rxjs';
+import {Tutorial} from '../../models/tutorial.model';
+import {HttpClient, HttpEvent, HttpHeaders, HttpRequest} from '@angular/common/http';
+import {GlobalConstants} from '../../global-constants';
+import {FileInfo} from '../../models/file-info';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UserApiService {
+ baseUrl = `${GlobalConstants.API_URL}/user`;
+
+ constructor(private http: HttpClient) {
+
+ }
+
+ upload(file: File): Observable> {
+ const formData: FormData = new FormData();
+ formData.append('file', file);
+
+ const req = new HttpRequest('POST', `${this.baseUrl}/upload`, formData, {
+ reportProgress: true,
+ responseType: 'json'
+ });
+
+ return this.http.request(req);
+ }
+
+
+ saveZhipuaiImage(url: string): Observable {
+ return this.http.post(`${this.baseUrl}/saveZhipuAiImage`, {imageUrl:url});
+ }
+
+ getImages(): Observable {
+ return this.http.get(`${this.baseUrl}/getImages`);
+ }
+
+ getUserAllTutorials(): Observable {
+ return this.http.get(`${this.baseUrl}/tutorials`);
+ }
+
+ getTutorial(id: string | null | undefined): Observable {
+ return this.http.get(`${this.baseUrl}/tutorial-get/${id}`);
+ }
+
+ create(data: any): Observable {
+ return this.http.post(`${this.baseUrl}/tutorial-add`, data);
+ }
+
+ update(id: any,data: any): Observable {
+ return this.http.put(`${this.baseUrl}/tutorial-update/${id}`, data);
+ }
+
+ deleteTutorials(tutorials: Tutorial[]): Observable {
+ return forkJoin(
+ tutorials.map((tutorial) =>
+ this.http.delete(`${this.baseUrl}/tutorials/${tutorial.id}`)
+ )
+ );
+ }
+
+ deleteTutorial(id: number): Observable {
+ return this.http.delete(`${this.baseUrl}/tutorials/${id}`);
+ }
+
+}
diff --git a/jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.html b/jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.html
similarity index 100%
rename from jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.html
rename to jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.html
diff --git a/jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.scss b/jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.scss
similarity index 100%
rename from jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.scss
rename to jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.scss
diff --git a/jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.spec.ts b/jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.spec.ts
rename to jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.spec.ts
diff --git a/jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.ts b/jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.ts
similarity index 100%
rename from jambotron-ui/src/app/user-module/user-welcome.component/user-welcome.component.ts
rename to jambotron-ui/src/app/modules/user-module/user-welcome.component/user-welcome.component.ts
diff --git a/jambotron-ui/src/app/modules/user-module/user.component/user.component.html b/jambotron-ui/src/app/modules/user-module/user.component/user.component.html
new file mode 100644
index 0000000..1668b71
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/user.component/user.component.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/jambotron-ui/src/app/modules/user-module/user.component/user.component.scss b/jambotron-ui/src/app/modules/user-module/user.component/user.component.scss
new file mode 100644
index 0000000..016384e
--- /dev/null
+++ b/jambotron-ui/src/app/modules/user-module/user.component/user.component.scss
@@ -0,0 +1,13 @@
+//---------------
+.pc-sidebar{
+ top: 65px;
+ overflow-y: auto;
+ background-color: rgba(153, 153, 153, 0.16);
+
+ backdrop-filter: blur(8px);
+}
+.pc-container{
+ top: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
diff --git a/jambotron-ui/src/app/user-module/user.component/user.component.spec.ts b/jambotron-ui/src/app/modules/user-module/user.component/user.component.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/user-module/user.component/user.component.spec.ts
rename to jambotron-ui/src/app/modules/user-module/user.component/user.component.spec.ts
diff --git a/jambotron-ui/src/app/user-module/user.component/user.component.ts b/jambotron-ui/src/app/modules/user-module/user.component/user.component.ts
similarity index 61%
rename from jambotron-ui/src/app/user-module/user.component/user.component.ts
rename to jambotron-ui/src/app/modules/user-module/user.component/user.component.ts
index b45bdb2..81a69b2 100644
--- a/jambotron-ui/src/app/user-module/user.component/user.component.ts
+++ b/jambotron-ui/src/app/modules/user-module/user.component/user.component.ts
@@ -1,27 +1,14 @@
import {Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, ViewChild} from '@angular/core';
import {RouterOutlet} from '@angular/router';
-import {SideBarComponent} from '../../components/side-bar.component/side-bar.component';
-import {MatSidenav, MatSidenavContainer} from '@angular/material/sidenav';
-import {NgClass} from '@angular/common';
-import {MatListItem, MatNavList} from '@angular/material/list';
-import {MatIcon} from '@angular/material/icon';
+import {MatSidenav} from '@angular/material/sidenav';
import {BreakpointObserver} from '@angular/cdk/layout';
-import {MatToolbar} from '@angular/material/toolbar';
-import {MatIconButton} from '@angular/material/button';
+import {SideBarUserComponent} from '../side-bar-user.component/side-bar-user.component';
@Component({
selector: 'app-user.component',
imports: [
RouterOutlet,
- SideBarComponent,
- MatSidenavContainer,
- NgClass,
- MatNavList,
- MatSidenav,
- MatListItem,
- MatIcon,
- MatToolbar,
- MatIconButton
+ SideBarUserComponent
],
templateUrl: './user.component.html',
styleUrl: './user.component.scss',
@@ -41,7 +28,9 @@ export class UserComponent implements OnInit {
ngOnInit(): void {
this.observer.observe(['(max-width: 800px)']).subscribe((screenSize) => {
this.isMobile = screenSize.matches;
- });
+ })
+
+
}
toggleMenu() {
if(this.isMobile){
diff --git a/jambotron-ui/src/app/user-module/user.module.ts b/jambotron-ui/src/app/modules/user-module/user.module.ts
similarity index 60%
rename from jambotron-ui/src/app/user-module/user.module.ts
rename to jambotron-ui/src/app/modules/user-module/user.module.ts
index 7255d3c..a0df98e 100644
--- a/jambotron-ui/src/app/user-module/user.module.ts
+++ b/jambotron-ui/src/app/modules/user-module/user.module.ts
@@ -2,6 +2,8 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {userRouting} from './user.routing';
import {UserComponent} from './user.component/user.component';
+import {AngularMarkdownEditorModule} from 'angular-markdown-editor';
+import {MatFormFieldModule} from '@angular/material/form-field';
@@ -10,7 +12,9 @@ import {UserComponent} from './user.component/user.component';
imports: [
userRouting,
UserComponent,
- CommonModule
+ CommonModule,
+ AngularMarkdownEditorModule,
+ MatFormFieldModule
]
})
export class UserModule { }
diff --git a/jambotron-ui/src/app/user-module/user.routing.spec.ts b/jambotron-ui/src/app/modules/user-module/user.routing.spec.ts
similarity index 100%
rename from jambotron-ui/src/app/user-module/user.routing.spec.ts
rename to jambotron-ui/src/app/modules/user-module/user.routing.spec.ts
diff --git a/jambotron-ui/src/app/user-module/user.routing.ts b/jambotron-ui/src/app/modules/user-module/user.routing.ts
similarity index 61%
rename from jambotron-ui/src/app/user-module/user.routing.ts
rename to jambotron-ui/src/app/modules/user-module/user.routing.ts
index c9f7228..b5c87f3 100644
--- a/jambotron-ui/src/app/user-module/user.routing.ts
+++ b/jambotron-ui/src/app/modules/user-module/user.routing.ts
@@ -16,9 +16,21 @@ const USER_ROUTES: Routes = [
path: 'tutorials-list',
loadComponent: () => import('../user-module/tutorials-list.component/tutorials-list.component').then((c) => c.TutorialsListComponent)
},
+ {
+ path: 'tutorial-add',
+ loadComponent: () => import('../user-module/tutorial-add.component/tutorial-add.component').then((c) => c.TutorialAddComponent)
+ },
+ {
+ path: 'tutorial-edit',
+ loadComponent: () => import('../user-module/tutorial-edit.component/tutorial-edit.component').then((c) => c.TutorialEditComponent)
+ },
{
path: 'ai-models',
loadComponent: () => import('../user-module/ai-models.component/ai-models.component').then((c) => c.AiModelsComponent)
+ },
+ {
+ path: 'images',
+ loadComponent: () => import('../user-module/images.component/images.component').then((c) => c.ImagesComponent)
}
]
}
diff --git a/jambotron-ui/src/app/services/auth.service.ts b/jambotron-ui/src/app/services/auth.service.ts
index e02cd4a..f9d5c08 100644
--- a/jambotron-ui/src/app/services/auth.service.ts
+++ b/jambotron-ui/src/app/services/auth.service.ts
@@ -1,8 +1,9 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
+import {GlobalConstants} from '../global-constants';
-const AUTH_API = 'http://localhost:8080/api/auth/';
+const AUTH_API = GlobalConstants.API_URL + '/auth/';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
@@ -15,10 +16,13 @@ export class AuthService {
constructor(private http: HttpClient) { }
login(username: string, password: string): Observable {
+ console.log(AUTH_API + 'signin');
return this.http.post(AUTH_API + 'signin', {
username,
password
}, httpOptions);
+
+
}
register(username: string, email: string, password: string): Observable {
diff --git a/jambotron-ui/src/app/services/roles.service.ts b/jambotron-ui/src/app/services/roles.service.ts
index dbee6f9..4266e87 100644
--- a/jambotron-ui/src/app/services/roles.service.ts
+++ b/jambotron-ui/src/app/services/roles.service.ts
@@ -2,8 +2,9 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Role } from '../models/role';
+import {GlobalConstants} from '../global-constants';
-const API_URL = 'http://localhost:8080/api/roles';
+const API_URL = `${GlobalConstants.API_URL}/roles`;
@Injectable({
diff --git a/jambotron-ui/src/app/services/system.service.ts b/jambotron-ui/src/app/services/system.service.ts
index 8885ca0..3fb9887 100644
--- a/jambotron-ui/src/app/services/system.service.ts
+++ b/jambotron-ui/src/app/services/system.service.ts
@@ -4,13 +4,14 @@ import {Observable} from 'rxjs';
import {Tutorial} from '../models/tutorial.model';
import {Bean} from '../models/Bean';
import {NameValueItem} from '../models/name-value-item';
+import {GlobalConstants} from '../global-constants';
@Injectable({
providedIn: 'root'
})
export class SystemService {
- baseUrl : string = 'http://localhost:8080/api/system';
+ baseUrl : string = `${GlobalConstants.API_URL}/system`;
constructor(private http: HttpClient) { }
getDataSourceProperties(): Observable{
diff --git a/jambotron-ui/src/app/services/tutorial.service.ts b/jambotron-ui/src/app/services/tutorial.service.ts
deleted file mode 100644
index adbf3e4..0000000
--- a/jambotron-ui/src/app/services/tutorial.service.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { Observable } from 'rxjs';
-import { Tutorial } from '../models/tutorial.model';
-
-const baseUrl = 'http://localhost:8080/api/tutorials';
-
-@Injectable({
- providedIn: 'root'
-})
-export class TutorialService {
-
- constructor(private http: HttpClient) { }
-
- getAll(): Observable {
- return this.http.get(baseUrl);
- }
-
- get(id: any): Observable {
- return this.http.get(`${baseUrl}/${id}`);
- }
-
- create(data: any): Observable {
- return this.http.post(baseUrl, data);
- }
-
- update(id: any, data: any): Observable {
- return this.http.put(`${baseUrl}/${id}`, data);
- }
-
- delete(id: any): Observable {
- return this.http.delete(`${baseUrl}/${id}`);
- }
-
- deleteAll(): Observable {
- return this.http.delete(baseUrl);
- }
-
- findByTitle(title: any): Observable {
- return this.http.get(`${baseUrl}?title=${title}`);
- }
-}
\ No newline at end of file
diff --git a/jambotron-ui/src/app/services/user.service.ts b/jambotron-ui/src/app/services/user.service.ts
index 21d6fa1..f6b1764 100644
--- a/jambotron-ui/src/app/services/user.service.ts
+++ b/jambotron-ui/src/app/services/user.service.ts
@@ -2,9 +2,9 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import {User} from "../models/user.model";
-import {Tutorial} from "../models/tutorial.model";
+import {GlobalConstants} from '../global-constants';
-const API_URL = 'http://localhost:8080/api/users';
+const API_URL = `${GlobalConstants.API_URL}/users`;
@Injectable({
providedIn: 'root'
diff --git a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.html b/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.html
deleted file mode 100644
index 088d130..0000000
--- a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.html
+++ /dev/null
@@ -1,6 +0,0 @@
-tutorials-list.component works!
-
- Item 1
- Item 2
- Item 3
-
diff --git a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.ts b/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.ts
deleted file mode 100644
index 6b34a98..0000000
--- a/jambotron-ui/src/app/user-module/tutorials-list.component/tutorials-list.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component } from '@angular/core';
-import {MatList, MatListItem} from '@angular/material/list';
-
-@Component({
- selector: 'app-tutorials-list.component',
- imports: [
- MatList,
- MatListItem
- ],
- templateUrl: './tutorials-list.component.html',
- styleUrl: './tutorials-list.component.scss'
-})
-export class TutorialsListComponent {
-
-}
diff --git a/jambotron-ui/src/app/user-module/user.component/user.component.html b/jambotron-ui/src/app/user-module/user.component/user.component.html
deleted file mode 100644
index 9467823..0000000
--- a/jambotron-ui/src/app/user-module/user.component/user.component.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/jambotron-ui/src/app/user-module/user.component/user.component.scss b/jambotron-ui/src/app/user-module/user.component/user.component.scss
deleted file mode 100644
index 070126d..0000000
--- a/jambotron-ui/src/app/user-module/user.component/user.component.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-
-mat-toolbar{
- position:fixed;
- top:0;
- z-index: 2;
-}
-
-mat-sidenav-container {
- height:100%;
-}
-
-// Move the content down so that it won't be hidden by the toolbar
-mat-sidenav {
- padding-top: 3.5rem;
- transition: width 0.3s ease;
- @media screen and (min-width: 600px) {
- padding-top: 4rem;
- }
-
- .entry{
- display: flex;
- align-items: center;
- gap: 1rem;
- padding:0.75rem;
- }
-
-}
-
-// Move the content down so that it won't be hidden by the toolbar
-mat-sidenav-content{
- padding-top: 3.5rem;
- @media screen and (min-width: 600px) {
- padding-top: 4rem;
- }
-}
-
-
-
-.expanded {
- width: 250px;
-}
-
-//---------------
-.pc-sidebar{
- top: 65px;
- overflow-y: auto;
-}
-.pc-container{
- top: 0px;
-}
diff --git a/jambotron-ui/src/environments/environment.development.ts b/jambotron-ui/src/environments/environment.development.ts
new file mode 100644
index 0000000..2c5c033
--- /dev/null
+++ b/jambotron-ui/src/environments/environment.development.ts
@@ -0,0 +1,8 @@
+export const environment = {
+ default_page: 'main/tutorials/tutorials-all',
+ port: 8080,
+ fromWeb: false,
+ production: false,
+ host_name: 'http://localhost',
+ title: 'JAMBOTRON.RUN.PLACE (dev)'
+};
diff --git a/jambotron-ui/src/environments/environment.prod.ts b/jambotron-ui/src/environments/environment.prod.ts
new file mode 100644
index 0000000..4154dc1
--- /dev/null
+++ b/jambotron-ui/src/environments/environment.prod.ts
@@ -0,0 +1,8 @@
+export const environment = {
+ default_page: 'main/tutorials/tutorials-all',
+ port: 8081,
+ production: true,
+ fromWeb: true,
+ host_name: 'https://jambotron.run.place',
+ title: 'JAMBOTRON.RUN.PLACE'
+};
diff --git a/jambotron-ui/src/environments/environment.ts b/jambotron-ui/src/environments/environment.ts
new file mode 100644
index 0000000..ef44e66
--- /dev/null
+++ b/jambotron-ui/src/environments/environment.ts
@@ -0,0 +1,8 @@
+export const environment = {
+ default_page: 'main/tutorials/tutorials-all',
+ port: 8080,
+ production: true,
+ fromWeb: false,
+ host_name: 'http://localhost',
+ title: 'Jambotron'
+};
diff --git a/jambotron-ui/src/index.html b/jambotron-ui/src/index.html
index 42711ff..55b4b5e 100644
--- a/jambotron-ui/src/index.html
+++ b/jambotron-ui/src/index.html
@@ -9,6 +9,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/jambotron-ui/src/index_dev.html b/jambotron-ui/src/index_dev.html
new file mode 100644
index 0000000..944af06
--- /dev/null
+++ b/jambotron-ui/src/index_dev.html
@@ -0,0 +1,15 @@
+
+
+
+
+ JambotronUi
+
+
+
+
+
+
+
+
+
+
diff --git a/jambotron-ui/src/main.server.ts b/jambotron-ui/src/main.server.ts
deleted file mode 100644
index 154ce1c..0000000
--- a/jambotron-ui/src/main.server.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { bootstrapApplication } from '@angular/platform-browser';
-import { App } from './app/app';
-import { config } from './app/app.config.server';
-
-const bootstrap = () => bootstrapApplication(App, config);
-
-export default bootstrap;
diff --git a/jambotron-ui/src/proxy.conf.json b/jambotron-ui/src/proxy.conf.json
new file mode 100644
index 0000000..36564e1
--- /dev/null
+++ b/jambotron-ui/src/proxy.conf.json
@@ -0,0 +1,10 @@
+{
+ "/api/**": {
+ "target": "http://localhost:8082",
+ "secure": false,
+ "changeOrigin": true,
+ "logLevel": "debug",
+ "pathRewrite": {"^/api" : "http://localhost:8082/api"}
+
+ }
+}
diff --git a/jambotron-ui/src/server.ts b/jambotron-ui/src/server.ts
deleted file mode 100644
index e6546c4..0000000
--- a/jambotron-ui/src/server.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import {
- AngularNodeAppEngine,
- createNodeRequestHandler,
- isMainModule,
- writeResponseToNodeResponse,
-} from '@angular/ssr/node';
-import express from 'express';
-import { join } from 'node:path';
-
-const browserDistFolder = join(import.meta.dirname, '../browser');
-
-const app = express();
-const angularApp = new AngularNodeAppEngine();
-
-/**
- * Example Express Rest API endpoints can be defined here.
- * Uncomment and define endpoints as necessary.
- *
- * Example:
- * ```ts
- * app.get('/api/{*splat}', (req, res) => {
- * // Handle API request
- * });
- * ```
- */
-
-/**
- * Serve static files from /browser
- */
-app.use(
- express.static(browserDistFolder, {
- maxAge: '1y',
- index: false,
- redirect: false,
- }),
-);
-
-/**
- * Handle all other requests by rendering the Angular application.
- */
-app.use((req, res, next) => {
- angularApp
- .handle(req)
- .then((response) =>
- response ? writeResponseToNodeResponse(response, res) : next(),
- )
- .catch(next);
-});
-
-/**
- * Start the server if this module is the main entry point.
- * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
- */
-if (isMainModule(import.meta.url)) {
- const port = process.env['PORT'] || 4000;
- app.listen(port, (error) => {
- if (error) {
- throw error;
- }
-
- console.log(`Node Express server listening on http://localhost:${port}`);
- });
-}
-
-/**
- * Request handler used by the Angular CLI (for dev-server and during build) or Firebase Cloud Functions.
- */
-export const reqHandler = createNodeRequestHandler(app);
diff --git a/jambotron-ui/src/styles.scss b/jambotron-ui/src/styles.scss
index 0421915..ac9efef 100644
--- a/jambotron-ui/src/styles.scss
+++ b/jambotron-ui/src/styles.scss
@@ -63,3 +63,5 @@ body {
padding-top: 60px;
scroll-padding-inline: 40%;
}
+
+
diff --git a/logs/application.log.2025-06-21.0.gz b/logs/application.log.2025-06-21.0.gz
deleted file mode 100644
index f916f89..0000000
Binary files a/logs/application.log.2025-06-21.0.gz and /dev/null differ
diff --git a/logs/application.log.2025-06-22.0.gz b/logs/application.log.2025-06-22.0.gz
deleted file mode 100644
index 3f93117..0000000
Binary files a/logs/application.log.2025-06-22.0.gz and /dev/null differ
diff --git a/logs/application.log.2025-06-23.0.gz b/logs/application.log.2025-06-23.0.gz
deleted file mode 100644
index 83ead94..0000000
Binary files a/logs/application.log.2025-06-23.0.gz and /dev/null differ
diff --git a/logs/application.log.2025-06-24.0.gz b/logs/application.log.2025-06-24.0.gz
deleted file mode 100644
index b9e1d05..0000000
Binary files a/logs/application.log.2025-06-24.0.gz and /dev/null differ
diff --git a/src/DevOps/Dockerfile b/src/DevOps/Dockerfile
new file mode 100644
index 0000000..32cb7a8
--- /dev/null
+++ b/src/DevOps/Dockerfile
@@ -0,0 +1,15 @@
+#FROM openjdk:24 AS BUILD_IMAGE
+
+#WORKDIR /jambotron/
+#COPY . ./
+#RUN microdnf install findutils
+#RUN ./gradlew build -x test
+
+FROM openjdk:24
+WORKDIR /jambotron/
+VOLUME /jambotron_data/uploads
+COPY './build/libs/jambotron-0.0.1-SNAPSHOT.jar' '/app/jambotron.jar'
+#COPY --from=BUILD_IMAGE /jambotron/build/libs/jambotron-0.0.1-SNAPSHOT.jar .
+#EXPOSE 8080
+CMD ["java","-jar","/app/jambotron.jar"]
+#CMD ["java","-jar","jambotron-0.0.1-SNAPSHOT.jar"]
\ No newline at end of file
diff --git a/src/Docker/db_password.txt b/src/DevOps/db_password.txt
similarity index 100%
rename from src/Docker/db_password.txt
rename to src/DevOps/db_password.txt
diff --git a/src/DevOps/docker-compose.yml b/src/DevOps/docker-compose.yml
new file mode 100644
index 0000000..687b871
--- /dev/null
+++ b/src/DevOps/docker-compose.yml
@@ -0,0 +1,69 @@
+services:
+ jambotron:
+ image: 'jambotron-image'
+ container_name: 'jambotron-container'
+ build:
+ context: ../../
+ dockerfile: /Dockerfile
+ ports:
+# - "8081:80"
+ - "8443:443"
+ depends_on:
+ - postgres_jambotron
+ volumes:
+ - certs:/certs
+ - jambotron_data:/jambotron_data
+# env_file: "webapp.env"
+ environment:
+ SSL_ENABLED: "true"
+ SERVER_PORT: 443
+ FULLCHAINPEM: /certs/live/jambotron.run.place/fullchain.pem
+ PRIVKEYPEM: /certs/live/jambotron.run.place/privkey.pem
+
+ SPRING_DATASOURCE_URL: jdbc:postgresql://postgres_jambotron:5432/jambotronDB
+ SPRING_DATASOURCE_USERNAME: admin
+ SPRING_DATASOURCE_PASSWORD: postgrespw
+ SPRING_FLYWAY_BASELINE-ON-MIGRATE: "true"
+ SPRING_FLYWAY_VALIDATE-ON-MIGRATE: "true"
+
+ SPRING_FLYWAY_USER: admin
+ SPRING_FLYWAY_PASSWORD: postgrespw
+ SPRING_FLYWAY_URL: jdbc:postgresql://postgres_jambotron:5432/jambotronDB
+
+# SPRING_DATASOURCE_URL: jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app:5432/jambotronDB
+# SPRING_DATASOURCE_USERNAME: koyeb-adm
+# SPRING_DATASOURCE_PASSWORD: npg_HfFEUA7bay1i
+# SPRING_FLYWAY_BASELINE-ON-MIGRATE: "true"
+# SPRING_FLYWAY_VALIDATE-ON-MIGRATE: "true"
+#
+# SPRING_FLYWAY_USER: koyeb-adm
+# SPRING_FLYWAY_PASSWORD: npg_HfFEUA7bay1i
+# SPRING_FLYWAY_URL: jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app:5432/jambotronDB
+
+
+# SPRING_DATASOURCE_PASSWORD: /run/secrets/db_password
+# secrets:
+# - db_password
+
+
+ postgres_jambotron:
+ image: 'postgres'
+ container_name: 'jambotron-postgres-container'
+ ports:
+ - "5432:5432"
+ environment:
+ POSTGRES_USER: admin
+ POSTGRES_PASSWORD: postgrespw
+# POSTGRES_PASSWORD: /run/secrets/db_password
+ POSTGRES_DB: jambotronDB
+# secrets:
+# - db_password
+#
+#secrets:
+# db_password:
+# file: db_password.txt
+volumes:
+ certs:
+ external: true
+ jambotron_data:
+# external: true
\ No newline at end of file
diff --git a/src/DevOps/run.bat b/src/DevOps/run.bat
new file mode 100644
index 0000000..512684e
--- /dev/null
+++ b/src/DevOps/run.bat
@@ -0,0 +1,5 @@
+// investigate docker-compose.yml files for details on how to run the application
+@echo off
+//docker compose -f docker-compose.yml down
+//docker compose -f docker-compose.yml build
+//docker compose -f docker-compose.yml up -d
\ No newline at end of file
diff --git a/src/DevOps/run.sh b/src/DevOps/run.sh
new file mode 100644
index 0000000..578245a
--- /dev/null
+++ b/src/DevOps/run.sh
@@ -0,0 +1,9 @@
+#additional investigation needed to run this script
+##!/bin/bash
+#
+## stop any previously running containers
+#docker compose --env-file .env -f DevOps/docker-compose.yml down
+## build the images
+#docker compose --env-file .env -f DevOps/docker-compose.yml build
+## start the containers
+#docker compose --env-file .env -f DevOps/docker-compose.yml up -d
\ No newline at end of file
diff --git a/src/DevOps/webapp.env b/src/DevOps/webapp.env
new file mode 100644
index 0000000..a463786
--- /dev/null
+++ b/src/DevOps/webapp.env
@@ -0,0 +1,3 @@
+SERVER_PORT=443
+FULLCHAINPEM=/certs/live/jambotron.run.place/fullchain.pem
+PRIVKEYPEM=/certs/live/jambotron.run.place/privkey.pem
diff --git a/src/Docker/docker-compose.yml b/src/Docker/docker-compose.yml
deleted file mode 100644
index bf8728d..0000000
--- a/src/Docker/docker-compose.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-services:
- jambotron:
- image: 'jambotron-image'
- container_name: 'jambotron-container'
- build:
- context: ../../
- dockerfile: ./src/Docker/Dockerfile
- ports:
- - "8080:8080"
- depends_on:
- - postgres_jambotron
- environment:
- SPRING_DATASOURCE_URL: jdbc:postgresql://postgres_jambotron:5432/jambotronDB
- SPRING_DATASOURCE_USERNAME: admin
- SPRING_DATASOURCE_PASSWORD: postgrespw
- SPRING_FLYWAY_BASELINE-ON-MIGRATE: true
- SPRING_FLYWAY_VALIDATE-ON-MIGRATE: true
-
- SPRING_FLYWAY_USER: admin
- SPRING_FLYWAY_PASSWORD: postgrespw
- SPRING_FLYWAY_URL: jdbc:postgresql://postgres_jambotron:5432/jambotronDB
-
-# SPRING_DATASOURCE_PASSWORD: /run/secrets/db_password
-# secrets:
-# - db_password
-
-
- postgres_jambotron:
- image: 'postgres'
- container_name: 'jambotron-postgres-container'
- ports:
- - "5432:5432"
- environment:
- POSTGRES_USER: admin
- POSTGRES_PASSWORD: postgrespw
-# POSTGRES_PASSWORD: /run/secrets/db_password
- POSTGRES_DB: jambotronDB
-# secrets:
-# - db_password
-#
-#secrets:
-# db_password:
-# file: db_password.txt
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
index 2bfd3f8..3c64e86 100644
--- a/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
+++ b/src/main/java/com/jambotronGroup/jambotron/JambotronApplication.java
@@ -1,17 +1,18 @@
package com.jambotronGroup.jambotron;
+
+import com.jambotronGroup.jambotron.fileUpload.FilesStorageService;
+import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.slf4j.Marker;
-import org.slf4j.event.Level;
-import org.slf4j.helpers.BasicMarker;
+import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import java.util.Iterator;
-
@SpringBootApplication
-public class JambotronApplication {
+public class JambotronApplication implements CommandLineRunner {
+ @Resource
+ FilesStorageService storageService;
private static final Logger logger = LoggerFactory.getLogger(JambotronApplication.class);
public static void main(String[] args) {
@@ -19,9 +20,15 @@ public class JambotronApplication {
SpringApplication.run(JambotronApplication.class, args);
- logger.error("Application Run.");
- logger.debug("Application Run.");
- logger.info("Application Run.");
+ logger.error("Application Run. This is an error message.");
+ logger.debug("Application Run. This is a debug message.");
+ logger.info("Application Run. This is an info message.");
+ logger.info("Application Run. This is an info message.!!!!!!");
+ }
+ @Override
+ public void run(String... arg) throws Exception {
+// storageService.deleteAll();
+ storageService.init();
}
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java
index f4effa1..a99c396 100644
--- a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ImageController.java
@@ -1,5 +1,7 @@
package com.jambotronGroup.jambotron.ZhiPuAi;
+import com.jambotronGroup.jambotron.controllers.FilesController;
+import com.jambotronGroup.jambotron.fileUpload.FilesStorageService;
import com.jambotronGroup.jambotron.model.NameValueItem;
import org.springframework.ai.image.Image;
import org.springframework.ai.image.ImagePrompt;
@@ -8,15 +10,32 @@ import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Path;
import java.util.List;
-@CrossOrigin(origins = "http://localhost:4200, https://pony-sincere-chimp.ngrok-free.app/", maxAge = 3600, allowCredentials="true")
+//@CrossOrigin(origins = "http://localhost:4200, https://pony-sincere-chimp.ngrok-free.app/",
+// maxAge = 3600,
+// allowCredentials="true",
+// allowedHeaders = {"Content-Type", "Authorization", "X-Requested-With"}
+//)
@RestController
-@RequestMapping("/api/zhipuai")
+@RequestMapping("/api/public/zhipuai")
public class ImageController {
+ @Autowired
+ FilesStorageService storageService;
+
@Autowired
ZhiPuAiImageService _zhiPuAiImageService;
@@ -26,7 +45,6 @@ public class ImageController {
Image returnValue = _zhiPuAiImageService.generateImage(query).getResult().getOutput();
//userRepository.findAll().forEach(users::add);
-
if (returnValue == null) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/MyAIImageModel.java b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/MyAIImageModel.java
new file mode 100644
index 0000000..192492d
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/MyAIImageModel.java
@@ -0,0 +1,109 @@
+package com.jambotronGroup.jambotron.ZhiPuAi;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.image.*;
+import org.springframework.ai.model.ModelOptionsUtils;
+import org.springframework.ai.retry.RetryUtils;
+import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
+import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
+import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
+import org.springframework.http.ResponseEntity;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.util.Assert;
+
+import java.util.List;
+
+public class MyAIImageModel implements ImageModel {
+
+ private static final Logger logger = LoggerFactory.getLogger(ZhiPuAiImageModel.class);
+
+ public final RetryTemplate retryTemplate;
+
+ private final MyImageOptions defaultOptions;
+
+ private final ZhiPuAiImageApi zhiPuAiImageApi;
+
+ public MyAIImageModel(ZhiPuAiImageApi zhiPuAiImageApi) {
+ this(zhiPuAiImageApi, MyImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE);
+ }
+
+ public MyAIImageModel(ZhiPuAiImageApi zhiPuAiImageApi, MyImageOptions defaultOptions,
+ RetryTemplate retryTemplate) {
+ Assert.notNull(zhiPuAiImageApi, "ZhiPuAiImageApi must not be null");
+ Assert.notNull(defaultOptions, "defaultOptions must not be null");
+ Assert.notNull(retryTemplate, "retryTemplate must not be null");
+ this.zhiPuAiImageApi = zhiPuAiImageApi;
+ this.defaultOptions = defaultOptions;
+ this.retryTemplate = retryTemplate;
+ }
+
+ public MyImageOptions getDefaultOptions() {
+ return this.defaultOptions;
+ }
+
+ @Override
+ public ImageResponse call(ImagePrompt imagePrompt) {
+ return this.retryTemplate.execute(ctx -> {
+
+ String instructions = imagePrompt.getInstructions().get(0).getText();
+
+ ZhiPuAiImageApi.ZhiPuAiImageRequest imageRequest = new ZhiPuAiImageApi.ZhiPuAiImageRequest(instructions,
+ ZhiPuAiImageApi.DEFAULT_IMAGE_MODEL);
+
+ if (this.defaultOptions != null) {
+ imageRequest = ModelOptionsUtils.merge(this.defaultOptions, imageRequest,
+ ZhiPuAiImageApi.ZhiPuAiImageRequest.class);
+ }
+
+ if (imagePrompt.getOptions() != null) {
+ imageRequest = ModelOptionsUtils.merge(toZhiPuAiImageOptions(imagePrompt.getOptions()), imageRequest,
+ ZhiPuAiImageApi.ZhiPuAiImageRequest.class);
+ }
+
+ // Make the request
+ ResponseEntity imageResponseEntity = this.zhiPuAiImageApi
+ .createImage(imageRequest);
+
+ // Convert to org.springframework.ai.model derived ImageResponse data type
+ return convertResponse(imageResponseEntity, imageRequest);
+ });
+ }
+
+ private ImageResponse convertResponse(ResponseEntity imageResponseEntity,
+ ZhiPuAiImageApi.ZhiPuAiImageRequest zhiPuAiImageRequest) {
+ ZhiPuAiImageApi.ZhiPuAiImageResponse imageApiResponse = imageResponseEntity.getBody();
+ if (imageApiResponse == null) {
+ logger.warn("No image response returned for request: {}", zhiPuAiImageRequest);
+ return new ImageResponse(List.of());
+ }
+
+ List imageGenerationList = imageApiResponse.data()
+ .stream()
+ .map(entry -> new ImageGeneration(new Image(entry.url(), null)))
+ .toList();
+
+ return new ImageResponse(imageGenerationList);
+ }
+
+ /**
+ * Convert the {@link ImageOptions} into {@link ZhiPuAiImageOptions}.
+ * @param runtimeImageOptions the image options to use.
+ * @return the converted {@link ZhiPuAiImageOptions}.
+ */
+ private MyImageOptions toZhiPuAiImageOptions(ImageOptions runtimeImageOptions) {
+ MyImageOptions.Builder myImageOptionsBuilder = MyImageOptions.builder();
+ if (runtimeImageOptions != null) {
+ if (runtimeImageOptions.getModel() != null) {
+ myImageOptionsBuilder.model(runtimeImageOptions.getModel());
+ }
+ if (runtimeImageOptions instanceof MyImageOptions myImageOptions) {
+ if (myImageOptions.getUser() != null) {
+ myImageOptionsBuilder.user(myImageOptions.getUser());
+ }
+ }
+ }
+ return myImageOptionsBuilder.build();
+ }
+
+}
diff --git a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/MyImageOptions.java b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/MyImageOptions.java
new file mode 100644
index 0000000..ebffdb1
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/MyImageOptions.java
@@ -0,0 +1,128 @@
+package com.jambotronGroup.jambotron.ZhiPuAi;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.springframework.ai.image.ImageOptions;
+import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
+import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
+
+import java.util.Objects;
+
+public class MyImageOptions implements ImageOptions {
+ /**
+ * The model to use for image generation.
+ */
+ @JsonProperty("model")
+ private String model = ZhiPuAiImageApi.DEFAULT_IMAGE_MODEL;
+
+ /**
+ * A unique identifier representing your end-user, which can help ZhiPuAI to monitor
+ * and detect abuse. User ID length requirement: minimum of 6 characters, maximum of
+ * 128 characters
+ */
+ @JsonProperty("user_id")
+ private String user;
+
+ public static MyImageOptions.Builder builder() {
+
+ return new MyImageOptions.Builder();
+ }
+
+ @Override
+ @JsonIgnore
+ public Integer getN() {
+ return null;
+ }
+
+ @Override
+ public String getModel() {
+ return this.model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ @Override
+ @JsonIgnore
+ public Integer getWidth() {
+ return 300;
+ }
+
+ @Override
+ @JsonIgnore
+ public Integer getHeight() {
+
+ return 200;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getResponseFormat() {
+ return null;
+ }
+
+ @Override
+ @JsonIgnore
+ public String getStyle() {
+ return null;
+ }
+
+ public String getUser() {
+ return this.user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ else {
+ return false;
+ }
+// if (!(o instanceof ZhiPuAiImageOptions that)) {
+// return false;
+// }
+// return Objects.equals(this.model, that.model) && Objects.equals(this.user, that.user);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.model, this.user);
+ }
+
+ @Override
+ public String toString() {
+ return "ZhiPuAiImageOptions{model='" + this.model + '\'' + ", user='" + this.user + '\'' + '}';
+ }
+
+ public static final class Builder {
+
+ private final MyImageOptions options;
+
+ Builder() {
+ this.options = new MyImageOptions();
+ }
+
+ public MyImageOptions.Builder model(String model) {
+ this.options.setModel(model);
+ return this;
+ }
+
+ public MyImageOptions.Builder user(String user) {
+ this.options.setUser(user);
+ return this;
+ }
+
+ public MyImageOptions build() {
+
+ return this.options;
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ZhiPuAiImageService.java b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ZhiPuAiImageService.java
index 59f5b96..2fc88ad 100644
--- a/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ZhiPuAiImageService.java
+++ b/src/main/java/com/jambotronGroup/jambotron/ZhiPuAi/ZhiPuAiImageService.java
@@ -1,9 +1,13 @@
package com.jambotronGroup.jambotron.ZhiPuAi;
+import org.springframework.ai.image.ImageOptions;
+import org.springframework.ai.image.ImageOptionsBuilder;
import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
+import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
+import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -21,18 +25,48 @@ public class ZhiPuAiImageService {
private ZhiPuAiImageApi _zhiPuAiImageApi;
+ private MyAIImageModel _myAIImageModel;
+
private ZhiPuAiImageModel _zhiPuAiImageModel;
+ private MyImageOptions _myImageOptions;
+
public ZhiPuAiImageService() {
_zhiPuAiImageApi = new ZhiPuAiImageApi("628447c8c65845a48a7226391464a2ea.Dw8ci6TiW0BRF5LI");
+ ImageOptionsBuilder imageOptionsBuilder = ImageOptionsBuilder.builder();
+
+ ImageOptions imageOptions = imageOptionsBuilder.model("Cogview-3-Flash")//ZhiPuAiImageApi.DEFAULT_IMAGE_MODEL)
+ .height(100)
+ .width(200)
+
+ .build();
+// _myImageOptions = new MyImageOptions.Builder()
+// .model(ZhiPuAiImageApi.DEFAULT_IMAGE_MODEL)
+// .user("jambotron")
+//
+// .build();
+
+
_zhiPuAiImageModel = new ZhiPuAiImageModel(_zhiPuAiImageApi);
+ _myAIImageModel = new MyAIImageModel(_zhiPuAiImageApi);
+
}
public ImageResponse generateImage(String prompt) {
+ ImageOptionsBuilder imageOptionsBuilder = ImageOptionsBuilder.builder();
+ ImageOptions imageOptions = imageOptionsBuilder.model("Cogview-3-Flash")//ZhiPuAiImageApi.DEFAULT_IMAGE_MODEL)
+ .height(1440)
+ .width(720)
+
+ .build();
// Create an ImagePrompt object with the desired prompt
- ImagePrompt imagePrompt = new ImagePrompt(prompt);
+ ImagePrompt imagePrompt = new ImagePrompt(prompt,imageOptions);
+
+ /*// Call the generate method to get the image response
+ ImageResponse imageResponse = _myAIImageModel.call(imagePrompt);
+*/
// Call the generate method to get the image response
ImageResponse imageResponse = _zhiPuAiImageModel.call(imagePrompt);
diff --git a/src/main/java/com/jambotronGroup/jambotron/configuretions/MvcConfig.java b/src/main/java/com/jambotronGroup/jambotron/configuretions/MvcConfig.java
deleted file mode 100644
index a637531..0000000
--- a/src/main/java/com/jambotronGroup/jambotron/configuretions/MvcConfig.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.jambotronGroup.jambotron.configuretions;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-/*
-@Configuration
-
-@EnableWebMvc
-public class MvcConfig implements WebMvcConfigurer {
- @Value("${spring.resources.static-locations}")
- String resourceLocations;
-
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- //registry.addResourceHandler("/public/**").addResourceLocations(resourceLocations);
- registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/");
-
- //registry.
- registry.addResourceHandler("/media/**").addResourceLocations("resources/main/public/media/");
-
- }
-}*/
diff --git a/src/main/java/com/jambotronGroup/jambotron/configuretions/NotFoundHandler.java b/src/main/java/com/jambotronGroup/jambotron/configuretions/NotFoundHandler.java
deleted file mode 100644
index df66fd9..0000000
--- a/src/main/java/com/jambotronGroup/jambotron/configuretions/NotFoundHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.jambotronGroup.jambotron.configuretions;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.util.ResourceUtils;
-import org.springframework.util.StreamUtils;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.servlet.NoHandlerFoundException;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.Charset;
-/*
-@ControllerAdvice
-public class NotFoundHandler {
- @Value("${spa.default-file}")
- String defaultFile;
-
- @ExceptionHandler(NoHandlerFoundException.class)
- public ResponseEntity renderDefaultPage() {
- try {
- File indexFile = ResourceUtils.getFile(defaultFile);
- FileInputStream inputStream = new FileInputStream(indexFile);
- String body = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
- return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(body);
- } catch (IOException e) {
- e.printStackTrace();
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("There was an error completing the action.");
- }
- }
-}*/
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java
index c1b1b97..a877b3c 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/AuthController.java
@@ -13,6 +13,8 @@ import com.jambotronGroup.jambotron.repository.UserRepository;
import com.jambotronGroup.jambotron.security.jwt.JwtUtils;
import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
import jakarta.validation.Valid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
@@ -29,11 +31,13 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
-//@CrossOrigin(origins = "*", maxAge = 3600)
-@CrossOrigin(origins = "http://localhost:4200,https://a576-5-248-149-207.ngrok-free.app, https://pony-sincere-chimp.ngrok-free.app", maxAge = 3600, allowCredentials="true")
+@CrossOrigin(origins = "*", maxAge = 3600)
+//@CrossOrigin(origins = "http://localhost:4200,https://a576-5-248-149-207.ngrok-free.app, https://pony-sincere-chimp.ngrok-free.app", maxAge = 3600, allowCredentials="true")
@RestController
@RequestMapping("/api/auth")
public class AuthController {
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
@Autowired
AuthenticationManager authenticationManager;
@@ -65,12 +69,16 @@ public class AuthController {
.map(item -> item.getAuthority())
.collect(Collectors.toList());
+ logger.info("User {} authenticated successfully with roles: {}", userDetails.getUsername(), roles);
+
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, jwtCookie.toString())
.body(new UserInfoResponse(
userDetails.getId(),
userDetails.getUsername(),
userDetails.getEmail(),
- roles));
+ roles,
+ jwtCookie.toString()
+ ));
}
@PostMapping("/signup")
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
new file mode 100644
index 0000000..1641451
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/FilesController.java
@@ -0,0 +1,160 @@
+package com.jambotronGroup.jambotron.controllers;
+
+import com.jambotronGroup.jambotron.fileUpload.FilesStorageService;
+import com.jambotronGroup.jambotron.fileUpload.ResponseImageUploadResult;
+import com.jambotronGroup.jambotron.fileUpload.ResponseMessage;
+import com.jambotronGroup.jambotron.model.FileInfo;
+import com.jambotronGroup.jambotron.model.User;
+import com.jambotronGroup.jambotron.payload.request.SaveZhipuAiImageRequest;
+import com.jambotronGroup.jambotron.security.AuthenticationFacade;
+import com.jambotronGroup.jambotron.system.SystemServiceImpl;
+import com.jambotronGroup.jambotron.utils.FilesRoutingHelper;
+import jakarta.validation.Valid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Controller
+public class FilesController {
+
+ private static final Logger logger = LoggerFactory.getLogger(FilesController.class);
+
+ @Autowired
+ AuthenticationFacade authenticationFacade;
+ @Autowired
+ FilesStorageService storageService;
+
+
+ @PostMapping("/api/user/saveZhipuAiImage")
+ public ResponseEntity saveZhipuAiImage(@Valid @RequestBody SaveZhipuAiImageRequest request) {
+
+ if (request.getImageUrl() == null || request.getImageUrl().isEmpty()) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseMessage("URL cannot be empty"));
+ }
+
+ String url = request.getImageUrl();
+
+ String message = "";
+
+ RestTemplate restTemplate = new RestTemplate();
+
+ try {
+ // Make a GET request to fetch the image as a byte array
+ ResponseEntity response = restTemplate.getForEntity(url, byte[].class);
+
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
+ // Save the image to a file
+
+ int lastSlashIndex = url.lastIndexOf("/");
+ String fileName = (lastSlashIndex != -1) ? url.substring(lastSlashIndex + 1) : url;
+
+ MultipartFile multipartFile = new MockMultipartFile(
+ fileName, // Name of the file
+ fileName, // Original filename
+ "application/octet-stream", // Content type
+ response.getBody() // File content
+ );
+
+ User user = authenticationFacade.getUser();
+
+ storageService.save(user.getId().toString(), multipartFile);
+
+ message = "Uploaded the file successfully: " + fileName;
+
+ } else {
+
+ message = "Failed to download image. HTTP Status: " + response.getStatusCode();
+ System.err.println(message);
+ return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
+ }
+ } catch (Exception e) {
+ message = "Error fetching the image from URL: " + url + ". Error: " + e.getMessage();
+ System.err.println(message);
+ return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
+ }
+
+ @PostMapping("/api/user/upload")
+ public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) {
+ String message = "";
+ try {
+ User user = authenticationFacade.getUser();
+
+ String localFullFileName = storageService.save(user.getId().toString(), file);
+
+ FileInfo fileInfo = new FileInfo(
+ file.getOriginalFilename(),
+ FilesRoutingHelper.getUserImageUrl(localFullFileName));
+
+
+ message = "Uploaded the file successfully: " + file.getOriginalFilename();
+ return ResponseEntity.status(HttpStatus.OK).body(new ResponseImageUploadResult(message,fileInfo));
+ } catch (Exception e) {
+ message = "Could not upload the file: " + file.getOriginalFilename() + ". Error: " + e.getMessage();
+ return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
+ }
+ }
+
+ @GetMapping("/api/user/getImages")
+ public ResponseEntity> getImages() {
+
+ User user = authenticationFacade.getUser();
+
+ List fileInfos = storageService.loadUserImages(user.getId().toString()).map(path -> {
+ String filename = path.getFileName().toString();
+
+ String url = FilesRoutingHelper.getUserImageUrl(filename);
+
+ return new FileInfo(filename, url);
+ }).collect(Collectors.toList());
+
+ return ResponseEntity.status(HttpStatus.OK).body(fileInfos);
+ }
+
+
+ @GetMapping("/api/file/files")
+ public ResponseEntity> getListFiles() {
+ List fileInfos = storageService.loadAll().map(path -> {
+ String filename = path.getFileName().toString();
+ String url = FilesRoutingHelper.getPublicImageUrl(filename);
+ return new FileInfo(filename, url);
+ }).collect(Collectors.toList());
+
+ return ResponseEntity.status(HttpStatus.OK).body(fileInfos);
+ }
+
+ @GetMapping("/files/{filename:.+}")
+ @ResponseBody
+ public ResponseEntity getFile(@PathVariable String filename) {
+ Resource file = storageService.load(filename);
+ return ResponseEntity.ok()
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
+ }
+
+ @GetMapping("/api/user/user-images/{filename:.+}")
+ @ResponseBody
+ public ResponseEntity getUserImage(@PathVariable String filename) {
+ User user = authenticationFacade.getUser();
+ Resource file = storageService.loadUserImage(user.getId().toString(),filename);
+ return ResponseEntity.ok()
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/RolesController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/RolesController.java
index c6a55fe..e20af0d 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/RolesController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/RolesController.java
@@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
-@CrossOrigin(origins = "${app.origin}", maxAge = 3600, allowCredentials="true")
@RestController
@RequestMapping("/api")
public class RolesController {
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
index 02ee9fd..dd1c6d8 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/TutorialController.java
@@ -1,36 +1,54 @@
package com.jambotronGroup.jambotron.controllers;
+import com.jambotronGroup.jambotron.fileUpload.FilesStorageService;
+import com.jambotronGroup.jambotron.fileUpload.FilesStorageServiceImpl;
import com.jambotronGroup.jambotron.model.Tutorial;
+import com.jambotronGroup.jambotron.model.User;
import com.jambotronGroup.jambotron.repository.TutorialRepository;
+import com.jambotronGroup.jambotron.repository.UserRepository;
+import com.jambotronGroup.jambotron.security.AuthenticationFacade;
+import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
+import com.jambotronGroup.jambotron.utils.FilesRoutingHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
+import java.nio.file.Path;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.*;
-
-@CrossOrigin(origins = "${app.origin}", maxAge = 3600, allowCredentials="true")
+//@CrossOrigin(origins = "http://localhost:4200,http://www.jambotron.run.place", maxAge = 3600, allowCredentials="true")
@RestController
@RequestMapping("/api")
public class TutorialController {
+ @Autowired
+ AuthenticationFacade authenticationFacade;
+ @Autowired
+ UserRepository userRepository;
+
@Autowired
TutorialRepository tutorialRepository;
- @GetMapping("/tutorials")
+ @Autowired
+ FilesStorageService filesStorageService;
+
+ //--------------------public methods----------------------------
+
+ @GetMapping("/public/tutorials")
public ResponseEntity> getAllTutorials(@RequestParam(required = false) String title) {
try {
List tutorials = new ArrayList();
- if (title == null)
- tutorialRepository.findAll().forEach(tutorials::add);
- else
- tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);
+ tutorialRepository.findByPublished(true).forEach(tutorials::add);
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
@@ -42,44 +60,152 @@ public class TutorialController {
}
}
- @GetMapping("/tutorials/{id}")
- public ResponseEntity getTutorialById(@PathVariable("id") long id) {
+ @GetMapping("public/tutorial-get/{id}")
+ public ResponseEntity getPublicTutorial(@PathVariable("id") long id) {
+
Optional tutorialData = tutorialRepository.findById(id);
- if (tutorialData.isPresent()) {
- return new ResponseEntity<>(tutorialData.get(), HttpStatus.OK);
+ if (tutorialData.isPresent() && tutorialData.get().isPublished()) {
+
+ return new ResponseEntity(tutorialData.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
- @PostMapping("/tutorials")
- public ResponseEntity createTutorial(@RequestBody Tutorial tutorial) {
+
+ //--------------------user methods----------------------------
+
+ @GetMapping("/user/tutorials")
+ public ResponseEntity> getUserTutorials(@RequestParam(required = false) String title) {
try {
+ List tutorials = new ArrayList();
+
+ User user = userRepository.findById(authenticationFacade.getUserDetails().getId()).get();
+
+
+ if(title == null){
+ // If no title is provided, return all tutorials for the user
+ tutorialRepository.findByUserId(user.getId()).forEach(tutorials::add);
+ } else {
+ // If a title is provided, filter tutorials by user and title
+ tutorialRepository.findByUserIdAndTitle(user.getId(), title).forEach(tutorials::add);
+ }
+
+ if (tutorials.isEmpty()) {
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+ return new ResponseEntity<>(tutorials, HttpStatus.OK);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @PostMapping("user/tutorial-add")
+ public ResponseEntity createTutorial(@RequestBody Tutorial tutorial) throws Exception {
+
+ // not work from white IP port 80 to docker container port 8080 or 8081
+ //UserDetails userDetails = authenticationFacade.getUserDetails();
+
+ User user = authenticationFacade.getUser();
+
+
+ String newFilename = FilesStorageServiceImpl.getFileNameFromUrl(tutorial.getTitleimage());
+ Path path= filesStorageService.moveFile(
+ user.getId().toString(),
+ tutorial.getTitleimage(),
+ String.format("Tutorial_%s",newFilename )
+ );
+
+ String url = FilesRoutingHelper.getPublicImageUrl(path.getFileName().toString());
+
+
+ try {
+ Tutorial newTutorial =new Tutorial(
+ tutorial.getTitle(),
+ tutorial.getDescription(),
+ false,
+ false,
+ user,
+ Timestamp.valueOf(LocalDateTime.now()),
+ Timestamp.valueOf(LocalDateTime.now()),
+ url,
+ tutorial.getBody()
+ );
+
Tutorial _tutorial = tutorialRepository
- .save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
+ .save(newTutorial);
return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
- @PutMapping("/tutorials/{id}")
- public ResponseEntity updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
+ @GetMapping("user/tutorial-get/{id}")
+ public ResponseEntity getTutorial(@PathVariable("id") long id) {
+
+ User user = userRepository.findById(authenticationFacade.getUserDetails().getId()).get();
+
Optional tutorialData = tutorialRepository.findById(id);
- if (tutorialData.isPresent()) {
- Tutorial _tutorial = tutorialData.get();
- _tutorial.setTitle(tutorial.getTitle());
- _tutorial.setDescription(tutorial.getDescription());
- _tutorial.setPublished(tutorial.isPublished());
- return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
+
+ if (tutorialData.isPresent() && tutorialData.get().getUser().getId() == user.getId()) {
+
+ return new ResponseEntity(tutorialData.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
- @DeleteMapping("/tutorials/{id}")
+ @PutMapping("user/tutorial-update/{id}")
+ public ResponseEntity> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial){
+
+ User user = authenticationFacade.getUser();
+
+
+
+ Optional tutorialData = tutorialRepository.findById(id);
+ Map map = new LinkedHashMap();
+ if (tutorialData.isPresent()) {
+ Tutorial servTutorial = tutorialData.get();
+ servTutorial.setTitle(tutorial.getTitle());
+ servTutorial.setDescription(tutorial.getDescription());
+ servTutorial.setPublished(tutorial.isPublished());
+ servTutorial.setTobepublished(tutorial.isTobepublished());
+
+ try {
+
+ String imageFileName = FilesStorageServiceImpl.getFileNameFromUrl(tutorial.getTitleimage());
+ String servImageFileName = FilesStorageServiceImpl.getFileNameFromUrl(servTutorial.getTitleimage());
+
+ if(!servImageFileName.equals(imageFileName)){
+ filesStorageService.deletePublicFile(servImageFileName);
+
+ Path path= filesStorageService.moveFile(
+ user.getId().toString(),
+ tutorial.getTitleimage(),
+ String.format("Tutorial_%s",imageFileName )
+ );
+
+ servTutorial.setTitleimage(FilesRoutingHelper.getPublicImageUrl(path.getFileName().toString()));
+ }
+
+ servTutorial = tutorialRepository.save(servTutorial);
+ } catch (Exception e) {
+
+ map.put("status", 0);
+ map.put("message", e.getMessage());
+ return new ResponseEntity<>(map,HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ return new ResponseEntity<>(servTutorial, HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @DeleteMapping("user/tutorials/{id}")
public ResponseEntity deleteTutorial(@PathVariable("id") long id) {
try {
tutorialRepository.deleteById(id);
@@ -89,6 +215,64 @@ public class TutorialController {
}
}
+
+ //--------------------moderator methods----------------------------
+
+ @GetMapping("/moderator/tutorials")
+ public ResponseEntity> getBePublishedTutorials(@RequestParam(required = false) String title) {
+ try {
+ List tutorials = new ArrayList();
+
+ tutorialRepository.findBytobepublished(true).forEach(tutorials::add);
+
+ if (tutorials.isEmpty()) {
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+ return new ResponseEntity<>(tutorials, HttpStatus.OK);
+ } catch (Exception e) {
+ return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @GetMapping("moderator/tutorial-get/{id}")
+ public ResponseEntity getBePublishedTutorial(@PathVariable("id") long id) {
+ Optional tutorialData = tutorialRepository.findById(id);
+
+ if (tutorialData.isPresent()) {
+
+ return new ResponseEntity(tutorialData.get(), HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+ }
+
+ @PutMapping("moderator/tutorial-update/{id}")
+ public ResponseEntity> moderatorUpdateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
+ Optional tutorialData = tutorialRepository.findById(id);
+ Map map = new LinkedHashMap();
+ if (tutorialData.isPresent()) {
+ Tutorial servTutorial = tutorialData.get();
+ servTutorial.setTitle(tutorial.getTitle());
+ servTutorial.setDescription(tutorial.getDescription());
+ servTutorial.setPublished(tutorial.isPublished());
+ servTutorial.setTobepublished(tutorial.isTobepublished());
+ try {
+ servTutorial = tutorialRepository.save(servTutorial);
+ } catch (Exception e) {
+
+ map.put("status", 0);
+ map.put("message", e.getMessage());
+ return new ResponseEntity<>(map,HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ return new ResponseEntity<>(servTutorial, HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+ }
+
+/*
+
@DeleteMapping("/tutorials")
public ResponseEntity deleteAllTutorials() {
try {
@@ -113,5 +297,6 @@ public class TutorialController {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
+*/
}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java b/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java
index 50882c7..0b4e2f1 100644
--- a/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java
+++ b/src/main/java/com/jambotronGroup/jambotron/controllers/UsersController.java
@@ -1,17 +1,14 @@
package com.jambotronGroup.jambotron.controllers;
-
+import com.jambotronGroup.jambotron.model.Tutorial;
import com.jambotronGroup.jambotron.model.User;
import com.jambotronGroup.jambotron.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
+import java.util.*;
-import java.util.ArrayList;
-import java.util.List;
-
-@CrossOrigin(origins = "${app.origin}", maxAge = 3600, allowCredentials="true")
@RestController
@RequestMapping("/api")
public class UsersController {
@@ -35,4 +32,31 @@ public class UsersController {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
+
+ @PutMapping("users/{id}")
+ public ResponseEntity> updateUserRoles(@PathVariable("id") long id, @RequestBody User user) {
+ Optional userData = userRepository.findById(id);
+ Map map = new LinkedHashMap();
+ if (userData.isPresent()) {
+ User servUser = userData.get();
+ if(user.getRoles().size() > 0)
+ servUser.setRoles(user.getRoles());
+ else {
+ map.put("status", 0);
+ map.put("message", "User must have at least one role");
+ return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST);
+ }
+ try {
+ servUser = userRepository.save(servUser);
+ } catch (Exception e) {
+
+ map.put("status", 0);
+ map.put("message", e.getMessage());
+ return new ResponseEntity<>(map,HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ return new ResponseEntity<>(servUser, HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ }
+ }
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/exceptionHandlers/FileUploadExceptionHandler.java b/src/main/java/com/jambotronGroup/jambotron/exceptionHandlers/FileUploadExceptionHandler.java
new file mode 100644
index 0000000..9e69a0c
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/exceptionHandlers/FileUploadExceptionHandler.java
@@ -0,0 +1,18 @@
+package com.jambotronGroup.jambotron.exceptionHandlers;
+
+import com.jambotronGroup.jambotron.fileUpload.ResponseMessage;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+//@ControllerAdvice
+//public class FileUploadExceptionHandler extends ResponseEntityExceptionHandler {
+//
+// @ExceptionHandler(MaxUploadSizeExceededException.class)
+// public ResponseEntity handleMaxSizeException(MaxUploadSizeExceededException exc) {
+// return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage("File too large!"));
+// }
+//}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FileUploadExceptionAdvice.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FileUploadExceptionAdvice.java
new file mode 100644
index 0000000..080beb2
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FileUploadExceptionAdvice.java
@@ -0,0 +1,19 @@
+package com.jambotronGroup.jambotron.fileUpload;
+
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+public class FileUploadExceptionAdvice extends ResponseEntityExceptionHandler {
+
+
+// @ExceptionHandler(MaxUploadSizeExceededException.class)
+// public ResponseEntity handleMaxSizeException(MaxUploadSizeExceededException exc) {
+// return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage("File too large!"));
+// }
+}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
new file mode 100644
index 0000000..b56dd4e
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageService.java
@@ -0,0 +1,30 @@
+package com.jambotronGroup.jambotron.fileUpload;
+
+import org.springframework.core.io.Resource;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+public interface FilesStorageService {
+ public void init();
+
+ public String save(String userID,MultipartFile file);
+
+ public void save(MultipartFile file);
+
+ public Resource load(String filename);
+
+ public Resource loadUserImage(String userID, String filename);
+
+ public void deleteAll();
+
+ public Stream loadAll();
+
+ public Stream loadUserImages(String userID);
+
+ public Path moveFile(String userID, String url, String newFilename) throws Exception;
+
+ public void deletePublicFile(String filename);
+}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
new file mode 100644
index 0000000..b572440
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/FilesStorageServiceImpl.java
@@ -0,0 +1,186 @@
+package com.jambotronGroup.jambotron.fileUpload;
+
+
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.stereotype.Service;
+import org.springframework.util.FileSystemUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Stream;
+
+@Service
+public class FilesStorageServiceImpl implements FilesStorageService {
+
+// private final Path root = Paths.get("uploads/user-images/");
+//
+// private final Path rootPublic = Paths.get("uploads/public-images/");
+
+ // Update paths to use the Docker volume
+ private final Path root = Paths.get("/jambotron_data/uploads/user-images/");
+ private final Path rootPublic = Paths.get("/jambotron_data/uploads/public-images/");
+
+
+ @Override
+ public void init() {
+ try {
+ Files.createDirectories(root);
+ Files.createDirectories(rootPublic);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not initialize folder for upload!");
+ }
+ }
+
+ public static String getFileNameFromUrl(String urlString) throws Exception {
+ URL url = new URL(urlString); // Create a URL object
+ String path = url.getPath(); // Get the path from the URL
+ return path.substring(path.lastIndexOf('/') + 1); // Extract the file name
+ }
+
+ /**
+ * Moves a file from a user's directory to the public directory with a new name.
+ * StandardCopyOption.REPLACE_EXISTING
+ *
+ * @param userID The ID of the user owning the file.
+ * @param url The URL of the file to move.
+ * @param newFilename The new name for the file in the public directory.
+ * @return The path to the moved file in the public directory.
+ * @throws Exception If the file cannot be moved.
+ */
+ @Override
+ public Path moveFile(String userID, String url, String newFilename) throws Exception {
+
+ String filename = FilesStorageServiceImpl.getFileNameFromUrl(url);
+
+ try {
+ Path sourcePath = this.root.resolve(userID).resolve(filename);
+ Path targetPath = this.rootPublic.resolve(newFilename);
+ Files.copy(sourcePath, targetPath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
+
+ } catch (IOException e) {
+ throw new RuntimeException("Could not move the file: " + e.getMessage());
+ }
+
+ return this.rootPublic.resolve(newFilename);
+ }
+
+ @Override
+ public void deletePublicFile(String filename) {
+ try {
+ Path filePath = this.rootPublic.resolve(filename);
+ Files.deleteIfExists(filePath);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not delete the file: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Saves a file to a user's directory.
+ * uploads/user-images/{userID}/{filename}
+ *
+ * @param userID The ID of the user.
+ * @param file The file to save.
+ */
+ @Override
+ public String save(String userID,MultipartFile file) {
+ Path targetPath = null;
+ try {
+ Path path = this.root.resolve(userID);
+ path.toFile().mkdirs(); // Ensure user directory exists
+
+ targetPath = path.resolve(file.getOriginalFilename());
+
+ Files.copy(file.getInputStream(), targetPath,
+ java.nio.file.StandardCopyOption.REPLACE_EXISTING);
+
+ } catch (Exception e) {
+ if (e instanceof FileAlreadyExistsException) {
+ throw new RuntimeException("A file of that name already exists.");
+ }
+
+ throw new RuntimeException(e.getMessage());
+ }
+
+ return targetPath.getFileName().toString();
+ }
+
+ @Override
+ public void save(MultipartFile file) {
+ try {
+ Files.copy(file.getInputStream(), this.root.resolve(file.getOriginalFilename()),
+ java.nio.file.StandardCopyOption.REPLACE_EXISTING);
+ } catch (Exception e) {
+ if (e instanceof FileAlreadyExistsException) {
+ throw new RuntimeException("A file of that name already exists.");
+ }
+
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ @Override
+ public Resource load(String filename) {
+ try {
+ Path file = rootPublic.resolve(filename);
+ Resource resource = new UrlResource(file.toUri());
+
+ if (resource.exists() || resource.isReadable()) {
+ return resource;
+ } else {
+ throw new RuntimeException("Could not read the file!");
+ }
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Error: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public Resource loadUserImage(String userID,String filename) {
+ try {
+ Path file = root.resolve(filename);
+ Path userPath = this.root.resolve(userID);
+ file = userPath.resolve(filename);
+ Resource resource = new UrlResource(file.toUri());
+
+ if (resource.exists() || resource.isReadable()) {
+ return resource;
+ } else {
+ throw new RuntimeException("Could not read the file!");
+ }
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Error: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public void deleteAll() {
+ FileSystemUtils.deleteRecursively(root.toFile());
+ }
+
+ @Override
+ public Stream loadAll() {
+ try {
+ return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not load the files!");
+ }
+ }
+
+ @Override
+ public Stream loadUserImages(String userID) {
+ Path userPath = this.root.resolve(userID);
+ userPath.toFile().mkdirs();
+ try {
+ return Files.walk(userPath, 1).filter(path -> !path.equals(userPath)).map(userPath::relativize);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not load the files!");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseImageUploadResult.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseImageUploadResult.java
new file mode 100644
index 0000000..26d2f09
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseImageUploadResult.java
@@ -0,0 +1,21 @@
+package com.jambotronGroup.jambotron.fileUpload;
+
+import com.jambotronGroup.jambotron.model.FileInfo;
+
+public class ResponseImageUploadResult extends ResponseMessage {
+ private FileInfo fileInfo;
+
+
+ public ResponseImageUploadResult(String message, FileInfo fileInfo) {
+ super(message);
+ this.fileInfo = fileInfo;
+ }
+
+ public FileInfo getFileInfo() {
+ return this.fileInfo;
+ }
+
+ public void setFileInfo(FileInfo fileInfo) {
+ this.fileInfo = fileInfo;
+ }
+}
diff --git a/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseMessage.java b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseMessage.java
new file mode 100644
index 0000000..d10fc55
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/fileUpload/ResponseMessage.java
@@ -0,0 +1,20 @@
+package com.jambotronGroup.jambotron.fileUpload;
+
+public class ResponseMessage {
+ private String message;
+
+
+
+ public ResponseMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/model/Bean.java b/src/main/java/com/jambotronGroup/jambotron/model/Bean.java
index c51da42..4df2633 100644
--- a/src/main/java/com/jambotronGroup/jambotron/model/Bean.java
+++ b/src/main/java/com/jambotronGroup/jambotron/model/Bean.java
@@ -20,6 +20,10 @@ public class Bean {
private String typeShortName;
private String scope;
+ public Bean() {
+ // Default constructor
+ }
+
public Bean(String name, String type, String scope) {
this.name = name;
this.type = type;
diff --git a/src/main/java/com/jambotronGroup/jambotron/model/DataBaseSettings.java b/src/main/java/com/jambotronGroup/jambotron/model/DataBaseSettings.java
index 7c8d2dd..5b7b3ba 100644
--- a/src/main/java/com/jambotronGroup/jambotron/model/DataBaseSettings.java
+++ b/src/main/java/com/jambotronGroup/jambotron/model/DataBaseSettings.java
@@ -23,7 +23,9 @@ public class DataBaseSettings {
private String password;
-
+ public DataBaseSettings() {
+ // Default constructor
+ }
public DataBaseSettings(String name) {
this.name = name;
diff --git a/src/main/java/com/jambotronGroup/jambotron/model/FileInfo.java b/src/main/java/com/jambotronGroup/jambotron/model/FileInfo.java
new file mode 100644
index 0000000..83ca3ef
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/model/FileInfo.java
@@ -0,0 +1,27 @@
+package com.jambotronGroup.jambotron.model;
+
+public class FileInfo {
+ private String name;
+ private String url;
+
+ public FileInfo(String name, String url) {
+ this.name = name;
+ this.url = url;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+}
diff --git a/src/main/java/com/jambotronGroup/jambotron/model/NameValueItem.java b/src/main/java/com/jambotronGroup/jambotron/model/NameValueItem.java
index b7e66c4..53a6825 100644
--- a/src/main/java/com/jambotronGroup/jambotron/model/NameValueItem.java
+++ b/src/main/java/com/jambotronGroup/jambotron/model/NameValueItem.java
@@ -6,6 +6,10 @@ import jakarta.persistence.Id;
@Entity
public class NameValueItem {
+ public NameValueItem() {
+ // Default constructor
+ }
+
public NameValueItem(String name, String value){
_name = name;
_value = value;
diff --git a/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java b/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java
index 0c92d84..d969305 100644
--- a/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java
+++ b/src/main/java/com/jambotronGroup/jambotron/model/Tutorial.java
@@ -1,11 +1,14 @@
package com.jambotronGroup.jambotron.model;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
+import java.sql.Timestamp;
@Entity
@Table(name = "tutorials")
+@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Tutorial {
@Id
@@ -21,14 +24,39 @@ public class Tutorial {
@Column(name = "published")
private boolean published;
+ @Column(name = "tobepublished")
+ private boolean tobepublished;
+
+ @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
+ @JoinColumn(name = "userID", nullable = false)
+ private User user;
+
+ @Column(name = "created")
+ private java.sql.Timestamp created;
+
+ @Column(name = "modified")
+ private java.sql.Timestamp modified;
+
+ @Column(name = "titleimage")
+ private String titleimage;
+
+ @Column(name = "body")
+ private String body;
+
public Tutorial() {
}
- public Tutorial(String title, String description, boolean published) {
+ public Tutorial(String title, String description, boolean published, boolean tobepublished, User user, Timestamp created, Timestamp modified, String titleimage, String body) {
this.title = title;
this.description = description;
this.published = published;
+ this.tobepublished = tobepublished;
+ this.user = user;
+ this.created = created;
+ this.modified = modified;
+ this.titleimage = titleimage;
+ this.body = body;
}
public long getId() {
@@ -59,8 +87,52 @@ public class Tutorial {
this.published = isPublished;
}
+ public boolean isTobepublished() {
+ return tobepublished;
+ }
+
+ public void setTobepublished(boolean tobepublished) {
+ this.tobepublished = tobepublished;
+ }
+
+ public void setCreated(java.sql.Timestamp created) {
+ this.created = created;
+ }
+
+ public java.sql.Timestamp getCreated() {
+ return created;
+ }
+
+ public void setModified(java.sql.Timestamp modified) {
+ this.modified = modified;
+ }
+
+ public java.sql.Timestamp getModified() {
+ return modified;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
}
+
+ public String getTitleimage() {
+ return titleimage;
+ }
+
+ public void setTitleimage(String titleimage) {
+ this.titleimage = titleimage;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/model/User.java b/src/main/java/com/jambotronGroup/jambotron/model/User.java
index cc0843a..ea22f3c 100644
--- a/src/main/java/com/jambotronGroup/jambotron/model/User.java
+++ b/src/main/java/com/jambotronGroup/jambotron/model/User.java
@@ -19,6 +19,7 @@ import java.util.Set;
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
private long id;
@Column(name = "username")
diff --git a/src/main/java/com/jambotronGroup/jambotron/payload/request/SaveZhipuAiImageRequest.java b/src/main/java/com/jambotronGroup/jambotron/payload/request/SaveZhipuAiImageRequest.java
new file mode 100644
index 0000000..5b41f0d
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/payload/request/SaveZhipuAiImageRequest.java
@@ -0,0 +1,18 @@
+package com.jambotronGroup.jambotron.payload.request;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class SaveZhipuAiImageRequest {
+ @NotBlank
+ private String imageUrl;
+
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
+ }
+
+
+}
diff --git a/src/main/java/com/jambotronGroup/jambotron/payload/response/UserInfoResponse.java b/src/main/java/com/jambotronGroup/jambotron/payload/response/UserInfoResponse.java
index 708470a..50ccb62 100644
--- a/src/main/java/com/jambotronGroup/jambotron/payload/response/UserInfoResponse.java
+++ b/src/main/java/com/jambotronGroup/jambotron/payload/response/UserInfoResponse.java
@@ -10,11 +10,14 @@ public class UserInfoResponse {
private String email;
private List roles;
- public UserInfoResponse(Long id, String username, String email, List roles) {
+ private String token;
+
+ public UserInfoResponse(Long id, String username, String email, List roles, String token) {
this.id = id;
this.username = username;
this.email = email;
this.roles = roles;
+ this.token = token;
}
public Long getId() {
@@ -44,4 +47,12 @@ public class UserInfoResponse {
public List getRoles() {
return roles;
}
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java b/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java
index df061a0..0709372 100644
--- a/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java
+++ b/src/main/java/com/jambotronGroup/jambotron/repository/TutorialRepository.java
@@ -10,6 +10,11 @@ import java.util.List;
@Repository
public interface TutorialRepository extends JpaRepository {
+
+ List findByUserId(Long userId);
+ List findByUserIdAndTitle(Long userId, String title);
List findByPublished(boolean published);
+ List findBytobepublished(boolean tobepublished);
+ List findByIdAndTobepublished(Long id,boolean tobepublished);
List findByTitleContaining(String title);
}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/repository/UserRepository.java b/src/main/java/com/jambotronGroup/jambotron/repository/UserRepository.java
index 0b9d998..7701ee0 100644
--- a/src/main/java/com/jambotronGroup/jambotron/repository/UserRepository.java
+++ b/src/main/java/com/jambotronGroup/jambotron/repository/UserRepository.java
@@ -9,6 +9,8 @@ import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository {
+
+
Optional findByUsername(String username);
Boolean existsByUsername(String username);
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/AuthenticationFacade.java b/src/main/java/com/jambotronGroup/jambotron/security/AuthenticationFacade.java
new file mode 100644
index 0000000..7cbfc2a
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/security/AuthenticationFacade.java
@@ -0,0 +1,62 @@
+package com.jambotronGroup.jambotron.security;
+
+import com.jambotronGroup.jambotron.controllers.AuthController;
+import com.jambotronGroup.jambotron.model.User;
+import com.jambotronGroup.jambotron.repository.UserRepository;
+import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+public class AuthenticationFacade implements IAuthenticationFacade {
+
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthenticationFacade.class);
+
+ @Autowired
+ UserRepository userRepository;
+
+ @Override
+ public Authentication getAuthentication() {
+
+ return SecurityContextHolder.getContext().getAuthentication();
+ }
+
+
+ //Deprecated method, use getUser() instead
+ @Override
+ public UserDetailsImpl getUserDetails() {
+
+ logger.warn("Retrieving user details from the security context");
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication == null || !authentication.isAuthenticated()) {
+ throw new IllegalStateException("No authenticated user found");
+ }
+ if (!(authentication.getPrincipal() instanceof UserDetails)) {
+
+ throw new IllegalStateException("Authentication principal is not an instance of UserDetails");
+ }
+ UserDetails userDetails = (UserDetails) authentication.getPrincipal();
+ if (!(userDetails instanceof UserDetailsImpl)) {
+ throw new IllegalStateException("UserDetails is not an instance of UserDetailsImpl");
+ }
+ return (UserDetailsImpl) userDetails;
+ }
+
+ @Override
+ public com.jambotronGroup.jambotron.model.User getUser() {
+
+ User returnValue = userRepository.findById(this.getUserDetails().getId()).get();
+
+ return returnValue;
+ }
+
+}
+
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/IAuthenticationFacade.java b/src/main/java/com/jambotronGroup/jambotron/security/IAuthenticationFacade.java
new file mode 100644
index 0000000..c4f530d
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/security/IAuthenticationFacade.java
@@ -0,0 +1,13 @@
+package com.jambotronGroup.jambotron.security;
+
+import com.jambotronGroup.jambotron.model.User;
+import com.jambotronGroup.jambotron.security.services.UserDetailsImpl;
+import org.springframework.security.core.Authentication;
+
+public interface IAuthenticationFacade {
+ Authentication getAuthentication();
+
+ UserDetailsImpl getUserDetails();
+
+ User getUser();
+}
\ No newline at end of file
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/MyCorsFilterConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/MyCorsFilterConfig.java
new file mode 100644
index 0000000..20ef6a0
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/security/MyCorsFilterConfig.java
@@ -0,0 +1,41 @@
+package com.jambotronGroup.jambotron.security;
+
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Component;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.io.IOException;
+/*
+
+@Configuration
+public class MyCorsFilterConfig extends CorsFilter {
+
+ public MyCorsFilterConfig(CorsConfigurationSource source) {
+ super((CorsConfigurationSource) source);
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ response.addHeader("Access-Control-Allow-Headers",
+ "Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
+ if (response.getHeader("Access-Control-Allow-Origin") == null)
+ response.addHeader("Access-Control-Allow-Origin", "http://localhost:4200");
+
+ if(!request.getRequestURI().startsWith("/api/auth")) {
+ response.addHeader("Access-Control-Allow-Credentials", "true");
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+}
+*/
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/RestConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/RestConfig.java
new file mode 100644
index 0000000..9dcc696
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/security/RestConfig.java
@@ -0,0 +1,43 @@
+package com.jambotronGroup.jambotron.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
+/*
+@Configuration
+public class RestConfig {
+
+ @Bean
+ public MyCorsFilterConfig corsFilter() {
+ CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowCredentials(true);
+ config.addAllowedOrigin("http://localhost:4200,http://www.jambotron.run.place");
+ config.addAllowedMethod(HttpMethod.DELETE);
+ config.addAllowedMethod(HttpMethod.GET);
+ config.addAllowedMethod(HttpMethod.OPTIONS);
+ config.addAllowedMethod(HttpMethod.PUT);
+ config.addAllowedMethod(HttpMethod.POST);
+ // ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", config);
+
+
+
+ config = new CorsConfiguration();
+ config.setAllowCredentials(false);
+ config.setAllowedOrigins(List.of("http://localhost:4200","http://www.jambotron.run.place"));
+ config.addAllowedMethod(HttpMethod.DELETE);
+ config.addAllowedMethod(HttpMethod.GET);
+ config.addAllowedMethod(HttpMethod.OPTIONS);
+ config.addAllowedMethod(HttpMethod.PUT);
+ config.addAllowedMethod(HttpMethod.POST);
+ config.addAllowedHeader("*");
+ ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("api/auth/**", config);
+
+ return new MyCorsFilterConfig(source);
+ }
+}*/
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
index 271c865..08bda7f 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/WebSecurityConfig.java
@@ -5,20 +5,32 @@ import com.jambotronGroup.jambotron.security.jwt.AuthTokenFilter;
import com.jambotronGroup.jambotron.security.services.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.springframework.security.config.Customizer.withDefaults;
+
@Configuration
@EnableMethodSecurity
//@EnableWebSecurity
@@ -26,13 +38,20 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// securedEnabled = true,
// jsr250Enabled = true,
//prePostEnabled = true)
+@ComponentScan("com.jambotronGroup.jambotron.security")
public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecurityConfigurerAdapter {
+
+ @Autowired
+ private Environment _environment;
+
@Autowired
UserDetailsServiceImpl userDetailsService;
@Autowired
private AuthEntryPointJwt unauthorizedHandler;
+
+
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
@@ -43,10 +62,7 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
// authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
// }
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- // Do not add any mappings to enable complete disabling of CORS.
- }
+
@Bean
public DaoAuthenticationProvider authenticationProvider() {
@@ -57,7 +73,6 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
return authProvider;
}
-
// @Bean
// @Override
// public AuthenticationManager authenticationManagerBean() throws Exception {
@@ -74,71 +89,70 @@ public class WebSecurityConfig implements WebMvcConfigurer {// extends WebSecuri
return new BCryptPasswordEncoder();
}
-// @Override
-// protected void configure(HttpSecurity http) throws Exception {
-// http
-// .csrf()
-// .disable()
-// .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
-// .authorizeRequests()
-//
-//
-// // //Доступ только для пользователей с ролью Администратор
-// //.antMatchers("/api/users").hasRole("ADMIN")
-// // .antMatchers("/news").hasRole("USER")
-// //Доступ разрешен всем пользователей
-// .antMatchers("/*", "/home", "/resources/**").permitAll()
-// .antMatchers("/api/auth/**").permitAll()
-// .antMatchers("/api/tutorials").permitAll()
-// .antMatchers("/api/tutorials/**").permitAll()
-// //.antMatchers("/api/test/**").permitAll()
-// //Все остальные страницы требуют аутентификации
-// .anyRequest().authenticated()
-// .and()
-// .sessionManagement()
-// .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
-// .and()
-// .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
-//
-//
-// //http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
-// }
+
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.csrf(csrf -> csrf.disable())
+ http.csrf(csrf -> csrf.disable())//cors.configurationSource(corsCongigSource()))
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
- .authorizeHttpRequests(auth ->
- auth.requestMatchers("/api/auth/**").permitAll()
- .requestMatchers("/*", "/home", "/resources/**").permitAll()
- .requestMatchers("/resources/public/media/**").permitAll()
- .requestMatchers("/resources/public/browser/**").permitAll()
- .requestMatchers("/media/**").permitAll()
+ .authorizeHttpRequests(auth ->auth
+ // Access without authentication
+ // Allow public access to the root and home pages
+ .requestMatchers("/*").permitAll()
+ .requestMatchers("/resources/**").permitAll()
- .requestMatchers("/main/**").permitAll()
- .requestMatchers("/api/test/**").permitAll()
- .requestMatchers("/api/tutorials").permitAll()
+ .requestMatchers("/tutorials-images/**").permitAll()
- .requestMatchers("/api/zhipuai/image/**").permitAll()
+ .requestMatchers("/api/file/files").permitAll()
+ .requestMatchers("/api/file/upload").permitAll()
- .requestMatchers("/api/users").permitAll()
- .requestMatchers("/api/users/**").permitAll()
+ .requestMatchers("/files/**").permitAll()
- .requestMatchers("/api/roles").permitAll()
- .requestMatchers("/api/roles/**").permitAll()
+ .requestMatchers("/api/auth/**").permitAll()
+ // Allow public access to tutorials (without login)
+ .requestMatchers("/api/public/**").permitAll()
+ //.requestMatchers("/api/public/tutorials").permitAll()
+ //.requestMatchers("/api/public/tutorials/**").permitAll()
+ //TODO: need check the puth
+ .requestMatchers("/api/public/zhipuai/image/**").permitAll()
- .requestMatchers("/api/tutorials/**").permitAll()
- .requestMatchers("/api/settings").permitAll()
- .requestMatchers("/api/system/**").permitAll()
- .anyRequest().authenticated()
+ // Access permitted for specific roles
+ .requestMatchers("/api/user/**").hasRole("USER")
+ .requestMatchers("/api/moderator/**").hasRole("MODERATOR")
+ .requestMatchers("/api/admin/**").hasRole("ADMIN")
+
+ .anyRequest().authenticated()
+
);
+ if(_environment.getProperty("server.port") != null &&
+ _environment.getProperty("server.port").equalsIgnoreCase("443")) {
+ http.redirectToHttps(withDefaults());
+ }
+
http.authenticationProvider(authenticationProvider());
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ exposeDirectory("tutorials-images", registry);
+ }
+
+ private void exposeDirectory(String dirName, ResourceHandlerRegistry registry) {
+ Path uploadDir = Paths.get(dirName);
+ String uploadPath = uploadDir.toFile().getAbsolutePath();
+
+ if (dirName.startsWith("../")) dirName = dirName.replace("../", "");
+
+ registry.addResourceHandler("/" + dirName + "/**").addResourceLocations("file:/"+ uploadPath + "/");
+ }
+
}
+
+
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java
index cf238df..c307684 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthEntryPointJwt.java
@@ -25,7 +25,9 @@ public class AuthEntryPointJwt implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
logger.error("Unauthorized error: {}", authException.getMessage());
+ logger.error("Unauthorized error to resource {}", request.getRequestURI());
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java
index fe7e239..9ed7fc1 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/jwt/AuthTokenFilter.java
@@ -9,7 +9,9 @@ import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@@ -19,6 +21,9 @@ import java.io.IOException;
public class AuthTokenFilter extends OncePerRequestFilter {
+
+ @Autowired
+ AuthenticationManager authenticationManager;
@Autowired
private JwtUtils jwtUtils;
@@ -35,8 +40,15 @@ public class AuthTokenFilter extends OncePerRequestFilter {
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
+ logger.warn("Username from JWT: {}", username);
+
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
+// Authentication authentication = authenticationManager.authenticate(
+// new UsernamePasswordAuthenticationToken(userDetails.getUsername(),userDetails.getPassword()));
+
+
+
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails,
null,
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java
index f29733f..07ca4b6 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsImpl.java
@@ -40,6 +40,8 @@ public class UserDetailsImpl implements UserDetails {
.map(role -> new SimpleGrantedAuthority(role.getName().name()))
.collect(Collectors.toList());
+
+
return new UserDetailsImpl(
user.getId(),
user.getUsername(),
diff --git a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java
index 1747be3..b23a0f1 100644
--- a/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java
+++ b/src/main/java/com/jambotronGroup/jambotron/security/services/UserDetailsServiceImpl.java
@@ -23,6 +23,8 @@ public class UserDetailsServiceImpl implements UserDetailsService {
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
return UserDetailsImpl.build(user);
+// UserDetailsImpl userDetails = UserDetailsImpl.build(user);
+// return new org.springframework.security.core.userdetails.User(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
}
}
diff --git a/src/main/java/com/jambotronGroup/jambotron/utils/DateTimeHelper.java b/src/main/java/com/jambotronGroup/jambotron/utils/DateTimeHelper.java
new file mode 100644
index 0000000..930bc47
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/utils/DateTimeHelper.java
@@ -0,0 +1,18 @@
+package com.jambotronGroup.jambotron.utils;
+
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+public class DateTimeHelper {
+
+ public static SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ public static java.sql.Timestamp parseTimestamp(String timestamp) {
+ try {
+ return new Timestamp(DATE_TIME_FORMAT.parse(timestamp).getTime());
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+}
diff --git a/src/main/java/com/jambotronGroup/jambotron/utils/FilesRoutingHelper.java b/src/main/java/com/jambotronGroup/jambotron/utils/FilesRoutingHelper.java
new file mode 100644
index 0000000..49e9a66
--- /dev/null
+++ b/src/main/java/com/jambotronGroup/jambotron/utils/FilesRoutingHelper.java
@@ -0,0 +1,25 @@
+package com.jambotronGroup.jambotron.utils;
+
+import com.jambotronGroup.jambotron.controllers.FilesController;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
+
+@Component
+public class FilesRoutingHelper {
+ /**
+ * Generates a URL for accessing a user's image.
+ * format: /api/user/user-images/{filename:.+}
+ *
+ * @param filename The name of the file.
+ * @return The URL to access the file.
+ */
+ public static String getUserImageUrl(String filename) {
+ return MvcUriComponentsBuilder
+ .fromMethodName(FilesController.class, "getUserImage", filename).build().toString();
+ }
+
+ public static String getPublicImageUrl(String filename) {
+ return MvcUriComponentsBuilder
+ .fromMethodName(FilesController.class, "getFile", filename).build().toString();
+ }
+}
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
new file mode 100644
index 0000000..ec0b3a8
--- /dev/null
+++ b/src/main/resources/application-dev.properties
@@ -0,0 +1,43 @@
+spring.config.import=classpath:/yml_properties/application_development.yml
+
+spring.application.name=jambotron
+
+server.port=8082
+#============Localhost Configurations========================
+
+spring.datasource.url= jdbc:postgresql://localhost:5432/jambotronDB
+spring.datasource.username= admin
+spring.datasource.password= postgrespw
+
+spring.flyway.baseline-on-migrate=true
+spring.flyway.validate-on-migrate=true
+
+spring.flyway.url=jdbc:postgresql://localhost:5432/jambotronDB
+spring.flyway.user=admin
+spring.flyway.password=postgrespw
+
+#============jpa=====================
+
+spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect
+# Hibernate ddl auto (create, create-drop, validate, update)
+#spring.jpa.hibernate.ddl-auto= update
+spring.jpa.show-sql=true
+
+#=============Zhipuai Configurations========================
+
+spring.ai.zhipuai.base-url=https://open.bigmodel.cn/api/paas/v4/images/generations
+spring.ai.zhipuai.api-key = 628447c8c65845a48a7226391464a2ea.Dw8ci6TiW0BRF5LI
+
+
+#============ Custom App Properties
+app.jwtSecret= ======================spring=back====================
+app.jwtExpirationMs= 800000
+app.jwtCookieName=springangularts
+
+#=============File Upload Configurations========================
+spring.servlet.multipart.max-file-size=50MB
+spring.servlet.multipart.max-request-size=50MB
+
+
+server.tomcat.max-swallow-size=100MB
+#=============
\ No newline at end of file
diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties
new file mode 100644
index 0000000..c171f29
--- /dev/null
+++ b/src/main/resources/application-prod.properties
@@ -0,0 +1,66 @@
+spring.config.import=classpath:/yml_properties/application_production.yml
+
+spring.application.name=jambotron
+
+server.port=443
+#============Localhost Configurations========================
+
+spring.datasource.url= jdbc:postgresql://localhost:5432/jambotronDB
+spring.datasource.username= admin
+spring.datasource.password= postgrespw
+
+spring.flyway.baseline-on-migrate=true
+spring.flyway.validate-on-migrate=true
+
+spring.flyway.url=jdbc:postgresql://localhost:5432/jambotronDB
+spring.flyway.user=admin
+spring.flyway.password=postgrespw
+
+
+#============Koyeb Configurations========================
+
+#spring.datasource.url= jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app/jambotronDB
+#spring.datasource.username= koyeb-adm
+#spring.datasource.password= npg_HfFEUA7bay1i
+#
+#spring.flyway.baseline-on-migrate=true
+#spring.flyway.validate-on-migrate=true
+#
+#spring.flyway.url=jdbc:postgresql://ep-red-breeze-a2cxhq5f.eu-central-1.pg.koyeb.app/jambotronDB
+#spring.flyway.user=koyeb-adm
+#spring.flyway.password=npg_HfFEUA7bay1i
+#
+
+#spring.datasource.url=${SPRING_DATASOURCE_URL}
+#spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
+#spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
+
+##============jpa=====================
+
+#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
+spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect
+
+# Hibernate ddl auto (create, create-drop, validate, update)
+#spring.jpa.hibernate.ddl-auto= update
+
+spring.jpa.show-sql=true
+
+#=============Zhipuai Configurations========================
+
+spring.ai.zhipuai.base-url=https://open.bigmodel.cn/api/paas/v4/images/generations
+spring.ai.zhipuai.api-key = 628447c8c65845a48a7226391464a2ea.Dw8ci6TiW0BRF5LI
+
+#==================Custom App Properties=====================
+
+app.jwtSecret= ======================spring=back====================
+app.jwtExpirationMs= 800000
+app.jwtCookieName=springangularts
+
+
+#=============File Upload Configurations========================
+spring.servlet.multipart.max-file-size=50MB
+spring.servlet.multipart.max-request-size=50MB
+
+server.tomcat.max-swallow-size=100MB
+
+#=============
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index fe7b370..39634b2 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,52 +1,4 @@
-spring.application.name=jambotron
+#bootJar with profile prod
+#spring.profiles.active=prod
-spring.datasource.url= jdbc:postgresql://localhost:5432/jambotronDB
-spring.datasource.username= admin
-spring.datasource.password= postgrespw
-
-#spring.datasource.url=${SPRING_DATASOURCE_URL}
-#spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
-#spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
-
-
-#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
-spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect
-
-# Hibernate ddl auto (create, create-drop, validate, update)
-#spring.jpa.hibernate.ddl-auto= update
-
-spring.jpa.show-sql=true
-
-
-# App Properties
-app.jwtSecret= ======================spring=back====================
-app.jwtExpirationMs= 30000
-app.jwtCookieName=springangularts
-app.origin=http://localhost:4200
-
-spring.mvc.throw-exception-if-no-handler-found=true
-
-# Disable the default mappings
-#spring.resources.add-mappings=false
-
-# UI(index.html) location
-#spring.resources.static-locations=classpath:/public/browser/
-#
-#spring.resources.cache.period=31557600s # 1 year if you want
-#spa.default-file=classpath:/public/browser/index.html
-#
-
-spring.docker.compose.file=../Docker/docker-compose.yml
-
-spring.flyway.baseline-on-migrate=true
-spring.flyway.validate-on-migrate=true
-
-spring.flyway.user=admin
-spring.flyway.password=postgrespw
-spring.flyway.url=jdbc:postgresql://localhost:5432/jambotronDB
-
-
-
-
-spring.ai.zhipuai.base-url=https://open.bigmodel.cn/api/paas/v4/images/generations
-spring.ai.zhipuai.api-key = 628447c8c65845a48a7226391464a2ea.Dw8ci6TiW0BRF5LI
+spring.profiles.active=dev
diff --git a/src/main/resources/db/migration/V1__Init.sql b/src/main/resources/db/migration/V1__Init.sql
index 4480b4e..9132d24 100644
--- a/src/main/resources/db/migration/V1__Init.sql
+++ b/src/main/resources/db/migration/V1__Init.sql
@@ -1,7 +1,9 @@
+
+
CREATE TABLE IF NOT EXISTS roles
(
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
- name character varying(20) COLLATE pg_catalog."default"
+ name character varying(20)
);
diff --git a/src/main/resources/db/migration/V7__add_column_userid_to_tutorials.sql b/src/main/resources/db/migration/V7__add_column_userid_to_tutorials.sql
new file mode 100644
index 0000000..e579d78
--- /dev/null
+++ b/src/main/resources/db/migration/V7__add_column_userid_to_tutorials.sql
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS public.tutorials;
+
+CREATE TABLE IF NOT EXISTS public.tutorials
+(
+ description character varying(255) COLLATE pg_catalog."default",
+ published boolean,
+ title character varying(255) COLLATE pg_catalog."default",
+ id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
+ userid bigint,
+ CONSTRAINT "tutorial_user_FK" FOREIGN KEY (userid)
+ REFERENCES public.users (id) MATCH SIMPLE
+ ON UPDATE NO ACTION
+ ON DELETE NO ACTION
+ NOT VALID
+)
+
+TABLESPACE pg_default;
+
+
+-- Index: fki_tutorial_user_FK
+
+-- DROP INDEX IF EXISTS public."fki_tutorial_user_FK";
+
+CREATE INDEX IF NOT EXISTS "fki_tutorial_user_FK"
+ ON public.tutorials USING btree
+ (userid ASC NULLS LAST)
+ TABLESPACE pg_default;
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V8__tutorial_remake.sql b/src/main/resources/db/migration/V8__tutorial_remake.sql
new file mode 100644
index 0000000..7d58047
--- /dev/null
+++ b/src/main/resources/db/migration/V8__tutorial_remake.sql
@@ -0,0 +1,19 @@
+DROP TABLE tutorials;
+
+CREATE TABLE tutorials
+(
+ id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
+ published boolean,
+ tobepublished boolean,
+ title character varying(255) COLLATE pg_catalog."default",
+ userid bigint,
+ created timestamp with time zone,
+ modified timestamp with time zone,
+ description character varying(255) COLLATE pg_catalog."default",
+ CONSTRAINT tutorials_title_key UNIQUE (title),
+ CONSTRAINT "tutorial_user_FK" FOREIGN KEY (userid)
+ REFERENCES public.users (id) MATCH SIMPLE
+ ON UPDATE NO ACTION
+ ON DELETE NO ACTION
+)
+
diff --git a/src/main/resources/db/migration/V9__title_image.sql b/src/main/resources/db/migration/V9__title_image.sql
new file mode 100644
index 0000000..862d17f
--- /dev/null
+++ b/src/main/resources/db/migration/V9__title_image.sql
@@ -0,0 +1,9 @@
+ALTER TABLE IF EXISTS public.tutorials DROP COLUMN IF EXISTS titleimage;
+
+ALTER TABLE IF EXISTS public.tutorials
+ ADD COLUMN titleimage text COLLATE pg_catalog."default";
+
+ALTER TABLE IF EXISTS public.tutorials DROP COLUMN IF EXISTS body;
+
+ALTER TABLE IF EXISTS public.tutorials
+ ADD COLUMN body text COLLATE pg_catalog."default";
\ No newline at end of file
diff --git a/application.yml b/src/main/resources/yml_properties/application_development.yml
similarity index 99%
rename from application.yml
rename to src/main/resources/yml_properties/application_development.yml
index 733c6a1..f7d3542 100644
--- a/application.yml
+++ b/src/main/resources/yml_properties/application_development.yml
@@ -10,3 +10,4 @@ logging:
name: logs/application.log
max-size: 10MB
max-history: 30
+
diff --git a/src/main/resources/yml_properties/application_production.yml b/src/main/resources/yml_properties/application_production.yml
new file mode 100644
index 0000000..43fb0bf
--- /dev/null
+++ b/src/main/resources/yml_properties/application_production.yml
@@ -0,0 +1,19 @@
+logging:
+ level:
+ root: INFO
+ com.jambotronGroup.jambotron: DEBUG
+ org.springframework: WARN
+ pattern:
+ console: "%d{yyyy-MM-dd HH:mm:ss} %-5level - %msg%n"
+ file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
+ file:
+ name: logs/application.log
+ max-size: 10MB
+ max-history: 30
+
+server:
+ ssl:
+ enabled: ${SSL_ENABLED:false} # Default to true if SSL_ENABLED is not set
+ certificate: ${FULLCHAINPEM:""} # Default to empty string if FULLCHAINPEM is not set
+ certificate-private-key: ${PRIVKEYPEM:""} # Default to empty string if PRIVKEYPEM is not set
+# port: ${SERVER_PORT:443} # Default to 443 if SERVER_PORT is not set