commit
ddfb35ddc3
128 changed files with 43367 additions and 0 deletions
-
2.gitattributes
-
33.gitignore
-
3.mvn/wrapper/maven-wrapper.properties
-
295mvnw
-
189mvnw.cmd
-
140pom.xml
-
17src/main/java/com/gcc_it_cell_services/springboot/GccItCellServicesApplication.java
-
41src/main/java/com/gcc_it_cell_services/springboot/configuration/AuditorAwareConfiguration.java
-
42src/main/java/com/gcc_it_cell_services/springboot/configuration/RateLimitConfiguration.java
-
136src/main/java/com/gcc_it_cell_services/springboot/configuration/SecurityConfiguration.java
-
37src/main/java/com/gcc_it_cell_services/springboot/configuration/WebConfiguration.java
-
70src/main/java/com/gcc_it_cell_services/springboot/controller/HomeController.java
-
61src/main/java/com/gcc_it_cell_services/springboot/controller/UrlDataController.java
-
102src/main/java/com/gcc_it_cell_services/springboot/interceptor/RateLimitInterceptor.java
-
38src/main/java/com/gcc_it_cell_services/springboot/mapper/OtpLogMapper.java
-
19src/main/java/com/gcc_it_cell_services/springboot/mapper/StatusMapper.java
-
29src/main/java/com/gcc_it_cell_services/springboot/mapper/UrlCategoryMapper.java
-
48src/main/java/com/gcc_it_cell_services/springboot/mapper/UrlDataMapper.java
-
70src/main/java/com/gcc_it_cell_services/springboot/model/AppUser.java
-
102src/main/java/com/gcc_it_cell_services/springboot/model/OtpLog.java
-
35src/main/java/com/gcc_it_cell_services/springboot/model/Status.java
-
106src/main/java/com/gcc_it_cell_services/springboot/model/UrlCategory.java
-
136src/main/java/com/gcc_it_cell_services/springboot/model/UrlData.java
-
66src/main/java/com/gcc_it_cell_services/springboot/model/UserRole.java
-
25src/main/java/com/gcc_it_cell_services/springboot/repository/AppUserRepository.java
-
15src/main/java/com/gcc_it_cell_services/springboot/repository/OtpLogRepository.java
-
11src/main/java/com/gcc_it_cell_services/springboot/repository/StatusRepository.java
-
18src/main/java/com/gcc_it_cell_services/springboot/repository/UrlCategoryRepository.java
-
28src/main/java/com/gcc_it_cell_services/springboot/repository/UrlDataRepository.java
-
22src/main/java/com/gcc_it_cell_services/springboot/request_dto/AddUrlDataRequestDTO.java
-
17src/main/java/com/gcc_it_cell_services/springboot/request_dto/AppUserRequestDTO.java
-
18src/main/java/com/gcc_it_cell_services/springboot/request_dto/GetOtpRequestDTO.java
-
22src/main/java/com/gcc_it_cell_services/springboot/request_dto/OtpLogRequestDTO.java
-
22src/main/java/com/gcc_it_cell_services/springboot/request_dto/UpdateUrlDataRequestDTO.java
-
18src/main/java/com/gcc_it_cell_services/springboot/request_dto/UrlCategoryRequestDTO.java
-
26src/main/java/com/gcc_it_cell_services/springboot/request_dto/UrlDataRequestDTO.java
-
19src/main/java/com/gcc_it_cell_services/springboot/request_dto/VerifyOtpRequestDTO.java
-
23src/main/java/com/gcc_it_cell_services/springboot/response_dto/OtpLogResponseDTO.java
-
23src/main/java/com/gcc_it_cell_services/springboot/response_dto/ServiceResponseDTO.java
-
23src/main/java/com/gcc_it_cell_services/springboot/response_dto/StatusResponseDTO.java
-
19src/main/java/com/gcc_it_cell_services/springboot/response_dto/UrlCategoryResponseDTO.java
-
37src/main/java/com/gcc_it_cell_services/springboot/response_dto/UrlDataResponseDTO.java
-
192src/main/java/com/gcc_it_cell_services/springboot/rest_controller/HomeRestController.java
-
43src/main/java/com/gcc_it_cell_services/springboot/rest_controller/UrlDataRestController.java
-
279src/main/java/com/gcc_it_cell_services/springboot/service/HomeService.java
-
174src/main/java/com/gcc_it_cell_services/springboot/service/UrlDataCacheService.java
-
280src/main/java/com/gcc_it_cell_services/springboot/service/UrlDataService.java
-
28src/main/java/com/gcc_it_cell_services/springboot/util/MobileNumberTokenStore.java
-
21src/main/java/com/gcc_it_cell_services/springboot/util/UrlCacheScheduler.java
-
22src/main/java/com/gcc_it_cell_services/springboot/util/UrlDataCacheInitializer.java
-
9src/main/resources/META-INF/additional-spring-configuration-metadata.json
-
33src/main/resources/application-local.properties
-
29src/main/resources/application-prod.properties
-
2src/main/resources/application.properties
-
11794src/main/resources/static/assets/bootstrap-5.3.2/css/bootstrap.min.css
-
2212src/main/resources/static/assets/bootstrap-5.3.2/js/bootstrap.min.js
-
193src/main/resources/static/assets/css/body-form-styles.css
-
114src/main/resources/static/assets/css/datatable-styles.css
-
77src/main/resources/static/assets/css/header-styles.css
-
295src/main/resources/static/assets/css/home-styles.css
-
272src/main/resources/static/assets/css/login-form-styles.css
-
532src/main/resources/static/assets/datatables-1.13.6/css/dataTables.bootstrap5.min.css
-
80src/main/resources/static/assets/datatables-1.13.6/js/dataTables.bootstrap5.min.js
-
2990src/main/resources/static/assets/datatables-1.13.6/js/jquery.dataTables.min.js
-
248src/main/resources/static/assets/datatables-buttons-2.4.1/css/buttons.bootstrap5.min.css
-
70src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.bootstrap5.min.js
-
538src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.html5.min.js
-
79src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.print.min.js
-
764src/main/resources/static/assets/datatables-buttons-2.4.1/js/dataTables.buttons.min.js
-
6995src/main/resources/static/assets/font-awesome-6.5.1/css/all.min.css
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-brands-400.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-brands-400.woff2
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-regular-400.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-regular-400.woff2
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-solid-900.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-solid-900.woff2
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-v4compatibility.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.1/webfonts/fa-v4compatibility.woff2
-
7023src/main/resources/static/assets/font-awesome-6.5.2/css/all.min.css
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-brands-400.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-brands-400.woff2
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-regular-400.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-regular-400.woff2
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-solid-900.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-solid-900.woff2
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-v4compatibility.ttf
-
BINsrc/main/resources/static/assets/font-awesome-6.5.2/webfonts/fa-v4compatibility.woff2
-
BINsrc/main/resources/static/assets/header-images/gcc1.png
-
3244src/main/resources/static/assets/jquery-3.6.4/js/jquery-3.6.4.min.js
-
93src/main/resources/static/assets/js/add-url-data-script.js
-
20src/main/resources/static/assets/js/fetch-csrf-script.js
-
3src/main/resources/static/assets/js/go-back-script.js
-
420src/main/resources/static/assets/js/home-script.js
-
28src/main/resources/static/assets/js/login-script.js
-
136src/main/resources/static/assets/js/update-url-data-script.js
-
56src/main/resources/static/assets/js/view-login-details-script.js
-
1123src/main/resources/static/assets/popper-2.11.8/js/popper.min.js
-
BINsrc/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-100.woff2
-
BINsrc/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-200.woff2
-
BINsrc/main/resources/static/assets/poppins-23.0/fonts/devanagari/poppins-v23-devanagari-300.woff2
@ -0,0 +1,2 @@ |
|||||
|
/mvnw text eol=lf |
||||
|
*.cmd text eol=crlf |
||||
@ -0,0 +1,33 @@ |
|||||
|
HELP.md |
||||
|
target/ |
||||
|
.mvn/wrapper/maven-wrapper.jar |
||||
|
!**/src/main/**/target/ |
||||
|
!**/src/test/**/target/ |
||||
|
|
||||
|
### STS ### |
||||
|
.apt_generated |
||||
|
.classpath |
||||
|
.factorypath |
||||
|
.project |
||||
|
.settings |
||||
|
.springBeans |
||||
|
.sts4-cache |
||||
|
|
||||
|
### IntelliJ IDEA ### |
||||
|
.idea |
||||
|
*.iws |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
|
||||
|
### NetBeans ### |
||||
|
/nbproject/private/ |
||||
|
/nbbuild/ |
||||
|
/dist/ |
||||
|
/nbdist/ |
||||
|
/.nb-gradle/ |
||||
|
build/ |
||||
|
!**/src/main/**/build/ |
||||
|
!**/src/test/**/build/ |
||||
|
|
||||
|
### VS Code ### |
||||
|
.vscode/ |
||||
@ -0,0 +1,3 @@ |
|||||
|
wrapperVersion=3.3.4 |
||||
|
distributionType=only-script |
||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip |
||||
@ -0,0 +1,295 @@ |
|||||
|
#!/bin/sh |
||||
|
# ---------------------------------------------------------------------------- |
||||
|
# Licensed to the Apache Software Foundation (ASF) under one |
||||
|
# or more contributor license agreements. See the NOTICE file |
||||
|
# distributed with this work for additional information |
||||
|
# regarding copyright ownership. The ASF licenses this file |
||||
|
# to you 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 |
||||
|
# |
||||
|
# http://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. |
||||
|
# ---------------------------------------------------------------------------- |
||||
|
|
||||
|
# ---------------------------------------------------------------------------- |
||||
|
# Apache Maven Wrapper startup batch script, version 3.3.4 |
||||
|
# |
||||
|
# Optional ENV vars |
||||
|
# ----------------- |
||||
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source |
||||
|
# MVNW_REPOURL - repo url base for downloading maven distribution |
||||
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven |
||||
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output |
||||
|
# ---------------------------------------------------------------------------- |
||||
|
|
||||
|
set -euf |
||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x |
||||
|
|
||||
|
# OS specific support. |
||||
|
native_path() { printf %s\\n "$1"; } |
||||
|
case "$(uname)" in |
||||
|
CYGWIN* | MINGW*) |
||||
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" |
||||
|
native_path() { cygpath --path --windows "$1"; } |
||||
|
;; |
||||
|
esac |
||||
|
|
||||
|
# set JAVACMD and JAVACCMD |
||||
|
set_java_home() { |
||||
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched |
||||
|
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" |
||||
|
JAVACCMD="$JAVA_HOME/jre/sh/javac" |
||||
|
else |
||||
|
JAVACMD="$JAVA_HOME/bin/java" |
||||
|
JAVACCMD="$JAVA_HOME/bin/javac" |
||||
|
|
||||
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then |
||||
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 |
||||
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
else |
||||
|
JAVACMD="$( |
||||
|
'set' +e |
||||
|
'unset' -f command 2>/dev/null |
||||
|
'command' -v java |
||||
|
)" || : |
||||
|
JAVACCMD="$( |
||||
|
'set' +e |
||||
|
'unset' -f command 2>/dev/null |
||||
|
'command' -v javac |
||||
|
)" || : |
||||
|
|
||||
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then |
||||
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# hash string like Java String::hashCode |
||||
|
hash_string() { |
||||
|
str="${1:-}" h=0 |
||||
|
while [ -n "$str" ]; do |
||||
|
char="${str%"${str#?}"}" |
||||
|
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) |
||||
|
str="${str#?}" |
||||
|
done |
||||
|
printf %x\\n $h |
||||
|
} |
||||
|
|
||||
|
verbose() { :; } |
||||
|
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } |
||||
|
|
||||
|
die() { |
||||
|
printf %s\\n "$1" >&2 |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
trim() { |
||||
|
# MWRAPPER-139: |
||||
|
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. |
||||
|
# Needed for removing poorly interpreted newline sequences when running in more |
||||
|
# exotic environments such as mingw bash on Windows. |
||||
|
printf "%s" "${1}" | tr -d '[:space:]' |
||||
|
} |
||||
|
|
||||
|
scriptDir="$(dirname "$0")" |
||||
|
scriptName="$(basename "$0")" |
||||
|
|
||||
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties |
||||
|
while IFS="=" read -r key value; do |
||||
|
case "${key-}" in |
||||
|
distributionUrl) distributionUrl=$(trim "${value-}") ;; |
||||
|
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; |
||||
|
esac |
||||
|
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" |
||||
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" |
||||
|
|
||||
|
case "${distributionUrl##*/}" in |
||||
|
maven-mvnd-*bin.*) |
||||
|
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ |
||||
|
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in |
||||
|
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; |
||||
|
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;; |
||||
|
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;; |
||||
|
:Linux*x86_64*) distributionPlatform=linux-amd64 ;; |
||||
|
*) |
||||
|
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 |
||||
|
distributionPlatform=linux-amd64 |
||||
|
;; |
||||
|
esac |
||||
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" |
||||
|
;; |
||||
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; |
||||
|
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; |
||||
|
esac |
||||
|
|
||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME |
||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash> |
||||
|
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" |
||||
|
distributionUrlName="${distributionUrl##*/}" |
||||
|
distributionUrlNameMain="${distributionUrlName%.*}" |
||||
|
distributionUrlNameMain="${distributionUrlNameMain%-bin}" |
||||
|
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" |
||||
|
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" |
||||
|
|
||||
|
exec_maven() { |
||||
|
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : |
||||
|
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" |
||||
|
} |
||||
|
|
||||
|
if [ -d "$MAVEN_HOME" ]; then |
||||
|
verbose "found existing MAVEN_HOME at $MAVEN_HOME" |
||||
|
exec_maven "$@" |
||||
|
fi |
||||
|
|
||||
|
case "${distributionUrl-}" in |
||||
|
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; |
||||
|
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; |
||||
|
esac |
||||
|
|
||||
|
# prepare tmp dir |
||||
|
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then |
||||
|
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } |
||||
|
trap clean HUP INT TERM EXIT |
||||
|
else |
||||
|
die "cannot create temp dir" |
||||
|
fi |
||||
|
|
||||
|
mkdir -p -- "${MAVEN_HOME%/*}" |
||||
|
|
||||
|
# Download and Install Apache Maven |
||||
|
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." |
||||
|
verbose "Downloading from: $distributionUrl" |
||||
|
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" |
||||
|
|
||||
|
# select .zip or .tar.gz |
||||
|
if ! command -v unzip >/dev/null; then |
||||
|
distributionUrl="${distributionUrl%.zip}.tar.gz" |
||||
|
distributionUrlName="${distributionUrl##*/}" |
||||
|
fi |
||||
|
|
||||
|
# verbose opt |
||||
|
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' |
||||
|
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v |
||||
|
|
||||
|
# normalize http auth |
||||
|
case "${MVNW_PASSWORD:+has-password}" in |
||||
|
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; |
||||
|
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; |
||||
|
esac |
||||
|
|
||||
|
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then |
||||
|
verbose "Found wget ... using wget" |
||||
|
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" |
||||
|
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then |
||||
|
verbose "Found curl ... using curl" |
||||
|
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" |
||||
|
elif set_java_home; then |
||||
|
verbose "Falling back to use Java to download" |
||||
|
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" |
||||
|
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" |
||||
|
cat >"$javaSource" <<-END |
||||
|
public class Downloader extends java.net.Authenticator |
||||
|
{ |
||||
|
protected java.net.PasswordAuthentication getPasswordAuthentication() |
||||
|
{ |
||||
|
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); |
||||
|
} |
||||
|
public static void main( String[] args ) throws Exception |
||||
|
{ |
||||
|
setDefault( new Downloader() ); |
||||
|
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); |
||||
|
} |
||||
|
} |
||||
|
END |
||||
|
# For Cygwin/MinGW, switch paths to Windows format before running javac and java |
||||
|
verbose " - Compiling Downloader.java ..." |
||||
|
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" |
||||
|
verbose " - Running Downloader.java ..." |
||||
|
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" |
||||
|
fi |
||||
|
|
||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file |
||||
|
if [ -n "${distributionSha256Sum-}" ]; then |
||||
|
distributionSha256Result=false |
||||
|
if [ "$MVN_CMD" = mvnd.sh ]; then |
||||
|
echo "Checksum validation is not supported for maven-mvnd." >&2 |
||||
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 |
||||
|
exit 1 |
||||
|
elif command -v sha256sum >/dev/null; then |
||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then |
||||
|
distributionSha256Result=true |
||||
|
fi |
||||
|
elif command -v shasum >/dev/null; then |
||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then |
||||
|
distributionSha256Result=true |
||||
|
fi |
||||
|
else |
||||
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 |
||||
|
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
if [ $distributionSha256Result = false ]; then |
||||
|
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 |
||||
|
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# unzip and move |
||||
|
if command -v unzip >/dev/null; then |
||||
|
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" |
||||
|
else |
||||
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" |
||||
|
fi |
||||
|
|
||||
|
# Find the actual extracted directory name (handles snapshots where filename != directory name) |
||||
|
actualDistributionDir="" |
||||
|
|
||||
|
# First try the expected directory name (for regular distributions) |
||||
|
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then |
||||
|
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then |
||||
|
actualDistributionDir="$distributionUrlNameMain" |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# If not found, search for any directory with the Maven executable (for snapshots) |
||||
|
if [ -z "$actualDistributionDir" ]; then |
||||
|
# enable globbing to iterate over items |
||||
|
set +f |
||||
|
for dir in "$TMP_DOWNLOAD_DIR"/*; do |
||||
|
if [ -d "$dir" ]; then |
||||
|
if [ -f "$dir/bin/$MVN_CMD" ]; then |
||||
|
actualDistributionDir="$(basename "$dir")" |
||||
|
break |
||||
|
fi |
||||
|
fi |
||||
|
done |
||||
|
set -f |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$actualDistributionDir" ]; then |
||||
|
verbose "Contents of $TMP_DOWNLOAD_DIR:" |
||||
|
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" |
||||
|
die "Could not find Maven distribution directory in extracted archive" |
||||
|
fi |
||||
|
|
||||
|
verbose "Found extracted Maven distribution directory: $actualDistributionDir" |
||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" |
||||
|
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" |
||||
|
|
||||
|
clean || : |
||||
|
exec_maven "$@" |
||||
@ -0,0 +1,189 @@ |
|||||
|
<# : batch portion |
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||
|
@REM or more contributor license agreements. See the NOTICE file |
||||
|
@REM distributed with this work for additional information |
||||
|
@REM regarding copyright ownership. The ASF licenses this file |
||||
|
@REM to you under the Apache License, Version 2.0 (the |
||||
|
@REM "License"); you may not use this file except in compliance |
||||
|
@REM with the License. You may obtain a copy of the License at |
||||
|
@REM |
||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
@REM |
||||
|
@REM Unless required by applicable law or agreed to in writing, |
||||
|
@REM software distributed under the License is distributed on an |
||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
|
@REM KIND, either express or implied. See the License for the |
||||
|
@REM specific language governing permissions and limitations |
||||
|
@REM under the License. |
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
|
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
@REM Apache Maven Wrapper startup batch script, version 3.3.4 |
||||
|
@REM |
||||
|
@REM Optional ENV vars |
||||
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution |
||||
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven |
||||
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output |
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
|
||||
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) |
||||
|
@SET __MVNW_CMD__= |
||||
|
@SET __MVNW_ERROR__= |
||||
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% |
||||
|
@SET PSModulePath= |
||||
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( |
||||
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) |
||||
|
) |
||||
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% |
||||
|
@SET __MVNW_PSMODULEP_SAVE= |
||||
|
@SET __MVNW_ARG0_NAME__= |
||||
|
@SET MVNW_USERNAME= |
||||
|
@SET MVNW_PASSWORD= |
||||
|
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) |
||||
|
@echo Cannot start maven from wrapper >&2 && exit /b 1 |
||||
|
@GOTO :EOF |
||||
|
: end batch / begin powershell #> |
||||
|
|
||||
|
$ErrorActionPreference = "Stop" |
||||
|
if ($env:MVNW_VERBOSE -eq "true") { |
||||
|
$VerbosePreference = "Continue" |
||||
|
} |
||||
|
|
||||
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties |
||||
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl |
||||
|
if (!$distributionUrl) { |
||||
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" |
||||
|
} |
||||
|
|
||||
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { |
||||
|
"maven-mvnd-*" { |
||||
|
$USE_MVND = $true |
||||
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" |
||||
|
$MVN_CMD = "mvnd.cmd" |
||||
|
break |
||||
|
} |
||||
|
default { |
||||
|
$USE_MVND = $false |
||||
|
$MVN_CMD = $script -replace '^mvnw','mvn' |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME |
||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash> |
||||
|
if ($env:MVNW_REPOURL) { |
||||
|
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } |
||||
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" |
||||
|
} |
||||
|
$distributionUrlName = $distributionUrl -replace '^.*/','' |
||||
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' |
||||
|
|
||||
|
$MAVEN_M2_PATH = "$HOME/.m2" |
||||
|
if ($env:MAVEN_USER_HOME) { |
||||
|
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" |
||||
|
} |
||||
|
|
||||
|
if (-not (Test-Path -Path $MAVEN_M2_PATH)) { |
||||
|
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null |
||||
|
} |
||||
|
|
||||
|
$MAVEN_WRAPPER_DISTS = $null |
||||
|
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { |
||||
|
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" |
||||
|
} else { |
||||
|
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" |
||||
|
} |
||||
|
|
||||
|
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" |
||||
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' |
||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" |
||||
|
|
||||
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { |
||||
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" |
||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" |
||||
|
exit $? |
||||
|
} |
||||
|
|
||||
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { |
||||
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" |
||||
|
} |
||||
|
|
||||
|
# prepare tmp dir |
||||
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile |
||||
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" |
||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null |
||||
|
trap { |
||||
|
if ($TMP_DOWNLOAD_DIR.Exists) { |
||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } |
||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null |
||||
|
|
||||
|
# Download and Install Apache Maven |
||||
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." |
||||
|
Write-Verbose "Downloading from: $distributionUrl" |
||||
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" |
||||
|
|
||||
|
$webclient = New-Object System.Net.WebClient |
||||
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { |
||||
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) |
||||
|
} |
||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 |
||||
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null |
||||
|
|
||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file |
||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum |
||||
|
if ($distributionSha256Sum) { |
||||
|
if ($USE_MVND) { |
||||
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." |
||||
|
} |
||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash |
||||
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { |
||||
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
# unzip and move |
||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null |
||||
|
|
||||
|
# Find the actual extracted directory name (handles snapshots where filename != directory name) |
||||
|
$actualDistributionDir = "" |
||||
|
|
||||
|
# First try the expected directory name (for regular distributions) |
||||
|
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" |
||||
|
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" |
||||
|
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { |
||||
|
$actualDistributionDir = $distributionUrlNameMain |
||||
|
} |
||||
|
|
||||
|
# If not found, search for any directory with the Maven executable (for snapshots) |
||||
|
if (!$actualDistributionDir) { |
||||
|
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { |
||||
|
$testPath = Join-Path $_.FullName "bin/$MVN_CMD" |
||||
|
if (Test-Path -Path $testPath -PathType Leaf) { |
||||
|
$actualDistributionDir = $_.Name |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!$actualDistributionDir) { |
||||
|
Write-Error "Could not find Maven distribution directory in extracted archive" |
||||
|
} |
||||
|
|
||||
|
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" |
||||
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null |
||||
|
try { |
||||
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null |
||||
|
} catch { |
||||
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { |
||||
|
Write-Error "fail to move MAVEN_HOME" |
||||
|
} |
||||
|
} finally { |
||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } |
||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } |
||||
|
} |
||||
|
|
||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" |
||||
@ -0,0 +1,140 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
<parent> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-parent</artifactId> |
||||
|
<!-- <version>3.5.7</version> --> |
||||
|
<version>2.5.4</version> |
||||
|
<relativePath /> <!-- lookup parent from repository --> |
||||
|
</parent> |
||||
|
<groupId>com.gcc_it_cell_services</groupId> |
||||
|
<artifactId>gcc-it-cell-services</artifactId> |
||||
|
<version>0.0.1-SNAPSHOT</version> |
||||
|
<name>gcc-it-cell-services</name> |
||||
|
<description>Springboot Project For GCC IT cell services</description> |
||||
|
<url /> |
||||
|
<licenses> |
||||
|
<license /> |
||||
|
</licenses> |
||||
|
<developers> |
||||
|
<developer /> |
||||
|
</developers> |
||||
|
<scm> |
||||
|
<connection /> |
||||
|
<developerConnection /> |
||||
|
<tag /> |
||||
|
<url /> |
||||
|
</scm> |
||||
|
<properties> |
||||
|
<java.version>17</java.version> |
||||
|
</properties> |
||||
|
<dependencies> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-data-rest</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-validation</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-devtools</artifactId> |
||||
|
<scope>runtime</scope> |
||||
|
<optional>true</optional> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.oracle.database.jdbc</groupId> |
||||
|
<artifactId>ojdbc11</artifactId> |
||||
|
<scope>runtime</scope> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.mysql</groupId> |
||||
|
<artifactId>mysql-connector-j</artifactId> |
||||
|
<version>8.0.33</version> <!-- Replace with the appropriate version --> |
||||
|
<scope>runtime</scope> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.projectlombok</groupId> |
||||
|
<artifactId>lombok</artifactId> |
||||
|
<optional>true</optional> |
||||
|
<version>1.18.32</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.github.vladimir-bukhtoyarov</groupId> |
||||
|
<artifactId>bucket4j-core</artifactId> |
||||
|
<version>7.6.0</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||
|
<scope>test</scope> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-security</artifactId> |
||||
|
<version>2.5.4</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.security</groupId> |
||||
|
<artifactId>spring-security-test</artifactId> |
||||
|
<scope>test</scope> |
||||
|
</dependency> |
||||
|
<!-- <dependency> |
||||
|
<groupId>jakarta.persistence</groupId> |
||||
|
<artifactId>jakarta.persistence-api</artifactId> |
||||
|
<version>2.2.1</version> |
||||
|
</dependency> --> |
||||
|
<!-- <dependency> |
||||
|
<groupId>jakarta.validation</groupId> |
||||
|
<artifactId>jakarta.validation-api</artifactId> |
||||
|
<version>2.0.1</version> |
||||
|
</dependency> --> |
||||
|
</dependencies> |
||||
|
<build> |
||||
|
<finalName>gcc-it-cell-services</finalName> |
||||
|
<plugins> |
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<version>3.8.1</version> |
||||
|
<configuration> |
||||
|
<annotationProcessorPaths> |
||||
|
<path> |
||||
|
<groupId>org.projectlombok</groupId> |
||||
|
<artifactId>lombok</artifactId> |
||||
|
<version>1.18.32</version> |
||||
|
</path> |
||||
|
</annotationProcessorPaths> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
|
<configuration> |
||||
|
<excludes> |
||||
|
<exclude> |
||||
|
<groupId>org.projectlombok</groupId> |
||||
|
<artifactId>lombok</artifactId> |
||||
|
</exclude> |
||||
|
</excludes> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
</plugins> |
||||
|
</build> |
||||
|
</project> |
||||
@ -0,0 +1,17 @@ |
|||||
|
package com.gcc_it_cell_services.springboot; |
||||
|
|
||||
|
import org.springframework.boot.SpringApplication; |
||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; |
||||
|
import org.springframework.scheduling.annotation.EnableScheduling; |
||||
|
|
||||
|
@SpringBootApplication |
||||
|
@EnableJpaAuditing(auditorAwareRef = "auditorAware") |
||||
|
@EnableScheduling |
||||
|
public class GccItCellServicesApplication { |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
SpringApplication.run(GccItCellServicesApplication.class, args); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.configuration; |
||||
|
|
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.data.domain.AuditorAware; |
||||
|
import org.springframework.security.core.Authentication; |
||||
|
import org.springframework.security.core.context.SecurityContextHolder; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.transaction.annotation.Propagation; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.AppUser; |
||||
|
import com.gcc_it_cell_services.springboot.repository.AppUserRepository; |
||||
|
|
||||
|
@Component("auditorAware") |
||||
|
public class AuditorAwareConfiguration implements AuditorAware<AppUser> { |
||||
|
|
||||
|
@Autowired |
||||
|
private AppUserRepository appUsrRepo; |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) |
||||
|
public Optional<AppUser> getCurrentAuditor() { |
||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); |
||||
|
|
||||
|
if (authentication == null || !authentication.isAuthenticated()) { |
||||
|
return Optional.empty(); |
||||
|
} |
||||
|
|
||||
|
// If it's already AppUser, no need to hit DB |
||||
|
Object principal = authentication.getPrincipal(); |
||||
|
if (principal instanceof AppUser user) { |
||||
|
return Optional.of(user); |
||||
|
} |
||||
|
|
||||
|
// Fetch AppUser from DB using username |
||||
|
String username = authentication.getName(); |
||||
|
return appUsrRepo.findByUsrName(username); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.configuration; |
||||
|
|
||||
|
import java.time.Duration; |
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
|
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
|
||||
|
import io.github.bucket4j.Bandwidth; |
||||
|
import io.github.bucket4j.Bucket; |
||||
|
|
||||
|
@Configuration |
||||
|
public class RateLimitConfiguration { |
||||
|
|
||||
|
// Map to store buckets for each key |
||||
|
private final Map<String, Bucket> rateLimitCache = new ConcurrentHashMap<String, Bucket>(); |
||||
|
|
||||
|
public Bucket resolveBucket(String key) { |
||||
|
return rateLimitCache.computeIfAbsent(key, newKey -> createNewBucket(newKey)); |
||||
|
} |
||||
|
|
||||
|
private Bucket createNewBucket(String key) { |
||||
|
// Set different limits based on key type |
||||
|
if (key.startsWith("OTP_")) { |
||||
|
// OTP = 5 req/min |
||||
|
return Bucket.builder().addLimit(Bandwidth.simple(5, Duration.ofMinutes(1))).build(); |
||||
|
} else if (key.startsWith("LOGIN_")) { |
||||
|
// Login = 5 req/min |
||||
|
return Bucket.builder().addLimit(Bandwidth.simple(5, Duration.ofMinutes(1))).build(); |
||||
|
} else if (key.startsWith("IP_")) { |
||||
|
// Public IP = 35 req/min |
||||
|
return Bucket.builder().addLimit(Bandwidth.simple(35, Duration.ofMinutes(1))).build(); |
||||
|
} else if (key.startsWith("USER_")) { |
||||
|
// Authenticated = 50/min |
||||
|
return Bucket.builder().addLimit(Bandwidth.simple(35, Duration.ofMinutes(1))).build(); |
||||
|
} |
||||
|
|
||||
|
// Default fallback |
||||
|
return Bucket.builder().addLimit(Bandwidth.simple(40, Duration.ofMinutes(1))).build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.configuration; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import javax.servlet.http.HttpSessionEvent; |
||||
|
import javax.servlet.http.HttpSessionListener; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; |
||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; |
||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
||||
|
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.AuthenticationEntryPoint; |
||||
|
import org.springframework.security.web.access.AccessDeniedHandler; |
||||
|
import org.springframework.security.web.csrf.CookieCsrfTokenRepository; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.service.HomeService; |
||||
|
|
||||
|
@Configuration |
||||
|
@EnableWebSecurity |
||||
|
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { |
||||
|
|
||||
|
/* |
||||
|
* @Value("${app.security.force-https}") private boolean isForceHttps; |
||||
|
*/ |
||||
|
|
||||
|
@Autowired |
||||
|
private HomeService homeSer; |
||||
|
|
||||
|
// List of all admin-only endpoints |
||||
|
private final List<String> adminEndpoints = List.of("/urls/add-url-data", "/urls/upd-url-data", "/api/urls/**"); |
||||
|
|
||||
|
@Bean |
||||
|
@Override |
||||
|
protected AuthenticationManager authenticationManager() throws Exception { |
||||
|
return super.authenticationManager(); |
||||
|
} |
||||
|
|
||||
|
// Return 404 for unauthenticated requests to admin endpoints |
||||
|
private final AuthenticationEntryPoint notFoundEntryPoint = (req, res, authException) -> { |
||||
|
if (adminEndpoints.contains(req.getRequestURI())) { |
||||
|
res.sendError(404); // hide existence |
||||
|
} else { |
||||
|
res.sendRedirect(req.getContextPath() + "/login"); // fallback for login for other endpoints |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Return 404 for access denied on admin endpoints |
||||
|
private final AccessDeniedHandler adminNotFoundAccessDeniedHandler = (req, res, accessDeniedException) -> { |
||||
|
if (adminEndpoints.contains(req.getRequestURI())) { |
||||
|
res.sendError(404); // hide existence |
||||
|
} else { |
||||
|
res.sendRedirect(req.getContextPath() + "/login"); // fallback for login for other endpoints |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
@Bean |
||||
|
PasswordEncoder passwordEncoder() { |
||||
|
return new BCryptPasswordEncoder(12); |
||||
|
} |
||||
|
|
||||
|
@Bean |
||||
|
public DaoAuthenticationProvider daoAuthProvider() { |
||||
|
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); |
||||
|
provider.setUserDetailsService(homeSer); |
||||
|
provider.setPasswordEncoder(passwordEncoder()); |
||||
|
provider.setHideUserNotFoundExceptions(false); // THIS FIXES USER NOT FOUND EXCEPTION ISSUE |
||||
|
return provider; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception { |
||||
|
auth.authenticationProvider(daoAuthProvider()); // use custom provider |
||||
|
} |
||||
|
|
||||
|
// disable default login form |
||||
|
@Override |
||||
|
protected void configure(HttpSecurity http) throws Exception { |
||||
|
// CSRF with Cookie token repository |
||||
|
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); |
||||
|
|
||||
|
// Force HTTPS if enabled |
||||
|
// if (isForceHttps) { |
||||
|
/* http.redirectToHttps(withDefaults()); */ |
||||
|
// http.requiresChannel().anyRequest().requiresSecure(); |
||||
|
// } |
||||
|
|
||||
|
// Authorization rules |
||||
|
http.authorizeRequests().antMatchers("/urls/add-url-data", "/urls/upd-url-data", "/api/urls/**") |
||||
|
.hasRole("ADMIN") // Admin-only |
||||
|
.antMatchers("/api/val-app-usr-login", "/api/get-role", "/api/get-otp", "/api/verify-otp", |
||||
|
"/api/get-mob-num", "/clr-otp-ses", "/", "/redirect", "/login", "/view-login-det", |
||||
|
"/get-{urlCatName}-urls", "/usr-logout") |
||||
|
.permitAll() // Publicly accessible |
||||
|
.anyRequest().permitAll(); // Requires authentication for any other path |
||||
|
|
||||
|
// Session management |
||||
|
http.sessionManagement().sessionFixation().migrateSession() // Prevent session fixation attacks |
||||
|
.invalidSessionUrl("/") // Handle session expiration |
||||
|
.maximumSessions(2).maxSessionsPreventsLogin(true).and() |
||||
|
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED); // Limit concurrent logins |
||||
|
|
||||
|
// Exception handling |
||||
|
http.exceptionHandling().authenticationEntryPoint(notFoundEntryPoint) |
||||
|
.accessDeniedHandler(adminNotFoundAccessDeniedHandler); |
||||
|
|
||||
|
// Custom login/logout pages |
||||
|
http.formLogin().loginPage("/login").permitAll().failureUrl("/login"); // Redirect on failed login |
||||
|
http.logout().logoutUrl("/usr-logout").logoutSuccessUrl("/").invalidateHttpSession(true) |
||||
|
.clearAuthentication(true).permitAll(); |
||||
|
} |
||||
|
|
||||
|
// Set the session timeout duration programmatically |
||||
|
@Bean |
||||
|
public HttpSessionListener httpSessionListener() { |
||||
|
return new HttpSessionListener() { |
||||
|
@Override |
||||
|
public void sessionCreated(HttpSessionEvent event) { |
||||
|
// Set the timeout period (e.g., 30 minutes) |
||||
|
event.getSession().setMaxInactiveInterval(30 * 60); // 30 minutes |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void sessionDestroyed(HttpSessionEvent event) { |
||||
|
// No specific action on session destruction |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.configuration; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.interceptor.RateLimitInterceptor; |
||||
|
|
||||
|
@Configuration |
||||
|
public class WebConfiguration implements WebMvcConfigurer { |
||||
|
|
||||
|
@Autowired |
||||
|
private RateLimitInterceptor rateLimitInterceptor; |
||||
|
|
||||
|
@Override |
||||
|
public void addInterceptors(InterceptorRegistry interceptorRegistry) { |
||||
|
// Fetch the application context path dynamically |
||||
|
String contextPath = System.getProperty("server.servlet.context-path", ""); |
||||
|
|
||||
|
// Define all paths for rate-limiting |
||||
|
String[] apiPaths = { "/api/**" }; |
||||
|
String[] publicPaths = { "/", "/login", "/view-login-det", "/redirect", "/urls/get-**" }; |
||||
|
|
||||
|
// Prepend context path dynamically |
||||
|
for (int i = 0; i < apiPaths.length; i++) { |
||||
|
apiPaths[i] = contextPath + apiPaths[i]; |
||||
|
} |
||||
|
for (int i = 0; i < publicPaths.length; i++) { |
||||
|
publicPaths[i] = contextPath + publicPaths[i]; |
||||
|
} |
||||
|
|
||||
|
// Register interceptor for API + public paths |
||||
|
interceptorRegistry.addInterceptor(rateLimitInterceptor).addPathPatterns(apiPaths).addPathPatterns(publicPaths); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.controller; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.security.core.Authentication; |
||||
|
import org.springframework.stereotype.Controller; |
||||
|
import org.springframework.ui.Model; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.service.UrlDataService; |
||||
|
|
||||
|
@Controller |
||||
|
@RequestMapping("/") |
||||
|
public class HomeController { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataService urlDataSer; |
||||
|
|
||||
|
@GetMapping({ "/", "" }) |
||||
|
public String home(Model model, Authentication auth) { |
||||
|
List<UrlDataResponseDTO> urls = urlDataSer.getAllUrls(); |
||||
|
// Add role information to the model for conditional navbar rendering |
||||
|
boolean isAdmin = auth != null |
||||
|
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); |
||||
|
model.addAttribute("isAdmin", isAdmin); |
||||
|
model.addAttribute("curPage", "home"); |
||||
|
model.addAttribute("urls", urls); |
||||
|
return "home/home"; |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/login") |
||||
|
public String login() { |
||||
|
return "home/login"; |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/view-login-det") |
||||
|
public String viewLoginDet(Model model, Authentication auth) { |
||||
|
// Add role information to the model for conditional navbar rendering |
||||
|
boolean isAdmin = auth != null |
||||
|
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); |
||||
|
model.addAttribute("isAdmin", isAdmin); |
||||
|
List<UrlDataResponseDTO> urls = urlDataSer.getAllUrlsWithUsrNameAndPass(); |
||||
|
model.addAttribute("urls", urls); |
||||
|
model.addAttribute("curPage", "view-login-det"); |
||||
|
return "home/view-login-details"; |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/redirect") |
||||
|
public void usrRedirect(Authentication auth, HttpServletResponse res, HttpServletRequest req) throws IOException { |
||||
|
if (auth == null || !auth.isAuthenticated()) { |
||||
|
res.sendError(404); // hide endpoint if not logged in |
||||
|
return; |
||||
|
} |
||||
|
boolean isAdmin = auth != null |
||||
|
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); |
||||
|
if (isAdmin) { |
||||
|
res.sendRedirect(req.getContextPath() + "/"); // admin goes to home |
||||
|
} else { |
||||
|
res.sendError(404); // non-admin users → hide endpoint |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.controller; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.security.core.Authentication; |
||||
|
import org.springframework.stereotype.Controller; |
||||
|
import org.springframework.ui.Model; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.service.UrlDataService; |
||||
|
|
||||
|
@Controller |
||||
|
@RequestMapping({ "/urls", "/itcell/urls" }) |
||||
|
public class UrlDataController { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataService urlDataSer; |
||||
|
|
||||
|
@GetMapping("/add-url-data") |
||||
|
public String addUrl(Model model, Authentication auth) { |
||||
|
// Add role information to the model for conditional navbar rendering |
||||
|
boolean isAdmin = auth != null |
||||
|
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); |
||||
|
model.addAttribute("isAdmin", isAdmin); |
||||
|
model.addAttribute("curPage", "add-url"); |
||||
|
return "urls/add-url-data"; |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/get-{urlCatName}-urls") |
||||
|
public String getUrlsByCategory(@PathVariable("urlCatName") String urlCatName, Model model, Authentication auth) { |
||||
|
// Add role information to the model for conditional navbar rendering |
||||
|
urlCatName = urlCatName.replace("-", " "); |
||||
|
List<UrlDataResponseDTO> urls = urlDataSer.getUrlsByUrlCatName(urlCatName); |
||||
|
urlCatName = urls.get(0).getUrlCatResDTO().getUrlCatName(); |
||||
|
boolean isAdmin = auth != null |
||||
|
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); |
||||
|
model.addAttribute("isAdmin", isAdmin); |
||||
|
model.addAttribute("urlCatName", urlCatName); |
||||
|
model.addAttribute("urls", urls); |
||||
|
return "urls/get-urls-by-url-category"; // Thymeleaf view name |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/upd-url-data") |
||||
|
public String updUrl(Model model, Authentication auth) { |
||||
|
// Add role information to the model for conditional navbar rendering |
||||
|
List<UrlDataResponseDTO> urls = urlDataSer.getAllUrlsSortedByUrlName(); |
||||
|
List<StatusResponseDTO> stats = urlDataSer.getAllStats(); |
||||
|
boolean isAdmin = auth != null |
||||
|
&& auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); |
||||
|
model.addAttribute("isAdmin", isAdmin); |
||||
|
model.addAttribute("curPage", "upd-url"); |
||||
|
model.addAttribute("stats", stats); |
||||
|
model.addAttribute("urls", urls); |
||||
|
return "urls/update-url-data"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.interceptor; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
|
||||
|
import org.springframework.security.core.Authentication; |
||||
|
import org.springframework.security.core.context.SecurityContextHolder; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.gcc_it_cell_services.springboot.configuration.RateLimitConfiguration; |
||||
|
import com.gcc_it_cell_services.springboot.util.MobileNumberTokenStore; |
||||
|
|
||||
|
import io.github.bucket4j.Bucket; |
||||
|
|
||||
|
@Component |
||||
|
public class RateLimitInterceptor implements HandlerInterceptor { |
||||
|
|
||||
|
private final RateLimitConfiguration rateLimitConfig; |
||||
|
private final MobileNumberTokenStore mobNumTokenStore; |
||||
|
|
||||
|
public RateLimitInterceptor(RateLimitConfiguration rateLimitConfig, MobileNumberTokenStore mobNumTokenStore) { |
||||
|
this.rateLimitConfig = rateLimitConfig; |
||||
|
this.mobNumTokenStore = mobNumTokenStore; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception { |
||||
|
String key = extractKey(req); |
||||
|
Bucket bucket = rateLimitConfig.resolveBucket(key); |
||||
|
if (bucket.tryConsume(1)) { |
||||
|
// allow request |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
res.setStatus(429); |
||||
|
res.setContentType("application/json"); // <--- important |
||||
|
Map<String, Object> resObj = new HashMap<String, Object>(); |
||||
|
resObj.put("result", false); |
||||
|
resObj.put("error", "Too many requests - Please try after some time"); |
||||
|
|
||||
|
// Write JSON |
||||
|
ObjectMapper mapper = new ObjectMapper(); |
||||
|
res.getWriter().write(mapper.writeValueAsString(resObj)); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private String extractKey(HttpServletRequest req) { |
||||
|
String uri = req.getRequestURI(); |
||||
|
String contextPath = req.getContextPath(); |
||||
|
if (uri.startsWith(contextPath)) { |
||||
|
uri = uri.substring(contextPath.length()); |
||||
|
} |
||||
|
|
||||
|
// OTP endpoints → per mobile number |
||||
|
if (uri.matches("/api/(get|verify)-otp")) { |
||||
|
String mobNumId = req.getParameter("mobNumId"); |
||||
|
if (mobNumId != null && !mobNumId.trim().isBlank()) { |
||||
|
// Retrieve actual mobile number from MobileNumberTokenStore |
||||
|
String mobNum = mobNumTokenStore.getMobNum(mobNumId); |
||||
|
if (mobNum != null && !mobNum.trim().isBlank()) { |
||||
|
return "OTP_" + mobNum; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Login endpoint → per username |
||||
|
if (uri.contains("/api/val-app-usr-login")) { |
||||
|
String usrName = req.getParameter("usrName"); |
||||
|
if (usrName != null && !usrName.trim().isBlank()) { |
||||
|
return "LOGIN_" + usrName; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Admin endpoints → per authenticated username |
||||
|
if (uri.startsWith("/urls/") || uri.startsWith("/api/urls/") || uri.startsWith("/itcell/urls/")) { |
||||
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); |
||||
|
if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getName())) { |
||||
|
return "USER_" + auth.getName(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Authenticated endpoints → per username |
||||
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); |
||||
|
if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getName())) { |
||||
|
return "USER_" + auth.getName(); |
||||
|
} |
||||
|
|
||||
|
// Dynamic public endpoints → fallback to IP |
||||
|
if (uri.matches("/urls/get-[a-zA-Z0-9\\-]+-urls")) { |
||||
|
return "IP_" + req.getRemoteAddr(); |
||||
|
} |
||||
|
|
||||
|
// Public endpoints → fallback to IP |
||||
|
return "IP_" + req.getRemoteAddr(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.mapper; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.OtpLog; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.OtpLogRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.OtpLogResponseDTO; |
||||
|
|
||||
|
@Component |
||||
|
public class OtpLogMapper { |
||||
|
|
||||
|
public OtpLog toOtpLogEnt(OtpLogRequestDTO otpLogReqDTO) { |
||||
|
OtpLog otpLog = new OtpLog(); |
||||
|
otpLog.setOtp(otpLogReqDTO.getOtp()); |
||||
|
otpLog.setOtpStat(otpLogReqDTO.getOtpStat()); |
||||
|
otpLog.setOtpReqMobNum(otpLogReqDTO.getOtpReqMobNum()); |
||||
|
otpLog.setOtpResMobNum(otpLogReqDTO.getOtpResMobNum()); |
||||
|
otpLog.setOtpLogMsg(otpLogReqDTO.getOtpLogMsg()); |
||||
|
return otpLog; |
||||
|
} |
||||
|
|
||||
|
public OtpLog toOtpLogEnt(OtpLogResponseDTO otpLogResDTO) { |
||||
|
OtpLog otpLog = new OtpLog(); |
||||
|
otpLog.setLogId(otpLogResDTO.getLogId()); |
||||
|
otpLog.setOtp(otpLogResDTO.getOtp()); |
||||
|
otpLog.setOtpLogMsg(otpLogResDTO.getOtpLogMsg()); |
||||
|
otpLog.setOtpReqMobNum(otpLogResDTO.getOtpReqMobNum()); |
||||
|
otpLog.setOtpResMobNum(otpLogResDTO.getOtpResMobNum()); |
||||
|
otpLog.setOtpStat(otpLogResDTO.getOtpStat()); |
||||
|
return otpLog; |
||||
|
} |
||||
|
|
||||
|
public OtpLogResponseDTO toOtpLogResDTO(OtpLog otpLog) { |
||||
|
return new OtpLogResponseDTO(otpLog.getLogId(), otpLog.getOtp(), otpLog.getOtpStat(), otpLog.getOtpReqMobNum(), |
||||
|
otpLog.getOtpResMobNum(), otpLog.getOtpLogMsg()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.mapper; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.Status; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO; |
||||
|
|
||||
|
@Component |
||||
|
public class StatusMapper { |
||||
|
|
||||
|
public StatusResponseDTO toStatResDTO(Status stat) { |
||||
|
return new StatusResponseDTO(stat.getStatId(), stat.getStatName()); |
||||
|
} |
||||
|
|
||||
|
public Status toStatEnt(StatusResponseDTO statResDTO) { |
||||
|
return new Status(statResDTO.getStatId(), statResDTO.getStatName()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.mapper; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.UrlCategory; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.UrlCategoryRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO; |
||||
|
|
||||
|
@Component |
||||
|
public class UrlCategoryMapper { |
||||
|
|
||||
|
public UrlCategoryResponseDTO toUrlCatResDTO(UrlCategory urlCat) { |
||||
|
return new UrlCategoryResponseDTO(urlCat.getUrlCatId(), urlCat.getUrlCatName()); |
||||
|
} |
||||
|
|
||||
|
public UrlCategory toUrlCatEnt(UrlCategoryRequestDTO urlCatReqDTO) { |
||||
|
UrlCategory urlCat = new UrlCategory(); |
||||
|
urlCat.setUrlCatName(urlCatReqDTO.getUrlCatName()); |
||||
|
return urlCat; |
||||
|
} |
||||
|
|
||||
|
public UrlCategory toUrlCatEnt(UrlCategoryResponseDTO urlCatResDTO) { |
||||
|
UrlCategory urlCat = new UrlCategory(); |
||||
|
urlCat.setUrlCatId(urlCatResDTO.getUrlCatId()); |
||||
|
urlCat.setUrlCatName(urlCatResDTO.getUrlCatName()); |
||||
|
return urlCat; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.mapper; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.UrlData; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.UrlDataRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO; |
||||
|
|
||||
|
@Component |
||||
|
public class UrlDataMapper { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlCategoryMapper urlCatMap; |
||||
|
|
||||
|
@Autowired |
||||
|
private StatusMapper statMap; |
||||
|
|
||||
|
public UrlData toUrlDatEnt(UrlDataRequestDTO urlDataReqDTO) { |
||||
|
UrlData urlData = new UrlData(); |
||||
|
urlData.setUrlDataLink(urlDataReqDTO.getUrlDataLink()); |
||||
|
urlData.setUrlDataName(urlDataReqDTO.getUrlDataName()); |
||||
|
urlData.setUrlDataUsrName(urlDataReqDTO.getUrlDataUsrName()); |
||||
|
urlData.setUrlDataUsrPass(urlDataReqDTO.getUrlDataUsrPass()); |
||||
|
urlData.setUrlCat(urlCatMap.toUrlCatEnt(urlDataReqDTO.getUrlCatResDTO())); |
||||
|
urlData.setStatId(statMap.toStatEnt(urlDataReqDTO.getStatResDTO())); |
||||
|
return urlData; |
||||
|
} |
||||
|
|
||||
|
public UrlDataResponseDTO toUrlDataResDTO(UrlData urlData) { |
||||
|
return new UrlDataResponseDTO(urlData.getUrlDataId(), urlData.getUrlDataName(), urlData.getUrlDataLink(), |
||||
|
urlData.getUrlDataUsrPass(), urlData.getUrlDataUsrName(), urlCatMap.toUrlCatResDTO(urlData.getUrlCat()), |
||||
|
statMap.toStatResDTO(urlData.getStatId())); |
||||
|
} |
||||
|
|
||||
|
public UrlData toUrlDatEnt(UrlDataResponseDTO urlDataResDTO) { |
||||
|
// TODO Auto-generated method stub |
||||
|
UrlData urlData = new UrlData(); |
||||
|
urlData.setUrlDataId(urlDataResDTO.getUrlDataId()); |
||||
|
urlData.setUrlDataLink(urlDataResDTO.getUrlDataLink()); |
||||
|
urlData.setUrlDataName(urlDataResDTO.getUrlDataName()); |
||||
|
urlData.setUrlDataUsrName(urlDataResDTO.getUrlDataUsrName()); |
||||
|
urlData.setUrlDataUsrPass(urlDataResDTO.getUrlDataUsrPass()); |
||||
|
urlData.setUrlCat(urlCatMap.toUrlCatEnt(urlDataResDTO.getUrlCatResDTO())); |
||||
|
urlData.setStatId(statMap.toStatEnt(urlDataResDTO.getStatResDTO())); |
||||
|
return urlData; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.model; |
||||
|
|
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.FetchType; |
||||
|
import javax.persistence.GeneratedValue; |
||||
|
import javax.persistence.GenerationType; |
||||
|
import javax.persistence.Id; |
||||
|
import javax.persistence.JoinColumn; |
||||
|
import javax.persistence.ManyToOne; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.validation.constraints.NotBlank; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonBackReference; |
||||
|
|
||||
|
// import jakarta.validation.constraints.NotBlank; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Entity |
||||
|
// @Table(name = "tbl_app_usr_det_2", schema = "fileadjust") |
||||
|
@Table(name = "tbl_app_usr_det") |
||||
|
/* @Table(name = "tbl_app_usr_det", schema = "gcc") */ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true, exclude = { "usrRole", "usrPass", "mobStatId", |
||||
|
"usrStatId" }) |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
public class AppUser { |
||||
|
@Id |
||||
|
@Column(name = "usr_id", nullable = false, updatable = false) |
||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
|
/* |
||||
|
* @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = |
||||
|
* "app_usr_seq_2") |
||||
|
* |
||||
|
* @SequenceGenerator(name = "app_usr_seq_2", sequenceName = "app_usr_id_seq_2", |
||||
|
* allocationSize = 1) |
||||
|
*/ |
||||
|
private Integer usrId; |
||||
|
|
||||
|
@Column(name = "usr_name", nullable = false) |
||||
|
@NotBlank |
||||
|
private String usrName; |
||||
|
|
||||
|
@Column(name = "usr_pass", nullable = false) |
||||
|
@NotBlank |
||||
|
private String usrPass; |
||||
|
|
||||
|
@Column(name = "usr_mob_num", nullable = false) |
||||
|
@NotBlank |
||||
|
private String usrMobNum; |
||||
|
|
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "mob_stat_id", referencedColumnName = "stat_id", nullable = false) |
||||
|
private Status mobStatId; |
||||
|
|
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "usr_stat_id", referencedColumnName = "stat_id", nullable = false) |
||||
|
private Status usrStatId; |
||||
|
|
||||
|
@ManyToOne |
||||
|
@JoinColumn(name = "usr_role_id", nullable = false, referencedColumnName = "usr_role_id") |
||||
|
@JsonBackReference |
||||
|
private UserRole usrRole; |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.model; |
||||
|
|
||||
|
import java.time.Instant; |
||||
|
import java.time.temporal.ChronoUnit; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.EntityListeners; |
||||
|
import javax.persistence.GeneratedValue; |
||||
|
import javax.persistence.GenerationType; |
||||
|
import javax.persistence.Id; |
||||
|
import javax.persistence.PrePersist; |
||||
|
import javax.persistence.PreUpdate; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.persistence.Temporal; |
||||
|
import javax.persistence.TemporalType; |
||||
|
import javax.validation.constraints.NotBlank; |
||||
|
import javax.validation.constraints.NotNull; |
||||
|
|
||||
|
import org.springframework.data.annotation.CreatedDate; |
||||
|
import org.springframework.data.annotation.LastModifiedDate; |
||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Entity |
||||
|
@EntityListeners(AuditingEntityListener.class) |
||||
|
@Table(name = "tbl_otp_log_det") |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
public class OtpLog { |
||||
|
|
||||
|
@Id |
||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
|
@Column(name = "log_id", nullable = false, updatable = false) |
||||
|
private Integer logId; |
||||
|
|
||||
|
@Column(name = "otp", nullable = false, updatable = false) |
||||
|
@NotNull |
||||
|
private Integer otp; |
||||
|
|
||||
|
@Column(name = "otp_stat", nullable = false) |
||||
|
@NotBlank |
||||
|
private String otpStat; |
||||
|
|
||||
|
@Column(name = "otp_req_mob_num", nullable = false, updatable = false) |
||||
|
@NotBlank |
||||
|
private String otpReqMobNum; |
||||
|
|
||||
|
@Column(name = "otp_res_mob_num", nullable = false) |
||||
|
@NotBlank |
||||
|
private String otpResMobNum; |
||||
|
|
||||
|
@Column(name = "otp_log_msg", nullable = false) |
||||
|
@NotBlank |
||||
|
private String otpLogMsg; |
||||
|
|
||||
|
// User timestamp |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(name = "usr_req_timestamp", nullable = false, updatable = false, columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date usrReqTimestamp; |
||||
|
|
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(name = "usr_res_timestamp", nullable = false, columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date usrResTimestamp; |
||||
|
|
||||
|
// DB-managed timestamps |
||||
|
@CreatedDate |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(updatable = false, nullable = false, name = "db_req_timestamp", columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date dbReqTimestamp; |
||||
|
|
||||
|
@LastModifiedDate |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(nullable = false, name = "db_res_timestamp", columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date dbResTimestamp; |
||||
|
|
||||
|
@PrePersist |
||||
|
public void onCreate() { |
||||
|
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS)); |
||||
|
this.usrReqTimestamp = now; |
||||
|
this.usrResTimestamp = now; |
||||
|
this.dbReqTimestamp = now; |
||||
|
this.dbResTimestamp = now; |
||||
|
} |
||||
|
|
||||
|
@PreUpdate |
||||
|
public void onUpdate() { |
||||
|
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS)); |
||||
|
this.usrResTimestamp = now; |
||||
|
this.dbResTimestamp = now; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.model; |
||||
|
|
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.GeneratedValue; |
||||
|
import javax.persistence.GenerationType; |
||||
|
import javax.persistence.Id; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.validation.constraints.NotBlank; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Entity |
||||
|
@ToString(includeFieldNames = true, doNotUseGetters = true) |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@Table(name = "tbl_stat_det") |
||||
|
public class Status { |
||||
|
|
||||
|
@Id |
||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
|
@Column(name = "stat_id", nullable = false, updatable = false) |
||||
|
private Long statId; |
||||
|
|
||||
|
@Column(name = "stat_name", nullable = false) |
||||
|
@NotBlank |
||||
|
private String statName; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.model; |
||||
|
|
||||
|
import java.time.Instant; |
||||
|
import java.time.temporal.ChronoUnit; |
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import javax.persistence.CascadeType; |
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.EntityListeners; |
||||
|
import javax.persistence.FetchType; |
||||
|
import javax.persistence.GeneratedValue; |
||||
|
import javax.persistence.GenerationType; |
||||
|
import javax.persistence.Id; |
||||
|
import javax.persistence.JoinColumn; |
||||
|
import javax.persistence.ManyToOne; |
||||
|
import javax.persistence.OneToMany; |
||||
|
import javax.persistence.PrePersist; |
||||
|
import javax.persistence.PreUpdate; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.persistence.Temporal; |
||||
|
import javax.persistence.TemporalType; |
||||
|
import javax.validation.constraints.NotBlank; |
||||
|
|
||||
|
import org.springframework.data.annotation.CreatedBy; |
||||
|
import org.springframework.data.annotation.CreatedDate; |
||||
|
import org.springframework.data.annotation.LastModifiedBy; |
||||
|
import org.springframework.data.annotation.LastModifiedDate; |
||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonManagedReference; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Entity |
||||
|
@Table(name = "tbl_url_cat_det") |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(includeFieldNames = true, doNotUseGetters = true, exclude = { "urlDatas", "createdBy", "updatedBy" }) |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@EntityListeners(AuditingEntityListener.class) |
||||
|
public class UrlCategory { |
||||
|
|
||||
|
@Id |
||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
|
@Column(name = "url_cat_id", nullable = false, updatable = false) |
||||
|
private Long urlCatId; |
||||
|
|
||||
|
@Column(name = "url_cat_name", nullable = false) |
||||
|
@NotBlank |
||||
|
private String urlCatName; |
||||
|
|
||||
|
@OneToMany(mappedBy = "urlCat", orphanRemoval = true, cascade = CascadeType.ALL) |
||||
|
@JsonManagedReference |
||||
|
private List<UrlData> urlDatas; |
||||
|
|
||||
|
@CreatedBy |
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "created_by_usr_id", referencedColumnName = "usr_id", updatable = false, nullable = false) |
||||
|
private AppUser createdBy; |
||||
|
|
||||
|
@LastModifiedBy |
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "updated_by_usr_id", referencedColumnName = "usr_id", nullable = false) |
||||
|
private AppUser updatedBy; |
||||
|
|
||||
|
// User timestamp |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(name = "usr_created_timestamp", nullable = false, updatable = false, columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date usrCreatedTimestamp; |
||||
|
|
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(name = "usr_updated_timestamp", nullable = false, columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date usrUpdatedTimestamp; |
||||
|
|
||||
|
// DB-managed timestamps |
||||
|
@CreatedDate |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(updatable = false, nullable = false, name = "db_created_timestamp", columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date dbCreatedTimestamp; |
||||
|
|
||||
|
@LastModifiedDate |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(nullable = false, name = "db_updated_timestamp", columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date dbUpdatedTimestamp; |
||||
|
|
||||
|
@PrePersist |
||||
|
public void onCreate() { |
||||
|
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS)); |
||||
|
this.usrCreatedTimestamp = now; |
||||
|
this.usrUpdatedTimestamp = now; |
||||
|
} |
||||
|
|
||||
|
@PreUpdate |
||||
|
public void onUpdate() { |
||||
|
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS)); |
||||
|
this.usrUpdatedTimestamp = now; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.model; |
||||
|
|
||||
|
import java.time.Instant; |
||||
|
import java.time.temporal.ChronoUnit; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.EntityListeners; |
||||
|
import javax.persistence.FetchType; |
||||
|
import javax.persistence.GeneratedValue; |
||||
|
import javax.persistence.GenerationType; |
||||
|
import javax.persistence.Id; |
||||
|
import javax.persistence.JoinColumn; |
||||
|
import javax.persistence.ManyToOne; |
||||
|
import javax.persistence.PrePersist; |
||||
|
import javax.persistence.PreUpdate; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.persistence.Temporal; |
||||
|
import javax.persistence.TemporalType; |
||||
|
import javax.validation.constraints.NotBlank; |
||||
|
|
||||
|
import org.springframework.data.annotation.CreatedBy; |
||||
|
import org.springframework.data.annotation.CreatedDate; |
||||
|
import org.springframework.data.annotation.LastModifiedBy; |
||||
|
import org.springframework.data.annotation.LastModifiedDate; |
||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonBackReference; |
||||
|
|
||||
|
// import jakarta.persistence.Column; |
||||
|
// import jakarta.persistence.Entity; |
||||
|
// import jakarta.persistence.GeneratedValue; |
||||
|
// import jakarta.persistence.GenerationType; |
||||
|
// import jakarta.persistence.Id; |
||||
|
// import jakarta.persistence.SequenceGenerator; |
||||
|
// import jakarta.persistence.Table; |
||||
|
// import jakarta.validation.constraints.NotBlank; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Entity |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@EntityListeners(AuditingEntityListener.class) |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true, exclude = { "urlCat", "createdBy", "updatedBy", "statId" }) |
||||
|
// @Table(name = "tbl_url_data_det", schema = "fileadjust") |
||||
|
@Table(name = "tbl_url_data_det") |
||||
|
public class UrlData { |
||||
|
|
||||
|
@Id |
||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
|
/* |
||||
|
* @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = |
||||
|
* "url_data_seq") |
||||
|
* |
||||
|
* @SequenceGenerator(name = "url_data_seq", sequenceName = "url_data_id_seq", |
||||
|
* allocationSize = 1) |
||||
|
*/ |
||||
|
@Column(name = "url_data_id", nullable = false, updatable = false) |
||||
|
private Long urlDataId; |
||||
|
|
||||
|
@Column(name = "url_data_link", nullable = false) |
||||
|
@NotBlank |
||||
|
private String urlDataLink; |
||||
|
|
||||
|
@Column(name = "url_data_name", nullable = false) |
||||
|
@NotBlank |
||||
|
private String urlDataName; |
||||
|
|
||||
|
@Column(name = "url_data_usr_pass", nullable = false) |
||||
|
@NotBlank |
||||
|
private String urlDataUsrPass; |
||||
|
|
||||
|
@Column(name = "url_data_usr_name", nullable = false) |
||||
|
@NotBlank |
||||
|
private String urlDataUsrName; |
||||
|
|
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "url_cat_id", referencedColumnName = "url_cat_id", nullable = false) |
||||
|
@JsonBackReference |
||||
|
private UrlCategory urlCat; |
||||
|
|
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "stat_id", referencedColumnName = "stat_id", nullable = false) |
||||
|
private Status statId; |
||||
|
|
||||
|
@CreatedBy |
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "created_by_usr_id", referencedColumnName = "usr_id", updatable = false, nullable = false) |
||||
|
private AppUser createdBy; |
||||
|
|
||||
|
@LastModifiedBy |
||||
|
@ManyToOne(fetch = FetchType.LAZY) |
||||
|
@JoinColumn(name = "updated_by_usr_id", referencedColumnName = "usr_id", nullable = false) |
||||
|
private AppUser updatedBy; |
||||
|
|
||||
|
// User timestamp |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(name = "usr_created_timestamp", nullable = false, updatable = false, columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date usrCreatedTimestamp; |
||||
|
|
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(name = "usr_updated_timestamp", nullable = false, columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date usrUpdatedTimestamp; |
||||
|
|
||||
|
// DB-managed timestamps |
||||
|
@CreatedDate |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(updatable = false, nullable = false, name = "db_created_timestamp", columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date dbCreatedTimestamp; |
||||
|
|
||||
|
@LastModifiedDate |
||||
|
@Temporal(TemporalType.TIMESTAMP) |
||||
|
@Column(nullable = false, name = "db_updated_timestamp", columnDefinition = "TIMESTAMP(0)") |
||||
|
private Date dbUpdatedTimestamp; |
||||
|
|
||||
|
@PrePersist |
||||
|
public void onCreate() { |
||||
|
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS)); |
||||
|
this.usrCreatedTimestamp = now; |
||||
|
this.usrUpdatedTimestamp = now; |
||||
|
} |
||||
|
|
||||
|
@PreUpdate |
||||
|
public void onUpdate() { |
||||
|
Date now = Date.from(Instant.now().truncatedTo(ChronoUnit.SECONDS)); |
||||
|
this.usrUpdatedTimestamp = now; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.model; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import javax.persistence.CascadeType; |
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.FetchType; |
||||
|
import javax.persistence.GeneratedValue; |
||||
|
import javax.persistence.GenerationType; |
||||
|
import javax.persistence.Id; |
||||
|
import javax.persistence.OneToMany; |
||||
|
import javax.persistence.Table; |
||||
|
import javax.validation.constraints.NotBlank; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonManagedReference; |
||||
|
|
||||
|
// import jakarta.persistence.CascadeType; |
||||
|
// import jakarta.persistence.Column; |
||||
|
// import jakarta.persistence.Entity; |
||||
|
// import jakarta.persistence.FetchType; |
||||
|
// import jakarta.persistence.GeneratedValue; |
||||
|
// import jakarta.persistence.GenerationType; |
||||
|
// import jakarta.persistence.Id; |
||||
|
// import jakarta.persistence.OneToMany; |
||||
|
// import jakarta.persistence.SequenceGenerator; |
||||
|
// import jakarta.persistence.Table; |
||||
|
// import jakarta.validation.constraints.NotBlank; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Entity |
||||
|
@ToString(includeFieldNames = true, doNotUseGetters = true, exclude = { "appUsrs" }) |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
/* @Table(name = "tbl_usr_role_det", schema = "gcc") */ |
||||
|
// @Table(name = "tbl_usr_role_det_2", schema = "fileadjust") |
||||
|
@Table(name = "tbl_usr_role_det") |
||||
|
public class UserRole { |
||||
|
|
||||
|
@Id |
||||
|
@Column(name = "usr_role_id", nullable = false, updatable = false) |
||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY) |
||||
|
/* |
||||
|
* @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = |
||||
|
* "usr_role_seq_2") |
||||
|
* |
||||
|
* @SequenceGenerator(name = "usr_role_seq_2", sequenceName = |
||||
|
* "usr_role_id_seq_2", allocationSize = 1) |
||||
|
*/ |
||||
|
private Integer usrRoleId; |
||||
|
|
||||
|
@Column(name = "usr_role_name", nullable = false) |
||||
|
@NotBlank |
||||
|
private String usrRoleName; |
||||
|
|
||||
|
@OneToMany(mappedBy = "usrRole", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) |
||||
|
@JsonManagedReference |
||||
|
private List<AppUser> appUsrs; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.repository; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
import org.springframework.data.jpa.repository.Query; |
||||
|
import org.springframework.data.repository.query.Param; |
||||
|
import org.springframework.stereotype.Repository; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.AppUser; |
||||
|
|
||||
|
@Repository |
||||
|
public interface AppUserRepository extends JpaRepository<AppUser, Integer> { |
||||
|
Optional<AppUser> findByUsrName(String usrName); |
||||
|
|
||||
|
@Query(value = """ |
||||
|
SELECT CASE WHEN COUNT(*) > 0 THEN TRUE ELSE FALSE END FROM tbl_app_usr_det |
||||
|
WHERE usr_mob_num = :mobNum """, nativeQuery = true) |
||||
|
Integer existsByUsrMobNum(@Param("mobNum") String mobNum); |
||||
|
|
||||
|
@Query("SELECT a.usrMobNum FROM AppUser a WHERE a.mobStatId.statId = 1") |
||||
|
List<String> findAllMobNums(); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.repository; |
||||
|
|
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
import org.springframework.stereotype.Repository; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.OtpLog; |
||||
|
|
||||
|
@Repository |
||||
|
public interface OtpLogRepository extends JpaRepository<OtpLog, Integer> { |
||||
|
|
||||
|
Optional<OtpLog> findTopByOtpReqMobNumOrderByDbReqTimestampDesc(String otpReqMobNum); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.repository; |
||||
|
|
||||
|
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
import org.springframework.stereotype.Repository; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.Status; |
||||
|
|
||||
|
@Repository |
||||
|
public interface StatusRepository extends JpaRepository<Status, Long> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.repository; |
||||
|
|
||||
|
import java.util.Optional; |
||||
|
|
||||
|
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
import org.springframework.data.jpa.repository.Query; |
||||
|
import org.springframework.data.repository.query.Param; |
||||
|
import org.springframework.stereotype.Repository; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.UrlCategory; |
||||
|
|
||||
|
@Repository |
||||
|
public interface UrlCategoryRepository extends JpaRepository<UrlCategory, Long> { |
||||
|
|
||||
|
@Query(nativeQuery = true, value = "SELECT * FROM tbl_url_cat_det WHERE url_cat_name = ?1") |
||||
|
Optional<UrlCategory> findUrlCatByUrlCatName(@Param("urlCatName") String urlCatName); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.repository; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
import org.springframework.data.jpa.repository.Query; |
||||
|
import org.springframework.data.repository.query.Param; |
||||
|
import org.springframework.stereotype.Repository; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.model.UrlData; |
||||
|
|
||||
|
@Repository |
||||
|
public interface UrlDataRepository extends JpaRepository<UrlData, Long> { |
||||
|
|
||||
|
@Query(value = "SELECT u FROM UrlData u ORDER BY LOWER(u.urlDataName)") |
||||
|
List<UrlData> findAllUrlsSortedByUrlName(); |
||||
|
|
||||
|
@Query(nativeQuery = true, value = """ |
||||
|
SELECT * FROM tbl_url_data_det WHERE url_data_usr_name != '-' AND url_data_usr_pass != '-' ORDER BY LOWER(url_data_name) """) |
||||
|
List<UrlData> findAllUrlsWithUsrNameAndPass(); |
||||
|
|
||||
|
@Query("SELECT u FROM UrlData u WHERE u.urlCat.urlCatId = ?1 ORDER BY LOWER(u.urlDataName)") |
||||
|
List<UrlData> findAllUrlsByUrlCatId(@Param("urlCatId") Long urlCatId); |
||||
|
|
||||
|
@Query("SELECT COUNT(u) FROM UrlData u WHERE u.urlCat.urlCatId = :urlCatId ORDER BY LOWER(u.urlDataName)") |
||||
|
Integer findUrlCountByUrlCatId(@Param("urlCatId") Long urlCatId); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
public class AddUrlDataRequestDTO { |
||||
|
|
||||
|
private String urlDataLink; |
||||
|
private String urlDataName; |
||||
|
private String urlDataUsrName; |
||||
|
private String urlDataUsrPass; |
||||
|
private String urlCatName; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
public class AppUserRequestDTO { |
||||
|
private String usrName; |
||||
|
private String usrPass; |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
public class GetOtpRequestDTO { |
||||
|
|
||||
|
private String mobNumId; // changed from mobNum to mobNumId (UUID) |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(includeFieldNames = true, doNotUseGetters = true) |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
public class OtpLogRequestDTO { |
||||
|
|
||||
|
private Integer otp; |
||||
|
private String otpStat; |
||||
|
private String otpReqMobNum; |
||||
|
private String otpResMobNum; |
||||
|
private String otpLogMsg; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
public class UpdateUrlDataRequestDTO { |
||||
|
private Long urlDataId; |
||||
|
private String urlDataName; |
||||
|
private String urlDataLink; |
||||
|
private String urlDataUsrPass; |
||||
|
private String urlDataUsrName; |
||||
|
private String urlCatName; |
||||
|
private Long statId; |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
public class UrlCategoryRequestDTO { |
||||
|
|
||||
|
private String urlCatName; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
public class UrlDataRequestDTO { |
||||
|
|
||||
|
private String urlDataLink; |
||||
|
private String urlDataName; |
||||
|
private String urlDataUsrPass; |
||||
|
private String urlDataUsrName; |
||||
|
private UrlCategoryResponseDTO urlCatResDTO; |
||||
|
private StatusResponseDTO statResDTO; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.request_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
public class VerifyOtpRequestDTO { |
||||
|
|
||||
|
private Integer otp; |
||||
|
private String mobNumId; // send the mobNumId from <select> |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.response_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
public class OtpLogResponseDTO { |
||||
|
|
||||
|
private Integer logId; |
||||
|
private Integer otp; |
||||
|
private String otpStat; |
||||
|
private String otpReqMobNum; |
||||
|
private String otpResMobNum; |
||||
|
private String otpLogMsg; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.response_dto; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.annotation.JsonInclude.Include; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@JsonInclude(value = Include.NON_NULL) |
||||
|
public class ServiceResponseDTO { |
||||
|
|
||||
|
private boolean resStat; |
||||
|
private String res; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.response_dto; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.annotation.JsonInclude.Include; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@JsonInclude(value = Include.NON_NULL) |
||||
|
public class StatusResponseDTO { |
||||
|
|
||||
|
private Long statId; |
||||
|
private String statName; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.response_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
public class UrlCategoryResponseDTO { |
||||
|
|
||||
|
private Long urlCatId; |
||||
|
private String urlCatName; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.response_dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString(doNotUseGetters = true, includeFieldNames = true) |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
public class UrlDataResponseDTO { |
||||
|
|
||||
|
private Long urlDataId; |
||||
|
private String urlDataName; |
||||
|
private String urlDataLink; |
||||
|
private String urlDataUsrPass; |
||||
|
private String urlDataUsrName; |
||||
|
private UrlCategoryResponseDTO urlCatResDTO; |
||||
|
private String urlDataNum; |
||||
|
private StatusResponseDTO statResDTO; |
||||
|
|
||||
|
public UrlDataResponseDTO(Long urlDataId, String urlDataName, String urlDataLink, String urlDataUsrPass, |
||||
|
String urlDataUsrName, UrlCategoryResponseDTO urlCatResDTO, StatusResponseDTO statResDTO) { |
||||
|
super(); |
||||
|
this.urlDataId = urlDataId; |
||||
|
this.urlDataName = urlDataName; |
||||
|
this.urlDataLink = urlDataLink; |
||||
|
this.urlDataUsrPass = urlDataUsrPass; |
||||
|
this.urlDataUsrName = urlDataUsrName; |
||||
|
this.urlCatResDTO = urlCatResDTO; |
||||
|
this.statResDTO = statResDTO; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,192 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.rest_controller; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpSession; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.http.HttpStatus; |
||||
|
import org.springframework.http.ResponseEntity; |
||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||
|
import org.springframework.security.authentication.BadCredentialsException; |
||||
|
import org.springframework.security.authentication.DisabledException; |
||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
||||
|
import org.springframework.security.core.Authentication; |
||||
|
import org.springframework.security.core.AuthenticationException; |
||||
|
import org.springframework.security.core.context.SecurityContext; |
||||
|
import org.springframework.security.core.context.SecurityContextHolder; |
||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository; |
||||
|
import org.springframework.web.bind.annotation.ModelAttribute; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.request_dto.AppUserRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.GetOtpRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.VerifyOtpRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.service.HomeService; |
||||
|
|
||||
|
@RestController |
||||
|
@RequestMapping("/api") |
||||
|
public class HomeRestController { |
||||
|
|
||||
|
@Autowired |
||||
|
private AuthenticationManager authManager; |
||||
|
|
||||
|
@Autowired |
||||
|
private HomeService homeSer; |
||||
|
|
||||
|
@Value("${app.getOtpUrl}") |
||||
|
private String otpUrl; |
||||
|
|
||||
|
@PostMapping("/get-mob-num") |
||||
|
public ResponseEntity<List<Map<String, String>>> getAllMobNum() { |
||||
|
List<Map<String, String>> mobNums = homeSer.fetchAllMobNum(); |
||||
|
if (!mobNums.isEmpty()) { |
||||
|
return ResponseEntity.ok(mobNums); |
||||
|
} else { |
||||
|
return ResponseEntity.noContent().build(); // 204 No Content |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/clr-otp-ses") |
||||
|
public void clrOtpSes() { |
||||
|
homeSer.clearMobNumTokenStore(); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/verify-otp") |
||||
|
public ResponseEntity<ServiceResponseDTO> verifyOtp(HttpServletRequest req, |
||||
|
@ModelAttribute VerifyOtpRequestDTO verifyOtpReqDTO) { |
||||
|
HttpSession ses = req.getSession(false); |
||||
|
ServiceResponseDTO serResDTO = homeSer.verifyOtp(verifyOtpReqDTO, ses); |
||||
|
if (serResDTO.isResStat()) { |
||||
|
// OTP verified successfully → cleanup session |
||||
|
ses.removeAttribute("otp"); |
||||
|
ses.removeAttribute("otpTimestamp"); |
||||
|
ses.removeAttribute("mobNum"); |
||||
|
ses.removeAttribute("otpLogId"); |
||||
|
return ResponseEntity.ok(serResDTO); |
||||
|
} else { |
||||
|
// OTP failed → remove old OTP only |
||||
|
ses.removeAttribute("otp"); |
||||
|
return ResponseEntity.ok(serResDTO); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/get-otp") |
||||
|
public ResponseEntity<Map<String, Object>> getOtp(HttpServletRequest req, |
||||
|
@ModelAttribute GetOtpRequestDTO getOtpReqDTO) { |
||||
|
Map<String, Object> data = homeSer.getOtp(getOtpReqDTO); |
||||
|
Integer otp = (Integer) data.remove("otp"); |
||||
|
Integer otpLogId = (Integer) data.remove("otpLogId"); |
||||
|
String mobNum = (String) data.remove("mobNum"); |
||||
|
if (data.get("status").equals("success")) { |
||||
|
HttpSession ses = req.getSession(); |
||||
|
ses.setAttribute("otp", otp); |
||||
|
ses.setAttribute("otpLogId", otpLogId); |
||||
|
ses.setAttribute("mobNum", mobNum); |
||||
|
ses.setAttribute("otpTimestamp", System.currentTimeMillis()); // <-- store timestamp |
||||
|
return ResponseEntity.ok(data); |
||||
|
} else { |
||||
|
String msg = (String) data.get("message"); |
||||
|
switch (msg.replace("Failed to get OTP: ", "").trim()) { |
||||
|
case "Mobile number missing", "Invalid mobile number. It should be 10 digits.", |
||||
|
"Unknown mobile number is entered" -> { |
||||
|
return ResponseEntity.badRequest().body(data); |
||||
|
} |
||||
|
default -> { |
||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(data); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/val-app-usr-login") |
||||
|
public ResponseEntity<?> valUsrLogin(@ModelAttribute AppUserRequestDTO appUsrReqDTO, HttpServletRequest req) { |
||||
|
Map<String, Object> res = new HashMap<String, Object>(); |
||||
|
try { |
||||
|
Authentication auth = authManager.authenticate( |
||||
|
new UsernamePasswordAuthenticationToken(appUsrReqDTO.getUsrName(), appUsrReqDTO.getUsrPass())); |
||||
|
|
||||
|
/** |
||||
|
* Store authentication in session so Spring Security knows the user is logged |
||||
|
* in |
||||
|
**/ |
||||
|
SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); |
||||
|
securityContext.setAuthentication(auth); |
||||
|
SecurityContextHolder.setContext(securityContext); |
||||
|
|
||||
|
HttpSession httpSession = req.getSession(true); |
||||
|
httpSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext); |
||||
|
|
||||
|
// Authentication successful |
||||
|
res.put("result", true); |
||||
|
res.put("usrName", auth.getName()); |
||||
|
return ResponseEntity.ok(res); |
||||
|
} catch (AuthenticationException ae) { |
||||
|
Throwable cause = ae.getCause(); |
||||
|
|
||||
|
// 1️ Deactivated user (wrapped) |
||||
|
if (cause instanceof DisabledException) { |
||||
|
res.put("result", false); |
||||
|
res.put("error", "User is deactivated"); |
||||
|
return ResponseEntity.status(403).body(res); |
||||
|
} |
||||
|
|
||||
|
// 2️ Username not found (direct) |
||||
|
if (ae instanceof UsernameNotFoundException) { |
||||
|
res.put("result", false); |
||||
|
res.put("error", "User not found"); |
||||
|
return ResponseEntity.status(404).body(res); |
||||
|
} |
||||
|
|
||||
|
// 3️ Wrong password (direct) |
||||
|
if (ae instanceof BadCredentialsException) { |
||||
|
res.put("result", false); |
||||
|
res.put("error", "Invalid username or password"); |
||||
|
return ResponseEntity.status(401).body(res); |
||||
|
} |
||||
|
// 4️ Default fallback |
||||
|
res.put("result", false); |
||||
|
res.put("error", "Authentication failed"); |
||||
|
return ResponseEntity.status(401).body(res); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/get-role") |
||||
|
public ResponseEntity<?> getUserRole(HttpServletRequest req) { |
||||
|
|
||||
|
HttpSession session = req.getSession(false); |
||||
|
|
||||
|
Map<String, Object> res = new HashMap<String, Object>(); |
||||
|
|
||||
|
if (session == null) { |
||||
|
res.put("authenticated", false); |
||||
|
res.put("isAdmin", false); // Not an admin if not authenticated |
||||
|
return ResponseEntity.ok(res); |
||||
|
} |
||||
|
|
||||
|
SecurityContext sc = (SecurityContext) session |
||||
|
.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); |
||||
|
|
||||
|
if (sc == null || sc.getAuthentication() == null) { |
||||
|
res.put("authenticated", false); |
||||
|
res.put("isAdmin", false); |
||||
|
return ResponseEntity.ok(res); |
||||
|
} |
||||
|
|
||||
|
Authentication auth = sc.getAuthentication(); |
||||
|
boolean isAdmin = auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); |
||||
|
res.put("authenticated", true); |
||||
|
res.put("isAdmin", isAdmin); // Send a simple boolean flag for isAdmin |
||||
|
|
||||
|
return ResponseEntity.ok(res); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.rest_controller; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.http.HttpStatus; |
||||
|
import org.springframework.http.ResponseEntity; |
||||
|
import org.springframework.web.bind.annotation.ModelAttribute; |
||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.request_dto.AddUrlDataRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.UpdateUrlDataRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.service.UrlDataService; |
||||
|
|
||||
|
@RestController |
||||
|
@RequestMapping("/api/urls") |
||||
|
public class UrlDataRestController { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataService urlSer; |
||||
|
|
||||
|
@PostMapping("/add-url-data") |
||||
|
public ResponseEntity<ServiceResponseDTO> addFile(@ModelAttribute AddUrlDataRequestDTO addUrlDataReqDTO) { |
||||
|
ServiceResponseDTO serResDTO = urlSer.addUrl(addUrlDataReqDTO); |
||||
|
if (serResDTO.isResStat()) { |
||||
|
return ResponseEntity.ok(serResDTO); |
||||
|
} else { |
||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(serResDTO); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/upd-url-data") |
||||
|
public ResponseEntity<ServiceResponseDTO> updFile(@ModelAttribute UpdateUrlDataRequestDTO updUrlDataReqDTO) { |
||||
|
ServiceResponseDTO serResDTO = urlSer.updUrl(updUrlDataReqDTO); |
||||
|
if (serResDTO.isResStat()) { |
||||
|
return ResponseEntity.ok(serResDTO); |
||||
|
} else { |
||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(serResDTO); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,279 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.service; |
||||
|
|
||||
|
import java.security.SecureRandom; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import javax.servlet.http.HttpSession; |
||||
|
|
||||
|
import org.slf4j.Logger; |
||||
|
import org.slf4j.LoggerFactory; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.security.authentication.DisabledException; |
||||
|
import org.springframework.security.core.userdetails.User; |
||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||
|
import org.springframework.security.core.userdetails.UserDetailsService; |
||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
import org.springframework.web.client.RestTemplate; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.mapper.OtpLogMapper; |
||||
|
import com.gcc_it_cell_services.springboot.model.AppUser; |
||||
|
import com.gcc_it_cell_services.springboot.repository.AppUserRepository; |
||||
|
import com.gcc_it_cell_services.springboot.repository.OtpLogRepository; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.GetOtpRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.OtpLogRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.VerifyOtpRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.OtpLogResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.util.MobileNumberTokenStore; |
||||
|
|
||||
|
@Service |
||||
|
public class HomeService implements UserDetailsService { |
||||
|
|
||||
|
@Autowired |
||||
|
private AppUserRepository appUsrRepo; |
||||
|
|
||||
|
@Autowired |
||||
|
private OtpLogRepository otpLogRepo; |
||||
|
|
||||
|
@Autowired |
||||
|
private OtpLogMapper otpLogMap; |
||||
|
|
||||
|
@Value("${app.getOtpUrl}") |
||||
|
private String otpUrl; |
||||
|
|
||||
|
@Autowired |
||||
|
private MobileNumberTokenStore mobNumTokenStore; |
||||
|
|
||||
|
private static final Logger logger = LoggerFactory.getLogger(HomeService.class); |
||||
|
|
||||
|
// Helper method |
||||
|
private String maskMobNum(String mobNum) { |
||||
|
if (mobNum == null || mobNum.length() < 4) { |
||||
|
throw new RuntimeException("Invalid Mobile Number: " + mobNum); |
||||
|
} |
||||
|
|
||||
|
String start = mobNum.substring(0, 2); |
||||
|
String end = mobNum.substring(mobNum.length() - 4); |
||||
|
return start + "****" + end; |
||||
|
} |
||||
|
|
||||
|
@Transactional(readOnly = true) |
||||
|
public List<Map<String, String>> fetchAllMobNum() { |
||||
|
List<Map<String, String>> res = new ArrayList<Map<String, String>>(); |
||||
|
try { |
||||
|
// Fetch the list of mobile numbers |
||||
|
List<String> mobNums = appUsrRepo.findAllMobNums(); |
||||
|
|
||||
|
// Iterate through each mobile number and generate a masked version |
||||
|
for (String mobNum : mobNums) { |
||||
|
String uuid = UUID.randomUUID().toString(); |
||||
|
mobNumTokenStore.storeMobNum(uuid, mobNum); // Store UUID and real mobile number mapping |
||||
|
|
||||
|
Map<String, String> maskMobNum = new HashMap<String, String>(); |
||||
|
maskMobNum.put("mobNumId", uuid); // Send the UUID to frontend |
||||
|
maskMobNum.put("mobNum", maskMobNum(mobNum)); // Masking the mobile number |
||||
|
|
||||
|
res.add(maskMobNum); |
||||
|
} |
||||
|
} catch (RuntimeException re) { |
||||
|
re.printStackTrace(); |
||||
|
logger.error("Exception occurred while fetching mobile numbers: {}", re.getMessage(), re); |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
logger.error("Exception occurred while fetching mobile numbers: {}", e.getMessage(), e); |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public UserDetails loadUserByUsername(String usrname) throws UsernameNotFoundException { |
||||
|
// TODO Auto-generated method stub |
||||
|
logger.info("Loading user: {}", usrname); |
||||
|
|
||||
|
AppUser appUsr = appUsrRepo.findByUsrName(usrname) |
||||
|
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + usrname)); |
||||
|
|
||||
|
if (appUsr.getUsrStatId().getStatId().equals(2L)) { |
||||
|
throw new DisabledException("User is deactivated"); |
||||
|
} |
||||
|
|
||||
|
// Bcrypt for password verification |
||||
|
UserDetails usrDets = User.withUsername(appUsr.getUsrName()).password(appUsr.getUsrPass()) |
||||
|
.authorities(appUsr.getUsrRole().getUsrRoleName()).build(); |
||||
|
|
||||
|
return usrDets; |
||||
|
} |
||||
|
|
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Map<String, Object> getOtp(GetOtpRequestDTO getOtpReqDTO) { |
||||
|
// TODO Auto-generated method stub |
||||
|
Map<String, Object> mapData = new HashMap<String, Object>(); |
||||
|
|
||||
|
// Lookup real mobile number from UUID |
||||
|
String mobNum = mobNumTokenStore.getMobNum(getOtpReqDTO.getMobNumId()); |
||||
|
|
||||
|
try { |
||||
|
|
||||
|
if (mobNum == null || mobNum.isBlank()) { |
||||
|
throw new RuntimeException("Mobile number missing"); |
||||
|
} |
||||
|
if (!mobNum.matches("\\d{10}")) { |
||||
|
throw new RuntimeException("Invalid mobile number. It should be 10 digits."); |
||||
|
} |
||||
|
if (appUsrRepo.existsByUsrMobNum(mobNum) == 0) { |
||||
|
throw new RuntimeException("Unknown mobile number is entered"); |
||||
|
} |
||||
|
|
||||
|
Integer otp = new SecureRandom().nextInt(90000) + 10000; |
||||
|
|
||||
|
// Construct the message using new template |
||||
|
// Template: "Welcome to GCC! Your OTP for {#var#} is {#var#}." |
||||
|
/* String appName = "GCC Swimming Pool booking"; */ |
||||
|
String appName = "GCC IT Cell Services"; |
||||
|
String generatedMessage = "Welcome to GCC! Your OTP for " + appName + " is " + otp + "."; |
||||
|
|
||||
|
// SMS API parameters |
||||
|
String key = "pfTEYN6H"; |
||||
|
String entityId = "1401572690000011081"; |
||||
|
String templateId = "1407175765770900925"; |
||||
|
|
||||
|
// Construct full API URL |
||||
|
String smsUrl = otpUrl + "?from=GCCCRP" + "&key=" + key + "&sender=GCCCRP" + "&to=" + mobNum + "&body=" |
||||
|
+ generatedMessage + "&entityid=" + entityId + "&templateid=" + templateId; |
||||
|
|
||||
|
System.out.println("urlString: " + smsUrl); |
||||
|
|
||||
|
String smsRes = new RestTemplate().getForObject(smsUrl, String.class); |
||||
|
|
||||
|
logger.info("SMS API response: {}", smsRes); |
||||
|
logger.info("OTP sent successfully to {} with {} ", mobNum, otp); |
||||
|
otpLogRepo.save(otpLogMap.toOtpLogEnt(new OtpLogRequestDTO(otp, "STATE 1(A): OTP REQUEST SUCCESS", mobNum, |
||||
|
"-", "OTP sent successfully to " + mobNum + " with " + otp + ". (OTP request phase)"))); |
||||
|
OtpLogResponseDTO otpLogResDTO = otpLogMap |
||||
|
.toOtpLogResDTO(otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(mobNum).get()); |
||||
|
mapData.put("status", "success"); |
||||
|
mapData.put("message", "OTP sent successfully"); |
||||
|
mapData.put("otp", otp); |
||||
|
mapData.put("mobNum", mobNum); |
||||
|
mapData.put("otpLogId", otpLogResDTO.getLogId()); |
||||
|
} catch (Exception e) { |
||||
|
logger.error("Exception occurred while generating OTP: {}", e.getMessage(), e); |
||||
|
e.printStackTrace(); |
||||
|
mapData.put("status", "error"); |
||||
|
mapData.put("message", "Failed to get OTP: " + e.getMessage()); |
||||
|
otpLogRepo.save(otpLogMap.toOtpLogEnt( |
||||
|
new OtpLogRequestDTO(0, "STATE 1(B): OTP REQUEST FAILED", mobNum, "-", e.getMessage()))); |
||||
|
} |
||||
|
return mapData; |
||||
|
} |
||||
|
|
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public ServiceResponseDTO verifyOtp(VerifyOtpRequestDTO verifyOtpReqDTO, HttpSession ses) { |
||||
|
// Lookup real mobile number from mobNumId |
||||
|
String mobNum = mobNumTokenStore.getMobNum(verifyOtpReqDTO.getMobNumId()); |
||||
|
Integer savedOtp = (Integer) ses.getAttribute("otp"); |
||||
|
String savedMobNum = (String) ses.getAttribute("mobNum"); |
||||
|
Integer otpLogId = (Integer) ses.getAttribute("otpLogId"); |
||||
|
|
||||
|
try { |
||||
|
if (ses == null || ses.getAttribute("otp") == null || ses.getAttribute("otpTimestamp") == null) { |
||||
|
throw new IllegalArgumentException("Session expired or OTP not found. Please request a new OTP."); |
||||
|
} |
||||
|
|
||||
|
// === TTL Check (e.g., 2 minutes) === |
||||
|
Long otpTimestamp = (Long) ses.getAttribute("otpTimestamp"); |
||||
|
if (otpTimestamp == null || (System.currentTimeMillis() - otpTimestamp) > 120000) { |
||||
|
ses.removeAttribute("otp"); |
||||
|
ses.removeAttribute("otpTimestamp"); |
||||
|
throw new IllegalArgumentException("OTP expired. Please request a new one."); |
||||
|
} |
||||
|
|
||||
|
// === Fetch Existing Log === |
||||
|
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId) |
||||
|
.orElseThrow(() -> new RuntimeException("Request OTP log record not found."))); |
||||
|
|
||||
|
if (mobNum == null) { |
||||
|
throw new IllegalArgumentException("Invalid mobile number"); |
||||
|
} |
||||
|
|
||||
|
// === Validation === |
||||
|
if (!mobNum.equals(savedMobNum)) { |
||||
|
throw new IllegalArgumentException("Mobile number mismatched."); |
||||
|
} else if (!verifyOtpReqDTO.getOtp().equals(savedOtp)) { |
||||
|
throw new IllegalArgumentException("Invalid OTP"); |
||||
|
} |
||||
|
|
||||
|
// === SUCCESS CASE === |
||||
|
otpLogResDTO.setOtpResMobNum(mobNum); |
||||
|
otpLogResDTO.setOtpLogMsg( |
||||
|
"OTP verified successfully from mobile number " + mobNum + ". (OTP verification phase)"); |
||||
|
otpLogResDTO.setOtpStat("STATE 2(A): OTP VERIFICATION SUCCESS"); |
||||
|
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO)); |
||||
|
|
||||
|
// Clear ALL UUID Mobile mappings after successful verification |
||||
|
mobNumTokenStore.clearAll(); |
||||
|
logger.info("Mobile Number Token Store Cleared"); |
||||
|
logger.info("OTP verified successfully"); |
||||
|
return new ServiceResponseDTO(true, "OTP verified successfully"); |
||||
|
|
||||
|
} catch (IllegalArgumentException iae) { |
||||
|
logger.error("Illegal Argument Exception occurred while verifying OTP: {}", iae.getMessage(), iae); |
||||
|
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId).orElseGet( |
||||
|
() -> otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(savedMobNum).orElse(null))); |
||||
|
if (otpLogResDTO != null) { |
||||
|
otpLogResDTO.setOtpResMobNum(mobNum); |
||||
|
otpLogResDTO.setOtpLogMsg("OTP verification failed for mobile number " + mobNum |
||||
|
+ ". (OTP verification phase). Reason: " + iae.getMessage()); |
||||
|
otpLogResDTO.setOtpStat("OTP expired. Please request a new one.".equalsIgnoreCase(iae.getMessage()) |
||||
|
? "STATE 2(B): OTP EXPIRED" |
||||
|
: "STATE 2(C): OTP VERIFICATION FAILED"); |
||||
|
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO)); |
||||
|
} |
||||
|
logger.info("Mobile Number Token Store Cleared"); |
||||
|
mobNumTokenStore.clearAll(); // CLEAR MAP ON FAILURE |
||||
|
return new ServiceResponseDTO(false, "Error: " + iae.getMessage()); |
||||
|
} catch (RuntimeException re) { |
||||
|
logger.error("Runtime Exception occurred while verifying OTP: {}", re.getMessage(), re); |
||||
|
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId).orElseGet( |
||||
|
() -> otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(savedMobNum).orElse(null))); |
||||
|
if (otpLogResDTO != null) { |
||||
|
otpLogResDTO.setOtpResMobNum(mobNum); |
||||
|
otpLogResDTO.setOtpLogMsg("OTP verification failed for mobile number " + mobNum |
||||
|
+ ". (OTP verification phase). Reason: " + re.getMessage()); |
||||
|
otpLogResDTO.setOtpStat("STATE 2(C): OTP VERIFICATION FAILED"); |
||||
|
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO)); |
||||
|
} |
||||
|
logger.info("Mobile Number Token Store Cleared"); |
||||
|
mobNumTokenStore.clearAll(); // CLEAR MAP ON FAILURE |
||||
|
return new ServiceResponseDTO(false, "Error: " + re.getMessage()); |
||||
|
} catch (Exception e) { |
||||
|
logger.error("Exception occurred while verifying OTP: {}", e.getMessage(), e); |
||||
|
OtpLogResponseDTO otpLogResDTO = otpLogMap.toOtpLogResDTO(otpLogRepo.findById(otpLogId).orElseGet( |
||||
|
() -> otpLogRepo.findTopByOtpReqMobNumOrderByDbReqTimestampDesc(savedMobNum).orElse(null))); |
||||
|
if (otpLogResDTO != null) { |
||||
|
otpLogResDTO.setOtpResMobNum(mobNum); |
||||
|
otpLogResDTO.setOtpLogMsg("OTP verification failed for mobile number " + mobNum |
||||
|
+ ". (OTP verification phase). Reason: " + e.getMessage()); |
||||
|
otpLogResDTO.setOtpStat("STATE 2(C): OTP VERIFICATION FAILED"); |
||||
|
otpLogRepo.save(otpLogMap.toOtpLogEnt(otpLogResDTO)); |
||||
|
} |
||||
|
logger.info("Mobile Number Token Store Cleared"); |
||||
|
mobNumTokenStore.clearAll(); // CLEAR MAP ON FAILURE |
||||
|
return new ServiceResponseDTO(false, "Error: " + e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void clearMobNumTokenStore() { |
||||
|
// TODO Auto-generated method stub |
||||
|
logger.info("Mobile Number Token Store Cleared"); |
||||
|
mobNumTokenStore.clearAll(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,174 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.service; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Comparator; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.mapper.UrlCategoryMapper; |
||||
|
import com.gcc_it_cell_services.springboot.mapper.UrlDataMapper; |
||||
|
import com.gcc_it_cell_services.springboot.repository.UrlCategoryRepository; |
||||
|
import com.gcc_it_cell_services.springboot.repository.UrlDataRepository; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO; |
||||
|
|
||||
|
@Service |
||||
|
public class UrlDataCacheService { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlCategoryRepository urlCatRepo; |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataRepository urlDataRepo; |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataMapper urlDataMap; |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlCategoryMapper urlCatMap; |
||||
|
|
||||
|
private final Map<String, String> globalUrlData = new ConcurrentHashMap<String, String>(); |
||||
|
private final Map<String, Map<String, String>> globalUrlCatData = new ConcurrentHashMap<String, Map<String, String>>(); |
||||
|
private final Map<String, String> globalUrlLinks = new ConcurrentHashMap<String, String>(); |
||||
|
|
||||
|
// Called from CacheInitializer |
||||
|
@Transactional(readOnly = true) |
||||
|
public synchronized void loadCache() { |
||||
|
buildCache(); |
||||
|
} |
||||
|
|
||||
|
// Clear and rebuild cache |
||||
|
@Transactional(readOnly = true) |
||||
|
public synchronized void refreshCache() { |
||||
|
buildCache(); |
||||
|
} |
||||
|
|
||||
|
// Getters used by other service |
||||
|
public Map<String, String> getGlobalUrlData() { |
||||
|
return globalUrlData; |
||||
|
} |
||||
|
|
||||
|
public Map<String, Map<String, String>> getGlobalUrlCatData() { |
||||
|
return globalUrlCatData; |
||||
|
} |
||||
|
|
||||
|
public Map<String, String> getGlobalUrlLinks() { |
||||
|
return globalUrlLinks; |
||||
|
} |
||||
|
|
||||
|
private boolean isAct(UrlDataResponseDTO dto) { |
||||
|
return dto.getStatResDTO() != null && dto.getStatResDTO().getStatId() != null |
||||
|
&& dto.getStatResDTO().getStatId() == 1L; |
||||
|
} |
||||
|
|
||||
|
private void buildCache() { |
||||
|
// TODO Auto-generated method stub |
||||
|
globalUrlData.clear(); |
||||
|
globalUrlCatData.clear(); |
||||
|
globalUrlLinks.clear(); |
||||
|
|
||||
|
List<UrlCategoryResponseDTO> urlCatResDTOs = urlCatRepo.findAll().stream().map(urlCatMap::toUrlCatResDTO) |
||||
|
.toList(); |
||||
|
|
||||
|
List<UrlDataResponseDTO> urlDataResDTOs = urlDataRepo.findAll().stream().map(urlDataMap::toUrlDataResDTO) |
||||
|
.filter(this::isAct).toList(); |
||||
|
|
||||
|
// Group URLS by category to avoid repeated filtering |
||||
|
Map<String, List<UrlDataResponseDTO>> urlsByCat = urlDataResDTOs.stream() |
||||
|
.collect(Collectors.groupingBy(urlDataResDTO -> urlDataResDTO.getUrlCatResDTO().getUrlCatName())); |
||||
|
|
||||
|
// Build numbering list |
||||
|
List<UrlDataResponseDTO> finalUrlDataResDTOs = new ArrayList<UrlDataResponseDTO>(); |
||||
|
|
||||
|
for (UrlCategoryResponseDTO urlCatResDTO : urlCatResDTOs) { |
||||
|
String catName = urlCatResDTO.getUrlCatName(); |
||||
|
if (catName.equalsIgnoreCase("-")) { |
||||
|
urlsByCat.getOrDefault("-", List.of()).forEach(urlDataResDTO -> { |
||||
|
finalUrlDataResDTOs.add(new UrlDataResponseDTO(null, urlDataResDTO.getUrlDataName(), |
||||
|
urlDataResDTO.getUrlDataLink(), null, null, null, null)); |
||||
|
}); |
||||
|
} else { |
||||
|
if (urlsByCat.containsKey(catName)) { |
||||
|
// Check: This category has at least one ACTIVE url |
||||
|
UrlDataResponseDTO urlDataResDTO = new UrlDataResponseDTO(); |
||||
|
urlDataResDTO.setUrlDataName(catName); |
||||
|
urlDataResDTO.setUrlDataLink("/urls/get-" + catName.replace(" ", "-") + "-urls"); |
||||
|
finalUrlDataResDTOs.add(urlDataResDTO); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Sort main list |
||||
|
finalUrlDataResDTOs.sort((urlDataResDTO1, urlDataResDTO2) -> { |
||||
|
String urlDataName1 = urlDataResDTO1.getUrlDataName().toLowerCase(); |
||||
|
String urlDataName2 = urlDataResDTO2.getUrlDataName().toLowerCase(); |
||||
|
|
||||
|
boolean isNum1 = Character.isDigit(urlDataName1.charAt(0)); |
||||
|
boolean isNum2 = Character.isDigit(urlDataName2.charAt(0)); |
||||
|
|
||||
|
if (isNum1 && !isNum2) { |
||||
|
return -1; |
||||
|
} |
||||
|
if (isNum1 && isNum2) { |
||||
|
return 1; |
||||
|
} |
||||
|
return urlDataName1.compareTo(urlDataName2); |
||||
|
}); |
||||
|
|
||||
|
// Assign main numbering |
||||
|
Integer count = 1; |
||||
|
for (UrlDataResponseDTO urlDataResDTO : finalUrlDataResDTOs) { |
||||
|
String mainNum = String.valueOf(count++); |
||||
|
// IMPORTANT: exact-case key |
||||
|
String nameKey = urlDataResDTO.getUrlDataName(); |
||||
|
globalUrlData.put(nameKey, mainNum); |
||||
|
globalUrlLinks.put(nameKey, urlDataResDTO.getUrlDataLink()); |
||||
|
} |
||||
|
|
||||
|
// Per category numbering |
||||
|
for (UrlCategoryResponseDTO urlCatResDTO : urlCatResDTOs) { |
||||
|
String catName = urlCatResDTO.getUrlCatName(); |
||||
|
// Skip "-" category |
||||
|
if (catName.equalsIgnoreCase("-")) { |
||||
|
continue; |
||||
|
} |
||||
|
// Get URLs under this category from memory (NOT DB) |
||||
|
List<UrlDataResponseDTO> urlDataResDTOsByUrlCat = urlsByCat.getOrDefault(catName, List.of()); |
||||
|
|
||||
|
// Skip empty category |
||||
|
if (urlDataResDTOsByUrlCat.isEmpty()) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
// Sort alpahbetically |
||||
|
urlDataResDTOsByUrlCat = urlDataResDTOsByUrlCat.stream() |
||||
|
.sorted(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER)) |
||||
|
.toList(); |
||||
|
|
||||
|
// Prepare per-category numbering |
||||
|
Map<String, String> perCatMap = new HashMap<String, String>(); |
||||
|
// exact category name |
||||
|
String UrlDataNum = globalUrlData.get(catName); |
||||
|
|
||||
|
Integer alphaCount = 0; |
||||
|
for (UrlDataResponseDTO urlDataResDTO : urlDataResDTOsByUrlCat) { |
||||
|
char alpha = (char) ('A' + alphaCount++); |
||||
|
String catNum = UrlDataNum + alpha; |
||||
|
// IMPORTANT: exact case |
||||
|
perCatMap.put(urlDataResDTO.getUrlDataName(), catNum); |
||||
|
} |
||||
|
|
||||
|
// Store this map under the category name |
||||
|
globalUrlCatData.put(catName, perCatMap); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,280 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.service; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Comparator; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
import org.slf4j.Logger; |
||||
|
import org.slf4j.LoggerFactory; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.mapper.StatusMapper; |
||||
|
import com.gcc_it_cell_services.springboot.mapper.UrlCategoryMapper; |
||||
|
import com.gcc_it_cell_services.springboot.mapper.UrlDataMapper; |
||||
|
import com.gcc_it_cell_services.springboot.model.UrlData; |
||||
|
import com.gcc_it_cell_services.springboot.repository.StatusRepository; |
||||
|
import com.gcc_it_cell_services.springboot.repository.UrlCategoryRepository; |
||||
|
import com.gcc_it_cell_services.springboot.repository.UrlDataRepository; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.AddUrlDataRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.UpdateUrlDataRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.UrlCategoryRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.request_dto.UrlDataRequestDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.ServiceResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.StatusResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlCategoryResponseDTO; |
||||
|
import com.gcc_it_cell_services.springboot.response_dto.UrlDataResponseDTO; |
||||
|
|
||||
|
@Service |
||||
|
public class UrlDataService { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataRepository urlDataRepo; |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlCategoryMapper urlCatMap; |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlCategoryRepository urlCatRepo; |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataMapper urlDataMap; |
||||
|
|
||||
|
@Autowired |
||||
|
private StatusMapper statMap; |
||||
|
|
||||
|
@Autowired |
||||
|
private StatusRepository statRepo; |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataCacheService urlDataCacheSer; |
||||
|
|
||||
|
private static final Logger logger = LoggerFactory.getLogger(UrlDataService.class); |
||||
|
|
||||
|
@Transactional(readOnly = true) |
||||
|
public List<StatusResponseDTO> getAllStats() { |
||||
|
return statRepo.findAll().stream().map(statMap::toStatResDTO).toList(); |
||||
|
} |
||||
|
|
||||
|
@Transactional(readOnly = true) |
||||
|
public List<UrlDataResponseDTO> getAllUrlsSortedByUrlName() { |
||||
|
// TODO Auto-generated method stub |
||||
|
return urlDataRepo.findAllUrlsSortedByUrlName().stream().map(urlData -> urlDataMap.toUrlDataResDTO(urlData)) |
||||
|
.collect(Collectors.toList()); |
||||
|
} |
||||
|
|
||||
|
@Transactional(readOnly = true) |
||||
|
public List<UrlDataResponseDTO> getAllUrls() { |
||||
|
// TODO Auto-generated method stub |
||||
|
Map<String, String> globalUrlData = urlDataCacheSer.getGlobalUrlData(); |
||||
|
Map<String, String> globalUrlLinks = urlDataCacheSer.getGlobalUrlLinks(); |
||||
|
|
||||
|
List<UrlDataResponseDTO> urlDataResDTOs = new ArrayList<UrlDataResponseDTO>(); |
||||
|
|
||||
|
// Build final list from main numbering map |
||||
|
for (Map.Entry<String, String> entry : globalUrlData.entrySet()) { |
||||
|
String nameKey = entry.getKey(); |
||||
|
String mainNum = entry.getValue(); |
||||
|
String link = globalUrlLinks.getOrDefault(nameKey, null); |
||||
|
|
||||
|
UrlDataResponseDTO urlDataResDTO = new UrlDataResponseDTO(); |
||||
|
urlDataResDTO.setUrlDataName(nameKey); |
||||
|
urlDataResDTO.setUrlDataNum(mainNum); |
||||
|
urlDataResDTO.setUrlDataLink(link); |
||||
|
|
||||
|
urlDataResDTOs.add(urlDataResDTO); |
||||
|
} |
||||
|
|
||||
|
// Sort by numbering |
||||
|
urlDataResDTOs.sort(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER)); |
||||
|
return urlDataResDTOs; |
||||
|
} |
||||
|
|
||||
|
@Transactional(readOnly = true) |
||||
|
public List<UrlDataResponseDTO> getAllUrlsWithUsrNameAndPass() { |
||||
|
// TODO Auto-generated method stub |
||||
|
List<UrlDataResponseDTO> urlDataResDTOs = urlDataRepo.findAllUrlsWithUsrNameAndPass().stream() |
||||
|
.map(urlDataMap::toUrlDataResDTO) |
||||
|
.filter(urlDataResDTO -> urlDataResDTO.getStatResDTO() != null |
||||
|
&& urlDataResDTO.getStatResDTO().getStatId().equals(1L)) |
||||
|
.sorted(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER)) |
||||
|
.collect(Collectors.toList()); |
||||
|
|
||||
|
// Assign numbering using cache |
||||
|
for (UrlDataResponseDTO urlDataResDTO : urlDataResDTOs) { |
||||
|
|
||||
|
String urlName = urlDataResDTO.getUrlDataName(); // Always exists |
||||
|
String catName = urlDataResDTO.getUrlCatResDTO().getUrlCatName(); |
||||
|
|
||||
|
// Always set MAIN number using URL NAME |
||||
|
String mainNum = urlDataCacheSer.getGlobalUrlData().get(urlName); |
||||
|
urlDataResDTO.setUrlDataNum(mainNum); |
||||
|
|
||||
|
// Skip alphabetic numbering for uncategorized |
||||
|
if (catName == null || catName.equals("-")) { |
||||
|
continue; // main numbering already applied |
||||
|
} |
||||
|
|
||||
|
// Apply subcategory numbering if available |
||||
|
Map<String, String> perCatMap = urlDataCacheSer.getGlobalUrlCatData().get(catName); |
||||
|
|
||||
|
if (perCatMap != null) { |
||||
|
String subNum = perCatMap.get(urlName); |
||||
|
if (subNum != null) { |
||||
|
urlDataResDTO.setUrlDataNum(subNum); // override main numbering |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// FINAL SORT BASED ON ASSIGNED NUMBER |
||||
|
urlDataResDTOs.sort((a, b) -> { |
||||
|
String num1 = a.getUrlDataNum(); |
||||
|
String num2 = b.getUrlDataNum(); |
||||
|
|
||||
|
if (num1 == null) |
||||
|
return 1; |
||||
|
if (num2 == null) |
||||
|
return -1; |
||||
|
|
||||
|
// Extract numeric + alpha suffix |
||||
|
var m1 = num1.matches("(\\d+)([A-Za-z]*)") ? num1.replaceAll("(\\d+)([A-Za-z]*)", "$1,$2").split(",") |
||||
|
: new String[] { "999999", "" }; |
||||
|
var m2 = num2.matches("(\\d+)([A-Za-z]*)") ? num2.replaceAll("(\\d+)([A-Za-z]*)", "$1,$2").split(",") |
||||
|
: new String[] { "999999", "" }; |
||||
|
|
||||
|
int n1 = Integer.parseInt(m1[0]); |
||||
|
int n2 = Integer.parseInt(m2[0]); |
||||
|
|
||||
|
// Compare numeric |
||||
|
if (n1 != n2) |
||||
|
return Integer.compare(n1, n2); |
||||
|
|
||||
|
// Compare alphabetic suffix (empty suffix should come first) |
||||
|
String s1 = m1.length > 1 ? m1[1] : ""; |
||||
|
String s2 = m2.length > 1 ? m2[1] : ""; |
||||
|
|
||||
|
if (s1.isEmpty() && !s2.isEmpty()) |
||||
|
return -1; |
||||
|
if (!s1.isEmpty() && s2.isEmpty()) |
||||
|
return 1; |
||||
|
|
||||
|
return s1.compareToIgnoreCase(s2); |
||||
|
}); |
||||
|
|
||||
|
return urlDataResDTOs; |
||||
|
} |
||||
|
|
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public ServiceResponseDTO addUrl(AddUrlDataRequestDTO addUrlDataReqDTO) { |
||||
|
// TODO Auto-generated method stub |
||||
|
try { |
||||
|
String urlLink = addUrlDataReqDTO.getUrlDataLink().trim(); |
||||
|
String urlName = addUrlDataReqDTO.getUrlDataName().trim(); |
||||
|
String reqUrlUsrName = addUrlDataReqDTO.getUrlDataUsrName().trim(); |
||||
|
String reqUrlUsrPass = addUrlDataReqDTO.getUrlDataUsrPass().trim(); |
||||
|
String reqUrlCatName = addUrlDataReqDTO.getUrlCatName().trim(); |
||||
|
String urlUsrName = reqUrlUsrName.isEmpty() ? "-" : reqUrlUsrName; |
||||
|
String urlUsrPass = reqUrlUsrPass.isEmpty() ? "-" : reqUrlUsrPass; |
||||
|
String urlCatName = reqUrlCatName.isEmpty() ? "-" : reqUrlCatName; |
||||
|
|
||||
|
UrlCategoryResponseDTO urlCatResDTO = urlCatMap |
||||
|
.toUrlCatResDTO(urlCatRepo.findUrlCatByUrlCatName(urlCatName).orElseGet(() -> { |
||||
|
UrlCategoryRequestDTO urlCatReqDTO = new UrlCategoryRequestDTO(urlCatName); |
||||
|
return urlCatRepo.save(urlCatMap.toUrlCatEnt(urlCatReqDTO)); |
||||
|
})); |
||||
|
|
||||
|
StatusResponseDTO statResDTO = statMap.toStatResDTO(statRepo.findById(1L).get()); |
||||
|
|
||||
|
urlDataRepo.save(urlDataMap.toUrlDatEnt( |
||||
|
new UrlDataRequestDTO(urlLink, urlName, urlUsrPass, urlUsrName, urlCatResDTO, statResDTO))); |
||||
|
// Refresh the cache after adding |
||||
|
urlDataCacheSer.refreshCache(); |
||||
|
logger.info("URL added successfully"); |
||||
|
return new ServiceResponseDTO(true, "URL added successfully"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
logger.error("Exception occurred while adding url: {}", e.getMessage(), e); |
||||
|
e.printStackTrace(); |
||||
|
return new ServiceResponseDTO(false, "Failed to add the URL. Error: " + e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public ServiceResponseDTO updUrl(UpdateUrlDataRequestDTO updUrlDataReqDTO) { |
||||
|
// TODO Auto-generated method stub |
||||
|
try { |
||||
|
|
||||
|
UrlData urlData = urlDataRepo.findById(updUrlDataReqDTO.getUrlDataId()) |
||||
|
.orElseThrow(() -> new RuntimeException("URL not found")); |
||||
|
|
||||
|
UrlDataResponseDTO urlDataResDTO = urlDataMap.toUrlDataResDTO(urlData); |
||||
|
|
||||
|
String reqUrlUsrName = updUrlDataReqDTO.getUrlDataUsrName().trim(); |
||||
|
String reqUrlUsrPass = updUrlDataReqDTO.getUrlDataUsrPass().trim(); |
||||
|
String reqUrlCatName = updUrlDataReqDTO.getUrlCatName().trim(); |
||||
|
String urlUsrName = reqUrlUsrName.isEmpty() ? "-" : reqUrlUsrName; |
||||
|
String urlUsrPass = reqUrlUsrPass.isEmpty() ? "-" : reqUrlUsrPass; |
||||
|
String urlCatName = reqUrlCatName.isEmpty() ? "-" : reqUrlCatName; |
||||
|
|
||||
|
UrlCategoryResponseDTO urlCatResDTO = urlCatMap |
||||
|
.toUrlCatResDTO(urlCatRepo.findUrlCatByUrlCatName(urlCatName).orElseGet(() -> { |
||||
|
UrlCategoryRequestDTO urlCatReqDTO = new UrlCategoryRequestDTO(urlCatName); |
||||
|
return urlCatRepo.save(urlCatMap.toUrlCatEnt(urlCatReqDTO)); |
||||
|
})); |
||||
|
|
||||
|
StatusResponseDTO statResDTO = statMap.toStatResDTO(statRepo.findById(updUrlDataReqDTO.getStatId()).get()); |
||||
|
|
||||
|
urlDataResDTO.setUrlDataId(updUrlDataReqDTO.getUrlDataId()); |
||||
|
urlDataResDTO.setUrlDataLink(updUrlDataReqDTO.getUrlDataLink()); |
||||
|
urlDataResDTO.setUrlDataName(updUrlDataReqDTO.getUrlDataName()); |
||||
|
urlDataResDTO.setUrlDataUsrName(urlUsrName); |
||||
|
urlDataResDTO.setUrlDataUsrPass(urlUsrPass); |
||||
|
urlDataResDTO.setUrlCatResDTO(urlCatResDTO); |
||||
|
urlDataResDTO.setStatResDTO(statResDTO); |
||||
|
|
||||
|
urlDataRepo.save(urlDataMap.toUrlDatEnt(urlDataResDTO)); |
||||
|
// Refresh the cache after adding |
||||
|
urlDataCacheSer.refreshCache(); |
||||
|
logger.info("URL updated successfully"); |
||||
|
return new ServiceResponseDTO(true, "URL updated successfully"); |
||||
|
} catch (Exception e) { |
||||
|
logger.error("Exception occurred while updating url: {}", e.getMessage(), e); |
||||
|
e.printStackTrace(); |
||||
|
return new ServiceResponseDTO(false, "Failed to update the URL. Error: " + e.getMessage()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Transactional(readOnly = true) |
||||
|
public List<UrlDataResponseDTO> getUrlsByUrlCatName(String urlCatName) { |
||||
|
// TODO Auto-generated method stub |
||||
|
// Fetch Category ID |
||||
|
Long urlCatId = urlCatRepo.findUrlCatByUrlCatName(urlCatName).map(urlCatMap::toUrlCatResDTO).get() |
||||
|
.getUrlCatId(); |
||||
|
|
||||
|
// Fetch URLs under category |
||||
|
List<UrlDataResponseDTO> urls = urlDataRepo.findAllUrlsByUrlCatId(urlCatId).stream() |
||||
|
.map(urlDataMap::toUrlDataResDTO) |
||||
|
.filter(urlDataResDTO -> urlDataResDTO.getStatResDTO() != null |
||||
|
&& urlDataResDTO.getStatResDTO().getStatId().equals(1L)) |
||||
|
.sorted(Comparator.comparing(UrlDataResponseDTO::getUrlDataName, String.CASE_INSENSITIVE_ORDER)) |
||||
|
.toList(); |
||||
|
|
||||
|
// Read per-category numbering map using category key (lowercase) |
||||
|
Map<String, String> perCatMap = urlDataCacheSer.getGlobalUrlCatData().get(urlCatName); |
||||
|
|
||||
|
// Assign numbering |
||||
|
if (perCatMap != null) { |
||||
|
urls.forEach(dto -> { |
||||
|
// EXACT NAME MATCH |
||||
|
String num = perCatMap.get(dto.getUrlDataName()); |
||||
|
dto.setUrlDataNum(num); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return urls; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.util; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Component |
||||
|
public class MobileNumberTokenStore { |
||||
|
|
||||
|
private final Map<String, String> tokenToMobNum = new ConcurrentHashMap<String, String>(); |
||||
|
|
||||
|
// Store all mobile number with UUID tokens when all mobile numbers are fetched |
||||
|
public void storeMobNum(String token, String mobNum) { |
||||
|
tokenToMobNum.put(token, mobNum); |
||||
|
} |
||||
|
|
||||
|
// Retrieve mobile number using UUID token |
||||
|
public String getMobNum(String token) { |
||||
|
return tokenToMobNum.get(token); |
||||
|
} |
||||
|
|
||||
|
// Remove all UUID tokens (after OTP verification) |
||||
|
public void clearAll() { |
||||
|
tokenToMobNum.clear(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.util; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.scheduling.annotation.Scheduled; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.service.UrlDataCacheService; |
||||
|
|
||||
|
@Component |
||||
|
public class UrlCacheScheduler { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataCacheService urlDataCacheSer; |
||||
|
|
||||
|
// Runs every 1 minute |
||||
|
@Scheduled(fixedRate = 60 * 60 * 1000) // 60 minutes * 60 seconds * 1000 ms = 1 hour |
||||
|
public void refreshUrlCachePeriodically() { |
||||
|
urlDataCacheSer.refreshCache(); // full rebuild |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package com.gcc_it_cell_services.springboot.util; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.ApplicationListener; |
||||
|
import org.springframework.context.event.ContextRefreshedEvent; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import com.gcc_it_cell_services.springboot.service.UrlDataCacheService; |
||||
|
|
||||
|
@Component |
||||
|
public class UrlDataCacheInitializer implements ApplicationListener<ContextRefreshedEvent> { |
||||
|
|
||||
|
@Autowired |
||||
|
private UrlDataCacheService urlDataCacheSer; |
||||
|
|
||||
|
@Override |
||||
|
public void onApplicationEvent(ContextRefreshedEvent event) { |
||||
|
// TODO Auto-generated method stub |
||||
|
urlDataCacheSer.loadCache(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"properties": [ |
||||
|
{ |
||||
|
"name": "app.getOtpUrl", |
||||
|
"type": "java.lang.String", |
||||
|
"description": "A description for 'app.getOtpUrl'" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
spring.application.name=gcc-it-cell-services |
||||
|
server.port=8051 |
||||
|
server.address=0.0.0.0 |
||||
|
|
||||
|
# spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration |
||||
|
|
||||
|
# =============================== |
||||
|
# MySQL Database Configuration |
||||
|
# =============================== |
||||
|
# Local DB server |
||||
|
spring.datasource.url=jdbc:mysql://localhost:3306/gcc-it-cell-services |
||||
|
spring.datasource.username=root |
||||
|
spring.datasource.password=1916008_Saec |
||||
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver |
||||
|
|
||||
|
# spring.datasource.url=jdbc:oracle:thin:@10.1.0.14:1521:chncorp |
||||
|
# spring.datasource.username=fileadjust |
||||
|
# spring.datasource.password=fileadjust |
||||
|
# spring.datasource.driver-class-name=oracle.jdbc.OracleDriver |
||||
|
|
||||
|
# =============================== |
||||
|
# JPA / Hibernate Config |
||||
|
# =============================== |
||||
|
spring.jpa.hibernate.ddl-auto=update |
||||
|
spring.jpa.show-sql=true |
||||
|
# spring.jpa.properties.hibernate.format_sql=true |
||||
|
spring.jpa.open-in-view=true |
||||
|
logging.level.org.hibernate.SQL=DEBUG |
||||
|
# logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE |
||||
|
logging.level.org.hibernate.type.descriptor.sql=TRACE |
||||
|
|
||||
|
# Production context path |
||||
|
server.servlet.context-path=/ |
||||
@ -0,0 +1,29 @@ |
|||||
|
spring.application.name=gcc-it-cell-services |
||||
|
server.port=8051 |
||||
|
server.address=0.0.0.0 |
||||
|
|
||||
|
# spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration |
||||
|
|
||||
|
# =============================== |
||||
|
# MySQL Database Configuration |
||||
|
# =============================== |
||||
|
# Local DB server |
||||
|
spring.datasource.url=jdbc:mysql://localhost:3306/gcc-it-cell-services |
||||
|
spring.datasource.username=root |
||||
|
spring.datasource.password=1916008_Saec |
||||
|
|
||||
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver |
||||
|
|
||||
|
# =============================== |
||||
|
# JPA / Hibernate Config |
||||
|
# =============================== |
||||
|
spring.jpa.hibernate.ddl-auto=update |
||||
|
spring.jpa.show-sql=true |
||||
|
# spring.jpa.properties.hibernate.format_sql=true |
||||
|
spring.jpa.open-in-view=true |
||||
|
logging.level.org.hibernate.SQL=DEBUG |
||||
|
# logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE |
||||
|
logging.level.org.hibernate.type.descriptor.sql=TRACE |
||||
|
|
||||
|
# Production context path |
||||
|
server.servlet.context-path=/itcell |
||||
@ -0,0 +1,2 @@ |
|||||
|
spring.profiles.active=prod |
||||
|
app.getOtpUrl=https://tmegov.onex-aura.com/api/sms |
||||
11794
src/main/resources/static/assets/bootstrap-5.3.2/css/bootstrap.min.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2212
src/main/resources/static/assets/bootstrap-5.3.2/js/bootstrap.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,193 @@ |
|||||
|
body { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
min-height: 100vh; /* ensures gradient fills screen */ |
||||
|
font-family: Arial, sans-serif; |
||||
|
background: linear-gradient(45deg, #f6f7fb, #4ca3b3); |
||||
|
background-repeat: no-repeat; |
||||
|
} |
||||
|
|
||||
|
.form-title { |
||||
|
text-align: center; |
||||
|
margin-bottom: 20px; |
||||
|
font-size: 1.8rem; |
||||
|
color: #003366; |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.header-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 20px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.back-button { |
||||
|
background-color: white; |
||||
|
border: 2px solid black !important; |
||||
|
border-radius: 4px; |
||||
|
padding: 10px 20px; |
||||
|
border-radius: 5px; |
||||
|
font-size: 16px; |
||||
|
position: relative; /* override absolute */ |
||||
|
color: #005f73; |
||||
|
cursor: pointer; |
||||
|
transition: background-color 0.3s, transform 0.2s; |
||||
|
} |
||||
|
|
||||
|
.back-button:hover { |
||||
|
background: #4ca3b3; |
||||
|
transform: scale(1.05); |
||||
|
color: #000000; |
||||
|
} |
||||
|
|
||||
|
.back-to-home-btn { |
||||
|
background-color: white; |
||||
|
border: 2px solid black !important; |
||||
|
border-radius: 4px; |
||||
|
padding: 10px 20px; |
||||
|
border-radius: 5px; |
||||
|
font-size: 16px; |
||||
|
position: relative; /* override absolute */ |
||||
|
color: #005f73; |
||||
|
cursor: pointer; |
||||
|
transition: background-color 0.3s, transform 0.2s; |
||||
|
} |
||||
|
|
||||
|
.back-to-home-btn:hover { |
||||
|
background: #4ca3b3; |
||||
|
transform: scale(1.05); |
||||
|
color: #000000; |
||||
|
} |
||||
|
|
||||
|
.required { |
||||
|
color: red; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.container { |
||||
|
padding: 20px; |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
h2 { |
||||
|
color: #0B3954; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
|
||||
|
.form-container { |
||||
|
background: white; |
||||
|
padding: 30px; |
||||
|
border-radius: 10px; |
||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.form-row { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 20px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.form-group { |
||||
|
margin-bottom: 20px; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.form-group label { |
||||
|
margin-bottom: 5px; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.form-control { |
||||
|
padding: 8px; |
||||
|
border: 1px solid #ddd; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.file-input { |
||||
|
display: flex; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.file-input button { |
||||
|
background: #f0f0f0; |
||||
|
border: none; |
||||
|
padding: 8px 15px; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.radio-group { |
||||
|
display: flex; |
||||
|
gap: 20px; |
||||
|
} |
||||
|
|
||||
|
.radio-option { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 5px; |
||||
|
} |
||||
|
|
||||
|
.submit-btn { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.submit-btn input[type="submit"]:disabled { |
||||
|
background-color: #cccccc; /* grey color */ |
||||
|
color: #666666; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
|
||||
|
.submit-btn input[type="submit"] { |
||||
|
background-color: #005f73; |
||||
|
color: #fff; |
||||
|
font-size: 1rem; |
||||
|
border: none; |
||||
|
padding: 10px 20px; |
||||
|
border-radius: 4px; |
||||
|
cursor: pointer; |
||||
|
transition: background 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.submit-btn input[type="submit"]:hover { |
||||
|
background: #005f05; |
||||
|
} |
||||
|
/* Responsive Design for Tablets */ |
||||
|
@media ( max-width : 768px) { |
||||
|
.form-row { |
||||
|
grid-template-columns: 1fr; |
||||
|
} |
||||
|
.form-container { |
||||
|
padding: 20px; |
||||
|
} |
||||
|
.form-title { |
||||
|
font-size: 1.5rem; |
||||
|
} |
||||
|
.form-control { |
||||
|
font-size: 1rem; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Responsive Design for Mobile */ |
||||
|
@media ( max-width : 480px) { |
||||
|
.form-row { |
||||
|
grid-template-columns: 1fr; |
||||
|
} |
||||
|
.submit-btn input[type="submit"] { |
||||
|
width: 100%; |
||||
|
font-size: 1.2rem; |
||||
|
padding: 12px; |
||||
|
} |
||||
|
.form-container { |
||||
|
padding: 15px; |
||||
|
} |
||||
|
.form-title { |
||||
|
font-size: 1.2rem; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,114 @@ |
|||||
|
#custom-info { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
#custom-search { |
||||
|
display: flex; |
||||
|
justify-content: flex-end; /* pushes it to the right */ |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
#custom-length { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 8px; /* Add spacing between dropdown and label */ |
||||
|
white-space: nowrap; /* Prevent wrapping */ |
||||
|
} |
||||
|
|
||||
|
.dataTables_length label { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0; |
||||
|
gap: 5px; /* Adjust spacing between text and dropdown */ |
||||
|
} |
||||
|
|
||||
|
.dataTables_length select { |
||||
|
display: inline-block; |
||||
|
width: auto; /* Ensure dropdown adjusts to content */ |
||||
|
} |
||||
|
|
||||
|
#custom-pagination { |
||||
|
display: flex; |
||||
|
justify-content: flex-end; /* Align pagination to the right */ |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.dataTables_filter { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 8px; /* space between label and input */ |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.dataTables_filter label { |
||||
|
margin: 0; |
||||
|
font-weight: 500; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
.dataTables_filter input { |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 5px; |
||||
|
padding: 5px 8px; |
||||
|
margin-left: 5px; /* small gap after label */ |
||||
|
outline: none; |
||||
|
width: 180px; |
||||
|
transition: all 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
/* Apply border-radius to the table */ |
||||
|
.det-table { |
||||
|
width: 100%; /* Table only grows as needed */ |
||||
|
border-collapse: collapse; |
||||
|
table-layout: auto; /* Browser decides column width based on content */ |
||||
|
border-spacing: 0; |
||||
|
border: 2px solid #f0f0f0; |
||||
|
margin-top: 20px; |
||||
|
font-family: Arial, sans-serif; |
||||
|
font-size: 14px; |
||||
|
border-radius: 12px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.det-table th, .det-table td { |
||||
|
border: 1px solid #e0e0e0; |
||||
|
padding: 6px 10px; /* Small padding keeps things compact */ |
||||
|
white-space: nowrap; /* Prevents text wrapping → keeps columns narrow */ |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.det-table td.no-word-wrap { |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.det-table th { |
||||
|
background-color: #005f73; |
||||
|
color: white; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.det-table tr:nth-child(even) { |
||||
|
background-color: #f9f9f9; |
||||
|
} |
||||
|
|
||||
|
.det-table tr:hover { |
||||
|
background-color: #f1f1f1; |
||||
|
} |
||||
|
|
||||
|
.det-table th, .det-table td { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 767.98px) { |
||||
|
.btn-group { |
||||
|
width: 100%; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.btn-group .btn { |
||||
|
width: 48%; /* make buttons fill container nicely */ |
||||
|
margin: 1px; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
.header { |
||||
|
background-color: #005f73; |
||||
|
padding: 10px 20px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
/* justify-content: space-between; */ |
||||
|
position: relative; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.header .logo-left img { |
||||
|
height: 60px; |
||||
|
width: 60px; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
|
||||
|
.header .title { |
||||
|
text-align: center; |
||||
|
/* flex-grow: 1; */ |
||||
|
position: absolute; |
||||
|
transform: translateX(-50%); |
||||
|
left: 50% |
||||
|
} |
||||
|
|
||||
|
.header .title h2 { |
||||
|
margin: 0; |
||||
|
font-size: 1.5rem; |
||||
|
} |
||||
|
|
||||
|
.header .logos-right img { |
||||
|
height: 60px; |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
|
||||
|
.header .logo-left { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.header .logo-text { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
line-height: 1; |
||||
|
} |
||||
|
|
||||
|
.header .logo-text h2 { |
||||
|
margin: 0; |
||||
|
font-size: 0.8rem; |
||||
|
color: white; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.header .logo-text p { |
||||
|
margin: 0; |
||||
|
font-size: 0.5rem; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
@media ( max-width : 480px) { |
||||
|
.header { |
||||
|
flex-direction: column; |
||||
|
align-items: flex-start; |
||||
|
position: relative; |
||||
|
} |
||||
|
.header .logo-left img { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
.header .logos-right img { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
.header .title { |
||||
|
position: static; /* Reset absolute positioning */ |
||||
|
transform: none; /* Remove the translate effect */ |
||||
|
margin-top: 10px; |
||||
|
text-align: center; /* Ensure the text stays centered */ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,295 @@ |
|||||
|
.button-row { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
gap: 40px; |
||||
|
flex-wrap: wrap; /* allows wrapping */ |
||||
|
margin-top: 20px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/* Center heading & align right-side search neatly */ |
||||
|
.row.align-items-center.gx-3.justify-content-between { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
|
||||
|
.form-title { |
||||
|
margin: 0; |
||||
|
font-weight: 600; |
||||
|
text-align: center; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
/* Ensure dropdown + search align nicely */ |
||||
|
.col-md-6.d-flex.justify-content-md-end { |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/* Center content properly in larger screens */ |
||||
|
.form-container { |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; /* centers horizontally */ |
||||
|
} |
||||
|
|
||||
|
.form-control { |
||||
|
width: auto !important; |
||||
|
flex: 0 0 auto; |
||||
|
} |
||||
|
|
||||
|
.custom-button { |
||||
|
padding: 15px 30px; |
||||
|
position: relative; /* Important: makes absolute badge work */ |
||||
|
background: white; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 10px; |
||||
|
font-size: 1rem; |
||||
|
color: #005f73; |
||||
|
cursor: pointer; |
||||
|
transition: background-color 0.3s, transform 0.2s; |
||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
||||
|
flex: 1 1 calc(33.333% - 40px); /* 3 per row, minus gap space */ |
||||
|
max-width: 300px; /* optional: limit button width */ |
||||
|
min-width: 200px; /* optional: keep from shrinking too small */ |
||||
|
} |
||||
|
|
||||
|
.custom-button:hover { |
||||
|
background: #4ca3b3; |
||||
|
transform: scale(1.05); |
||||
|
color: #000000; |
||||
|
} |
||||
|
|
||||
|
.custom-button:active { |
||||
|
transform: scale(0.95); |
||||
|
} |
||||
|
|
||||
|
#search-input2 { |
||||
|
min-width: 80px; |
||||
|
border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
#search-input { |
||||
|
width: 250px; |
||||
|
max-width: 100%; |
||||
|
padding: 8px 12px; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 8px; |
||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); |
||||
|
transition: all 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
#search-input:focus { |
||||
|
outline: none; |
||||
|
border-color: #4ca3b3; |
||||
|
box-shadow: 0 3px 8px rgba(76, 163, 179, 0.3); |
||||
|
} |
||||
|
|
||||
|
/* === Login Modal Overlay === */ |
||||
|
.login-modal-overlay { |
||||
|
display: none; |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background: rgba(0, 0, 0, 0.5); |
||||
|
z-index: 1050; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
/* === Modal Box === */ |
||||
|
.login-modal-box { |
||||
|
background: #fff; |
||||
|
width: 400px; |
||||
|
max-width: 90%; |
||||
|
border-radius: 8px; |
||||
|
padding: 25px 30px; |
||||
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); |
||||
|
position: relative; |
||||
|
animation: fadeIn 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
/* === Close Button === */ |
||||
|
.close-btn { |
||||
|
position: absolute; |
||||
|
top: 10px; |
||||
|
right: 15px; |
||||
|
font-size: 24px; |
||||
|
color: #666; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.close-btn:hover { |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* === Title === */ |
||||
|
.login-title { |
||||
|
text-align: center; |
||||
|
margin-bottom: 20px; |
||||
|
font-size: 22px; |
||||
|
color: #333; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
|
.login-btn-send { |
||||
|
background-color: #005f73; |
||||
|
color: #fff; |
||||
|
padding: 8px 16px; |
||||
|
border: none; |
||||
|
border-radius: 4px; |
||||
|
cursor: pointer; |
||||
|
font-weight: 600; |
||||
|
transition: background 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
.login-btn-send:hover { |
||||
|
background-color: #4ca3b3; |
||||
|
} |
||||
|
|
||||
|
/* === Form === */ |
||||
|
.login-form .form-label { |
||||
|
display: block; |
||||
|
font-size: 15px; |
||||
|
color: #444; |
||||
|
margin-bottom: 6px; |
||||
|
} |
||||
|
|
||||
|
.req { |
||||
|
color: red; |
||||
|
} |
||||
|
|
||||
|
.form-input { |
||||
|
width: 100%; |
||||
|
padding: 10px 12px; |
||||
|
font-size: 15px; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 4px; |
||||
|
outline: none; |
||||
|
} |
||||
|
|
||||
|
.form-input:focus { |
||||
|
border-color: #00bcd4; |
||||
|
box-shadow: 0 0 4px rgba(0, 188, 212, 0.4); |
||||
|
} |
||||
|
|
||||
|
/* === Button Row === */ |
||||
|
.btn-row { |
||||
|
margin-top: 20px; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
gap: 12px; |
||||
|
} |
||||
|
|
||||
|
/* === Animation states === */ |
||||
|
.login-modal-overlay.show { |
||||
|
animation: fadeIn 0.3s ease forwards; |
||||
|
} |
||||
|
|
||||
|
.login-modal-overlay.hide { |
||||
|
animation: fadeOut 0.3s ease forwards; |
||||
|
} |
||||
|
|
||||
|
.url-num-badge { |
||||
|
position: absolute; |
||||
|
top: -12px; |
||||
|
left: -12px; |
||||
|
background-color: #005f73; /* or any color */ |
||||
|
color: white; |
||||
|
font-size: 14px; |
||||
|
width: 28px; |
||||
|
height: 28px; |
||||
|
border-radius: 50%; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-weight: bold; |
||||
|
z-index: 10; |
||||
|
} |
||||
|
|
||||
|
/* === Fade Animations === */ |
||||
|
@keyframes fadeIn { |
||||
|
from { |
||||
|
transform: scale(0.95); |
||||
|
opacity: 0; |
||||
|
} |
||||
|
to { |
||||
|
transform: scale(1); |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes fadeOut { |
||||
|
from { |
||||
|
transform: scale(1); |
||||
|
opacity: 1; |
||||
|
} |
||||
|
to { |
||||
|
transform: scale(0.95); |
||||
|
opacity: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Fix heading centering in large screens */ |
||||
|
@media (min-width: 992px) { |
||||
|
.row.align-items-center.gy-3.justify-content-center.text-center { |
||||
|
justify-content: space-between !important; |
||||
|
} |
||||
|
|
||||
|
.row.align-items-center.gy-3.justify-content-center.text-center .col-md-6:first-child { |
||||
|
text-align: center !important; |
||||
|
flex: 1 1 100%; |
||||
|
max-width: 100%; |
||||
|
} |
||||
|
|
||||
|
.row.align-items-center.gy-3.justify-content-center.text-center .col-md-6:last-child { |
||||
|
flex: 1 1 auto; |
||||
|
max-width: fit-content; |
||||
|
margin-left: auto; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Tablet screens - shrink more */ |
||||
|
@media (max-width : 768px) { |
||||
|
.form-title { |
||||
|
font-size: 1.2rem !important; |
||||
|
} |
||||
|
|
||||
|
.button-row { |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
flex-direction: column; |
||||
|
gap: 15px; /* tighter spacing for small screens */ |
||||
|
} |
||||
|
|
||||
|
.custom-button { |
||||
|
flex: none; |
||||
|
width: 80%; /* nicely centered buttons */ |
||||
|
max-width: 320px; |
||||
|
min-width: unset; /* remove constraint */ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Responsive tweak for small screens */ |
||||
|
@media (max-width: 576px) { |
||||
|
.form-title { |
||||
|
font-size: 1.1rem !important; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
#search-input { |
||||
|
width: 100%; |
||||
|
max-width: 280px; |
||||
|
} |
||||
|
|
||||
|
#search-input2 { |
||||
|
width: 100% !important; |
||||
|
max-width: 100px; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,272 @@ |
|||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 100; |
||||
|
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-700.woff2") format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-200.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-300.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-regular.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-500.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-600.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 700; |
||||
|
src: url("../poppins-23.0/fonts/devanagari/poppins-v23-devanagari-700.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
|
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 100; |
||||
|
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-100.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-200.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-300.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-regular.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-500.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-600.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 700; |
||||
|
src: url("../poppins-23.0/fonts/latin-ext/poppins-v23-latin-ext-700.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 100; |
||||
|
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-100.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-200.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-300.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-regular.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-500.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-600.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
@font-face { |
||||
|
font-family: "Poppins"; |
||||
|
font-style: normal; |
||||
|
font-weight: 700; |
||||
|
src: url("../poppins-23.0/fonts/latin/poppins-v23-latin-700.woff2") |
||||
|
format("woff2"); |
||||
|
} |
||||
|
|
||||
|
* { |
||||
|
margin: 0; |
||||
|
border: 0; |
||||
|
padding: 0; |
||||
|
box-sizing: border-box; |
||||
|
font-family: "Poppins", sans-serif; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
background: linear-gradient(45deg, #f6f7fb, #4ca3b3); |
||||
|
background-repeat: no-repeat; |
||||
|
min-height: 100vh; |
||||
|
min-width: 100vw; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 100%; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
div.container { |
||||
|
background: white; |
||||
|
min-width: 320px; |
||||
|
min-height: 33vh; |
||||
|
padding: 2rem; |
||||
|
box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.2); |
||||
|
border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
div h2 { |
||||
|
font-weight: 600; |
||||
|
margin-bottom: 2rem; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.container h3, |
||||
|
.container h4 { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
div h2::before { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
height: 4px; |
||||
|
width: 25px; |
||||
|
bottom: 3px; |
||||
|
left: 0; |
||||
|
border-radius: 8px; |
||||
|
background: #005f73; |
||||
|
} |
||||
|
|
||||
|
form { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.input-field { |
||||
|
position: relative; |
||||
|
margin-bottom: 1.5rem; /* instead of targeting only first-child */ |
||||
|
} |
||||
|
|
||||
|
.input-field .underline::before { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
height: 1px; |
||||
|
width: 100%; |
||||
|
bottom: -5px; |
||||
|
left: 0; |
||||
|
background: rgba(0, 0, 0, 0.2); |
||||
|
} |
||||
|
|
||||
|
.input-field .underline::after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
height: 1px; |
||||
|
width: 100%; |
||||
|
bottom: -5px; |
||||
|
left: 0; |
||||
|
background: linear-gradient(45deg, #8e2de2, #4a00e0); |
||||
|
transform: scaleX(0); |
||||
|
transition: all 0.3s ease-in-out; |
||||
|
transform-origin: left; |
||||
|
} |
||||
|
|
||||
|
.input-field input:focus ~ .underline::after { |
||||
|
transform: scaleX(1); |
||||
|
} |
||||
|
|
||||
|
.input-field input { |
||||
|
outline: none; |
||||
|
font-size: 0.9rem; |
||||
|
color: rgba(0, 0, 0, 0.7); |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.input-field input::placeholder { |
||||
|
color: rgba(0, 0, 0, 0.5); |
||||
|
} |
||||
|
|
||||
|
form input[type="submit"] { |
||||
|
margin-top: 2rem; |
||||
|
padding: 0.4rem; |
||||
|
width: 100%; |
||||
|
background: #005f73; |
||||
|
cursor: pointer; |
||||
|
color: white; |
||||
|
font-size: 0.9rem; |
||||
|
font-weight: 300; |
||||
|
border-radius: 4px; |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
form input[type="submit"]:hover { |
||||
|
letter-spacing: 0.5px; |
||||
|
background: #005f05; |
||||
|
} |
||||
|
|
||||
|
.small-logo { |
||||
|
width: 60px; |
||||
|
height: 60px; |
||||
|
display: block; |
||||
|
margin: 0 auto; /* Centers the image */ |
||||
|
} |
||||
@ -0,0 +1,532 @@ |
|||||
|
:root { |
||||
|
--dt-row-selected: 13, 110, 253; |
||||
|
--dt-row-selected-text: 255, 255, 255; |
||||
|
--dt-row-selected-link: 9, 10, 11; |
||||
|
--dt-row-stripe: 0, 0, 0; |
||||
|
--dt-row-hover: 0, 0, 0; |
||||
|
--dt-column-ordering: 0, 0, 0; |
||||
|
--dt-html-background: white; |
||||
|
} |
||||
|
|
||||
|
:root.dark { |
||||
|
--dt-html-background: rgb(33, 37, 41); |
||||
|
} |
||||
|
|
||||
|
table.dataTable td.dt-control { |
||||
|
text-align: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
table.dataTable td.dt-control:before { |
||||
|
display: inline-block; |
||||
|
color: rgba(0, 0, 0, 0.5); |
||||
|
content: "►"; |
||||
|
} |
||||
|
|
||||
|
table.dataTable tr.dt-hasChild td.dt-control:before { |
||||
|
content: "▼"; |
||||
|
} |
||||
|
|
||||
|
html.dark table.dataTable td.dt-control:before { |
||||
|
color: rgba(255, 255, 255, 0.5); |
||||
|
} |
||||
|
|
||||
|
html.dark table.dataTable tr.dt-hasChild td.dt-control:before { |
||||
|
color: rgba(255, 255, 255, 0.5); |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead > tr > th.sorting, |
||||
|
table.dataTable thead > tr > th.sorting_asc, |
||||
|
table.dataTable thead > tr > th.sorting_desc, |
||||
|
table.dataTable thead > tr > th.sorting_asc_disabled, |
||||
|
table.dataTable thead > tr > th.sorting_desc_disabled, |
||||
|
table.dataTable thead > tr > td.sorting, |
||||
|
table.dataTable thead > tr > td.sorting_asc, |
||||
|
table.dataTable thead > tr > td.sorting_desc, |
||||
|
table.dataTable thead > tr > td.sorting_asc_disabled, |
||||
|
table.dataTable thead > tr > td.sorting_desc_disabled { |
||||
|
cursor: pointer; |
||||
|
position: relative; |
||||
|
padding-right: 26px; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead > tr > th.sorting:before, |
||||
|
table.dataTable thead > tr > th.sorting:after, |
||||
|
table.dataTable thead > tr > th.sorting_asc:before, |
||||
|
table.dataTable thead > tr > th.sorting_asc:after, |
||||
|
table.dataTable thead > tr > th.sorting_desc:before, |
||||
|
table.dataTable thead > tr > th.sorting_desc:after, |
||||
|
table.dataTable thead > tr > th.sorting_asc_disabled:before, |
||||
|
table.dataTable thead > tr > th.sorting_asc_disabled:after, |
||||
|
table.dataTable thead > tr > th.sorting_desc_disabled:before, |
||||
|
table.dataTable thead > tr > th.sorting_desc_disabled:after, |
||||
|
table.dataTable thead > tr > td.sorting:before, |
||||
|
table.dataTable thead > tr > td.sorting:after, |
||||
|
table.dataTable thead > tr > td.sorting_asc:before, |
||||
|
table.dataTable thead > tr > td.sorting_asc:after, |
||||
|
table.dataTable thead > tr > td.sorting_desc:before, |
||||
|
table.dataTable thead > tr > td.sorting_desc:after, |
||||
|
table.dataTable thead > tr > td.sorting_asc_disabled:before, |
||||
|
table.dataTable thead > tr > td.sorting_asc_disabled:after, |
||||
|
table.dataTable thead > tr > td.sorting_desc_disabled:before, |
||||
|
table.dataTable thead > tr > td.sorting_desc_disabled:after { |
||||
|
position: absolute; |
||||
|
display: block; |
||||
|
opacity: 0.125; |
||||
|
right: 10px; |
||||
|
line-height: 9px; |
||||
|
font-size: 0.8em; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead > tr > th.sorting:before, |
||||
|
table.dataTable thead > tr > th.sorting_asc:before, |
||||
|
table.dataTable thead > tr > th.sorting_desc:before, |
||||
|
table.dataTable thead > tr > th.sorting_asc_disabled:before, |
||||
|
table.dataTable thead > tr > th.sorting_desc_disabled:before, |
||||
|
table.dataTable thead > tr > td.sorting:before, |
||||
|
table.dataTable thead > tr > td.sorting_asc:before, |
||||
|
table.dataTable thead > tr > td.sorting_desc:before, |
||||
|
table.dataTable thead > tr > td.sorting_asc_disabled:before, |
||||
|
table.dataTable thead > tr > td.sorting_desc_disabled:before { |
||||
|
bottom: 50%; |
||||
|
content: "▲"; |
||||
|
content: "▲"/""; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead > tr > th.sorting:after, |
||||
|
table.dataTable thead > tr > th.sorting_asc:after, |
||||
|
table.dataTable thead > tr > th.sorting_desc:after, |
||||
|
table.dataTable thead > tr > th.sorting_asc_disabled:after, |
||||
|
table.dataTable thead > tr > th.sorting_desc_disabled:after, |
||||
|
table.dataTable thead > tr > td.sorting:after, |
||||
|
table.dataTable thead > tr > td.sorting_asc:after, |
||||
|
table.dataTable thead > tr > td.sorting_desc:after, |
||||
|
table.dataTable thead > tr > td.sorting_asc_disabled:after, |
||||
|
table.dataTable thead > tr > td.sorting_desc_disabled:after { |
||||
|
top: 50%; |
||||
|
content: "▼"; |
||||
|
content: "▼"/""; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead > tr > th.sorting_asc:before, |
||||
|
table.dataTable thead > tr > th.sorting_desc:after, |
||||
|
table.dataTable thead > tr > td.sorting_asc:before, |
||||
|
table.dataTable thead > tr > td.sorting_desc:after { |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead > tr > th.sorting_desc_disabled:after, |
||||
|
table.dataTable thead > tr > th.sorting_asc_disabled:before, |
||||
|
table.dataTable thead > tr > td.sorting_desc_disabled:after, |
||||
|
table.dataTable thead > tr > td.sorting_asc_disabled:before { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead > tr > th:active, |
||||
|
table.dataTable thead > tr > td:active { |
||||
|
outline: none; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollBody > table.dataTable > thead > tr > th:before, |
||||
|
div.dataTables_scrollBody > table.dataTable > thead > tr > th:after, |
||||
|
div.dataTables_scrollBody > table.dataTable > thead > tr > td:before, |
||||
|
div.dataTables_scrollBody > table.dataTable > thead > tr > td:after { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_processing { |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
left: 50%; |
||||
|
width: 200px; |
||||
|
margin-left: -100px; |
||||
|
margin-top: -26px; |
||||
|
text-align: center; |
||||
|
padding: 2px; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_processing > div:last-child { |
||||
|
position: relative; |
||||
|
width: 80px; |
||||
|
height: 15px; |
||||
|
margin: 1em auto; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_processing > div:last-child > div { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
width: 13px; |
||||
|
height: 13px; |
||||
|
border-radius: 50%; |
||||
|
background: rgb(13, 110, 253); |
||||
|
background: rgb(var(--dt-row-selected)); |
||||
|
animation-timing-function: cubic-bezier(0, 1, 1, 0); |
||||
|
} |
||||
|
|
||||
|
div.dataTables_processing > div:last-child > div:nth-child(1) { |
||||
|
left: 8px; |
||||
|
animation: datatables-loader-1 0.6s infinite; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_processing > div:last-child > div:nth-child(2) { |
||||
|
left: 8px; |
||||
|
animation: datatables-loader-2 0.6s infinite; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_processing > div:last-child > div:nth-child(3) { |
||||
|
left: 32px; |
||||
|
animation: datatables-loader-2 0.6s infinite; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_processing > div:last-child > div:nth-child(4) { |
||||
|
left: 56px; |
||||
|
animation: datatables-loader-3 0.6s infinite; |
||||
|
} |
||||
|
|
||||
|
@keyframes datatables-loader-1 { |
||||
|
0% { |
||||
|
transform: scale(0); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: scale(1); |
||||
|
} |
||||
|
} |
||||
|
@keyframes datatables-loader-3 { |
||||
|
0% { |
||||
|
transform: scale(1); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: scale(0); |
||||
|
} |
||||
|
} |
||||
|
@keyframes datatables-loader-2 { |
||||
|
0% { |
||||
|
transform: translate(0, 0); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: translate(24px, 0); |
||||
|
} |
||||
|
} |
||||
|
table.dataTable.nowrap th, |
||||
|
table.dataTable.nowrap td { |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
table.dataTable th.dt-left, |
||||
|
table.dataTable td.dt-left { |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
table.dataTable th.dt-center, |
||||
|
table.dataTable td.dt-center, |
||||
|
table.dataTable td.dataTables_empty { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
table.dataTable th.dt-right, |
||||
|
table.dataTable td.dt-right { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
table.dataTable th.dt-justify, |
||||
|
table.dataTable td.dt-justify { |
||||
|
text-align: justify; |
||||
|
} |
||||
|
|
||||
|
table.dataTable th.dt-nowrap, |
||||
|
table.dataTable td.dt-nowrap { |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead th, |
||||
|
table.dataTable thead td, |
||||
|
table.dataTable tfoot th, |
||||
|
table.dataTable tfoot td { |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead th.dt-head-left, |
||||
|
table.dataTable thead td.dt-head-left, |
||||
|
table.dataTable tfoot th.dt-head-left, |
||||
|
table.dataTable tfoot td.dt-head-left { |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead th.dt-head-center, |
||||
|
table.dataTable thead td.dt-head-center, |
||||
|
table.dataTable tfoot th.dt-head-center, |
||||
|
table.dataTable tfoot td.dt-head-center { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead th.dt-head-right, |
||||
|
table.dataTable thead td.dt-head-right, |
||||
|
table.dataTable tfoot th.dt-head-right, |
||||
|
table.dataTable tfoot td.dt-head-right { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead th.dt-head-justify, |
||||
|
table.dataTable thead td.dt-head-justify, |
||||
|
table.dataTable tfoot th.dt-head-justify, |
||||
|
table.dataTable tfoot td.dt-head-justify { |
||||
|
text-align: justify; |
||||
|
} |
||||
|
|
||||
|
table.dataTable thead th.dt-head-nowrap, |
||||
|
table.dataTable thead td.dt-head-nowrap, |
||||
|
table.dataTable tfoot th.dt-head-nowrap, |
||||
|
table.dataTable tfoot td.dt-head-nowrap { |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
table.dataTable tbody th.dt-body-left, |
||||
|
table.dataTable tbody td.dt-body-left { |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
table.dataTable tbody th.dt-body-center, |
||||
|
table.dataTable tbody td.dt-body-center { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
table.dataTable tbody th.dt-body-right, |
||||
|
table.dataTable tbody td.dt-body-right { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
table.dataTable tbody th.dt-body-justify, |
||||
|
table.dataTable tbody td.dt-body-justify { |
||||
|
text-align: justify; |
||||
|
} |
||||
|
|
||||
|
table.dataTable tbody th.dt-body-nowrap, |
||||
|
table.dataTable tbody td.dt-body-nowrap { |
||||
|
white-space: nowrap; |
||||
|
} /*! Bootstrap 5 integration for DataTables |
||||
|
* |
||||
|
* ©2020 SpryMedia Ltd, all rights reserved. |
||||
|
* License: MIT datatables.net/license/mit |
||||
|
*/ |
||||
|
table.dataTable { |
||||
|
clear: both; |
||||
|
margin-top: 6px !important; |
||||
|
margin-bottom: 6px !important; |
||||
|
max-width: none !important; |
||||
|
border-collapse: separate !important; |
||||
|
border-spacing: 0; |
||||
|
} |
||||
|
|
||||
|
table.dataTable td, |
||||
|
table.dataTable th { |
||||
|
-webkit-box-sizing: content-box; |
||||
|
box-sizing: content-box; |
||||
|
} |
||||
|
|
||||
|
table.dataTable td.dataTables_empty, |
||||
|
table.dataTable th.dataTables_empty { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
table.dataTable.nowrap th, |
||||
|
table.dataTable.nowrap td { |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
table.dataTable.table-striped > tbody > tr:nth-of-type(2n + 1) > * { |
||||
|
box-shadow: none; |
||||
|
} |
||||
|
|
||||
|
table.dataTable > tbody > tr { |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
table.dataTable > tbody > tr.selected > * { |
||||
|
box-shadow: inset 0 0 0 9999px rgb(13, 110, 253); |
||||
|
box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected)); |
||||
|
color: rgb(255, 255, 255); |
||||
|
color: rgb(var(--dt-row-selected-text)); |
||||
|
} |
||||
|
|
||||
|
table.dataTable > tbody > tr.selected a { |
||||
|
color: rgb(9, 10, 11); |
||||
|
color: rgb(var(--dt-row-selected-link)); |
||||
|
} |
||||
|
|
||||
|
table.dataTable.table-striped > tbody > tr.odd > * { |
||||
|
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05); |
||||
|
} |
||||
|
|
||||
|
table.dataTable.table-striped > tbody > tr.odd.selected > * { |
||||
|
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95); |
||||
|
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95); |
||||
|
} |
||||
|
|
||||
|
table.dataTable.table-hover > tbody > tr:hover > * { |
||||
|
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075); |
||||
|
} |
||||
|
|
||||
|
table.dataTable.table-hover > tbody > tr.selected:hover > * { |
||||
|
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975); |
||||
|
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975); |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_length label { |
||||
|
font-weight: normal; |
||||
|
text-align: left; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_length select { |
||||
|
width: auto; |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_filter { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_filter label { |
||||
|
font-weight: normal; |
||||
|
white-space: nowrap; |
||||
|
text-align: left; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_filter input { |
||||
|
margin-left: 0.5em; |
||||
|
display: inline-block; |
||||
|
width: auto; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_info { |
||||
|
padding-top: 0.85em; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_paginate { |
||||
|
margin: 0; |
||||
|
white-space: nowrap; |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dataTables_paginate ul.pagination { |
||||
|
margin: 2px 0; |
||||
|
white-space: nowrap; |
||||
|
justify-content: flex-end; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_wrapper div.dt-row { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollHead table.dataTable { |
||||
|
margin-bottom: 0 !important; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollBody > table { |
||||
|
border-top: none; |
||||
|
margin-top: 0 !important; |
||||
|
margin-bottom: 0 !important; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollBody > table > thead .sorting:before, |
||||
|
div.dataTables_scrollBody > table > thead .sorting_asc:before, |
||||
|
div.dataTables_scrollBody > table > thead .sorting_desc:before, |
||||
|
div.dataTables_scrollBody > table > thead .sorting:after, |
||||
|
div.dataTables_scrollBody > table > thead .sorting_asc:after, |
||||
|
div.dataTables_scrollBody > table > thead .sorting_desc:after { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollBody > table > tbody tr:first-child th, |
||||
|
div.dataTables_scrollBody > table > tbody tr:first-child td { |
||||
|
border-top: none; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollFoot > .dataTables_scrollFootInner { |
||||
|
box-sizing: content-box; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollFoot > .dataTables_scrollFootInner > table { |
||||
|
margin-top: 0 !important; |
||||
|
border-top: none; |
||||
|
} |
||||
|
|
||||
|
@media screen and (max-width: 767px) { |
||||
|
div.dataTables_wrapper div.dataTables_length, |
||||
|
div.dataTables_wrapper div.dataTables_filter, |
||||
|
div.dataTables_wrapper div.dataTables_info, |
||||
|
div.dataTables_wrapper div.dataTables_paginate { |
||||
|
text-align: center; |
||||
|
} |
||||
|
div.dataTables_wrapper div.dataTables_paginate ul.pagination { |
||||
|
justify-content: center !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
table.dataTable.table-sm > thead > tr > th:not(.sorting_disabled) { |
||||
|
padding-right: 20px; |
||||
|
} |
||||
|
|
||||
|
table.table-bordered.dataTable { |
||||
|
border-right-width: 0; |
||||
|
} |
||||
|
|
||||
|
table.table-bordered.dataTable thead tr:first-child th, |
||||
|
table.table-bordered.dataTable thead tr:first-child td { |
||||
|
border-top-width: 1px; |
||||
|
} |
||||
|
|
||||
|
table.table-bordered.dataTable th, |
||||
|
table.table-bordered.dataTable td { |
||||
|
border-left-width: 0; |
||||
|
} |
||||
|
|
||||
|
table.table-bordered.dataTable th:first-child, |
||||
|
table.table-bordered.dataTable th:first-child, |
||||
|
table.table-bordered.dataTable td:first-child, |
||||
|
table.table-bordered.dataTable td:first-child { |
||||
|
border-left-width: 1px; |
||||
|
} |
||||
|
|
||||
|
table.table-bordered.dataTable th:last-child, |
||||
|
table.table-bordered.dataTable th:last-child, |
||||
|
table.table-bordered.dataTable td:last-child, |
||||
|
table.table-bordered.dataTable td:last-child { |
||||
|
border-right-width: 1px; |
||||
|
} |
||||
|
|
||||
|
table.table-bordered.dataTable th, |
||||
|
table.table-bordered.dataTable td { |
||||
|
border-bottom-width: 1px; |
||||
|
} |
||||
|
|
||||
|
div.dataTables_scrollHead table.table-bordered { |
||||
|
border-bottom-width: 0; |
||||
|
} |
||||
|
|
||||
|
div.table-responsive > div.dataTables_wrapper > div.row { |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
div.table-responsive |
||||
|
> div.dataTables_wrapper |
||||
|
> div.row |
||||
|
> div[class^="col-"]:first-child { |
||||
|
padding-left: 0; |
||||
|
} |
||||
|
|
||||
|
div.table-responsive |
||||
|
> div.dataTables_wrapper |
||||
|
> div.row |
||||
|
> div[class^="col-"]:last-child { |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
|
||||
|
:root[data-bs-theme="dark"] { |
||||
|
--dt-row-hover: 255, 255, 255; |
||||
|
--dt-row-stripe: 255, 255, 255; |
||||
|
--dt-column-ordering: 255, 255, 255; |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
/*! DataTables Bootstrap 5 integration |
||||
|
* 2020 SpryMedia Ltd - datatables.net/license |
||||
|
*/ |
||||
|
! function(t) { |
||||
|
var n, r; |
||||
|
"function" == typeof define && define.amd ? define(["jquery", "datatables.net"], function(e) { |
||||
|
return t(e, window, document) |
||||
|
}) : "object" == typeof exports ? (n = require("jquery"), r = function(e, a) { |
||||
|
a.fn.dataTable || require("datatables.net")(e, a) |
||||
|
}, "undefined" == typeof window ? module.exports = function(e, a) { |
||||
|
return e = e || window, a = a || n(e), r(e, a), t(a, 0, e.document) |
||||
|
} : (r(window, n), module.exports = t(n, window, window.document))) : t(jQuery, window, document) |
||||
|
}(function(x, e, r, o) { |
||||
|
"use strict"; |
||||
|
var i = x.fn.dataTable; |
||||
|
return x.extend(!0, i.defaults, { |
||||
|
dom: "<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row dt-row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", |
||||
|
renderer: "bootstrap" |
||||
|
}), x.extend(i.ext.classes, { |
||||
|
sWrapper: "dataTables_wrapper dt-bootstrap5", |
||||
|
sFilterInput: "form-control form-control-sm", |
||||
|
sLengthSelect: "form-select form-select-sm", |
||||
|
sProcessing: "dataTables_processing card", |
||||
|
sPageButton: "paginate_button page-item" |
||||
|
}), i.ext.renderer.pageButton.bootstrap = function(d, e, s, a, l, c) { |
||||
|
function u(e, a) { |
||||
|
for (var t, n, r = function(e) { |
||||
|
e.preventDefault(), x(e.currentTarget).hasClass("disabled") || b.page() == e.data.action || b.page(e.data.action).draw("page") |
||||
|
}, o = 0, i = a.length; o < i; o++) |
||||
|
if (t = a[o], Array.isArray(t)) u(e, t); |
||||
|
else { |
||||
|
switch (f = p = "", t) { |
||||
|
case "ellipsis": |
||||
|
p = "…", f = "disabled"; |
||||
|
break; |
||||
|
case "first": |
||||
|
p = g.sFirst, f = t + (0 < l ? "" : " disabled"); |
||||
|
break; |
||||
|
case "previous": |
||||
|
p = g.sPrevious, f = t + (0 < l ? "" : " disabled"); |
||||
|
break; |
||||
|
case "next": |
||||
|
p = g.sNext, f = t + (l < c - 1 ? "" : " disabled"); |
||||
|
break; |
||||
|
case "last": |
||||
|
p = g.sLast, f = t + (l < c - 1 ? "" : " disabled"); |
||||
|
break; |
||||
|
default: |
||||
|
p = t + 1, f = l === t ? "active" : "" |
||||
|
} |
||||
|
p && (n = -1 !== f.indexOf("disabled"), n = x("<li>", { |
||||
|
class: m.sPageButton + " " + f, |
||||
|
id: 0 === s && "string" == typeof t ? d.sTableId + "_" + t : null |
||||
|
}).append(x("<a>", { |
||||
|
href: n ? null : "#", |
||||
|
"aria-controls": d.sTableId, |
||||
|
"aria-disabled": n ? "true" : null, |
||||
|
"aria-label": w[t], |
||||
|
role: "link", |
||||
|
"aria-current": "active" === f ? "page" : null, |
||||
|
"data-dt-idx": t, |
||||
|
tabindex: n ? -1 : d.iTabIndex, |
||||
|
class: "page-link" |
||||
|
}).html(p)).appendTo(e), d.oApi._fnBindAction(n, { |
||||
|
action: t |
||||
|
}, r)) |
||||
|
} |
||||
|
} |
||||
|
var p, f, t, b = new i.Api(d), |
||||
|
m = d.oClasses, |
||||
|
g = d.oLanguage.oPaginate, |
||||
|
w = d.oLanguage.oAria.paginate || {}, |
||||
|
e = x(e); |
||||
|
try { |
||||
|
t = e.find(r.activeElement).data("dt-idx") |
||||
|
} catch (e) { } |
||||
|
var n = e.children("ul.pagination"); |
||||
|
n.length ? n.empty() : n = e.html("<ul/>").children("ul").addClass("pagination"), u(n, a), t !== o && e.find("[data-dt-idx=" + t + "]").trigger("focus") |
||||
|
}, i |
||||
|
}); |
||||
2990
src/main/resources/static/assets/datatables-1.13.6/js/jquery.dataTables.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,248 @@ |
|||||
|
@keyframes dtb-spinner { |
||||
|
100% { |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
@-o-keyframes dtb-spinner { |
||||
|
100% { |
||||
|
-o-transform: rotate(360deg); |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
@-ms-keyframes dtb-spinner { |
||||
|
100% { |
||||
|
-ms-transform: rotate(360deg); |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
@-webkit-keyframes dtb-spinner { |
||||
|
100% { |
||||
|
-webkit-transform: rotate(360deg); |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
@-moz-keyframes dtb-spinner { |
||||
|
100% { |
||||
|
-moz-transform: rotate(360deg); |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
div.dataTables_wrapper { |
||||
|
position: relative; |
||||
|
} |
||||
|
div.dt-buttons { |
||||
|
position: initial; |
||||
|
} |
||||
|
div.dt-buttons .dt-button { |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
div.dt-button-info { |
||||
|
position: fixed; |
||||
|
top: 50%; |
||||
|
left: 50%; |
||||
|
width: 400px; |
||||
|
margin-top: -100px; |
||||
|
margin-left: -200px; |
||||
|
background-color: white; |
||||
|
border-radius: 0.75em; |
||||
|
box-shadow: 3px 4px 10px 1px rgba(0, 0, 0, 0.8); |
||||
|
text-align: center; |
||||
|
z-index: 2003; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
div.dt-button-info h2 { |
||||
|
padding: 2rem 2rem 1rem 2rem; |
||||
|
margin: 0; |
||||
|
font-weight: normal; |
||||
|
} |
||||
|
div.dt-button-info > div { |
||||
|
padding: 1em 2em 2em 2em; |
||||
|
} |
||||
|
div.dtb-popover-close { |
||||
|
position: absolute; |
||||
|
top: 6px; |
||||
|
right: 6px; |
||||
|
width: 22px; |
||||
|
height: 22px; |
||||
|
text-align: center; |
||||
|
border-radius: 3px; |
||||
|
cursor: pointer; |
||||
|
z-index: 2003; |
||||
|
} |
||||
|
button.dtb-hide-drop { |
||||
|
display: none !important; |
||||
|
} |
||||
|
div.dt-button-collection-title { |
||||
|
text-align: center; |
||||
|
padding: 0.3em 0 0.5em; |
||||
|
margin-left: 0.5em; |
||||
|
margin-right: 0.5em; |
||||
|
font-size: 0.9em; |
||||
|
} |
||||
|
div.dt-button-collection-title:empty { |
||||
|
display: none; |
||||
|
} |
||||
|
span.dt-button-spacer { |
||||
|
display: inline-block; |
||||
|
margin: 0.5em; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
span.dt-button-spacer.bar { |
||||
|
border-left: 1px solid rgba(0, 0, 0, 0.3); |
||||
|
vertical-align: middle; |
||||
|
padding-left: 0.5em; |
||||
|
} |
||||
|
span.dt-button-spacer.bar:empty { |
||||
|
height: 1em; |
||||
|
width: 1px; |
||||
|
padding-left: 0; |
||||
|
} |
||||
|
div.dt-button-collection .dt-button-active { |
||||
|
padding-right: 3em; |
||||
|
} |
||||
|
div.dt-button-collection .dt-button-active:after { |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
margin-top: -10px; |
||||
|
right: 1em; |
||||
|
display: inline-block; |
||||
|
content: "✓"; |
||||
|
color: inherit; |
||||
|
} |
||||
|
div.dt-button-collection .dt-button-active.dt-button-split { |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
div.dt-button-collection .dt-button-active.dt-button-split:after { |
||||
|
display: none; |
||||
|
} |
||||
|
div.dt-button-collection .dt-button-active.dt-button-split > *:first-child { |
||||
|
padding-right: 3em; |
||||
|
} |
||||
|
div.dt-button-collection |
||||
|
.dt-button-active.dt-button-split |
||||
|
> *:first-child:after { |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
margin-top: -10px; |
||||
|
right: 1em; |
||||
|
display: inline-block; |
||||
|
content: "✓"; |
||||
|
color: inherit; |
||||
|
} |
||||
|
div.dt-button-collection .dt-button-active-a a { |
||||
|
padding-right: 3em; |
||||
|
} |
||||
|
div.dt-button-collection .dt-button-active-a a:after { |
||||
|
position: absolute; |
||||
|
right: 1em; |
||||
|
display: inline-block; |
||||
|
content: "✓"; |
||||
|
color: inherit; |
||||
|
} |
||||
|
div.dt-button-collection span.dt-button-spacer { |
||||
|
width: 100%; |
||||
|
font-size: 0.9em; |
||||
|
text-align: center; |
||||
|
margin: 0.5em 0; |
||||
|
} |
||||
|
div.dt-button-collection span.dt-button-spacer:empty { |
||||
|
height: 0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
div.dt-button-collection span.dt-button-spacer.bar { |
||||
|
border-left: none; |
||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1); |
||||
|
padding-left: 0; |
||||
|
} |
||||
|
html.dark div.dt-button-info { |
||||
|
background-color: var(--dt-html-background); |
||||
|
border: 1px solid rgba(255, 255, 255, 0.15); |
||||
|
} |
||||
|
div.dt-buttons div.btn-group { |
||||
|
position: initial; |
||||
|
} |
||||
|
div.dt-buttons div.dropdown-menu { |
||||
|
margin-top: 4px; |
||||
|
} |
||||
|
div.dt-buttons div.dropdown-menu .dt-button { |
||||
|
position: relative; |
||||
|
} |
||||
|
div.dt-buttons div.dropdown-menu div.dt-button-split { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
flex-wrap: wrap; |
||||
|
justify-content: flex-start; |
||||
|
align-content: flex-start; |
||||
|
align-items: stretch; |
||||
|
} |
||||
|
div.dt-buttons div.dropdown-menu div.dt-button-split a:first-child { |
||||
|
min-width: auto; |
||||
|
flex: 1 0 50px; |
||||
|
padding-right: 0; |
||||
|
} |
||||
|
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child { |
||||
|
min-width: 33px; |
||||
|
flex: 0; |
||||
|
background: transparent; |
||||
|
border: none; |
||||
|
line-height: 1rem; |
||||
|
color: var(--bs-dropdown-link-color); |
||||
|
padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); |
||||
|
} |
||||
|
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:hover { |
||||
|
color: var(--bs-dropdown-link-hover-color); |
||||
|
background-color: var(--bs-dropdown-link-hover-bg); |
||||
|
} |
||||
|
div.dt-buttons div.dropdown-menu div.dt-button-split button:last-child:after { |
||||
|
position: relative; |
||||
|
left: -3px; |
||||
|
} |
||||
|
div.dt-buttons span.dt-button-spacer.empty { |
||||
|
margin: 1px; |
||||
|
} |
||||
|
div.dt-buttons span.dt-button-spacer.bar:empty { |
||||
|
height: inherit; |
||||
|
} |
||||
|
div.dt-buttons .btn.processing { |
||||
|
color: rgba(0, 0, 0, 0.2); |
||||
|
} |
||||
|
div.dt-buttons .btn.processing:after { |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
left: 50%; |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
margin: -8px 0 0 -8px; |
||||
|
box-sizing: border-box; |
||||
|
display: block; |
||||
|
content: " "; |
||||
|
border: 2px solid rgb(40, 40, 40); |
||||
|
border-radius: 50%; |
||||
|
border-left-color: transparent; |
||||
|
border-right-color: transparent; |
||||
|
animation: dtb-spinner 1500ms infinite linear; |
||||
|
-o-animation: dtb-spinner 1500ms infinite linear; |
||||
|
-ms-animation: dtb-spinner 1500ms infinite linear; |
||||
|
-webkit-animation: dtb-spinner 1500ms infinite linear; |
||||
|
-moz-animation: dtb-spinner 1500ms infinite linear; |
||||
|
} |
||||
|
div.dt-button-background { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
@media screen and (max-width: 767px) { |
||||
|
div.dt-buttons { |
||||
|
float: none; |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
margin-bottom: 0.5em; |
||||
|
} |
||||
|
div.dt-buttons a.btn { |
||||
|
float: none; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
/*! Bootstrap integration for DataTables' Buttons |
||||
|
* © SpryMedia Ltd - datatables.net/license |
||||
|
*/ |
||||
|
! function(o) { |
||||
|
var e, a; |
||||
|
"function" == typeof define && define.amd ? define(["jquery", "datatables.net-bs5", "datatables.net-buttons"], function(t) { |
||||
|
return o(t, window, document) |
||||
|
}) : "object" == typeof exports ? (e = require("jquery"), a = function(t, n) { |
||||
|
n.fn.dataTable || require("datatables.net-bs5")(t, n), n.fn.dataTable.Buttons || require("datatables.net-buttons")(t, n) |
||||
|
}, "undefined" == typeof window ? module.exports = function(t, n) { |
||||
|
return t = t || window, n = n || e(t), a(t, n), o(n, 0, t.document) |
||||
|
} : (a(window, e), module.exports = o(e, window, window.document))) : o(jQuery, window, document) |
||||
|
}(function(o, t, n, e) { |
||||
|
"use strict"; |
||||
|
var a = o.fn.dataTable; |
||||
|
return o.extend(!0, a.Buttons.defaults, { |
||||
|
dom: { |
||||
|
container: { |
||||
|
className: "dt-buttons btn-group flex-wrap" |
||||
|
}, |
||||
|
button: { |
||||
|
className: "btn btn-secondary", |
||||
|
active: "active" |
||||
|
}, |
||||
|
collection: { |
||||
|
action: { |
||||
|
dropHtml: "" |
||||
|
}, |
||||
|
container: { |
||||
|
tag: "div", |
||||
|
className: "dropdown-menu dt-button-collection" |
||||
|
}, |
||||
|
closeButton: !1, |
||||
|
button: { |
||||
|
tag: "a", |
||||
|
className: "dt-button dropdown-item", |
||||
|
active: "dt-button-active", |
||||
|
disabled: "disabled", |
||||
|
spacer: { |
||||
|
className: "dropdown-divider", |
||||
|
tag: "hr" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
split: { |
||||
|
action: { |
||||
|
tag: "a", |
||||
|
className: "btn btn-secondary dt-button-split-drop-button", |
||||
|
closeButton: !1 |
||||
|
}, |
||||
|
dropdown: { |
||||
|
tag: "button", |
||||
|
dropHtml: "", |
||||
|
className: "btn btn-secondary dt-button-split-drop dropdown-toggle dropdown-toggle-split", |
||||
|
closeButton: !1, |
||||
|
align: "split-left", |
||||
|
splitAlignClass: "dt-button-split-left" |
||||
|
}, |
||||
|
wrapper: { |
||||
|
tag: "div", |
||||
|
className: "dt-button-split btn-group", |
||||
|
closeButton: !1 |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
buttonCreated: function(t, n) { |
||||
|
return t.buttons ? o('<div class="btn-group"/>').append(n) : n |
||||
|
} |
||||
|
}), a.ext.buttons.collection.className += " dropdown-toggle", a.ext.buttons.collection.rightAlignClassName = "dropdown-menu-right", a |
||||
|
}); |
||||
538
src/main/resources/static/assets/datatables-buttons-2.4.1/js/buttons.html5.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,79 @@ |
|||||
|
/*! |
||||
|
* Print button for Buttons and DataTables. |
||||
|
* © SpryMedia Ltd - datatables.net/license |
||||
|
*/ |
||||
|
! function(n) { |
||||
|
var o, r; |
||||
|
"function" == typeof define && define.amd ? define(["jquery", "datatables.net", "datatables.net-buttons"], function(t) { |
||||
|
return n(t, window, document) |
||||
|
}) : "object" == typeof exports ? (o = require("jquery"), r = function(t, e) { |
||||
|
e.fn.dataTable || require("datatables.net")(t, e), e.fn.dataTable.Buttons || require("datatables.net-buttons")(t, e) |
||||
|
}, "undefined" == typeof window ? module.exports = function(t, e) { |
||||
|
return t = t || window, e = e || o(t), r(t, e), n(e, t, t.document) |
||||
|
} : (r(window, o), module.exports = n(o, window, window.document))) : n(jQuery, window, document) |
||||
|
}(function(m, b, t, p) { |
||||
|
"use strict"; |
||||
|
|
||||
|
function h(t) { |
||||
|
return n.href = t, -1 === (t = n.host).indexOf("/") && 0 !== n.pathname.indexOf("/") && (t += "/"), n.protocol + "//" + t + n.pathname + n.search |
||||
|
} |
||||
|
var e = m.fn.dataTable, |
||||
|
n = t.createElement("a"); |
||||
|
return e.ext.buttons.print = { |
||||
|
className: "buttons-print", |
||||
|
text: function(t) { |
||||
|
return t.i18n("buttons.print", "Print") |
||||
|
}, |
||||
|
action: function(t, e, n, o) { |
||||
|
function r(t, e) { |
||||
|
for (var n = "<tr>", o = 0, r = t.length; o < r; o++) { |
||||
|
var i = null === t[o] || t[o] === p ? "" : t[o]; |
||||
|
n += "<" + e + " " + (s[o] ? 'class="' + s[o] + '"' : "") + ">" + i + "</" + e + ">" |
||||
|
} |
||||
|
return n + "</tr>" |
||||
|
} |
||||
|
var i = e.buttons.exportData(m.extend({ |
||||
|
decodeEntities: !1 |
||||
|
}, o.exportOptions)), |
||||
|
a = e.buttons.exportInfo(o), |
||||
|
s = e.columns(o.exportOptions.columns).flatten().map(function(t) { |
||||
|
return e.settings()[0].aoColumns[e.column(t).index()].sClass |
||||
|
}).toArray(), |
||||
|
u = '<table class="' + e.table().node().className + '">'; |
||||
|
o.header && (u += "<thead>" + r(i.header, "th") + "</thead>"), u += "<tbody>"; |
||||
|
for (var d = 0, c = i.body.length; d < c; d++) u += r(i.body[d], "td"); |
||||
|
u += "</tbody>", o.footer && i.footer && (u += "<tfoot>" + r(i.footer, "th") + "</tfoot>"), u += "</table>"; |
||||
|
var l = b.open("", ""); |
||||
|
if (l) { |
||||
|
l.document.close(); |
||||
|
var f = "<title>" + a.title + "</title>"; |
||||
|
m("style, link").each(function() { |
||||
|
f += function(t) { |
||||
|
t = m(t).clone()[0]; |
||||
|
return "link" === t.nodeName.toLowerCase() && (t.href = h(t.href)), t.outerHTML |
||||
|
}(this) |
||||
|
}); |
||||
|
try { |
||||
|
l.document.head.innerHTML = f |
||||
|
} catch (t) { |
||||
|
m(l.document.head).html(f) |
||||
|
} |
||||
|
l.document.body.innerHTML = "<h1>" + a.title + "</h1><div>" + (a.messageTop || "") + "</div>" + u + "<div>" + (a.messageBottom || "") + "</div>", m(l.document.body).addClass("dt-print-view"), m("img", l.document.body).each(function(t, e) { |
||||
|
e.setAttribute("src", h(e.getAttribute("src"))) |
||||
|
}), o.customize && o.customize(l, o, e); |
||||
|
a = function() { |
||||
|
o.autoPrint && (l.print(), l.close()) |
||||
|
}; |
||||
|
navigator.userAgent.match(/Trident\/\d.\d/) ? a() : l.setTimeout(a, 1e3) |
||||
|
} else e.buttons.info(e.i18n("buttons.printErrorTitle", "Unable to open print view"), e.i18n("buttons.printErrorMsg", "Please allow popups in your browser for this site to be able to view the print view."), 5e3) |
||||
|
}, |
||||
|
title: "*", |
||||
|
messageTop: "*", |
||||
|
messageBottom: "*", |
||||
|
exportOptions: {}, |
||||
|
header: !0, |
||||
|
footer: !1, |
||||
|
autoPrint: !0, |
||||
|
customize: null |
||||
|
}, e |
||||
|
}); |
||||
@ -0,0 +1,764 @@ |
|||||
|
/*! Buttons for DataTables 2.4.1 |
||||
|
* © SpryMedia Ltd - datatables.net/license |
||||
|
*/ |
||||
|
! function(e) { |
||||
|
var o, i; |
||||
|
"function" == typeof define && define.amd ? define(["jquery", "datatables.net"], function(t) { |
||||
|
return e(t, window, document) |
||||
|
}) : "object" == typeof exports ? (o = require("jquery"), i = function(t, n) { |
||||
|
n.fn.dataTable || require("datatables.net")(t, n) |
||||
|
}, "undefined" == typeof window ? module.exports = function(t, n) { |
||||
|
return t = t || window, n = n || o(t), i(t, n), e(n, t, t.document) |
||||
|
} : (i(window, o), module.exports = e(o, window, window.document))) : e(jQuery, window, document) |
||||
|
}(function(y, v, x, w) { |
||||
|
"use strict"; |
||||
|
var e = y.fn.dataTable, |
||||
|
o = 0, |
||||
|
C = 0, |
||||
|
_ = e.ext.buttons; |
||||
|
|
||||
|
function A(t, n, e) { |
||||
|
y.fn.animate ? t.stop().fadeIn(n, e) : (t.css("display", "block"), e && e.call(t)) |
||||
|
} |
||||
|
|
||||
|
function k(t, n, e) { |
||||
|
y.fn.animate ? t.stop().fadeOut(n, e) : (t.css("display", "none"), e && e.call(t)) |
||||
|
} |
||||
|
|
||||
|
function B(n, t) { |
||||
|
if (!(this instanceof B)) return function(t) { |
||||
|
return new B(t, n).container() |
||||
|
}; |
||||
|
!0 === (t = void 0 === t ? {} : t) && (t = {}), Array.isArray(t) && (t = { |
||||
|
buttons: t |
||||
|
}), this.c = y.extend(!0, {}, B.defaults, t), t.buttons && (this.c.buttons = t.buttons), this.s = { |
||||
|
dt: new e.Api(n), |
||||
|
buttons: [], |
||||
|
listenKeys: "", |
||||
|
namespace: "dtb" + o++ |
||||
|
}, this.dom = { |
||||
|
container: y("<" + this.c.dom.container.tag + "/>").addClass(this.c.dom.container.className) |
||||
|
}, this._constructor() |
||||
|
} |
||||
|
y.extend(B.prototype, { |
||||
|
action: function(t, n) { |
||||
|
t = this._nodeToButton(t); |
||||
|
return n === w ? t.conf.action : (t.conf.action = n, this) |
||||
|
}, |
||||
|
active: function(t, n) { |
||||
|
var t = this._nodeToButton(t), |
||||
|
e = this.c.dom.button.active, |
||||
|
o = y(t.node); |
||||
|
return t.inCollection && this.c.dom.collection.button && this.c.dom.collection.button.active !== w && (e = this.c.dom.collection.button.active), n === w ? o.hasClass(e) : (o.toggleClass(e, n === w || n), this) |
||||
|
}, |
||||
|
add: function(t, n, e) { |
||||
|
var o = this.s.buttons; |
||||
|
if ("string" == typeof n) { |
||||
|
for (var i = n.split("-"), s = this.s, a = 0, r = i.length - 1; a < r; a++) s = s.buttons[+i[a]]; |
||||
|
o = s.buttons, n = +i[i.length - 1] |
||||
|
} |
||||
|
return this._expandButton(o, t, t !== w ? t.split : w, (t === w || t.split === w || 0 === t.split.length) && s !== w, !1, n), e !== w && !0 !== e || this._draw(), this |
||||
|
}, |
||||
|
collectionRebuild: function(t, n) { |
||||
|
var e = this._nodeToButton(t); |
||||
|
if (n !== w) { |
||||
|
for (var o = e.buttons.length - 1; 0 <= o; o--) this.remove(e.buttons[o].node); |
||||
|
for (e.conf.prefixButtons && n.unshift.apply(n, e.conf.prefixButtons), e.conf.postfixButtons && n.push.apply(n, e.conf.postfixButtons), o = 0; o < n.length; o++) { |
||||
|
var i = n[o]; |
||||
|
this._expandButton(e.buttons, i, i !== w && i.config !== w && i.config.split !== w, !0, i.parentConf !== w && i.parentConf.split !== w, null, i.parentConf) |
||||
|
} |
||||
|
} |
||||
|
this._draw(e.collection, e.buttons) |
||||
|
}, |
||||
|
container: function() { |
||||
|
return this.dom.container |
||||
|
}, |
||||
|
disable: function(t) { |
||||
|
t = this._nodeToButton(t); |
||||
|
return y(t.node).addClass(this.c.dom.button.disabled).prop("disabled", !0), this |
||||
|
}, |
||||
|
destroy: function() { |
||||
|
y("body").off("keyup." + this.s.namespace); |
||||
|
for (var t = this.s.buttons.slice(), n = 0, e = t.length; n < e; n++) this.remove(t[n].node); |
||||
|
this.dom.container.remove(); |
||||
|
var o = this.s.dt.settings()[0]; |
||||
|
for (n = 0, e = o.length; n < e; n++) |
||||
|
if (o.inst === this) { |
||||
|
o.splice(n, 1); |
||||
|
break |
||||
|
} return this |
||||
|
}, |
||||
|
enable: function(t, n) { |
||||
|
return !1 === n ? this.disable(t) : (n = this._nodeToButton(t), y(n.node).removeClass(this.c.dom.button.disabled).prop("disabled", !1), this) |
||||
|
}, |
||||
|
index: function(t, n, e) { |
||||
|
n || (n = "", e = this.s.buttons); |
||||
|
for (var o = 0, i = e.length; o < i; o++) { |
||||
|
var s = e[o].buttons; |
||||
|
if (e[o].node === t) return n + o; |
||||
|
if (s && s.length) { |
||||
|
s = this.index(t, o + "-", s); |
||||
|
if (null !== s) return s |
||||
|
} |
||||
|
} |
||||
|
return null |
||||
|
}, |
||||
|
name: function() { |
||||
|
return this.c.name |
||||
|
}, |
||||
|
node: function(t) { |
||||
|
return t ? (t = this._nodeToButton(t), y(t.node)) : this.dom.container |
||||
|
}, |
||||
|
processing: function(t, n) { |
||||
|
var e = this.s.dt, |
||||
|
o = this._nodeToButton(t); |
||||
|
return n === w ? y(o.node).hasClass("processing") : (y(o.node).toggleClass("processing", n), y(e.table().node()).triggerHandler("buttons-processing.dt", [n, e.button(t), e, y(t), o.conf]), this) |
||||
|
}, |
||||
|
remove: function(t) { |
||||
|
var n = this._nodeToButton(t), |
||||
|
e = this._nodeToHost(t), |
||||
|
o = this.s.dt; |
||||
|
if (n.buttons.length) |
||||
|
for (var i = n.buttons.length - 1; 0 <= i; i--) this.remove(n.buttons[i].node); |
||||
|
n.conf.destroying = !0, n.conf.destroy && n.conf.destroy.call(o.button(t), o, y(t), n.conf), this._removeKey(n.conf), y(n.node).remove(); |
||||
|
o = y.inArray(n, e); |
||||
|
return e.splice(o, 1), this |
||||
|
}, |
||||
|
text: function(t, n) { |
||||
|
function e(t) { |
||||
|
return "function" == typeof t ? t(i, s, o.conf) : t |
||||
|
} |
||||
|
var o = this._nodeToButton(t), |
||||
|
t = o.textNode, |
||||
|
i = this.s.dt, |
||||
|
s = y(o.node); |
||||
|
return n === w ? e(o.conf.text) : (o.conf.text = n, t.html(e(n)), this) |
||||
|
}, |
||||
|
_constructor: function() { |
||||
|
var e = this, |
||||
|
t = this.s.dt, |
||||
|
o = t.settings()[0], |
||||
|
n = this.c.buttons; |
||||
|
o._buttons || (o._buttons = []), o._buttons.push({ |
||||
|
inst: this, |
||||
|
name: this.c.name |
||||
|
}); |
||||
|
for (var i = 0, s = n.length; i < s; i++) this.add(n[i]); |
||||
|
t.on("destroy", function(t, n) { |
||||
|
n === o && e.destroy() |
||||
|
}), y("body").on("keyup." + this.s.namespace, function(t) { |
||||
|
var n; |
||||
|
x.activeElement && x.activeElement !== x.body || (n = String.fromCharCode(t.keyCode).toLowerCase(), -1 !== e.s.listenKeys.toLowerCase().indexOf(n) && e._keypress(n, t)) |
||||
|
}) |
||||
|
}, |
||||
|
_addKey: function(t) { |
||||
|
t.key && (this.s.listenKeys += (y.isPlainObject(t.key) ? t.key : t).key) |
||||
|
}, |
||||
|
_draw: function(t, n) { |
||||
|
t || (t = this.dom.container, n = this.s.buttons), t.children().detach(); |
||||
|
for (var e = 0, o = n.length; e < o; e++) t.append(n[e].inserter), t.append(" "), n[e].buttons && n[e].buttons.length && this._draw(n[e].collection, n[e].buttons) |
||||
|
}, |
||||
|
_expandButton: function(t, n, e, o, i, s, a) { |
||||
|
for (var r, l = this.s.dt, c = this.c.dom.collection, u = Array.isArray(n) ? n : [n], d = 0, f = (u = n === w ? Array.isArray(e) ? e : [e] : u).length; d < f; d++) { |
||||
|
var p = this._resolveExtends(u[d]); |
||||
|
if (p) |
||||
|
if (r = !(!p.config || !p.config.split), Array.isArray(p)) this._expandButton(t, p, h !== w && h.conf !== w ? h.conf.split : w, o, a !== w && a.split !== w, s, a); |
||||
|
else { |
||||
|
var h = this._buildButton(p, o, p.split !== w || p.config !== w && p.config.split !== w, i); |
||||
|
if (h) { |
||||
|
if (s !== w && null !== s ? (t.splice(s, 0, h), s++) : t.push(h), h.conf.buttons && (h.collection = y("<" + c.container.content.tag + "/>"), h.conf._collection = h.collection, y(h.node).append(c.action.dropHtml), this._expandButton(h.buttons, h.conf.buttons, h.conf.split, !r, r, s, h.conf)), h.conf.split) { |
||||
|
h.collection = y("<" + c.container.tag + "/>"), h.conf._collection = h.collection; |
||||
|
for (var b = 0; b < h.conf.split.length; b++) { |
||||
|
var g = h.conf.split[b]; |
||||
|
"object" == typeof g && (g.parent = a, g.collectionLayout === w && (g.collectionLayout = h.conf.collectionLayout), g.dropup === w && (g.dropup = h.conf.dropup), g.fade === w) && (g.fade = h.conf.fade) |
||||
|
} |
||||
|
this._expandButton(h.buttons, h.conf.buttons, h.conf.split, !r, r, s, h.conf) |
||||
|
} |
||||
|
h.conf.parent = a, p.init && p.init.call(l.button(h.node), l, y(h.node), p) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
_buildButton: function(n, t, e, o) { |
||||
|
function i(t) { |
||||
|
return "function" == typeof t ? t(u, l, n) : t |
||||
|
} |
||||
|
var s, a, r, l, c = this.c.dom, |
||||
|
u = this.s.dt, |
||||
|
d = y.extend(!0, {}, c.button); |
||||
|
if (t && e && c.collection.split ? y.extend(!0, d, c.collection.split.action) : o || t ? y.extend(!0, d, c.collection.button) : e && y.extend(!0, d, c.split.button), n.spacer) return c = y("<" + d.spacer.tag + "/>").addClass("dt-button-spacer " + n.style + " " + d.spacer.className).html(i(n.text)), { |
||||
|
conf: n, |
||||
|
node: c, |
||||
|
inserter: c, |
||||
|
buttons: [], |
||||
|
inCollection: t, |
||||
|
isSplit: e, |
||||
|
collection: null, |
||||
|
textNode: c |
||||
|
}; |
||||
|
if (n.available && !n.available(u, n) && !n.hasOwnProperty("html")) return !1; |
||||
|
n.hasOwnProperty("html") ? l = y(n.html) : (a = function(t, n, e, o) { |
||||
|
o.action.call(n.button(e), t, n, e, o), y(n.table().node()).triggerHandler("buttons-action.dt", [n.button(e), n, e, o]) |
||||
|
}, c = n.tag || d.tag, r = n.clickBlurs === w || n.clickBlurs, l = y("<" + c + "/>").addClass(d.className).attr("tabindex", this.s.dt.settings()[0].iTabIndex).attr("aria-controls", this.s.dt.table().node().id).on("click.dtb", function(t) { |
||||
|
t.preventDefault(), !l.hasClass(d.disabled) && n.action && a(t, u, l, n), r && l.trigger("blur") |
||||
|
}).on("keypress.dtb", function(t) { |
||||
|
13 === t.keyCode && (t.preventDefault(), !l.hasClass(d.disabled)) && n.action && a(t, u, l, n) |
||||
|
}), "a" === c.toLowerCase() && l.attr("href", "#"), "button" === c.toLowerCase() && l.attr("type", "button"), s = d.liner.tag ? (c = y("<" + d.liner.tag + "/>").html(i(n.text)).addClass(d.liner.className), "a" === d.liner.tag.toLowerCase() && c.attr("href", "#"), l.append(c), c) : (l.html(i(n.text)), l), !1 === n.enabled && l.addClass(d.disabled), n.className && l.addClass(n.className), n.titleAttr && l.attr("title", i(n.titleAttr)), n.attr && l.attr(n.attr), n.namespace || (n.namespace = ".dt-button-" + C++), n.config !== w && n.config.split && (n.split = n.config.split)); |
||||
|
var f, p, h, b, g, m, c = this.c.dom.buttonContainer, |
||||
|
c = c && c.tag ? y("<" + c.tag + "/>").addClass(c.className).append(l) : l; |
||||
|
return this._addKey(n), this.c.buttonCreated && (c = this.c.buttonCreated(n, c)), e && (p = (f = t ? y.extend(!0, this.c.dom.split, this.c.dom.collection.split) : this.c.dom.split).wrapper, h = y("<" + p.tag + "/>").addClass(p.className).append(l), b = y.extend(n, { |
||||
|
align: f.dropdown.align, |
||||
|
attr: { |
||||
|
"aria-haspopup": "dialog", |
||||
|
"aria-expanded": !1 |
||||
|
}, |
||||
|
className: f.dropdown.className, |
||||
|
closeButton: !1, |
||||
|
splitAlignClass: f.dropdown.splitAlignClass, |
||||
|
text: f.dropdown.text |
||||
|
}), this._addKey(b), g = function(t, n, e, o) { |
||||
|
_.split.action.call(n.button(h), t, n, e, o), y(n.table().node()).triggerHandler("buttons-action.dt", [n.button(e), n, e, o]), e.attr("aria-expanded", !0) |
||||
|
}, m = y('<button class="' + f.dropdown.className + ' dt-button"></button>').html(f.dropdown.dropHtml).on("click.dtb", function(t) { |
||||
|
t.preventDefault(), t.stopPropagation(), m.hasClass(d.disabled) || g(t, u, m, b), r && m.trigger("blur") |
||||
|
}).on("keypress.dtb", function(t) { |
||||
|
13 === t.keyCode && (t.preventDefault(), m.hasClass(d.disabled) || g(t, u, m, b)) |
||||
|
}), 0 === n.split.length && m.addClass("dtb-hide-drop"), h.append(m).attr(b.attr)), { |
||||
|
conf: n, |
||||
|
node: (e ? h : l).get(0), |
||||
|
inserter: e ? h : c, |
||||
|
buttons: [], |
||||
|
inCollection: t, |
||||
|
isSplit: e, |
||||
|
inSplit: o, |
||||
|
collection: null, |
||||
|
textNode: s |
||||
|
} |
||||
|
}, |
||||
|
_nodeToButton: function(t, n) { |
||||
|
for (var e = 0, o = (n = n || this.s.buttons).length; e < o; e++) { |
||||
|
if (n[e].node === t) return n[e]; |
||||
|
if (n[e].buttons.length) { |
||||
|
var i = this._nodeToButton(t, n[e].buttons); |
||||
|
if (i) return i |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
_nodeToHost: function(t, n) { |
||||
|
for (var e = 0, o = (n = n || this.s.buttons).length; e < o; e++) { |
||||
|
if (n[e].node === t) return n; |
||||
|
if (n[e].buttons.length) { |
||||
|
var i = this._nodeToHost(t, n[e].buttons); |
||||
|
if (i) return i |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
_keypress: function(s, a) { |
||||
|
var r; |
||||
|
a._buttonsHandled || (r = function(t) { |
||||
|
for (var n, e, o = 0, i = t.length; o < i; o++) n = t[o].conf, e = t[o].node, !n.key || n.key !== s && (!y.isPlainObject(n.key) || n.key.key !== s || n.key.shiftKey && !a.shiftKey || n.key.altKey && !a.altKey || n.key.ctrlKey && !a.ctrlKey || n.key.metaKey && !a.metaKey) || (a._buttonsHandled = !0, y(e).click()), t[o].buttons.length && r(t[o].buttons) |
||||
|
})(this.s.buttons) |
||||
|
}, |
||||
|
_removeKey: function(t) { |
||||
|
var n; |
||||
|
t.key && (t = (y.isPlainObject(t.key) ? t.key : t).key, n = this.s.listenKeys.split(""), t = y.inArray(t, n), n.splice(t, 1), this.s.listenKeys = n.join("")) |
||||
|
}, |
||||
|
_resolveExtends: function(e) { |
||||
|
function t(t) { |
||||
|
for (var n = 0; !y.isPlainObject(t) && !Array.isArray(t);) { |
||||
|
if (t === w) return; |
||||
|
if ("function" == typeof t) { |
||||
|
if (!(t = t.call(i, s, e))) return !1 |
||||
|
} else if ("string" == typeof t) { |
||||
|
if (!_[t]) return { |
||||
|
html: t |
||||
|
}; |
||||
|
t = _[t] |
||||
|
} |
||||
|
if (30 < ++n) throw "Buttons: Too many iterations" |
||||
|
} |
||||
|
return Array.isArray(t) ? t : y.extend({}, t) |
||||
|
} |
||||
|
var n, o, i = this, |
||||
|
s = this.s.dt; |
||||
|
for (e = t(e); e && e.extend;) { |
||||
|
if (!_[e.extend]) throw "Cannot extend unknown button type: " + e.extend; |
||||
|
var a = t(_[e.extend]); |
||||
|
if (Array.isArray(a)) return a; |
||||
|
if (!a) return !1; |
||||
|
var r = a.className; |
||||
|
e.config !== w && a.config !== w && (e.config = y.extend({}, a.config, e.config)), e = y.extend({}, a, e), r && e.className !== r && (e.className = r + " " + e.className), e.extend = a.extend |
||||
|
} |
||||
|
var l = e.postfixButtons; |
||||
|
if (l) |
||||
|
for (e.buttons || (e.buttons = []), n = 0, o = l.length; n < o; n++) e.buttons.push(l[n]); |
||||
|
var c = e.prefixButtons; |
||||
|
if (c) |
||||
|
for (e.buttons || (e.buttons = []), n = 0, o = c.length; n < o; n++) e.buttons.splice(n, 0, c[n]); |
||||
|
return e |
||||
|
}, |
||||
|
_popover: function(o, t, n, e) { |
||||
|
function i() { |
||||
|
h = !0, k(y(g), b.fade, function() { |
||||
|
y(this).detach() |
||||
|
}), y(f.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes()).attr("aria-expanded", "false"), y("div.dt-button-background").off("click.dtb-collection"), B.background(!1, b.backgroundClassName, b.fade, m), y(v).off("resize.resize.dtb-collection"), y("body").off(".dtb-collection"), f.off("buttons-action.b-internal"), f.off("destroy") |
||||
|
} |
||||
|
var s, a, r, l, c, u, d, f = t, |
||||
|
p = this.c, |
||||
|
h = !1, |
||||
|
b = y.extend({ |
||||
|
align: "button-left", |
||||
|
autoClose: !1, |
||||
|
background: !0, |
||||
|
backgroundClassName: "dt-button-background", |
||||
|
closeButton: !0, |
||||
|
containerClassName: p.dom.collection.container.className, |
||||
|
contentClassName: p.dom.collection.container.content.className, |
||||
|
collectionLayout: "", |
||||
|
collectionTitle: "", |
||||
|
dropup: !1, |
||||
|
fade: 400, |
||||
|
popoverTitle: "", |
||||
|
rightAlignClassName: "dt-button-right", |
||||
|
tag: p.dom.collection.container.tag |
||||
|
}, n), |
||||
|
g = b.tag + "." + b.containerClassName.replace(/ /g, "."), |
||||
|
m = t.node(); |
||||
|
!1 === o ? i() : ((p = y(f.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes())).length && (m.closest(g).length && (m = p.eq(0)), i()), n = y(".dt-button", o).length, p = "", 3 === n ? p = "dtb-b3" : 2 === n ? p = "dtb-b2" : 1 === n && (p = "dtb-b1"), s = y("<" + b.tag + "/>").addClass(b.containerClassName).addClass(b.collectionLayout).addClass(b.splitAlignClass).addClass(p).css("display", "none").attr({ |
||||
|
"aria-modal": !0, |
||||
|
role: "dialog" |
||||
|
}), o = y(o).addClass(b.contentClassName).attr("role", "menu").appendTo(s), m.attr("aria-expanded", "true"), m.parents("body")[0] !== x.body && (m = x.body.lastChild), b.popoverTitle ? s.prepend('<div class="dt-button-collection-title">' + b.popoverTitle + "</div>") : b.collectionTitle && s.prepend('<div class="dt-button-collection-title">' + b.collectionTitle + "</div>"), b.closeButton && s.prepend('<div class="dtb-popover-close">×</div>').addClass("dtb-collection-closeable"), A(s.insertAfter(m), b.fade), n = y(t.table().container()), d = s.css("position"), "container" !== b.span && "dt-container" !== b.align || (m = m.parent(), s.css("width", n.width())), "absolute" === d ? (p = y(m[0].offsetParent), t = m.position(), n = m.offset(), a = p.offset(), r = p.position(), l = v.getComputedStyle(p[0]), a.height = p.outerHeight(), a.width = p.width() + parseFloat(l.paddingLeft), a.right = a.left + a.width, a.bottom = a.top + a.height, p = t.top + m.outerHeight(), c = t.left, s.css({ |
||||
|
top: p, |
||||
|
left: c |
||||
|
}), l = v.getComputedStyle(s[0]), (u = s.offset()).height = s.outerHeight(), u.width = s.outerWidth(), u.right = u.left + u.width, u.bottom = u.top + u.height, u.marginTop = parseFloat(l.marginTop), u.marginBottom = parseFloat(l.marginBottom), b.dropup && (p = t.top - u.height - u.marginTop - u.marginBottom), "button-right" !== b.align && !s.hasClass(b.rightAlignClassName) || (c = t.left - u.width + m.outerWidth()), "dt-container" !== b.align && "container" !== b.align || (c = c < t.left ? -t.left : c) + u.width > a.width && (c = a.width - u.width), r.left + c + u.width > y(v).width() && (c = y(v).width() - u.width - r.left), n.left + c < 0 && (c = -n.left), r.top + p + u.height > y(v).height() + y(v).scrollTop() && (p = t.top - u.height - u.marginTop - u.marginBottom), r.top + p < y(v).scrollTop() && (p = t.top + m.outerHeight()), s.css({ |
||||
|
top: p, |
||||
|
left: c |
||||
|
})) : ((d = function() { |
||||
|
var t = y(v).height() / 2, |
||||
|
n = s.height() / 2; |
||||
|
s.css("marginTop", -1 * (n = t < n ? t : n)) |
||||
|
})(), y(v).on("resize.dtb-collection", function() { |
||||
|
d() |
||||
|
})), b.background && B.background(!0, b.backgroundClassName, b.fade, b.backgroundHost || m), y("div.dt-button-background").on("click.dtb-collection", function() { }), b.autoClose && setTimeout(function() { |
||||
|
f.on("buttons-action.b-internal", function(t, n, e, o) { |
||||
|
o[0] !== m[0] && i() |
||||
|
}) |
||||
|
}, 0), y(s).trigger("buttons-popover.dt"), f.on("destroy", i), setTimeout(function() { |
||||
|
h = !1, y("body").on("click.dtb-collection", function(t) { |
||||
|
var n, e; |
||||
|
!h && (n = y.fn.addBack ? "addBack" : "andSelf", e = y(t.target).parent()[0], !y(t.target).parents()[n]().filter(o).length && !y(e).hasClass("dt-buttons") || y(t.target).hasClass("dt-button-background")) && i() |
||||
|
}).on("keyup.dtb-collection", function(t) { |
||||
|
27 === t.keyCode && i() |
||||
|
}).on("keydown.dtb-collection", function(t) { |
||||
|
var n = y("a, button", o), |
||||
|
e = x.activeElement; |
||||
|
9 === t.keyCode && (-1 === n.index(e) ? (n.first().focus(), t.preventDefault()) : t.shiftKey ? e === n[0] && (n.last().focus(), t.preventDefault()) : e === n.last()[0] && (n.first().focus(), t.preventDefault())) |
||||
|
}) |
||||
|
}, 0)) |
||||
|
} |
||||
|
}), B.background = function(t, n, e, o) { |
||||
|
e === w && (e = 400), o = o || x.body, t ? A(y("<div/>").addClass(n).css("display", "none").insertAfter(o), e) : k(y("div." + n), e, function() { |
||||
|
y(this).removeClass(n).remove() |
||||
|
}) |
||||
|
}, B.instanceSelector = function(t, i) { |
||||
|
var s, a, r; |
||||
|
return t === w || null === t ? y.map(i, function(t) { |
||||
|
return t.inst |
||||
|
}) : (s = [], a = y.map(i, function(t) { |
||||
|
return t.name |
||||
|
}), (r = function(t) { |
||||
|
var n; |
||||
|
if (Array.isArray(t)) |
||||
|
for (var e = 0, o = t.length; e < o; e++) r(t[e]); |
||||
|
else "string" == typeof t ? -1 !== t.indexOf(",") ? r(t.split(",")) : -1 !== (n = y.inArray(t.trim(), a)) && s.push(i[n].inst) : "number" == typeof t ? s.push(i[t].inst) : "object" == typeof t && s.push(t) |
||||
|
})(t), s) |
||||
|
}, B.buttonSelector = function(t, n) { |
||||
|
for (var c = [], u = function(t, n, e) { |
||||
|
for (var o, i, s = 0, a = n.length; s < a; s++)(o = n[s]) && (t.push({ |
||||
|
node: o.node, |
||||
|
name: o.conf.name, |
||||
|
idx: i = e !== w ? e + s : s + "" |
||||
|
}), o.buttons) && u(t, o.buttons, i + "-") |
||||
|
}, d = function(t, n) { |
||||
|
var e = [], |
||||
|
o = (u(e, n.s.buttons), y.map(e, function(t) { |
||||
|
return t.node |
||||
|
})); |
||||
|
if (Array.isArray(t) || t instanceof y) |
||||
|
for (s = 0, a = t.length; s < a; s++) d(t[s], n); |
||||
|
else if (null === t || t === w || "*" === t) |
||||
|
for (s = 0, a = e.length; s < a; s++) c.push({ |
||||
|
inst: n, |
||||
|
node: e[s].node |
||||
|
}); |
||||
|
else if ("number" == typeof t) n.s.buttons[t] && c.push({ |
||||
|
inst: n, |
||||
|
node: n.s.buttons[t].node |
||||
|
}); |
||||
|
else if ("string" == typeof t) |
||||
|
if (-1 !== t.indexOf(",")) |
||||
|
for (var i = t.split(","), s = 0, a = i.length; s < a; s++) d(i[s].trim(), n); |
||||
|
else if (t.match(/^\d+(\-\d+)*$/)) { |
||||
|
var r = y.map(e, function(t) { |
||||
|
return t.idx |
||||
|
}); |
||||
|
c.push({ |
||||
|
inst: n, |
||||
|
node: e[y.inArray(t, r)].node |
||||
|
}) |
||||
|
} else if (-1 !== t.indexOf(":name")) { |
||||
|
var l = t.replace(":name", ""); |
||||
|
for (s = 0, a = e.length; s < a; s++) e[s].name === l && c.push({ |
||||
|
inst: n, |
||||
|
node: e[s].node |
||||
|
}) |
||||
|
} else y(o).filter(t).each(function() { |
||||
|
c.push({ |
||||
|
inst: n, |
||||
|
node: this |
||||
|
}) |
||||
|
}); |
||||
|
else "object" == typeof t && t.nodeName && -1 !== (r = y.inArray(t, o)) && c.push({ |
||||
|
inst: n, |
||||
|
node: o[r] |
||||
|
}) |
||||
|
}, e = 0, o = t.length; e < o; e++) { |
||||
|
var i = t[e]; |
||||
|
d(n, i) |
||||
|
} |
||||
|
return c |
||||
|
}, B.stripData = function(t, n) { |
||||
|
return "string" == typeof t && (t = (t = t.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")).replace(/<!\-\-.*?\-\->/g, ""), n && !n.stripHtml || (t = t.replace(/<[^>]*>/g, "")), n && !n.trim || (t = t.replace(/^\s+|\s+$/g, "")), n && !n.stripNewlines || (t = t.replace(/\n/g, " ")), !n || n.decodeEntities) && (l.innerHTML = t, t = l.value), t |
||||
|
}, B.defaults = { |
||||
|
buttons: ["copy", "excel", "csv", "pdf", "print"], |
||||
|
name: "main", |
||||
|
tabIndex: 0, |
||||
|
dom: { |
||||
|
container: { |
||||
|
tag: "div", |
||||
|
className: "dt-buttons" |
||||
|
}, |
||||
|
collection: { |
||||
|
action: { |
||||
|
dropHtml: '<span class="dt-button-down-arrow">▼</span>' |
||||
|
}, |
||||
|
container: { |
||||
|
className: "dt-button-collection", |
||||
|
content: { |
||||
|
className: "", |
||||
|
tag: "div" |
||||
|
}, |
||||
|
tag: "div" |
||||
|
} |
||||
|
}, |
||||
|
button: { |
||||
|
tag: "button", |
||||
|
className: "dt-button", |
||||
|
active: "dt-button-active", |
||||
|
disabled: "disabled", |
||||
|
spacer: { |
||||
|
className: "dt-button-spacer", |
||||
|
tag: "span" |
||||
|
}, |
||||
|
liner: { |
||||
|
tag: "span", |
||||
|
className: "" |
||||
|
} |
||||
|
}, |
||||
|
split: { |
||||
|
action: { |
||||
|
className: "dt-button-split-drop-button dt-button", |
||||
|
tag: "button" |
||||
|
}, |
||||
|
dropdown: { |
||||
|
align: "split-right", |
||||
|
className: "dt-button-split-drop", |
||||
|
dropHtml: '<span class="dt-button-down-arrow">▼</span>', |
||||
|
splitAlignClass: "dt-button-split-left", |
||||
|
tag: "button" |
||||
|
}, |
||||
|
wrapper: { |
||||
|
className: "dt-button-split", |
||||
|
tag: "div" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, y.extend(_, { |
||||
|
collection: { |
||||
|
text: function(t) { |
||||
|
return t.i18n("buttons.collection", "Collection") |
||||
|
}, |
||||
|
className: "buttons-collection", |
||||
|
closeButton: !(B.version = "2.4.1"), |
||||
|
init: function(t, n, e) { |
||||
|
n.attr("aria-expanded", !1) |
||||
|
}, |
||||
|
action: function(t, n, e, o) { |
||||
|
o._collection.parents("body").length ? this.popover(!1, o) : this.popover(o._collection, o), "keypress" === t.type && y("a, button", o._collection).eq(0).focus() |
||||
|
}, |
||||
|
attr: { |
||||
|
"aria-haspopup": "dialog" |
||||
|
} |
||||
|
}, |
||||
|
split: { |
||||
|
text: function(t) { |
||||
|
return t.i18n("buttons.split", "Split") |
||||
|
}, |
||||
|
className: "buttons-split", |
||||
|
closeButton: !1, |
||||
|
init: function(t, n, e) { |
||||
|
return n.attr("aria-expanded", !1) |
||||
|
}, |
||||
|
action: function(t, n, e, o) { |
||||
|
this.popover(o._collection, o) |
||||
|
}, |
||||
|
attr: { |
||||
|
"aria-haspopup": "dialog" |
||||
|
} |
||||
|
}, |
||||
|
copy: function(t, n) { |
||||
|
if (_.copyHtml5) return "copyHtml5" |
||||
|
}, |
||||
|
csv: function(t, n) { |
||||
|
if (_.csvHtml5 && _.csvHtml5.available(t, n)) return "csvHtml5" |
||||
|
}, |
||||
|
excel: function(t, n) { |
||||
|
if (_.excelHtml5 && _.excelHtml5.available(t, n)) return "excelHtml5" |
||||
|
}, |
||||
|
pdf: function(t, n) { |
||||
|
if (_.pdfHtml5 && _.pdfHtml5.available(t, n)) return "pdfHtml5" |
||||
|
}, |
||||
|
pageLength: function(t) { |
||||
|
var n = t.settings()[0].aLengthMenu, |
||||
|
e = [], |
||||
|
o = []; |
||||
|
if (Array.isArray(n[0])) e = n[0], o = n[1]; |
||||
|
else |
||||
|
for (var i = 0; i < n.length; i++) { |
||||
|
var s = n[i]; |
||||
|
y.isPlainObject(s) ? (e.push(s.value), o.push(s.label)) : (e.push(s), o.push(s)) |
||||
|
} |
||||
|
return { |
||||
|
extend: "collection", |
||||
|
text: function(t) { |
||||
|
return t.i18n("buttons.pageLength", { |
||||
|
"-1": "Show all rows", |
||||
|
_: "Show %d rows" |
||||
|
}, t.page.len()) |
||||
|
}, |
||||
|
className: "buttons-page-length", |
||||
|
autoClose: !0, |
||||
|
buttons: y.map(e, function(s, t) { |
||||
|
return { |
||||
|
text: o[t], |
||||
|
className: "button-page-length", |
||||
|
action: function(t, n) { |
||||
|
n.page.len(s).draw() |
||||
|
}, |
||||
|
init: function(t, n, e) { |
||||
|
function o() { |
||||
|
i.active(t.page.len() === s) |
||||
|
} |
||||
|
var i = this; |
||||
|
t.on("length.dt" + e.namespace, o), o() |
||||
|
}, |
||||
|
destroy: function(t, n, e) { |
||||
|
t.off("length.dt" + e.namespace) |
||||
|
} |
||||
|
} |
||||
|
}), |
||||
|
init: function(t, n, e) { |
||||
|
var o = this; |
||||
|
t.on("length.dt" + e.namespace, function() { |
||||
|
o.text(e.text) |
||||
|
}) |
||||
|
}, |
||||
|
destroy: function(t, n, e) { |
||||
|
t.off("length.dt" + e.namespace) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
spacer: { |
||||
|
style: "empty", |
||||
|
spacer: !0, |
||||
|
text: function(t) { |
||||
|
return t.i18n("buttons.spacer", "") |
||||
|
} |
||||
|
} |
||||
|
}), e.Api.register("buttons()", function(n, e) { |
||||
|
e === w && (e = n, n = w), this.selector.buttonGroup = n; |
||||
|
var t = this.iterator(!0, "table", function(t) { |
||||
|
if (t._buttons) return B.buttonSelector(B.instanceSelector(n, t._buttons), e) |
||||
|
}, !0); |
||||
|
return t._groupSelector = n, t |
||||
|
}), e.Api.register("button()", function(t, n) { |
||||
|
t = this.buttons(t, n); |
||||
|
return 1 < t.length && t.splice(1, t.length), t |
||||
|
}), e.Api.registerPlural("buttons().active()", "button().active()", function(n) { |
||||
|
return n === w ? this.map(function(t) { |
||||
|
return t.inst.active(t.node) |
||||
|
}) : this.each(function(t) { |
||||
|
t.inst.active(t.node, n) |
||||
|
}) |
||||
|
}), e.Api.registerPlural("buttons().action()", "button().action()", function(n) { |
||||
|
return n === w ? this.map(function(t) { |
||||
|
return t.inst.action(t.node) |
||||
|
}) : this.each(function(t) { |
||||
|
t.inst.action(t.node, n) |
||||
|
}) |
||||
|
}), e.Api.registerPlural("buttons().collectionRebuild()", "button().collectionRebuild()", function(e) { |
||||
|
return this.each(function(t) { |
||||
|
for (var n = 0; n < e.length; n++) "object" == typeof e[n] && (e[n].parentConf = t); |
||||
|
t.inst.collectionRebuild(t.node, e) |
||||
|
}) |
||||
|
}), e.Api.register(["buttons().enable()", "button().enable()"], function(n) { |
||||
|
return this.each(function(t) { |
||||
|
t.inst.enable(t.node, n) |
||||
|
}) |
||||
|
}), e.Api.register(["buttons().disable()", "button().disable()"], function() { |
||||
|
return this.each(function(t) { |
||||
|
t.inst.disable(t.node) |
||||
|
}) |
||||
|
}), e.Api.register("button().index()", function() { |
||||
|
var n = null; |
||||
|
return this.each(function(t) { |
||||
|
t = t.inst.index(t.node); |
||||
|
null !== t && (n = t) |
||||
|
}), n |
||||
|
}), e.Api.registerPlural("buttons().nodes()", "button().node()", function() { |
||||
|
var n = y(); |
||||
|
return y(this.each(function(t) { |
||||
|
n = n.add(t.inst.node(t.node)) |
||||
|
})), n |
||||
|
}), e.Api.registerPlural("buttons().processing()", "button().processing()", function(n) { |
||||
|
return n === w ? this.map(function(t) { |
||||
|
return t.inst.processing(t.node) |
||||
|
}) : this.each(function(t) { |
||||
|
t.inst.processing(t.node, n) |
||||
|
}) |
||||
|
}), e.Api.registerPlural("buttons().text()", "button().text()", function(n) { |
||||
|
return n === w ? this.map(function(t) { |
||||
|
return t.inst.text(t.node) |
||||
|
}) : this.each(function(t) { |
||||
|
t.inst.text(t.node, n) |
||||
|
}) |
||||
|
}), e.Api.registerPlural("buttons().trigger()", "button().trigger()", function() { |
||||
|
return this.each(function(t) { |
||||
|
t.inst.node(t.node).trigger("click") |
||||
|
}) |
||||
|
}), e.Api.register("button().popover()", function(n, e) { |
||||
|
return this.map(function(t) { |
||||
|
return t.inst._popover(n, this.button(this[0].node), e) |
||||
|
}) |
||||
|
}), e.Api.register("buttons().containers()", function() { |
||||
|
var i = y(), |
||||
|
s = this._groupSelector; |
||||
|
return this.iterator(!0, "table", function(t) { |
||||
|
if (t._buttons) |
||||
|
for (var n = B.instanceSelector(s, t._buttons), e = 0, o = n.length; e < o; e++) i = i.add(n[e].container()) |
||||
|
}), i |
||||
|
}), e.Api.register("buttons().container()", function() { |
||||
|
return this.containers().eq(0) |
||||
|
}), e.Api.register("button().add()", function(t, n, e) { |
||||
|
var o = this.context; |
||||
|
return o.length && (o = B.instanceSelector(this._groupSelector, o[0]._buttons)).length && o[0].add(n, t, e), this.button(this._groupSelector, t) |
||||
|
}), e.Api.register("buttons().destroy()", function() { |
||||
|
return this.pluck("inst").unique().each(function(t) { |
||||
|
t.destroy() |
||||
|
}), this |
||||
|
}), e.Api.registerPlural("buttons().remove()", "buttons().remove()", function() { |
||||
|
return this.each(function(t) { |
||||
|
t.inst.remove(t.node) |
||||
|
}), this |
||||
|
}), e.Api.register("buttons.info()", function(t, n, e) { |
||||
|
var o = this; |
||||
|
return !1 === t ? (this.off("destroy.btn-info"), k(y("#datatables_buttons_info"), 400, function() { |
||||
|
y(this).remove() |
||||
|
}), clearTimeout(i), i = null) : (i && clearTimeout(i), y("#datatables_buttons_info").length && y("#datatables_buttons_info").remove(), t = t ? "<h2>" + t + "</h2>" : "", A(y('<div id="datatables_buttons_info" class="dt-button-info"/>').html(t).append(y("<div/>")["string" == typeof n ? "html" : "append"](n)).css("display", "none").appendTo("body")), e !== w && 0 !== e && (i = setTimeout(function() { |
||||
|
o.buttons.info(!1) |
||||
|
}, e)), this.on("destroy.btn-info", function() { |
||||
|
o.buttons.info(!1) |
||||
|
})), this |
||||
|
}), e.Api.register("buttons.exportData()", function(t) { |
||||
|
if (this.context.length) return c(new e.Api(this.context[0]), t) |
||||
|
}), e.Api.register("buttons.exportInfo()", function(t) { |
||||
|
return { |
||||
|
filename: n(t = t || {}), |
||||
|
title: a(t), |
||||
|
messageTop: r(this, t.message || t.messageTop, "top"), |
||||
|
messageBottom: r(this, t.messageBottom, "bottom") |
||||
|
} |
||||
|
}); |
||||
|
var i, n = function(t) { |
||||
|
var n; |
||||
|
return (n = "function" == typeof (n = "*" === t.filename && "*" !== t.title && t.title !== w && null !== t.title && "" !== t.title ? t.title : t.filename) ? n() : n) === w || null === n ? null : (n = (n = -1 !== n.indexOf("*") ? n.replace("*", y("head > title").text()).trim() : n).replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "")) + (s(t.extension) || "") |
||||
|
}, |
||||
|
s = function(t) { |
||||
|
return null === t || t === w ? null : "function" == typeof t ? t() : t |
||||
|
}, |
||||
|
a = function(t) { |
||||
|
t = s(t.title); |
||||
|
return null === t ? null : -1 !== t.indexOf("*") ? t.replace("*", y("head > title").text() || "Exported data") : t |
||||
|
}, |
||||
|
r = function(t, n, e) { |
||||
|
n = s(n); |
||||
|
return null === n ? null : (t = y("caption", t.table().container()).eq(0), "*" === n ? t.css("caption-side") !== e ? null : t.length ? t.text() : "" : n) |
||||
|
}, |
||||
|
l = y("<textarea/>")[0], |
||||
|
c = function(e, t) { |
||||
|
for (var o = y.extend(!0, {}, { |
||||
|
rows: null, |
||||
|
columns: "", |
||||
|
modifier: { |
||||
|
search: "applied", |
||||
|
order: "applied" |
||||
|
}, |
||||
|
orthogonal: "display", |
||||
|
stripHtml: !0, |
||||
|
stripNewlines: !0, |
||||
|
decodeEntities: !0, |
||||
|
trim: !0, |
||||
|
format: { |
||||
|
header: function(t) { |
||||
|
return B.stripData(t, o) |
||||
|
}, |
||||
|
footer: function(t) { |
||||
|
return B.stripData(t, o) |
||||
|
}, |
||||
|
body: function(t) { |
||||
|
return B.stripData(t, o) |
||||
|
} |
||||
|
}, |
||||
|
customizeData: null |
||||
|
}, t), t = e.columns(o.columns).indexes().map(function(t) { |
||||
|
var n = e.column(t).header(); |
||||
|
return o.format.header(n.innerHTML, t, n) |
||||
|
}).toArray(), n = e.table().footer() ? e.columns(o.columns).indexes().map(function(t) { |
||||
|
var n = e.column(t).footer(); |
||||
|
return o.format.footer(n ? n.innerHTML : "", t, n) |
||||
|
}).toArray() : null, i = y.extend({}, o.modifier), i = (e.select && "function" == typeof e.select.info && i.selected === w && e.rows(o.rows, y.extend({ |
||||
|
selected: !0 |
||||
|
}, i)).any() && y.extend(i, { |
||||
|
selected: !0 |
||||
|
}), e.rows(o.rows, i).indexes().toArray()), i = e.cells(i, o.columns), s = i.render(o.orthogonal).toArray(), a = i.nodes().toArray(), r = t.length, l = [], c = 0, u = 0, d = 0 < r ? s.length / r : 0; u < d; u++) { |
||||
|
for (var f = [r], p = 0; p < r; p++) f[p] = o.format.body(s[c], u, p, a[c]), c++; |
||||
|
l[u] = f |
||||
|
} |
||||
|
i = { |
||||
|
header: t, |
||||
|
footer: n, |
||||
|
body: l |
||||
|
}; |
||||
|
return o.customizeData && o.customizeData(i), i |
||||
|
}; |
||||
|
|
||||
|
function t(t, n) { |
||||
|
t = new e.Api(t), n = n || t.init().buttons || e.defaults.buttons; |
||||
|
return new B(t, n).container() |
||||
|
} |
||||
|
return y.fn.dataTable.Buttons = B, y.fn.DataTable.Buttons = B, y(x).on("init.dt plugin-init.dt", function(t, n) { |
||||
|
"dt" === t.namespace && (t = n.oInit.buttons || e.defaults.buttons) && !n._buttons && new B(n, t).container() |
||||
|
}), e.ext.feature.push({ |
||||
|
fnInit: t, |
||||
|
cFeature: "B" |
||||
|
}), e.ext.features && e.ext.features.register("buttons", t), e |
||||
|
}); |
||||
6995
src/main/resources/static/assets/font-awesome-6.5.1/css/all.min.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
7023
src/main/resources/static/assets/font-awesome-6.5.2/css/all.min.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
|
After Width: 325 | Height: 435 | Size: 204 KiB |
3244
src/main/resources/static/assets/jquery-3.6.4/js/jquery-3.6.4.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,93 @@ |
|||||
|
// Global flags
|
||||
|
let otpSent = false; |
||||
|
let isAdmin = false; |
||||
|
|
||||
|
async function loadRole() { |
||||
|
try { |
||||
|
const res = await fetch(`${contextPath}api/get-role`, { |
||||
|
method: "POST" |
||||
|
}); |
||||
|
|
||||
|
const data = await res.json(); |
||||
|
|
||||
|
if (!data.authenticated) { |
||||
|
isAdmin = false; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isAdmin = data.isAdmin; |
||||
|
|
||||
|
} catch (e) { |
||||
|
// swallow network errors
|
||||
|
console.error("Error: ", error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Open the login modal
|
||||
|
function openLoginModal() { |
||||
|
if (isAdmin) { |
||||
|
// For admin → directly go to the post-verification page
|
||||
|
window.open(`${contextPath}view-login-det`, '_blank'); |
||||
|
return; |
||||
|
} |
||||
|
const modal = document.getElementById("login-modal"); |
||||
|
modal.classList.remove("hide"); |
||||
|
modal.classList.add("show"); |
||||
|
modal.style.display = "flex"; // make it visible immediately before animation
|
||||
|
} |
||||
|
|
||||
|
// Load role when page loads
|
||||
|
document.addEventListener("DOMContentLoaded", () => { |
||||
|
loadRole(); |
||||
|
}); |
||||
|
|
||||
|
document.addEventListener("DOMContentLoaded", function() { |
||||
|
const togglePass = document.getElementById("togglePass"); |
||||
|
const urlUsrPassInput = document.getElementById("urlDataUsrPass"); |
||||
|
const form = document.getElementById("frm-url-det"); |
||||
|
|
||||
|
// safety check
|
||||
|
if (!togglePass || !urlUsrPassInput) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// if (!urlUsrPassInput) return;
|
||||
|
|
||||
|
// --- Toggle Password Visibility ---
|
||||
|
togglePass.addEventListener("click", function() { |
||||
|
const isPassword = urlUsrPassInput.type === "password"; |
||||
|
urlUsrPassInput.type = isPassword ? "text" : "password"; |
||||
|
togglePass.classList.toggle("fa-eye"); |
||||
|
togglePass.classList.toggle("fa-eye-slash"); |
||||
|
}); |
||||
|
|
||||
|
// --- Form submission logic ---
|
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
const formData = new FormData(form); |
||||
|
|
||||
|
fetch(form.getAttribute("action"), { |
||||
|
method: "POST", |
||||
|
body: formData |
||||
|
}) |
||||
|
.then(response => { |
||||
|
if (!response.ok) { |
||||
|
throw new Error("Network response was not ok: " + response.statusText); |
||||
|
} |
||||
|
return response.json(); |
||||
|
}) |
||||
|
.then(data => { |
||||
|
if (data.resStat) { |
||||
|
alert("Success: " + data.res); |
||||
|
form.reset(); |
||||
|
} else { |
||||
|
alert("Failed: " + data.res); |
||||
|
} |
||||
|
}) |
||||
|
.catch(error => { |
||||
|
console.error("Error adding url:", error); |
||||
|
alert("Error adding url. Please try again."); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,20 @@ |
|||||
|
const csrfToken = document.querySelector('meta[name="_csrf"]').content; |
||||
|
const csrfHeader = document.querySelector('meta[name="_csrf_header"]').content; |
||||
|
|
||||
|
// Save original fetch
|
||||
|
const originalFetch = window.fetch; |
||||
|
|
||||
|
window.fetch = (url, options = {}) => { |
||||
|
// Default method is GET if not provided
|
||||
|
const method = (options.method || "GET").toUpperCase(); |
||||
|
|
||||
|
// Only attach CSRF for unsafe methods
|
||||
|
if (["POST", "PUT", "DELETE", "PATCH"].includes(method)) { |
||||
|
options.headers = { |
||||
|
...options.headers, |
||||
|
[csrfHeader]: csrfToken |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
return originalFetch(url, options); |
||||
|
}; |
||||
@ -0,0 +1,3 @@ |
|||||
|
function goBack() { |
||||
|
window.history.back(); |
||||
|
} |
||||
@ -0,0 +1,420 @@ |
|||||
|
// Global flags
|
||||
|
let otpSent = false; |
||||
|
let isAdmin = false; |
||||
|
|
||||
|
async function clearBackendOtpSession() { |
||||
|
try { |
||||
|
await fetch(`${contextPath}api/clr-otp-ses`, { method: "POST" }); |
||||
|
} catch (e) { |
||||
|
console.error("Failed to clear backend OTP session", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function reloadMobileDropdown() { |
||||
|
try { |
||||
|
const res = await fetch(`${contextPath}api/get-mob-num`, { |
||||
|
method: "POST", |
||||
|
headers: { |
||||
|
"Content-Type": "application/json" |
||||
|
}, |
||||
|
body: "{}" // empty JSON payload
|
||||
|
}); |
||||
|
|
||||
|
const mobNumInput = document.getElementById("mobNum"); |
||||
|
mobNumInput.innerHTML = ""; // clear all old options
|
||||
|
|
||||
|
if (!res.ok) { |
||||
|
mobNumInput.innerHTML = '<option value="">-- Failed to load numbers --</option>'; |
||||
|
console.error(`Failed to fetch mobile numbers: ${res.status} ${res.statusText}`); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (res.status === 204) { |
||||
|
// No data returned
|
||||
|
mobNumInput.innerHTML = '<option value="">-- No mobile numbers available --</option>'; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const data = await res.json(); |
||||
|
data.forEach(item => { |
||||
|
const opt = document.createElement("option"); |
||||
|
opt.value = item.mobNumId; // new UUID
|
||||
|
opt.textContent = item.mobNum; // masked number
|
||||
|
mobNumInput.appendChild(opt); |
||||
|
}); |
||||
|
|
||||
|
} catch (err) { |
||||
|
console.error("Error loading mobile numbers:", err); |
||||
|
const mobNumInput = document.getElementById("mobNum"); |
||||
|
mobNumInput.innerHTML = '<option value="">-- Failed to load numbers --</option>'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function loadRole() { |
||||
|
try { |
||||
|
const res = await fetch(`${contextPath}api/get-role`, { |
||||
|
method: "POST" |
||||
|
}); |
||||
|
|
||||
|
const data = await res.json(); |
||||
|
|
||||
|
if (!data.authenticated) { |
||||
|
isAdmin = false; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isAdmin = data.isAdmin; |
||||
|
|
||||
|
} catch (e) { |
||||
|
// swallow network errors
|
||||
|
console.error("Error: ", error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function loadMobNum() { |
||||
|
try { |
||||
|
const res = await fetch(`${contextPath}api/get-mob-num`, { |
||||
|
method: "POST", |
||||
|
headers: { |
||||
|
"Content-Type": "application/json" |
||||
|
}, |
||||
|
body: "{}" |
||||
|
}); |
||||
|
|
||||
|
const mobSelect = document.getElementById("mobNum"); |
||||
|
mobSelect.innerHTML = '<option value="">-- Select Mobile Number --</option>'; |
||||
|
|
||||
|
if (res.status === 204) { |
||||
|
// No data returned
|
||||
|
mobSelect.innerHTML = '<option value="">-- No mobile numbers available --</option>'; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!res.ok) { |
||||
|
throw new Error(`Server error: ${res.status}`); |
||||
|
} |
||||
|
|
||||
|
const data = await res.json(); |
||||
|
data.forEach(item => { |
||||
|
const opt = document.createElement("option"); |
||||
|
opt.value = item.mobNumId; // mobNumId for backend
|
||||
|
opt.textContent = item.mobNum; // Masked number for display
|
||||
|
mobSelect.appendChild(opt); |
||||
|
}); |
||||
|
|
||||
|
} catch (err) { |
||||
|
console.error("Failed to load mobile numbers", err); |
||||
|
const mobSelect = document.getElementById("mobNum"); |
||||
|
mobSelect.innerHTML = '<option value="">-- Failed to load numbers --</option>'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Open the login modal
|
||||
|
function openLoginModal() { |
||||
|
if (isAdmin) { |
||||
|
// For admin → directly go to the post-verification page
|
||||
|
window.open(`${contextPath}view-login-det`, '_blank'); |
||||
|
return; |
||||
|
} |
||||
|
const modal = document.getElementById("login-modal"); |
||||
|
modal.classList.remove("hide"); |
||||
|
modal.classList.add("show"); |
||||
|
modal.style.display = "flex"; // make it visible immediately before animation
|
||||
|
|
||||
|
// Load only when modal is opened
|
||||
|
loadMobNum(); |
||||
|
} |
||||
|
|
||||
|
// Close the modal and reset its state
|
||||
|
async function closeLoginModal() { |
||||
|
const modal = document.getElementById("login-modal"); |
||||
|
const otpForm = document.getElementById("otp-form"); |
||||
|
const otpSection = document.getElementById("otp-section"); |
||||
|
const sendOtpBtn = document.getElementById("send-otp-btn"); |
||||
|
const messageEl = document.getElementById("otp-message"); |
||||
|
const mobInput = document.getElementById("mobNum"); |
||||
|
const otpInput = document.getElementById("otp"); |
||||
|
|
||||
|
// Reset form and state
|
||||
|
if (otpForm) otpForm.reset(); // Reset the form fields
|
||||
|
if (otpSection) otpSection.style.display = "none"; // Hide OTP section
|
||||
|
if (sendOtpBtn) { |
||||
|
sendOtpBtn.textContent = "Get OTP"; // Reset button text
|
||||
|
sendOtpBtn.disabled = false; // Enable the button
|
||||
|
} |
||||
|
if (messageEl) { |
||||
|
messageEl.textContent = ""; // Clear any message
|
||||
|
messageEl.style.color = "#333"; // Reset the default message color
|
||||
|
} |
||||
|
|
||||
|
// Reset mobile number input and OTP input
|
||||
|
if (mobInput) mobInput.value = ''; |
||||
|
if (otpInput) otpInput.value = ''; |
||||
|
|
||||
|
// Reset JS flag
|
||||
|
otpSent = false; |
||||
|
|
||||
|
await clearBackendOtpSession(); |
||||
|
|
||||
|
// Animate and hide the modal
|
||||
|
modal.classList.remove("show"); |
||||
|
modal.classList.add("hide"); |
||||
|
|
||||
|
// Handle the animation and hide modal after animation ends
|
||||
|
modal.addEventListener( |
||||
|
"animationend", |
||||
|
function handler() { |
||||
|
if (modal.classList.contains("hide")) { |
||||
|
modal.style.display = "none"; // Hide modal after animation
|
||||
|
} |
||||
|
modal.removeEventListener("animationend", handler); // Clean up the event listener
|
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// Close the modal when clicking outside of it
|
||||
|
window.onclick = function(event) { |
||||
|
const modal = document.getElementById("login-modal"); |
||||
|
if (event.target === modal) { |
||||
|
closeLoginModal(); // Also reset everything when clicking outside
|
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Load role when page loads
|
||||
|
document.addEventListener("DOMContentLoaded", () => { |
||||
|
loadRole(); |
||||
|
}); |
||||
|
|
||||
|
// OTP Form Submission Handling
|
||||
|
document.addEventListener("DOMContentLoaded", () => { |
||||
|
const otpForm = document.getElementById("otp-form"); |
||||
|
if (!otpForm) return; |
||||
|
|
||||
|
const messageEl = document.getElementById("otp-message"); |
||||
|
const otpSection = document.getElementById("otp-section"); |
||||
|
const sendOtpBtn = document.getElementById("send-otp-btn"); |
||||
|
const mobNumInput = document.getElementById("mobNum"); |
||||
|
const otpInput = document.getElementById("otp"); |
||||
|
|
||||
|
otpForm.addEventListener("submit", async (e) => { |
||||
|
e.preventDefault(); |
||||
|
messageEl.textContent = ""; |
||||
|
messageEl.style.color = "inherit"; |
||||
|
|
||||
|
const mobId = mobNumInput.value; // mobNumId
|
||||
|
const selectedText = mobNumInput.options[mobNumInput.selectedIndex]?.text; |
||||
|
|
||||
|
if (!mobId) { |
||||
|
messageEl.textContent = "Please select a mobile number."; |
||||
|
messageEl.style.color = "red"; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
sendOtpBtn.disabled = true; |
||||
|
|
||||
|
try { |
||||
|
if (!otpSent) { |
||||
|
// === GET OTP PHASE ===
|
||||
|
sendOtpBtn.textContent = "Sending..."; |
||||
|
|
||||
|
const formData = new FormData(); |
||||
|
formData.append("mobNumId", mobId); |
||||
|
|
||||
|
const res = await fetch(`${contextPath}api/get-otp`, { |
||||
|
method: "POST", |
||||
|
body: formData, // sends as multipart/form-data
|
||||
|
}); |
||||
|
|
||||
|
const data = await res.json(); |
||||
|
|
||||
|
if (res.ok && data.status === "success") { |
||||
|
messageEl.textContent = data.message || "OTP sent successfully!"; |
||||
|
messageEl.style.color = "green"; |
||||
|
otpSection.style.display = "block"; |
||||
|
sendOtpBtn.textContent = "Verify OTP"; |
||||
|
otpSent = true; |
||||
|
} else { |
||||
|
// backend may respond with 400 or 500
|
||||
|
messageEl.textContent = data.message || "Failed to send OTP."; |
||||
|
messageEl.style.color = "red"; |
||||
|
sendOtpBtn.textContent = "Get OTP"; |
||||
|
} |
||||
|
|
||||
|
} else { |
||||
|
// === VERIFY OTP PHASE ===
|
||||
|
const enteredOtp = otpInput.value.trim(); |
||||
|
if (!enteredOtp) { |
||||
|
messageEl.textContent = "Please enter the OTP."; |
||||
|
messageEl.style.color = "red"; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
sendOtpBtn.textContent = "Verifying..."; |
||||
|
|
||||
|
const formData = new FormData(); |
||||
|
formData.append("mobNumId", mobId); |
||||
|
formData.append("otp", enteredOtp); |
||||
|
|
||||
|
const res = await fetch(`${contextPath}api/verify-otp`, { |
||||
|
method: "POST", |
||||
|
body: formData, // sends as multipart/form-data
|
||||
|
}); |
||||
|
|
||||
|
const data = await res.json(); |
||||
|
|
||||
|
if (res.ok && data.resStat) { |
||||
|
// OTP verified successfully
|
||||
|
messageEl.textContent = data.res || "OTP verified successfully!"; |
||||
|
messageEl.style.color = "green"; |
||||
|
otpSection.style.display = "none"; |
||||
|
sendOtpBtn.disabled = true; |
||||
|
|
||||
|
await reloadMobileDropdown(); |
||||
|
|
||||
|
// Close modal, then redirect to view-login-det
|
||||
|
setTimeout(() => { |
||||
|
closeLoginModal(); |
||||
|
window.open(`${contextPath}view-login-det`, '_blank'); |
||||
|
}, 500); |
||||
|
|
||||
|
} else { |
||||
|
// OTP verification failed or session expired
|
||||
|
messageEl.textContent = data.res || "OTP verification failed. Please request a new one."; |
||||
|
messageEl.style.color = "red"; |
||||
|
sendOtpBtn.textContent = "Get OTP"; |
||||
|
otpSection.style.display = "none"; // hide OTP field
|
||||
|
otpSent = false; // reset flag → next click sends a new OTP
|
||||
|
otpInput.value = ""; // clear OTP field only
|
||||
|
|
||||
|
await reloadMobileDropdown(); |
||||
|
|
||||
|
// If session expired, allow new OTP request
|
||||
|
if (data.res && data.res.includes("Session expired")) { |
||||
|
mobNumInput.disabled = false; // ensure user can still use the same number
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("Error:", err); |
||||
|
messageEl.textContent = "Something went wrong. Please try again."; |
||||
|
messageEl.style.color = "red"; |
||||
|
sendOtpBtn.textContent = otpSent ? "Verify OTP" : "Get OTP"; |
||||
|
} finally { |
||||
|
sendOtpBtn.disabled = false; |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
/* === PAGE BUTTON FILTER & SEARCH === */ |
||||
|
document.addEventListener("DOMContentLoaded", function() { |
||||
|
const buttonContainer = document.querySelector(".button-row"); |
||||
|
if (!buttonContainer) return; |
||||
|
|
||||
|
const buttons = Array.from(buttonContainer.querySelectorAll(".custom-button")); |
||||
|
|
||||
|
function getButtonName(btn) { |
||||
|
return btn.childNodes[btn.childNodes.length - 1].textContent.trim().toLowerCase(); |
||||
|
} |
||||
|
|
||||
|
const searchInput = document.getElementById("search-input"); |
||||
|
const alphabetSelect = document.getElementById("search-input2"); |
||||
|
const clearBtn = document.getElementById("clear-btn"); |
||||
|
const noResultsMsg = document.getElementById("no-res-msg"); |
||||
|
|
||||
|
/* === Reusable Dropdown Renderer === */ |
||||
|
function populateAlphabetDropdown(onlyVisible = false) { |
||||
|
const prevSelected = alphabetSelect.value; // keep previous selection
|
||||
|
|
||||
|
// Reset dropdown
|
||||
|
alphabetSelect.innerHTML = '<option value="">-</option>'; |
||||
|
|
||||
|
const letters = new Set(); |
||||
|
|
||||
|
buttons.forEach(btn => { |
||||
|
if (onlyVisible && btn.style.display === "none") return; |
||||
|
|
||||
|
const name = getButtonName(btn); |
||||
|
if (name.length > 0) { |
||||
|
letters.add(name[0].toUpperCase()); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const sortedLetters = [...letters].sort(); |
||||
|
|
||||
|
sortedLetters.forEach(letter => { |
||||
|
const opt = document.createElement("option"); |
||||
|
opt.value = letter; |
||||
|
opt.textContent = letter; |
||||
|
alphabetSelect.appendChild(opt); |
||||
|
}); |
||||
|
|
||||
|
// Restore previous selection if still valid
|
||||
|
if (letters.has(prevSelected.toUpperCase())) { |
||||
|
alphabetSelect.value = prevSelected.toUpperCase(); |
||||
|
} else { |
||||
|
alphabetSelect.value = ""; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Initial dropdown load (all buttons)
|
||||
|
populateAlphabetDropdown(false); |
||||
|
|
||||
|
/* === FILTER BUTTONS === */ |
||||
|
function updateButtonVisibility() { |
||||
|
const query = searchInput.value.trim().toLowerCase(); |
||||
|
const selectedLetter = alphabetSelect.value.toLowerCase(); |
||||
|
|
||||
|
let matchedButtons = 0; |
||||
|
|
||||
|
buttons.forEach(btn => { |
||||
|
const name = getButtonName(btn); |
||||
|
|
||||
|
const matchesLetter = |
||||
|
selectedLetter === "" || name.startsWith(selectedLetter); |
||||
|
|
||||
|
const matchesSearch = |
||||
|
query === "" || name.includes(query); |
||||
|
|
||||
|
if (matchesLetter && matchesSearch) { |
||||
|
btn.style.display = "inline-block"; |
||||
|
matchedButtons++; |
||||
|
} else { |
||||
|
btn.style.display = "none"; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
noResultsMsg.style.display = matchedButtons > 0 ? "none" : "block"; |
||||
|
|
||||
|
// Show or hide clear button
|
||||
|
clearBtn.style.display = |
||||
|
(searchInput.value.trim() !== "" || alphabetSelect.value !== "") |
||||
|
? "inline-block" |
||||
|
: "none"; |
||||
|
|
||||
|
// Rebuild dropdown based on visible buttons
|
||||
|
populateAlphabetDropdown(true); |
||||
|
} |
||||
|
|
||||
|
/* === CLEAR BUTTON HANDLER === */ |
||||
|
clearBtn.addEventListener("click", function() { |
||||
|
searchInput.value = ""; |
||||
|
alphabetSelect.value = ""; |
||||
|
|
||||
|
clearBtn.style.display = "none"; |
||||
|
|
||||
|
// Reset all buttons visible
|
||||
|
buttons.forEach(btn => btn.style.display = "inline-block"); |
||||
|
|
||||
|
// Rebuild dropdown from all buttons
|
||||
|
populateAlphabetDropdown(false); |
||||
|
|
||||
|
noResultsMsg.style.display = "none"; |
||||
|
}); |
||||
|
|
||||
|
/* === EVENT LISTENERS === */ |
||||
|
alphabetSelect.addEventListener("change", updateButtonVisibility); |
||||
|
searchInput.addEventListener("keyup", updateButtonVisibility); |
||||
|
|
||||
|
// Initial render
|
||||
|
updateButtonVisibility(); |
||||
|
}); |
||||
@ -0,0 +1,28 @@ |
|||||
|
function handleLoginSubmit2(event) { |
||||
|
event.preventDefault(); // prevent normal form submission
|
||||
|
|
||||
|
const form = document.getElementById("frm-usr-login-det"); |
||||
|
const formData = new FormData(form); |
||||
|
|
||||
|
fetch(contextPath + 'api/val-app-usr-login', { |
||||
|
method: 'POST', |
||||
|
body: formData |
||||
|
}) |
||||
|
.then(response => response.json()) |
||||
|
.then(data => { |
||||
|
if (data.result) { |
||||
|
let usrName = data.usrName && data.usrName.length > 0 ? data.usrName : null; |
||||
|
if (usrName) { |
||||
|
alert("Login Success"); |
||||
|
window.location.href = contextPath + 'redirect'; |
||||
|
} else { |
||||
|
alert("No role found for this user!"); |
||||
|
} |
||||
|
} else { |
||||
|
alert(data.error || "Login failed"); |
||||
|
} |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
console.error("Error during login:", err); |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
// Global flags
|
||||
|
let otpSent = false; |
||||
|
let isAdmin = false; |
||||
|
|
||||
|
async function loadRole() { |
||||
|
try { |
||||
|
const res = await fetch(`${contextPath}api/get-role`, { |
||||
|
method: "POST" |
||||
|
}); |
||||
|
|
||||
|
const data = await res.json(); |
||||
|
|
||||
|
if (!data.authenticated) { |
||||
|
isAdmin = false; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isAdmin = data.isAdmin; |
||||
|
|
||||
|
} catch (e) { |
||||
|
// swallow network errors
|
||||
|
console.error("Error: ", error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Open the login modal
|
||||
|
function openLoginModal() { |
||||
|
if (isAdmin) { |
||||
|
// For admin → directly go to the post-verification page
|
||||
|
window.open(`${contextPath}view-login-det`, '_blank'); |
||||
|
return; |
||||
|
} |
||||
|
const modal = document.getElementById("login-modal"); |
||||
|
modal.classList.remove("hide"); |
||||
|
modal.classList.add("show"); |
||||
|
modal.style.display = "flex"; // make it visible immediately before animation
|
||||
|
} |
||||
|
|
||||
|
// Load role when page loads
|
||||
|
document.addEventListener("DOMContentLoaded", () => { |
||||
|
loadRole(); |
||||
|
}); |
||||
|
|
||||
|
document.addEventListener("DOMContentLoaded", function() { |
||||
|
const dropdown = document.getElementById("urlDropdown"); |
||||
|
const urlStatDropdown = document.getElementById("urlStatDropdown"); |
||||
|
const urlLinkInput = document.getElementById("urlDataLink"); |
||||
|
const urlNameInput = document.getElementById("urlDataName"); |
||||
|
const urlUsrNameInput = document.getElementById("urlDataUsrName"); |
||||
|
const urlUsrPassInput = document.getElementById("urlDataUsrPass"); |
||||
|
const urlCatNameInput = document.getElementById("urlCatName"); |
||||
|
const togglePass = document.getElementById("togglePass"); |
||||
|
|
||||
|
// safety check
|
||||
|
if (!dropdown || !togglePass) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// if (!dropdown) return;
|
||||
|
|
||||
|
// --- Dropdown change handler ---
|
||||
|
dropdown.addEventListener("change", function() { |
||||
|
const selected = dropdown.options[dropdown.selectedIndex]; |
||||
|
|
||||
|
if (!selected.value) { |
||||
|
urlLinkInput.value = ""; |
||||
|
urlNameInput.value = ""; |
||||
|
urlUsrNameInput.value = ""; |
||||
|
urlUsrPassInput.value = ""; |
||||
|
urlCatNameInput.value = ""; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const statId = selected.getAttribute("data-statId"); |
||||
|
if (statId) { |
||||
|
urlStatDropdown.value = statId; |
||||
|
} |
||||
|
|
||||
|
const link = selected.getAttribute("data-link"); |
||||
|
const name = selected.getAttribute("data-name"); |
||||
|
const usrname = selected.getAttribute("data-usrname"); |
||||
|
const pass = selected.getAttribute("data-pass"); |
||||
|
const urlCatName = selected.getAttribute("data-urlCatName"); |
||||
|
|
||||
|
urlLinkInput.value = link || ""; |
||||
|
urlNameInput.value = name || ""; |
||||
|
urlUsrNameInput.value = (usrname && usrname !== "-") ? usrname : ""; |
||||
|
urlUsrPassInput.value = (pass && pass !== "-") ? pass : ""; |
||||
|
urlCatNameInput.value = (urlCatName && urlCatName !== "-") ? urlCatName : ""; |
||||
|
|
||||
|
// Reset to hidden state
|
||||
|
urlUsrPassInput.type = "password"; |
||||
|
togglePass.classList.add("fa-eye"); |
||||
|
togglePass.classList.remove("fa-eye-slash"); |
||||
|
}); |
||||
|
|
||||
|
// --- Toggle Password Visibility ---
|
||||
|
togglePass.addEventListener("click", function() { |
||||
|
const isPassword = urlUsrPassInput.type === "password"; |
||||
|
urlUsrPassInput.type = isPassword ? "text" : "password"; |
||||
|
togglePass.classList.toggle("fa-eye"); |
||||
|
togglePass.classList.toggle("fa-eye-slash"); |
||||
|
}); |
||||
|
|
||||
|
const form = document.getElementById("frm-url-det"); |
||||
|
|
||||
|
// Existing form submission logic...
|
||||
|
form.addEventListener("submit", function(e) { |
||||
|
e.preventDefault(); // Prevent default form submission
|
||||
|
|
||||
|
const formData = new FormData(form); |
||||
|
|
||||
|
fetch(form.getAttribute("action"), { |
||||
|
method: "POST", |
||||
|
body: formData |
||||
|
}) |
||||
|
.then(response => { |
||||
|
if (!response.ok) { |
||||
|
throw new Error("Network response was not ok: " + response.statusText); |
||||
|
} |
||||
|
return response.json(); |
||||
|
}) |
||||
|
.then(data => { |
||||
|
if (data.resStat) { |
||||
|
alert("Success: " + data.res); |
||||
|
form.reset(); |
||||
|
} else { |
||||
|
alert("Failed: " + data.res); |
||||
|
} |
||||
|
}) |
||||
|
.catch(error => { |
||||
|
console.error("Error updating url:", error); |
||||
|
alert("Error updating url. Please try again."); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,56 @@ |
|||||
|
document.addEventListener("DOMContentLoaded", function() { |
||||
|
// Auto-close window after 2 minutes (120000 ms)
|
||||
|
setTimeout(() => { |
||||
|
window.close(); |
||||
|
}, 120000); |
||||
|
|
||||
|
// Initialize DataTables
|
||||
|
const table = $("#url-det-tbl").DataTable({ |
||||
|
pageLength: -1, // default = show ALL rows
|
||||
|
lengthMenu: [ |
||||
|
[5, 10, 20, 50, -1], // values
|
||||
|
[5, 10, 20, 50, "All"] // labels
|
||||
|
], |
||||
|
ordering: false, // Disable client-side sorting
|
||||
|
language: { |
||||
|
info: "Showing _START_ to _END_ of _TOTAL_ entries", |
||||
|
lengthMenu: "Show _MENU_ entries" |
||||
|
}, |
||||
|
dom: 'lfrtip', // hides default search bar
|
||||
|
|
||||
|
// Auto-insert S.No
|
||||
|
drawCallback: function(settings) { |
||||
|
let api = this.api(); |
||||
|
api.rows({ page: 'current' }).every(function(rowIdx, tableLoop, rowLoop) { |
||||
|
let index = rowIdx + 1; |
||||
|
api.cell(rowIdx, 0).data(index); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Move DataTables elements to your custom divs
|
||||
|
$('#custom-search').html($('.dataTables_filter')); |
||||
|
$('#custom-pagination').html($('.dataTables_paginate')); |
||||
|
$('#custom-info').html($('.dataTables_info')); |
||||
|
$('#custom-length').html($('.dataTables_length')); |
||||
|
}); |
||||
|
|
||||
|
const toggleIcons = document.querySelectorAll(".toggle-pass"); |
||||
|
|
||||
|
toggleIcons.forEach(icon => { |
||||
|
icon.addEventListener("click", function() { |
||||
|
const targetId = this.getAttribute("data-target"); |
||||
|
const passwordElement = document.getElementById(targetId); |
||||
|
const originalPassword = this.getAttribute("data-pass"); |
||||
|
|
||||
|
if (passwordElement.textContent.includes("*")) { |
||||
|
// Show actual password
|
||||
|
passwordElement.textContent = originalPassword; |
||||
|
this.classList.replace("fa-eye", "fa-eye-slash"); |
||||
|
} else { |
||||
|
// Hide password
|
||||
|
passwordElement.textContent = "*".repeat(originalPassword.length); |
||||
|
this.classList.replace("fa-eye-slash", "fa-eye"); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
1123
src/main/resources/static/assets/popper-2.11.8/js/popper.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue