commit a276ac2318d2a704615f1074dfa7b1fa1abe9463 Author: DahoudG <87090717+gbanedahoud@users.noreply.github.com> Date: Sun Dec 8 11:08:24 2024 +0000 feat: Commit initial avec application Quarkus JSF et page d'accueil - Ajout de la configuration JSF/Quarkus - Ajout des contrôleurs et composants JSF - Ajout de la page d'accueil avec animations - Configuration Docker et environnement de build diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..94810d0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0d1c8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,107 @@ +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea/ +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml +nbproject/ +nbactions.xml + +# Visual Studio Code +.vscode/ +.factorypath + +# OSX +.DS_Store + +# Windows +Thumbs.db +Desktop.ini + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env +.env.local +.env.* + +# Quarkus +.quarkus/ +/.quarkus/cli/plugins/ +quarkus.log +hs_err_pid* + +# Application Specific +.certs/ +.certificates/ +*.jks +*.p12 + +# Logs +*.log +log/ +logs/ + +# Temporary files +*~ +*.bak +*.tmp +temp/ + +# Node (in case you add frontend tools later) +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log + +# Build output +dist/ +build/ +out/ + +# Application specific properties +src/main/resources/application-*.properties +!src/main/resources/application.properties + +# Compiled files +*.class +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# Test coverage +/coverage/ +.nyc_output/ + +# Maven Wrapper +.mvn/ +mvnw +mvnw.cmd + +# Docker +.dockerignore \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..10fdc55 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# lionsdev-client-impl-quarkus + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: + +```shell script +./mvnw quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at . + +## Packaging and running the application + +The application can be packaged using: + +```shell script +./mvnw package +``` + +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: + +```shell script +./mvnw package -Dquarkus.package.jar.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: + +```shell script +./mvnw package -Dnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: + +```shell script +./mvnw package -Dnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/lionsdev-client-impl-quarkus-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult . + +## Related Guides + +- PrimeFaces ([guide](https://quarkiverse.github.io/quarkiverse-docs/quarkus-primefaces/dev/)): PrimeFaces - lets you utilize PrimeFaces and PF Extensions to make JavaServer Faces (JSF) development so much easier! diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..03aa493 --- /dev/null +++ b/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + dev.lions + lionsdev-client-impl-quarkus + 1.0.0-SNAPSHOT + + + 3.13.0 + 17 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.6.4 + true + 3.5.0 + 4.0.1 + 13.0.4 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + + io.quarkus + quarkus-undertow + + + io.quarkus + quarkus-arc + + + + + org.apache.myfaces.core + myfaces-api + ${myfaces.version} + + + org.apache.myfaces.core + myfaces-impl + ${myfaces.version} + + + + + io.quarkiverse.primefaces + quarkus-primefaces + 3.14.0 + + + + + io.quarkus + quarkus-junit5 + test + + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + \ No newline at end of file diff --git a/src/main/docker/Dockerfile.jvm b/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..69733e6 --- /dev/null +++ b/src/main/docker/Dockerfile.jvm @@ -0,0 +1,97 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/lionsdev-client-impl-quarkus-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/lionsdev-client-impl-quarkus-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/lionsdev-client-impl-quarkus-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.20 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] + diff --git a/src/main/docker/Dockerfile.legacy-jar b/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..3710c02 --- /dev/null +++ b/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,93 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/lionsdev-client-impl-quarkus-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/lionsdev-client-impl-quarkus-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/lionsdev-client-impl-quarkus-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.20 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/src/main/docker/Dockerfile.native b/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..32a8425 --- /dev/null +++ b/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/lionsdev-client-impl-quarkus . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/lionsdev-client-impl-quarkus +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/src/main/docker/Dockerfile.native-micro b/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..6452654 --- /dev/null +++ b/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,30 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/lionsdev-client-impl-quarkus . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/lionsdev-client-impl-quarkus +# +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/src/main/java/dev/lions/config/JSFConfiguration.java b/src/main/java/dev/lions/config/JSFConfiguration.java new file mode 100644 index 0000000..2cde36f --- /dev/null +++ b/src/main/java/dev/lions/config/JSFConfiguration.java @@ -0,0 +1,9 @@ +package dev.lions.config; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.faces.annotation.FacesConfig; + +@ApplicationScoped +@FacesConfig +public class JSFConfiguration { +} \ No newline at end of file diff --git a/src/main/java/dev/lions/controllers/HomeController.java b/src/main/java/dev/lions/controllers/HomeController.java new file mode 100644 index 0000000..181ab76 --- /dev/null +++ b/src/main/java/dev/lions/controllers/HomeController.java @@ -0,0 +1,17 @@ +package dev.lions.controllers; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Named; +import java.io.Serializable; + +@Named +@RequestScoped +public class HomeController implements Serializable { + + private static final long serialVersionUID = 1L; + + public String getWelcomeMessage() { + System.out.println("HomeController.getWelcomeMessage() called"); + return "Welcome to Lions Dev"; + } +} \ No newline at end of file diff --git a/src/main/java/dev/lions/controllers/IndexController.java b/src/main/java/dev/lions/controllers/IndexController.java new file mode 100644 index 0000000..cb455d3 --- /dev/null +++ b/src/main/java/dev/lions/controllers/IndexController.java @@ -0,0 +1,19 @@ +package dev.lions.controllers; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Named; + +@Named +@RequestScoped +public class IndexController { + + private String message = "Welcome to Lions Dev!"; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/faces-config.xml b/src/main/resources/META-INF/faces-config.xml new file mode 100644 index 0000000..f360bb4 --- /dev/null +++ b/src/main/resources/META-INF/faces-config.xml @@ -0,0 +1,28 @@ + + + + LionsDev + + + + en + fr + + + + + * + + + Page index + index + /private/index.xhtml + + + + + \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/WEB-INF/template/page.xhtml b/src/main/resources/META-INF/resources/WEB-INF/template/page.xhtml new file mode 100644 index 0000000..a779f6c --- /dev/null +++ b/src/main/resources/META-INF/resources/WEB-INF/template/page.xhtml @@ -0,0 +1,73 @@ + + + + + + + Lions Dev - <ui:insert name="title">Welcome</ui:insert> + + + + + + +
+ +
+
+ + +
+
+ +
+
+ + +
+ +

Default content

+
+
+ + +
+ + +
+
+ + + +
+ \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/css/animations.css b/src/main/resources/META-INF/resources/css/animations.css new file mode 100644 index 0000000..a7067b3 --- /dev/null +++ b/src/main/resources/META-INF/resources/css/animations.css @@ -0,0 +1,162 @@ +.animate-on-scroll { + opacity: 0; + transform: translateY(20px); + transition: all 0.6s ease-out; +} + +.animate-on-scroll.animated { + opacity: 1; + transform: translateY(0); +} + +.fade-in { + opacity: 0; + transform: translateY(20px); + transition: all 0.6s ease-out; +} + +.slide-in-left { + opacity: 0; + transform: translateX(-50px); + transition: all 0.6s ease-out; +} + +.slide-in-right { + opacity: 0; + transform: translateX(50px); + transition: all 0.6s ease-out; +} + +.animated { + opacity: 1; + transform: translate(0, 0); +} + +.hover-effect { + transition: transform 0.3s ease-in-out; +} + +.hover-effect:hover { + transform: translateY(-10px); +} + +.icon-animate { + transition: all 0.3s ease-in-out; +} + +.card-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(33, 150, 243, 0.9); + opacity: 0; + display: flex; + justify-content: center; + align-items: center; + transition: opacity 0.3s ease-in-out; + border-radius: 8px; +} + +.overlay-text { + color: white; + font-size: 1.2rem; + font-weight: bold; +} + +.pulse-animation { + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } +} + +/* Stats Container Styles */ +.stats-container { + display: flex; + justify-content: space-around; + margin: 2rem 0; + flex-wrap: wrap; +} + +.stat-item { + text-align: center; + padding: 1rem; +} + +.counter { + font-size: 2.5rem; + font-weight: bold; + color: var(--primary-color); +} + +/* Loading Button Styles */ +.loading-button { + position: relative; + transition: all 0.3s ease; +} + +.loading-button.loading { + background-color: #ccc; + cursor: not-allowed; +} + +.loading-button.loading::after { + content: ''; + position: absolute; + width: 20px; + height: 20px; + top: 50%; + right: 10px; + transform: translateY(-50%); + border: 2px solid #fff; + border-radius: 50%; + border-top-color: transparent; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { + transform: translateY(-50%) rotate(360deg); + } +} + +/* Dans animations.css */ +.service-card { + position: relative; + overflow: hidden; + transition: all 0.3s ease; +} + +.service-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + opacity: 0; + transition: opacity 0.3s ease; +} + +.service-card:hover::before { + opacity: 0.1; +} + +.service-card .icon-animate { + font-size: 2.5rem; + color: var(--primary-color); + margin-bottom: 1rem; + display: inline-block; +} \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/css/custom.css b/src/main/resources/META-INF/resources/css/custom.css new file mode 100644 index 0000000..efa00f3 --- /dev/null +++ b/src/main/resources/META-INF/resources/css/custom.css @@ -0,0 +1,219 @@ +/* Global Styles */ +:root { + --primary-color: #2196F3; + --secondary-color: #1976D2; + --text-color: #333333; + --background-color: #FFFFFF; +} + +body { + margin: 0; + padding: 0; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + color: var(--text-color); + background-color: var(--background-color); +} + +/* Layout */ +.layout-wrapper { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +/* Header */ +.layout-header { + background-color: var(--background-color); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + padding: 1rem 2rem; +} + +.header-content { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 1200px; + margin: 0 auto; +} + +.nav-list { + display: flex; + list-style: none; + gap: 2rem; + margin: 0; + padding: 0; +} + +.nav-button { + background-color: var(--primary-color); + color: white; + padding: 0.5rem 1rem; + border-radius: 4px; + text-decoration: none; +} + +/* Hero Section */ +.hero-section { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)), + url('#{resource["images/pattern.png"]}') repeat; + color: white; + padding: 4rem 2rem; + text-align: center; + position: relative; + overflow: hidden; +} + +.hero-content { + max-width: 800px; + margin: 0 auto; + position: relative; + z-index: 1; +} + +.hero-button { + font-size: 1.2rem; + padding: 1rem 2rem; + margin-top: 2rem; +} + +.hero-section::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(45deg, rgba(33, 150, 243, 0.9), rgba(25, 118, 210, 0.9)); +} +/* Services Section */ +.services-section { + padding: 4rem 2rem; + background-color: #f5f5f5; +} + +.service-card { + background-color: white; + padding: 2rem; + border-radius: 8px; + text-align: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +/* Footer */ +.layout-footer { + background-color: #333; + color: white; + padding: 3rem 2rem 1rem; + margin-top: auto; +} + +.footer-content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.footer-bottom { + text-align: center; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + margin-top: 2rem; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .nav-list { + display: none; + } + + .header-content { + flex-direction: column; + gap: 1rem; + } +} + +.layout-header { + background-color: var(--background-color); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + padding: 1rem 2rem; + position: fixed; + width: 100%; + top: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.layout-header.scroll-down { + transform: translateY(-100%); +} + +.layout-header.scroll-up { + transform: translateY(0); + background-color: rgba(255, 255, 255, 0.95); + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +/* Ajout d'un padding-top au main pour compenser le header fixe */ +main { + padding-top: 80px; +} + +/* Amélioration du style des boutons */ +.hero-button { + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + border: none; + color: white; + padding: 1rem 2.5rem; + border-radius: 50px; + font-size: 1.1rem; + transition: transform 0.3s ease, box-shadow 0.3s ease; + box-shadow: 0 4px 15px rgba(33, 150, 243, 0.3); +} + +.hero-button:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(33, 150, 243, 0.4); +} + +/* Style pour la grille de services */ +.services-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + padding: 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.mobile-menu-button { + display: none; + font-size: 1.5rem; + cursor: pointer; +} + +@media (max-width: 768px) { + .mobile-menu-button { + display: block; + } + + .nav-list { + position: absolute; + top: 100%; + left: 0; + right: 0; + background: white; + flex-direction: column; + padding: 1rem; + display: none; + } + + .nav-list.active { + display: flex; + } + + .nav-list li { + margin: 0.5rem 0; + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/images/logo.png b/src/main/resources/META-INF/resources/images/logo.png new file mode 100644 index 0000000..7a3b646 Binary files /dev/null and b/src/main/resources/META-INF/resources/images/logo.png differ diff --git a/src/main/resources/META-INF/resources/js/animations.js b/src/main/resources/META-INF/resources/js/animations.js new file mode 100644 index 0000000..980ee48 --- /dev/null +++ b/src/main/resources/META-INF/resources/js/animations.js @@ -0,0 +1,119 @@ +document.addEventListener('DOMContentLoaded', function() { + // Initialize animations + initializeAnimations(); + // Start counters when visible + initializeCounters(); + // Initialize parallax effect + initializeParallax(); +}); + +function initializeAnimations() { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('animated'); + if (entry.target.classList.contains('fade-in')) { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + } + } + }); + }, { threshold: 0.1 }); + + // Observe all elements with animation classes + document.querySelectorAll('.animate-on-scroll, .fade-in, .slide-in-left, .slide-in-right') + .forEach(element => observer.observe(element)); +} + +function handleCardHover(card, isHovering) { + const icon = card.querySelector('.icon-animate'); + const overlay = card.querySelector('.card-overlay'); + + if (isHovering) { + icon.style.transform = 'scale(1.2) translateY(-10px)'; + overlay.style.opacity = '1'; + } else { + icon.style.transform = 'scale(1) translateY(0)'; + overlay.style.opacity = '0'; + } +} + +function initializeCounters() { + const counterObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const counter = entry.target; + const target = parseInt(counter.dataset.count); + animateCounter(counter, target); + counterObserver.unobserve(counter); + } + }); + }, { threshold: 0.5 }); + + document.querySelectorAll('.stat-item').forEach(stat => { + counterObserver.observe(stat); + }); +} + +function animateCounter(element, target) { + const counter = element.querySelector('.counter'); + let current = 0; + const increment = target / 50; // Divide animation into 50 steps + const duration = 2000; // 2 seconds + const interval = duration / 50; + + const timer = setInterval(() => { + current += increment; + counter.textContent = Math.round(current); + + if (current >= target) { + counter.textContent = target; + clearInterval(timer); + } + }, interval); +} + +function initializeParallax() { + window.addEventListener('scroll', () => { + const parallaxElements = document.querySelectorAll('[data-parallax="scroll"]'); + + parallaxElements.forEach(element => { + const speed = element.dataset.speed || 0.5; + const offset = window.pageYOffset; + element.style.backgroundPositionY = `${offset * speed}px`; + }); + }); +} + +function handleButtonClick(button) { + // Add loading state + button.classList.add('loading'); + button.disabled = true; + + // Store original text + const originalText = button.textContent; + button.textContent = 'Loading...'; + + // Simulate loading (remove in production) + setTimeout(() => { + button.classList.remove('loading'); + button.disabled = false; + button.textContent = originalText; + }, 1000); + + return true; // Allow navigation to proceed +} + +// Add smooth scrolling for all anchor links +document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + }); +}); \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/js/custom.js b/src/main/resources/META-INF/resources/js/custom.js new file mode 100644 index 0000000..dd0864e --- /dev/null +++ b/src/main/resources/META-INF/resources/js/custom.js @@ -0,0 +1,176 @@ +document.addEventListener('DOMContentLoaded', function() { + // Navigation mobile toggle + const mobileMenuButton = document.querySelector('.mobile-menu-button'); + const navList = document.querySelector('.nav-list'); + + if (mobileMenuButton) { + mobileMenuButton.addEventListener('click', function() { + navList.classList.toggle('active'); + }); + } + + // Smooth scrolling for anchor links + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + }); + }); + + // Header scroll effect + const header = document.querySelector('.layout-header'); + let lastScroll = 0; + + window.addEventListener('scroll', () => { + const currentScroll = window.pageYOffset; + + if (currentScroll <= 0) { + header.classList.remove('scroll-up'); + return; + } + + if (currentScroll > lastScroll && !header.classList.contains('scroll-down')) { + // Down + header.classList.remove('scroll-up'); + header.classList.add('scroll-down'); + } else if (currentScroll < lastScroll && header.classList.contains('scroll-down')) { + // Up + header.classList.remove('scroll-down'); + header.classList.add('scroll-up'); + } + lastScroll = currentScroll; + }); + + // Intersection Observer for animations + const animateOnScroll = () => { + const elements = document.querySelectorAll('.animate-on-scroll'); + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('animated'); + } + }); + }, { + threshold: 0.1 + }); + + elements.forEach(element => { + observer.observe(element); + }); + }; + + animateOnScroll(); + + // Form validation enhancement + const enhanceFormValidation = () => { + const forms = document.querySelectorAll('form'); + + forms.forEach(form => { + const inputs = form.querySelectorAll('input, textarea'); + + inputs.forEach(input => { + // Real-time validation + input.addEventListener('input', function() { + validateInput(this); + }); + + // Blur validation + input.addEventListener('blur', function() { + validateInput(this); + }); + }); + }); + }; + + const validateInput = (input) => { + const parent = input.parentElement; + const errorElement = parent.querySelector('.error-message') || + document.createElement('span'); + + errorElement.classList.add('error-message'); + + if (!input.value && input.hasAttribute('required')) { + errorElement.textContent = 'Ce champ est requis'; + !parent.contains(errorElement) && parent.appendChild(errorElement); + } else if (input.type === 'email' && !validateEmail(input.value)) { + errorElement.textContent = 'Email invalide'; + !parent.contains(errorElement) && parent.appendChild(errorElement); + } else { + errorElement.remove(); + } + }; + + const validateEmail = (email) => { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); + }; + + enhanceFormValidation(); + + // Loading state management for buttons + const setupLoadingButtons = () => { + const buttons = document.querySelectorAll('.loading-button'); + + buttons.forEach(button => { + button.addEventListener('click', function(e) { + if (!this.classList.contains('loading')) { + this.classList.add('loading'); + this.setAttribute('disabled', 'disabled'); + + // Store original text + const originalText = this.textContent; + this.setAttribute('data-original-text', originalText); + this.textContent = 'Chargement...'; + + // Simulate loading (remove in production and handle with actual form submission) + setTimeout(() => { + this.classList.remove('loading'); + this.removeAttribute('disabled'); + this.textContent = originalText; + }, 2000); + } + }); + }); + }; + + setupLoadingButtons(); + + // Lazy loading for images + const setupLazyLoading = () => { + const images = document.querySelectorAll('img[data-src]'); + + const imageObserver = new IntersectionObserver((entries, observer) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const img = entry.target; + img.src = img.dataset.src; + img.removeAttribute('data-src'); + observer.unobserve(img); + } + }); + }); + + images.forEach(img => imageObserver.observe(img)); + }; + + setupLazyLoading(); +}); + +// Service Worker Registration for PWA support +if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register('/sw.js') + .then(registration => { + console.log('ServiceWorker registration successful'); + }) + .catch(error => { + console.log('ServiceWorker registration failed:', error); + }); + }); +} \ No newline at end of file diff --git a/src/main/resources/META-INF/resources/private/index.xhtml b/src/main/resources/META-INF/resources/private/index.xhtml new file mode 100644 index 0000000..ada9d8e --- /dev/null +++ b/src/main/resources/META-INF/resources/private/index.xhtml @@ -0,0 +1,93 @@ + + + Accueil + + + +
+
+

#{homeController.welcomeMessage}

+

Solutions digitales innovantes pour vos projets

+ +
+
+ + +
+
+

Nos Services

+
+
+ +

Développement Web

+

Applications web modernes et performantes

+
+ En savoir plus +
+
+ +
+ +

Applications Mobiles

+

Solutions mobiles cross-platform

+
+ En savoir plus +
+
+ +
+ +

Cloud Solutions

+

Infrastructure cloud sécurisée

+
+ En savoir plus +
+
+
+
+
+ + +
+
+
+
0
+

Projets Réalisés

+
+
+
0
+

Clients Satisfaits

+
+
+
0
+

Années d'Expérience

+
+
+
+ + +
+ +
+

Contactez-nous

+
+ +
+
+ +
+
+ +
+ +
+
+
+
+ +
\ No newline at end of file diff --git a/src/main/resources/META-INF/web.xml b/src/main/resources/META-INF/web.xml new file mode 100644 index 0000000..8ccaf8a --- /dev/null +++ b/src/main/resources/META-INF/web.xml @@ -0,0 +1,25 @@ + + + + + private/index.xhtml + + + + jakarta.faces.PROJECT_STAGE + Development + + + + Faces Servlet + jakarta.faces.webapp.FacesServlet + 1 + + + + Faces Servlet + *.xhtml + + \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..16101f0 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,22 @@ +# Configuration serveur +quarkus.http.port=8707 +quarkus.servlet.context-path=/lions-dev + +# Configuration JSF +quarkus.faces.enable=true +quarkus.faces.servlet-mappings=*.xhtml +quarkus.faces.project-stage=Development + +# Configuration chemins d'accès +quarkus.http.auth.permission.public.paths=/private/*,/index.xhtml +quarkus.http.auth.permission.public.policy=permit + +# Configuration ressources statiques +quarkus.http.static-resources.enabled=true + +# Configuration PrimeFaces +quarkus.primefaces.theme=saga + +# Configuration MyFaces +quarkus.myfaces.mapping-suffix=.xhtml +jakarta.faces.PROJECT_STAGE=Development \ No newline at end of file