feat: complete faculty directory changes
This commit is contained in:
3
backend/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
3
backend/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -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.12/apache-maven-3.9.12-bin.zip
|
||||||
295
backend/mvnw
vendored
Normal file
295
backend/mvnw
vendored
Normal file
@@ -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 "$@"
|
||||||
189
backend/mvnw.cmd
vendored
Normal file
189
backend/mvnw.cmd
vendored
Normal file
@@ -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"
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
# ─── DATABASE CONNECTION CONFIGURATION ───
|
# ─── DATABASE CONNECTION CONFIGURATION ───
|
||||||
spring.datasource.url=jdbc:postgresql://localhost:5432/rit_freshers_hub?sslmode=disable
|
spring.datasource.url=jdbc:postgresql://localhost:5432/rit_freshers_hub?sslmode=disable
|
||||||
spring.datasource.username=postgres
|
spring.datasource.username=postgres
|
||||||
spring.datasource.password=Amudiesh22@.
|
|
||||||
|
spring.datasource.password=murugan06
|
||||||
|
|
||||||
|
|
||||||
# ─── JPA / HIBERNATE SETTINGS ───
|
# ─── JPA / HIBERNATE SETTINGS ───
|
||||||
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
|
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
|
||||||
|
|||||||
78
package-lock.json
generated
78
package-lock.json
generated
@@ -340,9 +340,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -360,9 +357,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -380,9 +374,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -400,9 +391,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -420,9 +408,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -440,9 +425,6 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -460,9 +442,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -480,9 +459,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1605,9 +1581,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1625,9 +1598,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1645,9 +1615,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1665,9 +1632,6 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1685,9 +1649,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1705,9 +1666,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1959,9 +1917,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1983,9 +1938,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2007,9 +1959,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2031,9 +1980,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2206,9 +2152,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2226,9 +2169,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2246,9 +2186,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -2266,9 +2203,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3211,9 +3145,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3235,9 +3166,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3259,9 +3187,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3283,9 +3208,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { motion } from 'framer-motion';
|
import { useState } from 'react';
|
||||||
import { Mail, Phone, Building2, User } from 'lucide-react';
|
import { createPortal } from 'react-dom';
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { Mail, Phone, Building2, User, X, BookOpen, ChevronRight, Monitor, Command, GraduationCap, Briefcase, Microscope, Clock } from 'lucide-react';
|
||||||
import type { Faculty } from '@/types';
|
import type { Faculty } from '@/types';
|
||||||
|
|
||||||
interface FacultyCardProps {
|
interface FacultyCardProps {
|
||||||
@@ -16,6 +18,9 @@ const AVATAR_COLORS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function FacultyCard({ faculty }: FacultyCardProps) {
|
export default function FacultyCard({ faculty }: FacultyCardProps) {
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [showEmailOptions, setShowEmailOptions] = useState(false);
|
||||||
|
|
||||||
const colorIndex = parseInt(faculty.id) % AVATAR_COLORS.length;
|
const colorIndex = parseInt(faculty.id) % AVATAR_COLORS.length;
|
||||||
const [from, to] = AVATAR_COLORS[colorIndex];
|
const [from, to] = AVATAR_COLORS[colorIndex];
|
||||||
const initials = faculty.name
|
const initials = faculty.name
|
||||||
@@ -24,73 +29,359 @@ export default function FacultyCard({ faculty }: FacultyCardProps) {
|
|||||||
.map((n) => n[0])
|
.map((n) => n[0])
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
|
const mailOptions = [
|
||||||
|
{ name: 'Default Mail App', url: `mailto:${faculty.email}`, icon: Monitor, color: 'text-gray-700', bg: 'bg-gray-100' },
|
||||||
|
{ name: 'Gmail', url: `https://mail.google.com/mail/?view=cm&fs=1&to=${faculty.email}`, icon: Mail, color: 'text-red-500', bg: 'bg-red-50' },
|
||||||
|
{ name: 'Outlook', url: `https://outlook.office.com/mail/deeplink/compose?to=${faculty.email}`, icon: Mail, color: 'text-blue-600', bg: 'bg-blue-50' },
|
||||||
|
{ name: 'Yahoo Mail', url: `https://compose.mail.yahoo.com/?to=${faculty.email}`, icon: Mail, color: 'text-purple-600', bg: 'bg-purple-50' },
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<>
|
||||||
whileHover={{ y: -5, boxShadow: '0 16px 40px -8px rgba(0,0,0,0.12)' }}
|
<motion.div
|
||||||
transition={{ duration: 0.25 }}
|
whileHover={{ y: -6, boxShadow: '0 20px 40px -10px rgba(0,0,0,0.1)' }}
|
||||||
className="bg-white border border-[#E5E7EB] rounded-2xl p-5 flex flex-col gap-4"
|
transition={{ duration: 0.3 }}
|
||||||
style={{ boxShadow: '0 2px 15px -3px rgba(0,0,0,0.07)' }}
|
className="bg-white border border-[#E8ECF4] rounded-[20px] p-5 flex flex-col gap-4 relative"
|
||||||
>
|
style={{ boxShadow: '0 2px 15px -3px rgba(0,0,0,0.07)' }}
|
||||||
{/* Avatar + Name */}
|
>
|
||||||
<div className="flex items-center gap-4">
|
{/* Avatar + Name */}
|
||||||
<div
|
<div className="flex items-center gap-4">
|
||||||
className="w-14 h-14 rounded-2xl flex items-center justify-center text-white font-bold text-lg shrink-0"
|
<div className="relative">
|
||||||
style={{ background: `linear-gradient(135deg, ${from}, ${to})`, fontFamily: 'Poppins, sans-serif' }}
|
<div
|
||||||
>
|
className="w-14 h-14 rounded-2xl flex items-center justify-center text-white font-bold text-lg shrink-0 shadow-sm"
|
||||||
{initials}
|
style={{ background: `linear-gradient(135deg, ${from}, ${to})`, fontFamily: 'Poppins, sans-serif' }}
|
||||||
|
>
|
||||||
|
{initials}
|
||||||
|
</div>
|
||||||
|
{/* Online Indicator */}
|
||||||
|
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-[#22C55E] border-2 border-white rounded-full z-10 shadow-sm" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-[#1E293B] text-sm leading-tight" style={{ fontFamily: 'Poppins, sans-serif' }}>
|
||||||
|
{faculty.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-[#FF7A00] font-medium mt-0.5" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
|
{faculty.designation}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<h3 className="font-semibold text-[#1E293B] text-sm leading-tight" style={{ fontFamily: 'Poppins, sans-serif' }}>
|
|
||||||
{faculty.name}
|
|
||||||
</h3>
|
|
||||||
<p className="text-xs text-[#F97316] font-medium mt-0.5" style={{ fontFamily: 'Inter, sans-serif' }}>
|
|
||||||
{faculty.designation}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Details */}
|
{/* Details */}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center gap-2 text-xs text-[#475569]">
|
|
||||||
<Building2 className="w-3.5 h-3.5 text-[#94A3B8] shrink-0" />
|
|
||||||
<span style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.department}</span>
|
|
||||||
</div>
|
|
||||||
{faculty.office && (
|
|
||||||
<div className="flex items-center gap-2 text-xs text-[#475569]">
|
<div className="flex items-center gap-2 text-xs text-[#475569]">
|
||||||
<User className="w-3.5 h-3.5 text-[#94A3B8] shrink-0" />
|
<Building2 className="w-3.5 h-3.5 text-[#94A3B8] shrink-0" />
|
||||||
<span style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.office}</span>
|
<span style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.department}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
{faculty.specialization && (
|
|
||||||
<div className="text-xs text-[#94A3B8] pl-5" style={{ fontFamily: 'Inter, sans-serif' }}>
|
|
||||||
{faculty.specialization}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Contact */}
|
{/* Contact & Actions */}
|
||||||
<div className="flex items-center gap-2 pt-2 border-t border-[#E5E7EB]">
|
<div className="flex items-center gap-2 pt-2 border-t border-[#E5E7EB] mt-auto">
|
||||||
<a
|
<button
|
||||||
href={`mailto:${faculty.email}`}
|
onClick={() => setShowEmailOptions(true)}
|
||||||
className="flex-1 flex items-center justify-center gap-1.5 py-2 rounded-xl text-xs font-medium transition-all hover:bg-[#FFF7ED]"
|
className="flex-1 flex items-center justify-center gap-1.5 py-2 rounded-xl text-xs font-medium text-[#475569] transition-all hover:bg-gray-50 border border-transparent hover:border-gray-200"
|
||||||
style={{ color: '#F97316', fontFamily: 'Poppins, sans-serif' }}
|
|
||||||
aria-label={`Email ${faculty.name}`}
|
|
||||||
>
|
|
||||||
<Mail className="w-3.5 h-3.5" />
|
|
||||||
Email
|
|
||||||
</a>
|
|
||||||
{faculty.phone && (
|
|
||||||
<a
|
|
||||||
href={`tel:${faculty.phone}`}
|
|
||||||
className="flex-1 flex items-center justify-center gap-1.5 py-2 rounded-xl text-xs font-medium text-[#475569] transition-all hover:bg-gray-50"
|
|
||||||
style={{ fontFamily: 'Poppins, sans-serif' }}
|
style={{ fontFamily: 'Poppins, sans-serif' }}
|
||||||
aria-label={`Call ${faculty.name}`}
|
aria-label={`Email ${faculty.name}`}
|
||||||
>
|
>
|
||||||
<Phone className="w-3.5 h-3.5" />
|
<Mail className="w-3.5 h-3.5" />
|
||||||
Call
|
Email
|
||||||
</a>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(true)}
|
||||||
|
className="flex-1 flex items-center justify-center gap-1.5 py-2 rounded-xl text-xs font-medium transition-all hover:bg-[#FFF7ED]"
|
||||||
|
style={{ color: '#F97316', fontFamily: 'Poppins, sans-serif' }}
|
||||||
|
>
|
||||||
|
View Profile
|
||||||
|
<ChevronRight className="w-3.5 h-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Profile Modal */}
|
||||||
|
{createPortal(
|
||||||
|
<AnimatePresence>
|
||||||
|
{showModal && (
|
||||||
|
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 sm:p-6">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="absolute inset-0 bg-[#0F172A]/40 backdrop-blur-sm"
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
transition={{ type: "spring", duration: 0.4, bounce: 0.2 }}
|
||||||
|
className="relative w-full max-w-5xl bg-[#F8FAFC] shadow-2xl overflow-y-auto"
|
||||||
|
style={{ maxHeight: '90vh', borderRadius: '24px' }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowModal(false)}
|
||||||
|
className="absolute top-4 right-4 p-2 bg-white hover:bg-gray-100 rounded-full transition-colors z-50 shadow-sm border border-gray-200"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5 text-gray-500" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 w-full p-4 sm:p-8 relative">
|
||||||
|
{/* LEFT PANEL */}
|
||||||
|
<div className="lg:col-span-4 h-full relative">
|
||||||
|
<div
|
||||||
|
className="rounded-[20px] shadow-sm border p-8 flex flex-col items-center relative lg:sticky lg:top-0"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${from}10, ${to}10)`,
|
||||||
|
borderColor: `${from}20`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="w-32 h-32 rounded-full flex items-center justify-center text-white font-bold text-5xl mb-5 shadow-lg"
|
||||||
|
style={{ background: `linear-gradient(135deg, ${from}, ${to})`, fontFamily: 'Poppins, sans-serif' }}
|
||||||
|
>
|
||||||
|
{initials}
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold text-center text-[#1E293B] mb-1" style={{ fontFamily: 'Poppins, sans-serif' }}>
|
||||||
|
{faculty.name}
|
||||||
|
</h2>
|
||||||
|
<p className="text-base font-semibold text-[#FF7A00] text-center" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
|
{faculty.designation}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs font-semibold text-[#64748B] text-center mt-2 uppercase tracking-wider" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
|
{faculty.department}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr className="w-full my-6" style={{ borderColor: `${from}20` }} />
|
||||||
|
|
||||||
|
<div className="w-full flex flex-col gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowEmailOptions(true)}
|
||||||
|
className="flex items-center gap-4 p-3 rounded-xl hover:bg-white/80 transition-colors border group text-left w-full"
|
||||||
|
style={{ backgroundColor: 'rgba(255, 255, 255, 0.6)', borderColor: `${from}15` }}
|
||||||
|
>
|
||||||
|
<div className="w-10 h-10 rounded-full bg-[#FFF7ED] flex items-center justify-center group-hover:bg-[#F97316] transition-colors shrink-0">
|
||||||
|
<Mail className="w-4 h-4 text-[#F97316] group-hover:text-white transition-colors" />
|
||||||
|
</div>
|
||||||
|
<div className="overflow-hidden">
|
||||||
|
<p className="text-[11px] font-semibold text-[#94A3B8] uppercase tracking-wider mb-0.5" style={{ fontFamily: 'Inter, sans-serif' }}>Email Address</p>
|
||||||
|
<p className="text-sm font-semibold text-[#1E293B] truncate" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.email}</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{faculty.phone && (
|
||||||
|
<a
|
||||||
|
href={`tel:${faculty.phone}`}
|
||||||
|
className="flex items-center gap-4 p-3 rounded-xl hover:bg-white/80 transition-colors border group w-full"
|
||||||
|
style={{ backgroundColor: 'rgba(255, 255, 255, 0.6)', borderColor: `${from}15` }}
|
||||||
|
>
|
||||||
|
<div className="w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center group-hover:bg-blue-500 transition-colors shrink-0">
|
||||||
|
<Phone className="w-4 h-4 text-blue-500 group-hover:text-white transition-colors" />
|
||||||
|
</div>
|
||||||
|
<div className="overflow-hidden">
|
||||||
|
<p className="text-[11px] font-semibold text-[#94A3B8] uppercase tracking-wider mb-0.5" style={{ fontFamily: 'Inter, sans-serif' }}>Phone Number</p>
|
||||||
|
<p className="text-sm font-semibold text-[#1E293B] truncate" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.phone}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* RIGHT PANEL */}
|
||||||
|
<div className="lg:col-span-8 flex flex-col gap-6">
|
||||||
|
{/* Section 1: Professional Details */}
|
||||||
|
<div className="bg-white rounded-[20px] shadow-sm border border-[#E8ECF4] p-6 sm:p-8">
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-orange-50 flex items-center justify-center shrink-0">
|
||||||
|
<User className="w-5 h-5 text-[#FF7A00]" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>Professional Details</h3>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div className="p-4 rounded-xl bg-gray-50/80 border border-gray-100 transition-colors hover:bg-gray-50">
|
||||||
|
<p className="text-xs font-semibold text-[#94A3B8] uppercase tracking-wider mb-1" style={{ fontFamily: 'Inter, sans-serif' }}>Department</p>
|
||||||
|
<p className="text-sm font-semibold text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.department}</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 rounded-xl bg-gray-50/80 border border-gray-100 transition-colors hover:bg-gray-50">
|
||||||
|
<p className="text-xs font-semibold text-[#94A3B8] uppercase tracking-wider mb-1" style={{ fontFamily: 'Inter, sans-serif' }}>Specialization</p>
|
||||||
|
<p className="text-sm font-semibold text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.specialization || 'Not specified'}</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 rounded-xl bg-gray-50/80 border border-gray-100 transition-colors hover:bg-gray-50">
|
||||||
|
<p className="text-xs font-semibold text-[#94A3B8] uppercase tracking-wider mb-1" style={{ fontFamily: 'Inter, sans-serif' }}>Qualification</p>
|
||||||
|
<p className="text-sm font-semibold text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.qualification || 'Not specified'}</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 rounded-xl bg-gray-50/80 border border-gray-100 transition-colors hover:bg-gray-50">
|
||||||
|
<p className="text-xs font-semibold text-[#94A3B8] uppercase tracking-wider mb-1" style={{ fontFamily: 'Inter, sans-serif' }}>Experience</p>
|
||||||
|
<p className="text-sm font-semibold text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.experience || 'Not specified'}</p>
|
||||||
|
</div>
|
||||||
|
{faculty.joinedInstitution && (
|
||||||
|
<div className="p-4 rounded-xl bg-gray-50/80 border border-gray-100 transition-colors hover:bg-gray-50 md:col-span-2">
|
||||||
|
<p className="text-xs font-semibold text-[#94A3B8] uppercase tracking-wider mb-1" style={{ fontFamily: 'Inter, sans-serif' }}>Joined Institution</p>
|
||||||
|
<p className="text-sm font-semibold text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.joinedInstitution}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section 2: Academic Info */}
|
||||||
|
<div className="bg-white rounded-[20px] shadow-sm border border-[#E8ECF4] p-6 sm:p-8">
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-indigo-50 flex items-center justify-center shrink-0">
|
||||||
|
<BookOpen className="w-5 h-5 text-indigo-500" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>Academic Information</h3>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="p-5 rounded-xl bg-indigo-50/30 border border-indigo-100/50">
|
||||||
|
<p className="text-xs font-bold text-indigo-400 uppercase tracking-wider mb-2" style={{ fontFamily: 'Inter, sans-serif' }}>Subjects Handling</p>
|
||||||
|
<p className="text-sm font-medium text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif', lineHeight: '1.6' }}>{faculty.subjectsHandling || 'Not specified'}</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 rounded-xl bg-teal-50/30 border border-teal-100/50">
|
||||||
|
<p className="text-xs font-bold text-teal-500 uppercase tracking-wider mb-2" style={{ fontFamily: 'Inter, sans-serif' }}>Research Areas</p>
|
||||||
|
<p className="text-sm font-medium text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif', lineHeight: '1.6' }}>{faculty.researchAreas || 'Not specified'}</p>
|
||||||
|
</div>
|
||||||
|
{faculty.studentsGuided && (
|
||||||
|
<div className="p-5 rounded-xl bg-blue-50/30 border border-blue-100/50">
|
||||||
|
<p className="text-xs font-bold text-blue-500 uppercase tracking-wider mb-3" style={{ fontFamily: 'Inter, sans-serif' }}>Students Guided</p>
|
||||||
|
<div className="flex gap-8">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-2xl font-bold text-[#1E293B]">{faculty.studentsGuided.ug}</span>
|
||||||
|
<span className="text-xs font-semibold text-[#64748B]">UG</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-2xl font-bold text-[#1E293B]">{faculty.studentsGuided.pg}</span>
|
||||||
|
<span className="text-xs font-semibold text-[#64748B]">PG</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-2xl font-bold text-[#1E293B]">{faculty.studentsGuided.phd}</span>
|
||||||
|
<span className="text-xs font-semibold text-[#64748B]">Ph.D.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section 3: Achievements (Render only if present) */}
|
||||||
|
{(faculty.achievements || faculty.publications) && (
|
||||||
|
<div className="bg-white rounded-[20px] shadow-sm border border-[#E8ECF4] p-6 sm:p-8">
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-pink-50 flex items-center justify-center shrink-0">
|
||||||
|
<GraduationCap className="w-5 h-5 text-pink-500" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>Achievements</h3>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{faculty.achievements && faculty.achievements.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-bold text-pink-400 uppercase tracking-wider mb-3" style={{ fontFamily: 'Inter, sans-serif' }}>Awards & Certifications</p>
|
||||||
|
<ul className="list-disc list-inside space-y-2">
|
||||||
|
{faculty.achievements.map((ach, i) => (
|
||||||
|
<li key={i} className="text-sm font-medium text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{ach}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{faculty.publications && faculty.publications.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-bold text-pink-400 uppercase tracking-wider mb-3" style={{ fontFamily: 'Inter, sans-serif' }}>Recent Publications</p>
|
||||||
|
<ul className="list-disc list-inside space-y-2">
|
||||||
|
{faculty.publications.map((pub, i) => (
|
||||||
|
<li key={i} className="text-sm font-medium text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{pub}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Section 4: Office Info */}
|
||||||
|
<div className="bg-white rounded-[20px] shadow-sm border border-[#E8ECF4] p-6 sm:p-8">
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-emerald-50 flex items-center justify-center shrink-0">
|
||||||
|
<Clock className="w-5 h-5 text-emerald-500" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>Office Information</h3>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div className="p-5 rounded-xl bg-emerald-50/50 border border-emerald-100 flex items-start gap-4">
|
||||||
|
<Clock className="w-6 h-6 text-emerald-500 shrink-0 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-bold text-emerald-600 uppercase tracking-wider mb-1" style={{ fontFamily: 'Inter, sans-serif' }}>Office Hours</p>
|
||||||
|
<p className="text-sm font-medium text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.officeHours || 'Not specified'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-5 rounded-xl bg-blue-50/30 border border-blue-100/50 flex items-start gap-4">
|
||||||
|
<Building2 className="w-6 h-6 text-blue-500 shrink-0 mt-0.5" />
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-bold text-blue-500 uppercase tracking-wider mb-1" style={{ fontFamily: 'Inter, sans-serif' }}>Office Location</p>
|
||||||
|
<p className="text-sm font-medium text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>{faculty.office || 'Not specified'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</AnimatePresence>,
|
||||||
</motion.div>
|
document.body
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Email Options Modal */}
|
||||||
|
{createPortal(
|
||||||
|
<AnimatePresence>
|
||||||
|
{showEmailOptions && (
|
||||||
|
<div className="fixed inset-0 z-[110] flex items-center justify-center p-4">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
onClick={() => setShowEmailOptions(false)}
|
||||||
|
className="absolute inset-0 bg-[#0F172A]/40 backdrop-blur-sm"
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
transition={{ type: "spring", duration: 0.4, bounce: 0.2 }}
|
||||||
|
className="relative w-full max-w-sm bg-white rounded-3xl shadow-2xl p-6 flex flex-col gap-5"
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>Choose Mail App</h3>
|
||||||
|
<p className="text-xs text-[#64748B] mt-0.5" style={{ fontFamily: 'Inter, sans-serif' }}>Compose email to {faculty.name}</p>
|
||||||
|
</div>
|
||||||
|
<button onClick={() => setShowEmailOptions(false)} className="p-2 rounded-full hover:bg-gray-100 transition-colors">
|
||||||
|
<X className="w-5 h-5 text-[#94A3B8]" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{mailOptions.map((option, idx) => (
|
||||||
|
<a
|
||||||
|
key={idx}
|
||||||
|
href={option.url}
|
||||||
|
target={option.name !== 'Default Mail App' ? '_blank' : undefined}
|
||||||
|
rel={option.name !== 'Default Mail App' ? 'noopener noreferrer' : undefined}
|
||||||
|
onClick={() => setShowEmailOptions(false)}
|
||||||
|
className="flex items-center gap-3 p-3 rounded-xl hover:bg-gray-50 border border-gray-100 transition-all group hover:border-gray-200"
|
||||||
|
>
|
||||||
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center shrink-0 ${option.bg}`}>
|
||||||
|
<option.icon className={`w-4 h-4 ${option.color}`} />
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-semibold text-[#1E293B] group-hover:text-black" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
|
{option.name}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,55 +179,302 @@ export const FACULTY_DATA: Faculty[] = [
|
|||||||
name: 'Dr. S. Ramesh Kumar',
|
name: 'Dr. S. Ramesh Kumar',
|
||||||
designation: 'Professor & HOD',
|
designation: 'Professor & HOD',
|
||||||
department: 'Computer Science & Engineering',
|
department: 'Computer Science & Engineering',
|
||||||
email: 'ramesh@rit.edu',
|
|
||||||
office: 'CSE Block, Room 301',
|
office: 'CSE Block, Room 301',
|
||||||
specialization: 'Machine Learning, AI',
|
specialization: 'Machine Learning, Artificial Intelligence',
|
||||||
|
email: 'ramesh@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '18 Years',
|
||||||
|
subjectsHandling: 'Machine Learning, Artificial Intelligence, Data Mining',
|
||||||
|
researchAreas: 'AI, Deep Learning, Computer Vision',
|
||||||
|
officeHours: 'Mon–Fri, 10:00 AM–12:00 PM',
|
||||||
|
phone: '+91 98765 43001',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
name: 'Dr. P. Mahalakshmi',
|
|
||||||
designation: 'Associate Professor',
|
|
||||||
department: 'Electronics & Communication',
|
|
||||||
email: 'mahalakshmi@rit.edu',
|
|
||||||
office: 'ECE Block, Room 205',
|
|
||||||
specialization: 'VLSI Design, Embedded Systems',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
name: 'Prof. R. Vijayakumar',
|
|
||||||
designation: 'Assistant Professor',
|
|
||||||
department: 'Mechanical Engineering',
|
|
||||||
email: 'vijay@rit.edu',
|
|
||||||
office: 'MECH Block, Room 102',
|
|
||||||
specialization: 'CAD/CAM, Robotics',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
name: 'Dr. K. Anitha',
|
|
||||||
designation: 'Professor',
|
|
||||||
department: 'Information Technology',
|
|
||||||
email: 'anitha@rit.edu',
|
|
||||||
office: 'IT Block, Room 401',
|
|
||||||
specialization: 'Data Science, Cloud Computing',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '5',
|
|
||||||
name: 'Prof. M. Selvakumar',
|
|
||||||
designation: 'Associate Professor',
|
|
||||||
department: 'Civil Engineering',
|
|
||||||
email: 'selva@rit.edu',
|
|
||||||
office: 'CIVIL Block, Room 110',
|
|
||||||
specialization: 'Structural Engineering',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '6',
|
|
||||||
name: 'Dr. S. Priya',
|
name: 'Dr. S. Priya',
|
||||||
designation: 'Assistant Professor',
|
designation: 'Assistant Professor',
|
||||||
department: 'Computer Science & Engineering',
|
department: 'Computer Science & Engineering',
|
||||||
email: 'priya@rit.edu',
|
|
||||||
office: 'CSE Block, Room 205',
|
office: 'CSE Block, Room 205',
|
||||||
specialization: 'Cyber Security, Networks',
|
specialization: 'Cyber Security, Computer Networks',
|
||||||
|
email: 'priya@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '8 Years',
|
||||||
|
subjectsHandling: 'Network Security, Computer Networks, Ethical Hacking',
|
||||||
|
researchAreas: 'Cyber Security, IoT Security',
|
||||||
|
officeHours: 'Mon–Fri, 2:00 PM–4:00 PM',
|
||||||
|
phone: '+91 98765 43002',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
name: 'Dr. K. Aravind',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Computer Science & Engineering',
|
||||||
|
office: 'CSE Block, Room 212',
|
||||||
|
specialization: 'Cloud Computing, Distributed Systems',
|
||||||
|
email: 'aravind@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '11 Years',
|
||||||
|
subjectsHandling: 'Cloud Computing, Distributed Systems, Operating Systems',
|
||||||
|
researchAreas: 'Cloud Infrastructure, Edge Computing',
|
||||||
|
officeHours: 'Mon–Fri, 9:30 AM–11:30 AM',
|
||||||
|
phone: '+91 98765 43003',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
name: 'Dr. P. Mahalakshmi',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Electronics & Communication Engineering',
|
||||||
|
office: 'ECE Block, Room 205',
|
||||||
|
specialization: 'VLSI Design, Embedded Systems',
|
||||||
|
email: 'mahalakshmi@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '13 Years',
|
||||||
|
subjectsHandling: 'VLSI Design, Embedded Systems',
|
||||||
|
researchAreas: 'Chip Design, IoT Devices',
|
||||||
|
officeHours: 'Mon–Fri, 10:00 AM–12:00 PM',
|
||||||
|
phone: '+91 98765 43006',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
name: 'Dr. V. Prakash',
|
||||||
|
designation: 'Professor',
|
||||||
|
department: 'Electronics & Communication Engineering',
|
||||||
|
office: 'ECE Block, Room 310',
|
||||||
|
specialization: 'Digital Signal Processing, IoT',
|
||||||
|
email: 'prakash@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '17 Years',
|
||||||
|
subjectsHandling: 'DSP, IoT, Microprocessors',
|
||||||
|
researchAreas: 'Wireless Systems, Signal Processing',
|
||||||
|
officeHours: 'Mon–Fri, 2:00 PM–4:00 PM',
|
||||||
|
phone: '+91 98765 43007',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
name: 'Dr. A. Deepika',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Electronics & Communication Engineering',
|
||||||
|
office: 'ECE Block, Room 214',
|
||||||
|
specialization: 'Wireless Communication, 5G Networks',
|
||||||
|
email: 'deepika@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '6 Years',
|
||||||
|
subjectsHandling: 'Wireless Networks, Mobile Communication',
|
||||||
|
researchAreas: '5G, IoT Networks',
|
||||||
|
officeHours: 'Mon–Fri, 9:00 AM–11:00 AM',
|
||||||
|
phone: '+91 98765 43008',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
name: 'Prof. R. Vijayakumar',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Mechanical Engineering',
|
||||||
|
office: 'MECH Block, Room 102',
|
||||||
|
specialization: 'CAD/CAM, Robotics',
|
||||||
|
email: 'vijayakumar@rit.edu',
|
||||||
|
qualification: 'B.E., M.E.',
|
||||||
|
experience: '9 Years',
|
||||||
|
subjectsHandling: 'CAD/CAM, Robotics',
|
||||||
|
researchAreas: 'Industrial Automation',
|
||||||
|
officeHours: 'Mon–Fri, 11:00 AM–1:00 PM',
|
||||||
|
phone: '+91 98765 43009',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
name: 'Dr. S. Manikandan',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Mechanical Engineering',
|
||||||
|
office: 'MECH Block, Room 118',
|
||||||
|
specialization: 'Thermal Engineering, Manufacturing',
|
||||||
|
email: 'manikandan@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '12 Years',
|
||||||
|
subjectsHandling: 'Manufacturing Technology, Thermal Engineering',
|
||||||
|
researchAreas: 'Renewable Energy, Thermal Systems',
|
||||||
|
officeHours: 'Mon–Fri, 1:00 PM–3:00 PM',
|
||||||
|
phone: '+91 98765 43010',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '11',
|
||||||
|
name: 'Prof. M. Selvakumar',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Civil Engineering',
|
||||||
|
office: 'CIVIL Block, Room 110',
|
||||||
|
specialization: 'Structural Engineering',
|
||||||
|
email: 'selvakumar@rit.edu',
|
||||||
|
qualification: 'B.E., M.E.',
|
||||||
|
experience: '14 Years',
|
||||||
|
subjectsHandling: 'Structural Analysis, RCC Design',
|
||||||
|
researchAreas: 'Earthquake Resistant Structures',
|
||||||
|
officeHours: 'Mon–Fri, 10:00 AM–12:00 PM',
|
||||||
|
phone: '+91 98765 43011',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '12',
|
||||||
|
name: 'Dr. J. Revathi',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Civil Engineering',
|
||||||
|
office: 'CIVIL Block, Room 123',
|
||||||
|
specialization: 'Environmental Engineering, Surveying',
|
||||||
|
email: 'revathi@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '8 Years',
|
||||||
|
subjectsHandling: 'Surveying, Environmental Engineering',
|
||||||
|
researchAreas: 'Water Resource Management',
|
||||||
|
officeHours: 'Mon–Fri, 2:00 PM–4:00 PM',
|
||||||
|
phone: '+91 98765 43012',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '13',
|
||||||
|
name: 'Dr. N. Hariharan',
|
||||||
|
designation: 'Professor & HOD',
|
||||||
|
department: 'Artificial Intelligence & Data Science',
|
||||||
|
office: 'AIDS Block, Room 501',
|
||||||
|
specialization: 'Deep Learning, Data Mining',
|
||||||
|
email: 'hariharan@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '19 Years',
|
||||||
|
subjectsHandling: 'Deep Learning, Data Mining, AI',
|
||||||
|
researchAreas: 'Artificial Intelligence, Predictive Analytics',
|
||||||
|
officeHours: 'Mon–Fri, 9:30 AM–11:30 AM',
|
||||||
|
phone: '+91 98765 43013',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '14',
|
||||||
|
name: 'Dr. P. Divya',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Artificial Intelligence & Data Science',
|
||||||
|
office: 'AIDS Block, Room 505',
|
||||||
|
specialization: 'Natural Language Processing, Computer Vision',
|
||||||
|
email: 'divya@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '7 Years',
|
||||||
|
subjectsHandling: 'NLP, Computer Vision',
|
||||||
|
researchAreas: 'Generative AI, Image Processing',
|
||||||
|
officeHours: 'Mon–Fri, 1:00 PM–3:00 PM',
|
||||||
|
phone: '+91 98765 43014',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '15',
|
||||||
|
name: 'Dr. L. Karthikeyan',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Electrical & Electronics Engineering',
|
||||||
|
office: 'EEE Block, Room 303',
|
||||||
|
specialization: 'Power Systems, Renewable Energy',
|
||||||
|
email: 'karthikeyan@rit.edu',
|
||||||
|
qualification: 'B.E., M.E., Ph.D.',
|
||||||
|
experience: '15 Years',
|
||||||
|
subjectsHandling: 'Power Systems, Electrical Machines',
|
||||||
|
researchAreas: 'Smart Grid, Solar Energy',
|
||||||
|
officeHours: 'Mon–Fri, 10:00 AM–12:00 PM',
|
||||||
|
phone: '+91 98765 43015',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '16',
|
||||||
|
name: 'Dr. M. Murugan',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Computer Science & Engineering',
|
||||||
|
office: 'CSE Block, Room 218',
|
||||||
|
specialization: 'Artificial Intelligence, Machine Learning',
|
||||||
|
email: 'murugan@rit.edu',
|
||||||
|
qualification: 'B.E. (CSE), M.E. (Computer Science & Engineering), Ph.D. (Artificial Intelligence)',
|
||||||
|
experience: '13 Years',
|
||||||
|
subjectsHandling: 'Artificial Intelligence, Machine Learning, Python Programming, Data Mining',
|
||||||
|
researchAreas: 'Deep Learning, Computer Vision, Natural Language Processing, Explainable AI',
|
||||||
|
officeHours: 'Mon–Fri, 10:00 AM–12:00 PM',
|
||||||
|
phone: '+91 98765 43125',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '17',
|
||||||
|
name: 'Dr. R. Balamurugan',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Computer Science & Business Systems',
|
||||||
|
office: 'CSBS Block, Room 305',
|
||||||
|
specialization: 'Business Analytics, Data Science',
|
||||||
|
email: 'balamurugan@rit.edu',
|
||||||
|
qualification: 'B.E. (CSE), M.B.A., M.E. (Software Engineering), Ph.D. (Business Analytics)',
|
||||||
|
experience: '12 Years',
|
||||||
|
subjectsHandling: 'Business Analytics, Data Analytics, Database Management Systems, Python for Business Applications',
|
||||||
|
researchAreas: 'Business Intelligence, Data Analytics, Digital Transformation, Decision Support Systems',
|
||||||
|
officeHours: 'Mon–Fri, 9:30 AM–11:30 AM',
|
||||||
|
phone: '+91 98765 43221',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '18',
|
||||||
|
name: 'Dr. K. Nandhini',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Artificial Intelligence & Machine Learning',
|
||||||
|
office: 'AIML Block, Room 412',
|
||||||
|
specialization: 'Artificial Intelligence, Machine Learning',
|
||||||
|
email: 'nandhini@rit.edu',
|
||||||
|
qualification: 'B.E. (CSE), M.Tech. (Artificial Intelligence), Ph.D. (Machine Learning)',
|
||||||
|
experience: '8 Years',
|
||||||
|
subjectsHandling: 'Artificial Intelligence, Machine Learning, Deep Learning, Python Programming',
|
||||||
|
researchAreas: 'Generative AI, Computer Vision, Explainable AI, Neural Networks',
|
||||||
|
officeHours: 'Mon–Fri, 2:00 PM–4:00 PM',
|
||||||
|
phone: '+91 98765 43222',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '19',
|
||||||
|
name: 'Dr. S. Rajeshwari',
|
||||||
|
designation: 'Professor',
|
||||||
|
department: 'Computer Science & Business Systems',
|
||||||
|
office: 'CSBS Block, Room 302',
|
||||||
|
specialization: 'Business Intelligence, Enterprise Systems',
|
||||||
|
email: 'rajeshwari@rit.edu',
|
||||||
|
qualification: 'B.E. (CSE), M.E. (Computer Science), Ph.D. (Business Intelligence)',
|
||||||
|
experience: '16 Years',
|
||||||
|
subjectsHandling: 'Business Intelligence, Enterprise Systems, Software Engineering, Project Management',
|
||||||
|
researchAreas: 'Business Intelligence, Enterprise Computing, ERP Systems',
|
||||||
|
officeHours: 'Mon–Fri, 10:00 AM–12:00 PM',
|
||||||
|
phone: '+91 98765 43223',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '20',
|
||||||
|
name: 'Dr. V. Pradeep Kumar',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Computer Science & Business Systems',
|
||||||
|
office: 'CSBS Block, Room 308',
|
||||||
|
specialization: 'FinTech, Cloud Computing',
|
||||||
|
email: 'pradeepkumar@rit.edu',
|
||||||
|
qualification: 'B.E. (IT), M.Tech. (Cloud Computing), Ph.D. (Financial Technology)',
|
||||||
|
experience: '7 Years',
|
||||||
|
subjectsHandling: 'Cloud Computing, FinTech Applications, Data Structures, Java Programming',
|
||||||
|
researchAreas: 'Cloud Security, Financial Analytics, Blockchain Technology',
|
||||||
|
officeHours: 'Mon–Fri, 1:00 PM–3:00 PM',
|
||||||
|
phone: '+91 98765 43224',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '21',
|
||||||
|
name: 'Dr. P. Harini',
|
||||||
|
designation: 'Associate Professor',
|
||||||
|
department: 'Artificial Intelligence & Machine Learning',
|
||||||
|
office: 'AIML Block, Room 408',
|
||||||
|
specialization: 'Deep Learning, Computer Vision',
|
||||||
|
email: 'harini@rit.edu',
|
||||||
|
qualification: 'B.E. (CSE), M.Tech. (Artificial Intelligence), Ph.D. (Computer Vision)',
|
||||||
|
experience: '10 Years',
|
||||||
|
subjectsHandling: 'Deep Learning, Computer Vision, Image Processing, Python Programming',
|
||||||
|
researchAreas: 'Medical Image Analysis, Deep Neural Networks, AI in Healthcare',
|
||||||
|
officeHours: 'Mon–Fri, 9:00 AM–11:00 AM',
|
||||||
|
phone: '+91 98765 43225',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '22',
|
||||||
|
name: 'Dr. A. Vignesh',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Artificial Intelligence & Machine Learning',
|
||||||
|
office: 'AIML Block, Room 415',
|
||||||
|
specialization: 'Natural Language Processing, Generative AI',
|
||||||
|
email: 'vignesh@rit.edu',
|
||||||
|
qualification: 'B.E. (CSE), M.E. (Artificial Intelligence), Ph.D. (Natural Language Processing)',
|
||||||
|
experience: '6 Years',
|
||||||
|
subjectsHandling: 'Natural Language Processing, Generative AI, Machine Learning, Data Science',
|
||||||
|
researchAreas: 'Large Language Models, Conversational AI, Explainable AI',
|
||||||
|
officeHours: 'Mon–Fri, 2:00 PM–4:00 PM',
|
||||||
|
phone: '+91 98765 43226',
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// ─── Events ──────────────────────────────────────────────────────────────────
|
// ─── Events ──────────────────────────────────────────────────────────────────
|
||||||
@@ -390,10 +637,11 @@ export const AI_SUGGESTED_PROMPTS = [
|
|||||||
export const DEPARTMENTS = [
|
export const DEPARTMENTS = [
|
||||||
'All Departments',
|
'All Departments',
|
||||||
'Computer Science & Engineering',
|
'Computer Science & Engineering',
|
||||||
'Electronics & Communication',
|
'Computer Science & Business Systems',
|
||||||
|
'Artificial Intelligence & Machine Learning',
|
||||||
|
'Electronics & Communication Engineering',
|
||||||
'Mechanical Engineering',
|
'Mechanical Engineering',
|
||||||
'Civil Engineering',
|
'Civil Engineering',
|
||||||
'Information Technology',
|
|
||||||
'Electrical & Electronics',
|
|
||||||
'Artificial Intelligence & Data Science',
|
'Artificial Intelligence & Data Science',
|
||||||
|
'Electrical & Electronics Engineering',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Search, ChevronRight, MapPin } from 'lucide-react';
|
import { Search, ChevronRight, MapPin, Users, BookOpen, Filter, ArrowUpDown } from 'lucide-react';
|
||||||
import SectionTitle from '@/components/SectionTitle/SectionTitle';
|
import SectionTitle from '@/components/SectionTitle/SectionTitle';
|
||||||
import BusCard from '@/components/BusCard/BusCard';
|
import BusCard from '@/components/BusCard/BusCard';
|
||||||
import FacultyCard from '@/components/FacultyCard/FacultyCard';
|
import FacultyCard from '@/components/FacultyCard/FacultyCard';
|
||||||
@@ -14,14 +14,29 @@ export default function Campus() {
|
|||||||
const [activeTab, setActiveTab] = useState<Tab>('map');
|
const [activeTab, setActiveTab] = useState<Tab>('map');
|
||||||
const [searchFaculty, setSearchFaculty] = useState('');
|
const [searchFaculty, setSearchFaculty] = useState('');
|
||||||
const [selectedDept, setSelectedDept] = useState('All Departments');
|
const [selectedDept, setSelectedDept] = useState('All Departments');
|
||||||
|
const [sortBy, setSortBy] = useState('Name A-Z');
|
||||||
const [busSearch, setBusSearch] = useState('');
|
const [busSearch, setBusSearch] = useState('');
|
||||||
|
|
||||||
const filteredFaculty = FACULTY_DATA.filter((f) => {
|
const filteredAndSortedFaculty = useMemo(() => {
|
||||||
const matchSearch = f.name.toLowerCase().includes(searchFaculty.toLowerCase()) ||
|
const filtered = FACULTY_DATA.filter((f) => {
|
||||||
f.department.toLowerCase().includes(searchFaculty.toLowerCase());
|
const searchLower = searchFaculty.trim().toLowerCase();
|
||||||
const matchDept = selectedDept === 'All Departments' || f.department === selectedDept;
|
const matchSearch =
|
||||||
return matchSearch && matchDept;
|
f.name.toLowerCase().includes(searchLower) ||
|
||||||
});
|
f.department.toLowerCase().includes(searchLower) ||
|
||||||
|
f.designation.toLowerCase().includes(searchLower) ||
|
||||||
|
(f.specialization && f.specialization.toLowerCase().includes(searchLower));
|
||||||
|
|
||||||
|
const matchDept = selectedDept === 'All Departments' || f.department === selectedDept;
|
||||||
|
return matchSearch && matchDept;
|
||||||
|
});
|
||||||
|
|
||||||
|
return filtered.sort((a, b) => {
|
||||||
|
if (sortBy === 'Name A-Z') return a.name.localeCompare(b.name);
|
||||||
|
if (sortBy === 'Department') return a.department.localeCompare(b.department);
|
||||||
|
if (sortBy === 'Designation') return a.designation.localeCompare(b.designation);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}, [searchFaculty, selectedDept, sortBy]);
|
||||||
|
|
||||||
const filteredRoutes = BUS_ROUTES.filter((r) =>
|
const filteredRoutes = BUS_ROUTES.filter((r) =>
|
||||||
r.name.toLowerCase().includes(busSearch.toLowerCase()) ||
|
r.name.toLowerCase().includes(busSearch.toLowerCase()) ||
|
||||||
@@ -36,8 +51,18 @@ export default function Campus() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen" style={{ backgroundColor: '#FAFAFA' }}>
|
<div className="min-h-screen relative overflow-hidden" style={{ backgroundColor: activeTab === 'faculty' ? '#F8FAFC' : '#FAFAFA' }}>
|
||||||
{/* Header */}
|
{/* Background decorations for Faculty tab */}
|
||||||
|
{activeTab === 'faculty' && (
|
||||||
|
<div className="absolute inset-0 pointer-events-none overflow-hidden">
|
||||||
|
<div className="absolute top-0 left-0 w-full h-96 bg-gradient-to-b from-white/80 to-transparent" />
|
||||||
|
<div className="absolute top-20 -left-64 w-[500px] h-[500px] bg-purple-200/40 rounded-full blur-3xl opacity-50" />
|
||||||
|
<div className="absolute top-40 -right-64 w-[600px] h-[600px] bg-orange-200/40 rounded-full blur-3xl opacity-40" />
|
||||||
|
<div className="absolute inset-0" style={{ backgroundImage: 'radial-gradient(rgba(148, 163, 184, 0.1) 2px, transparent 2px)', backgroundSize: '32px 32px' }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="relative z-10">
|
||||||
|
{/* Header */}
|
||||||
<div className="bg-white border-b border-[#E5E7EB] py-10">
|
<div className="bg-white border-b border-[#E5E7EB] py-10">
|
||||||
<div className="container-custom">
|
<div className="container-custom">
|
||||||
<div className="flex items-center gap-2 text-xs text-[#94A3B8] mb-3">
|
<div className="flex items-center gap-2 text-xs text-[#94A3B8] mb-3">
|
||||||
@@ -191,40 +216,128 @@ export default function Campus() {
|
|||||||
|
|
||||||
{/* Faculty Tab */}
|
{/* Faculty Tab */}
|
||||||
{activeTab === 'faculty' && (
|
{activeTab === 'faculty' && (
|
||||||
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }}>
|
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }} className="space-y-8">
|
||||||
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
{/* Faculty Hero Section */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-stretch">
|
||||||
|
<div className="lg:col-span-5 bg-white rounded-3xl p-8 border border-[#E8ECF4] shadow-sm flex flex-col justify-center relative overflow-hidden">
|
||||||
|
<div className="absolute top-0 right-0 w-32 h-32 bg-orange-50 rounded-bl-full -z-10" />
|
||||||
|
<div className="w-14 h-14 rounded-2xl bg-gradient-to-br from-[#FF7A00] to-[#FB923C] flex items-center justify-center text-white mb-6 shadow-md">
|
||||||
|
<Users className="w-7 h-7" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-3xl font-bold text-[#1E293B] mb-3" style={{ fontFamily: 'Poppins, sans-serif' }}>
|
||||||
|
Faculty Directory
|
||||||
|
</h2>
|
||||||
|
<p className="text-[#64748B] text-sm leading-relaxed" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
|
Browse and connect with experienced faculty members. Search by name, department, or specialization.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="lg:col-span-7 bg-white rounded-3xl p-6 border border-[#E8ECF4] shadow-sm">
|
||||||
|
<h3 className="text-sm font-semibold text-[#1E293B] mb-4 uppercase tracking-wider" style={{ fontFamily: 'Inter, sans-serif' }}>Department Overview</h3>
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||||
|
{[
|
||||||
|
{ abrv: 'CSE', full: 'Computer Science & Engineering', color: 'blue' },
|
||||||
|
{ abrv: 'CSBS', full: 'Computer Science & Business Systems', color: 'indigo' },
|
||||||
|
{ abrv: 'AIML', full: 'Artificial Intelligence & Machine Learning', color: 'sky' },
|
||||||
|
{ abrv: 'ECE', full: 'Electronics & Communication Engineering', color: 'purple' },
|
||||||
|
{ abrv: 'MECH', full: 'Mechanical Engineering', color: 'orange' },
|
||||||
|
{ abrv: 'CIVIL', full: 'Civil Engineering', color: 'emerald' },
|
||||||
|
{ abrv: 'AI & DS', full: 'Artificial Intelligence & Data Science', color: 'pink' },
|
||||||
|
{ abrv: 'EEE', full: 'Electrical & Electronics Engineering', color: 'yellow' },
|
||||||
|
].map(d => {
|
||||||
|
const count = FACULTY_DATA.filter(f => f.department === d.full).length;
|
||||||
|
return (
|
||||||
|
<div key={d.abrv} className={`p-4 rounded-2xl bg-${d.color}-50 border border-${d.color}-100 flex flex-col gap-1 transition-transform hover:scale-105 cursor-default`}>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className={`text-xs font-bold text-${d.color}-700`} style={{ fontFamily: 'Poppins, sans-serif' }}>{d.abrv}</span>
|
||||||
|
<BookOpen className={`w-3.5 h-3.5 text-${d.color}-400`} />
|
||||||
|
</div>
|
||||||
|
<span className="text-2xl font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>{count}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Search Section */}
|
||||||
|
<div className="bg-white rounded-3xl p-4 border border-[#E8ECF4] shadow-sm flex flex-col md:flex-row gap-4">
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4.5 h-4.5 text-[#94A3B8]" />
|
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-[#94A3B8]" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search faculty by name or department..."
|
placeholder="Search by faculty name, department, designation or specialization..."
|
||||||
value={searchFaculty}
|
value={searchFaculty}
|
||||||
onChange={(e) => setSearchFaculty(e.target.value)}
|
onChange={(e) => setSearchFaculty(e.target.value)}
|
||||||
className="w-full pl-11 pr-4 py-3 rounded-xl border border-[#E5E7EB] bg-white text-sm text-[#1E293B] placeholder-[#94A3B8] focus:outline-none focus:border-[#F97316] transition-colors"
|
className="w-full pl-12 pr-4 py-3.5 rounded-2xl bg-[#F8FAFC] border-none text-sm text-[#1E293B] placeholder-[#94A3B8] focus:ring-2 focus:ring-[#FF7A00]/20 focus:outline-none transition-all"
|
||||||
style={{ fontFamily: 'Inter, sans-serif' }}
|
style={{ fontFamily: 'Inter, sans-serif' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<select
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
value={selectedDept}
|
<div className="relative">
|
||||||
onChange={(e) => setSelectedDept(e.target.value)}
|
<Filter className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-[#94A3B8] pointer-events-none" />
|
||||||
className="px-4 py-3 rounded-xl border border-[#E5E7EB] bg-white text-sm text-[#475569] focus:outline-none focus:border-[#F97316] transition-colors"
|
<select
|
||||||
style={{ fontFamily: 'Inter, sans-serif' }}
|
value={selectedDept}
|
||||||
>
|
onChange={(e) => setSelectedDept(e.target.value)}
|
||||||
{DEPARTMENTS.map((dept) => (
|
className="h-full pl-10 pr-10 py-3.5 rounded-2xl bg-[#F8FAFC] border-none text-sm font-medium text-[#475569] focus:ring-2 focus:ring-[#FF7A00]/20 focus:outline-none transition-all appearance-none cursor-pointer min-w-[200px]"
|
||||||
<option key={dept} value={dept}>{dept}</option>
|
style={{ fontFamily: 'Inter, sans-serif', backgroundImage: 'url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%2394A3B8%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E")', backgroundRepeat: 'no-repeat', backgroundPosition: 'right 1rem top 50%', backgroundSize: '0.65rem auto' }}
|
||||||
))}
|
>
|
||||||
</select>
|
{DEPARTMENTS.map((dept) => {
|
||||||
|
const count = dept === 'All Departments' ? FACULTY_DATA.length : FACULTY_DATA.filter((f) => f.department === dept).length;
|
||||||
|
return <option key={dept} value={dept}>{dept} ({count})</option>;
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<ArrowUpDown className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-[#94A3B8] pointer-events-none" />
|
||||||
|
<select
|
||||||
|
value={sortBy}
|
||||||
|
onChange={(e) => setSortBy(e.target.value)}
|
||||||
|
className="h-full pl-10 pr-10 py-3.5 rounded-2xl bg-[#F8FAFC] border-none text-sm font-medium text-[#475569] focus:ring-2 focus:ring-[#FF7A00]/20 focus:outline-none transition-all appearance-none cursor-pointer"
|
||||||
|
style={{ fontFamily: 'Inter, sans-serif', backgroundImage: 'url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%2394A3B8%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E")', backgroundRepeat: 'no-repeat', backgroundPosition: 'right 1rem top 50%', backgroundSize: '0.65rem auto' }}
|
||||||
|
>
|
||||||
|
<option value="Name A-Z">Sort: Name A-Z</option>
|
||||||
|
<option value="Department">Sort: Department</option>
|
||||||
|
<option value="Designation">Sort: Designation</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Grid */}
|
||||||
|
{filteredAndSortedFaculty.length > 0 ? (
|
||||||
|
<StaggerContainer key={`${searchFaculty}-${selectedDept}-${sortBy}`} className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 pb-20">
|
||||||
|
{filteredAndSortedFaculty.map((faculty) => (
|
||||||
|
<StaggerItem key={faculty.id}>
|
||||||
|
<FacultyCard faculty={faculty} />
|
||||||
|
</StaggerItem>
|
||||||
|
))}
|
||||||
|
</StaggerContainer>
|
||||||
|
) : (
|
||||||
|
<div className="py-20 text-center flex flex-col items-center justify-center bg-white rounded-3xl border border-[#E8ECF4] shadow-sm">
|
||||||
|
<Users className="w-12 h-12 text-[#CBD5E1] mb-4" />
|
||||||
|
<h3 className="text-lg font-bold text-[#1E293B]" style={{ fontFamily: 'Poppins, sans-serif' }}>No faculty found</h3>
|
||||||
|
<p className="text-[#64748B] text-sm mt-1" style={{ fontFamily: 'Inter, sans-serif' }}>Try adjusting your search or filters.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Bottom Status */}
|
||||||
|
<div className="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 pointer-events-none">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="px-6 py-3 bg-white/80 backdrop-blur-md border border-[#E8ECF4] shadow-[0_8px_30px_rgb(0,0,0,0.08)] rounded-full flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<span className="text-lg">👥</span>
|
||||||
|
<span className="text-sm font-semibold text-[#1E293B]" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
|
Showing {filteredAndSortedFaculty.length} Faculty Members
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
<StaggerContainer className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
||||||
{filteredFaculty.map((faculty) => (
|
|
||||||
<StaggerItem key={faculty.id}>
|
|
||||||
<FacultyCard faculty={faculty} />
|
|
||||||
</StaggerItem>
|
|
||||||
))}
|
|
||||||
</StaggerContainer>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,16 @@ export interface Faculty {
|
|||||||
office?: string;
|
office?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
specialization?: string;
|
specialization?: string;
|
||||||
|
qualification?: string;
|
||||||
|
experience?: string;
|
||||||
|
subjectsHandling?: string;
|
||||||
|
researchAreas?: string;
|
||||||
|
officeHours?: string;
|
||||||
|
achievements?: string[];
|
||||||
|
publications?: string[];
|
||||||
|
studentsGuided?: { ug: number; pg: number; phd: number };
|
||||||
|
languagesKnown?: string[];
|
||||||
|
joinedInstitution?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Bus Routes ──────────────────────────────────────────────────────────────
|
// ─── Bus Routes ──────────────────────────────────────────────────────────────
|
||||||
|
|||||||
28
test-filter.js
Normal file
28
test-filter.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const FACULTY_DATA = [
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
name: 'Prof. R. Vijayakumar',
|
||||||
|
designation: 'Assistant Professor',
|
||||||
|
department: 'Mechanical Engineering',
|
||||||
|
office: 'MECH Block, Room 102',
|
||||||
|
specialization: 'CAD/CAM, Robotics',
|
||||||
|
email: 'vijayakumar@rit.edu',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const searchFaculty = "";
|
||||||
|
const selectedDept = "Mechanical Engineering";
|
||||||
|
|
||||||
|
const filteredFaculty = FACULTY_DATA.filter((f) => {
|
||||||
|
const searchLower = searchFaculty.toLowerCase();
|
||||||
|
const matchSearch =
|
||||||
|
f.name.toLowerCase().includes(searchLower) ||
|
||||||
|
f.department.toLowerCase().includes(searchLower) ||
|
||||||
|
f.designation.toLowerCase().includes(searchLower) ||
|
||||||
|
(f.specialization && f.specialization.toLowerCase().includes(searchLower));
|
||||||
|
|
||||||
|
const matchDept = selectedDept === 'All Departments' || f.department === selectedDept;
|
||||||
|
return matchSearch && matchDept;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(filteredFaculty);
|
||||||
1
test-render.js
Normal file
1
test-render.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const { FACULTY_DATA } = require('./src/constants/index.ts'); // wait this is TS
|
||||||
Reference in New Issue
Block a user