forked from Abdulbari/sgeUpdated
Compare commits
232 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 47f06688e0 | |||
| a0ec570147 | |||
| 7696d0f133 | |||
| e67dcea72e | |||
| 7a948f3b7e | |||
| 49ce97d394 | |||
| a7ba6fe3e4 | |||
| 7976d56552 | |||
| e3536ea6a3 | |||
| 66aeed7fda | |||
| fcc5edcbe2 | |||
| 8f41ce3d51 | |||
|
|
4ea1cfa9b4 | ||
| 68835f5919 | |||
| ee29ecd766 | |||
| f4dd4a9dce | |||
| 853230e742 | |||
| 0a4462923e | |||
| 7f56158c02 | |||
| 1174707918 | |||
| 93cad886d6 | |||
| a15f249016 | |||
| 5c1f255c3f | |||
| 453d35702c | |||
| cf08be1ddc | |||
| 4a9b65cc30 | |||
| 472a5daf09 | |||
| dcdc1bf43a | |||
| c9b5ebf80e | |||
| caa0549983 | |||
| 4400572a45 | |||
| 04ee05a96b | |||
| 7580bf7cdb | |||
| 0d017c0a4b | |||
| 1eeb91c91c | |||
| cd18444b08 | |||
| 1354d01878 | |||
| 9856c1f9aa | |||
| b1ea7bf736 | |||
| 8b82b44591 | |||
| 5c534314df | |||
| dde5fece3b | |||
| f3b893f05c | |||
| 08e8badae2 | |||
| dd1083db82 | |||
| fad8037371 | |||
| 61faf1bf46 | |||
| 230c4bcb6e | |||
| cbe2660080 | |||
| cf38f431a8 | |||
| f585763563 | |||
| 7bee15a901 | |||
| 1ae3d76781 | |||
| 53d5bf2e77 | |||
| 60e50dc9e8 | |||
| aaf116ca71 | |||
| 061c0cb376 | |||
| bd155fadb2 | |||
| 11541714f5 | |||
| 91559134cd | |||
| d26ca12e0c | |||
| 18c8c49a3d | |||
| 97253a54eb | |||
| f411f44006 | |||
| f4f4fb976b | |||
| 385eda2132 | |||
| 23a3d8252e | |||
| eeda227698 | |||
| 871bcf9651 | |||
| 614cbe8b04 | |||
| ce19adb1a0 | |||
| 5471db463f | |||
| 845417ee49 | |||
| 1c12ac1e16 | |||
| eb2ec01d28 | |||
| 4d40015fd1 | |||
| c55f5ce7d1 | |||
| 07a8973b92 | |||
| 54b47eee05 | |||
| 3f0550f9f2 | |||
| 6499c9471e | |||
| 71e507c21b | |||
| 737fd6da53 | |||
| 490f3b539e | |||
| 9c0c4c4828 | |||
| b28a73b4ff | |||
| f087b6eb88 | |||
| d07932cc1e | |||
| 76d37d6038 | |||
| 50abcab358 | |||
| 445f9256a1 | |||
| 4d1a82fe92 | |||
| 6d6e935b52 | |||
| a51a81ce7c | |||
| 3b6000bc40 | |||
| 78ed73acc6 | |||
| 7f8a7b1bf8 | |||
| 8c6e37ad72 | |||
| 85151e536d | |||
| 573db7fee9 | |||
| a11f9614ed | |||
| 02bd570573 | |||
| 5da26152a9 | |||
| 4f4c2e2ce5 | |||
| 79ddbf2207 | |||
| 23d9e65a7f | |||
| c8f9abe5e9 | |||
| fa96969bc7 | |||
| dc67f75027 | |||
| e22c880673 | |||
| b37ed8797b | |||
| 04ed88e45e | |||
| 5f8de8105b | |||
| 8fb923a4db | |||
| a088889a67 | |||
| b3de688265 | |||
| 080924f788 | |||
| e43199f086 | |||
| 49e2020455 | |||
| 876227286c | |||
| af518cef20 | |||
| 2e5bf41ed9 | |||
| f61199cedb | |||
| 5001a44406 | |||
| 87487510ce | |||
| 547ade53ae | |||
| 95afe7b877 | |||
| ae0e112572 | |||
| b35eac74a1 | |||
| 20aabd605c | |||
| 517d4d499b | |||
| 1aa745ebf5 | |||
| 88a312d37c | |||
| b1019b21cf | |||
| 57c28e4a90 | |||
| b1cf711420 | |||
| 1d6b026710 | |||
| 02e4993c2c | |||
| be71b59a3e | |||
| 7e5761993b | |||
| 66bd7b5a68 | |||
| 206d8a2bbf | |||
| 6fbca1c29a | |||
| 1f6b06260c | |||
| b00a8e1bf1 | |||
| 0f01ddefe9 | |||
| 2116b0f28a | |||
| 5161286279 | |||
| 29963d5388 | |||
| 990b98c171 | |||
| f2d5182215 | |||
| e9a361489e | |||
| 37873ad091 | |||
| cac67b815c | |||
| 28fc21e9f2 | |||
| 98a2515735 | |||
| 4503d7c319 | |||
| e2a1b6f940 | |||
| d25a7975c7 | |||
| 926c6b0b66 | |||
| 986bec559b | |||
| 77d5c94cf7 | |||
| 75c2f7ff19 | |||
| 55577469de | |||
| f50803f469 | |||
| adce8cddd8 | |||
| c2ddc08802 | |||
| 2c79300663 | |||
| a6bb799caa | |||
| a4791d6a57 | |||
| 168cfaa8a7 | |||
| 710655c1fd | |||
| 6f445f205a | |||
| 32b9533a33 | |||
| 62829194e2 | |||
| 87a80f63ed | |||
| f12c5cb8d4 | |||
| 7148bdf834 | |||
| 9eb903a450 | |||
| 991aef0417 | |||
| a1c0f71fbf | |||
| 64c36cb617 | |||
| 0f2bf5a800 | |||
| d6375c320a | |||
| 3d440798e3 | |||
| 8cdc00f921 | |||
| 593e5ceb6a | |||
| b2eebfce64 | |||
| c3ed359103 | |||
| 7b4143ba52 | |||
| 0322235404 | |||
| 95a344a7a6 | |||
| c477143b30 | |||
| 6202f7ec26 | |||
| 8eb9835e13 | |||
| 08941cc7d5 | |||
| 994e81b1fb | |||
| 0c03a110e5 | |||
| 7c5eb3a210 | |||
| a943925325 | |||
| 5bd98fb09d | |||
| cdb6d98ae7 | |||
| 243f89457f | |||
| 03875dde53 | |||
| e695d06f5b | |||
| 837353bc45 | |||
| 2459ba092b | |||
| 4bfd2ee1f8 | |||
| 95fbbc340e | |||
| 78ffa4fe7e | |||
| a14bc4e73a | |||
| 50c6a2ef5b | |||
| d4e40f5a6b | |||
| bbb0976aa1 | |||
| 085a417016 | |||
| edd8feb4d2 | |||
| 2d39883b7f | |||
| 21b757cf77 | |||
| 8bd6d94174 | |||
|
|
b5901a049c | ||
| 82f86a62ca | |||
| 47959fb35f | |||
| 2257ec79f6 | |||
| 17d77fcda7 | |||
| 3245040274 | |||
| 92d88df213 | |||
| a66b01334d | |||
| c7e60c25eb | |||
| ebd997a33d | |||
| dd04a057b1 | |||
| d224905ba0 | |||
| b5ceb1591e |
29
.gitea/workflows/sgeupdated.yml
Normal file
29
.gitea/workflows/sgeupdated.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: sgeUpdated CI/CD
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed # Fires when a PR is closed (either merged or manually closed)
|
||||
branches:
|
||||
- main # Only when PR targets main
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
if: github.event.pull_request.merged == true # Run only if the PR was merged (not just closed)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Setup SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh/
|
||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Run deploy script on server
|
||||
run: |
|
||||
ssh ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} << 'EOF'
|
||||
echo "✅ PR merged into main — running deploy script..."
|
||||
cd /home/ubuntu/Bgreen/sgeUpdated
|
||||
./deploy.sh
|
||||
EOF
|
||||
24
config.conf
Normal file
24
config.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
# SGE Application Configuration
|
||||
# This file contains configuration for both backend and frontend
|
||||
|
||||
# Database Configuration
|
||||
SPRING_DATASOURCE_URL=jdbc:postgresql://bgreen-database:5432/sge
|
||||
SPRING_DATASOURCE_USERNAME=sge
|
||||
SPRING_DATASOURCE_PASSWORD=147
|
||||
|
||||
# Server Configuration
|
||||
SERVER_PORT=8080
|
||||
|
||||
# Mail Configuration
|
||||
MAIL_HOSTNAME=mail.spacemail.com
|
||||
MAIL_SMTP_PORT=465
|
||||
MAIL_ADDRESS=info@blc-css.com
|
||||
MAIL_PASSWORD=123456Bb@
|
||||
|
||||
# React Application Configuration
|
||||
# API Configuration
|
||||
API_PROTOCOL=http
|
||||
API_HOST=bgreen-backend
|
||||
|
||||
# Application URLs
|
||||
APP_SURVEY_BASE_URL=https://bgreen.blc-css.com
|
||||
110
deploy.sh
Executable file
110
deploy.sh
Executable file
@@ -0,0 +1,110 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
cd /home/ubuntu/Bgreen/sgeUpdated
|
||||
|
||||
# -----------------------
|
||||
# Harbor settings
|
||||
# -----------------------
|
||||
HARBOR_REGISTRY="10.150.1.166"
|
||||
HARBOR_PROJECT="bgreen"
|
||||
|
||||
BACKEND_IMAGE_REPO="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/bgreen-backend"
|
||||
FRONTEND_IMAGE_REPO="${HARBOR_REGISTRY}/${HARBOR_PROJECT}/bgreen-frontend"
|
||||
|
||||
# Tag images with git commit (better than latest; enables rollback)
|
||||
VERSION="$(git rev-parse --short HEAD)"
|
||||
|
||||
echo "📦 Fetching latest changes from origin/main..."
|
||||
git fetch myfork main
|
||||
|
||||
# Detect which files changed between local HEAD and the latest remote version
|
||||
CHANGED_FILES=$(git diff --name-only HEAD myfork/main || true)
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "✅ No file changes detected between HEAD and origin/main."
|
||||
else
|
||||
echo "🪶 Changed files:"
|
||||
echo "$CHANGED_FILES"
|
||||
fi
|
||||
|
||||
# Update to the latest version
|
||||
git reset --hard myfork/main
|
||||
|
||||
BACKEND_CHANGED=false
|
||||
FRONTEND_CHANGED=false
|
||||
|
||||
# Check if backend folder changed (excluding README.md)
|
||||
if echo "$CHANGED_FILES" | grep "^sge-backend/" | grep -qv "README.md$"; then
|
||||
BACKEND_CHANGED=true
|
||||
fi
|
||||
|
||||
# Check if frontend folder changed (excluding README.md)
|
||||
if echo "$CHANGED_FILES" | grep "^sge-frontend/" | grep -qv "README.md$"; then
|
||||
FRONTEND_CHANGED=true
|
||||
fi
|
||||
|
||||
# -----------------------
|
||||
# Backend section
|
||||
# -----------------------
|
||||
if [ "$BACKEND_CHANGED" = true ]; then
|
||||
echo "⚡ Backend changes detected."
|
||||
|
||||
cd sge-backend
|
||||
echo "Running Maven build..."
|
||||
/opt/apache-maven-3.9.11/bin/mvn clean install -DskipTests
|
||||
|
||||
echo "🐳 Building backend Docker image..."
|
||||
docker build -t "${BACKEND_IMAGE_REPO}:${VERSION}" .
|
||||
|
||||
echo "📤 Pushing backend image to Harbor..."
|
||||
docker push "${BACKEND_IMAGE_REPO}:${VERSION}"
|
||||
|
||||
echo "📥 Pulling backend image from Harbor (to ensure registry is source of truth)..."
|
||||
docker pull "${BACKEND_IMAGE_REPO}:${VERSION}"
|
||||
|
||||
cd ..
|
||||
|
||||
echo "🚀 Recreating backend container using Harbor image..."
|
||||
VERSION="$VERSION" docker compose up -d bgreen-backend
|
||||
|
||||
else
|
||||
echo "✅ No backend changes."
|
||||
fi
|
||||
|
||||
# -----------------------
|
||||
# Frontend section
|
||||
# -----------------------
|
||||
if [ "$FRONTEND_CHANGED" = true ]; then
|
||||
echo "⚡ Frontend changes detected."
|
||||
cd sge-frontend
|
||||
|
||||
# Check if package.json or package-lock.json changed
|
||||
if echo "$CHANGED_FILES" | grep -qE "^sge-frontend/(package\.json|package-lock\.json)$"; then
|
||||
echo "📦 package.json changed. Running 'npm install' and 'npm run build'..."
|
||||
npm install
|
||||
npm run build
|
||||
else
|
||||
echo "📦 only code changes. Running 'npm run build'..."
|
||||
npm run build
|
||||
fi
|
||||
|
||||
echo "🐳 Building frontend Docker image..."
|
||||
docker build -t "${FRONTEND_IMAGE_REPO}:${VERSION}" .
|
||||
|
||||
echo "📤 Pushing frontend image to Harbor..."
|
||||
docker push "${FRONTEND_IMAGE_REPO}:${VERSION}"
|
||||
|
||||
echo "📥 Pulling frontend image from Harbor (to ensure registry is source of truth)..."
|
||||
docker pull "${FRONTEND_IMAGE_REPO}:${VERSION}"
|
||||
|
||||
cd ..
|
||||
|
||||
echo "🚀 Recreating frontend container using Harbor image..."
|
||||
VERSION="$VERSION" docker compose up -d bgreen-frontend
|
||||
|
||||
else
|
||||
echo "✅ No frontend changes."
|
||||
fi
|
||||
|
||||
echo "✅ Deployment complete."
|
||||
69
deploy.sh.save
Executable file
69
deploy.sh.save
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
export GIT_SSH_COMMAND="ssh -i ~/.ssh/deploy_id_rsa -o StrictHostKeyC
|
||||
|
||||
cd /home/ubuntu/Bgreen/sgeUpdated
|
||||
|
||||
echo "📦 Fetching latest changes from origin/main..."
|
||||
git fetch origin main
|
||||
|
||||
# Detect which files changed between local HEAD and the latest remote version
|
||||
CHANGED_FILES=$(git diff --name-only HEAD origin/main || true)
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "✅ No file changes detected between HEAD and origin/main."
|
||||
else
|
||||
echo "🪶 Changed files:"
|
||||
echo "$CHANGED_FILES"
|
||||
fi
|
||||
|
||||
# Update to the latest version
|
||||
git reset --hard origin/main
|
||||
|
||||
BACKEND_CHANGED=false
|
||||
FRONTEND_CHANGED=false
|
||||
|
||||
# Check if backend folder changed
|
||||
if echo "$CHANGED_FILES" | grep -q "^sge-backend/"; then
|
||||
BACKEND_CHANGED=true
|
||||
fi
|
||||
|
||||
# Check if frontend folder changed
|
||||
if echo "$CHANGED_FILES" | grep -q "^sge-frontend/"; then
|
||||
FRONTEND_CHANGED=true
|
||||
fi
|
||||
|
||||
# -----------------------
|
||||
# Backend section
|
||||
# -----------------------
|
||||
if [ "$BACKEND_CHANGED" = true ]; then
|
||||
echo "⚡ Backend changes detected."
|
||||
cd sge-backend
|
||||
echo "Running Maven build..."
|
||||
/opt/apache-maven-3.9.11/bin/mvn clean install -DskipTests
|
||||
cd ..
|
||||
echo "Rebuilding backend Docker container..."
|
||||
docker compose up -d --build bgreen-backend
|
||||
else
|
||||
echo "✅ No backend changes."
|
||||
fi
|
||||
|
||||
# -----------------------
|
||||
# Frontend section
|
||||
# -----------------------
|
||||
if [ "$FRONTEND_CHANGED" = true ]; then
|
||||
echo "⚡ Frontend changes detected."
|
||||
cd sge-frontend
|
||||
echo "Running npm build..."
|
||||
npm install
|
||||
npm run build
|
||||
cd ..
|
||||
echo "Rebuilding frontend Docker container..."
|
||||
docker compose up -d --build bgreen-frontend
|
||||
else
|
||||
echo "✅ No frontend changes."
|
||||
fi
|
||||
|
||||
echo "✅ Deployment complete."
|
||||
|
||||
35
docker-compose.yml
Normal file
35
docker-compose.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
services:
|
||||
bgreen-backend:
|
||||
image: 10.150.1.166/bgreen/bgreen-backend:${VERSION}
|
||||
ports:
|
||||
- "8080:8080"
|
||||
env_file:
|
||||
- ./config.conf
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: docker
|
||||
depends_on:
|
||||
- database
|
||||
restart: unless-stopped
|
||||
|
||||
bgreen-frontend:
|
||||
image: 10.150.1.166/bgreen/bgreen-frontend:${VERSION}
|
||||
ports:
|
||||
- "80:80"
|
||||
env_file:
|
||||
- ./config.conf
|
||||
restart: unless-stopped
|
||||
|
||||
database:
|
||||
image: postgres:15
|
||||
environment:
|
||||
POSTGRES_DB: sge
|
||||
POSTGRES_USER: sge
|
||||
POSTGRES_PASSWORD: 147
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
1
sge-backend/README.md
Normal file
1
sge-backend/README.md
Normal file
@@ -0,0 +1 @@
|
||||
## CI/CD Pipeline Check v23
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,12 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.5.5</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<groupId>com.sgs</groupId>
|
||||
<artifactId>sgs</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.4-SNAPSHOT</version>
|
||||
<name>sgs</name>
|
||||
<description>SGS project for Spring Boot</description>
|
||||
<properties>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
package com.sgs.graphql.dataCenter.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import com.sgs.graphql.area.domain.Area;
|
||||
import com.sgs.graphql.city.domain.City;
|
||||
import com.sgs.graphql.sector.domain.Sector;
|
||||
import com.sgs.graphql.subSector.domain.SubSector;
|
||||
import com.sgs.graphql.emissionSource.domain.EmissionSource;
|
||||
import com.sgs.graphql.emissionScope.domain.EmissionScope;
|
||||
import com.sgs.graphql.consuptionUnit.domain.ConsuptionUnit;
|
||||
import com.sgs.graphql.activitySubUnit.domain.ActivitySubUnit;
|
||||
import com.sgs.lib.dao.domain.BaseDomain;
|
||||
|
||||
@@ -14,7 +14,8 @@ import javax.persistence.*;
|
||||
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -26,28 +27,26 @@ public class DataCenter extends BaseDomain {
|
||||
|
||||
@Column(unique = true)
|
||||
private Integer externalId;
|
||||
|
||||
|
||||
private Integer number;
|
||||
private Area area;
|
||||
private City city;
|
||||
|
||||
private List<Project> projects = new ArrayList<>();
|
||||
private List<PhysicalMachine> physicalMachines = new ArrayList<>();
|
||||
private List<DataCenterEmissionSource> dataCenterEmissionSources = new ArrayList<>();
|
||||
private Sector sector;
|
||||
private SubSector subSector;
|
||||
private EmissionSource emissionSource;
|
||||
private EmissionScope emissionScope;
|
||||
private Double consuptionAmount;
|
||||
private ConsuptionUnit consuptionUnit;
|
||||
private ActivitySubUnit activitySubUnit;
|
||||
|
||||
|
||||
// New attributes
|
||||
private String ayposURL;
|
||||
private String address;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
|
||||
|
||||
@Column(name = "data_center_name")
|
||||
@Transactional
|
||||
public String getDataCenter() {
|
||||
return dataCenter;
|
||||
}
|
||||
@@ -63,12 +62,23 @@ public class DataCenter extends BaseDomain {
|
||||
|
||||
@OneToMany(mappedBy = "dataCenter", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||
@JsonManagedReference
|
||||
public List<Project> getProjects() {
|
||||
return projects;
|
||||
public List<PhysicalMachine> getPhysicalMachines() {
|
||||
return physicalMachines;
|
||||
}
|
||||
|
||||
public void setProjects(List<Project> projects) {
|
||||
this.projects = projects;
|
||||
public void setPhysicalMachines(List<PhysicalMachine> physicalMachines) {
|
||||
this.physicalMachines = physicalMachines;
|
||||
}
|
||||
|
||||
@OneToMany(mappedBy = "dataCenter", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
@JsonManagedReference
|
||||
public List<DataCenterEmissionSource> getDataCenterEmissionSources() {
|
||||
return dataCenterEmissionSources;
|
||||
}
|
||||
|
||||
public void setDataCenterEmissionSources(List<DataCenterEmissionSource> dataCenterEmissionSources) {
|
||||
this.dataCenterEmissionSources = dataCenterEmissionSources;
|
||||
}
|
||||
|
||||
public void setExternalId(Integer externalId) {
|
||||
@@ -84,7 +94,6 @@ public class DataCenter extends BaseDomain {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "sector_id")
|
||||
public Sector getSector() {
|
||||
@@ -114,16 +123,6 @@ public class DataCenter extends BaseDomain {
|
||||
this.consuptionAmount = consuptionAmount;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "consuption_unit_id")
|
||||
public ConsuptionUnit getConsuptionUnit() {
|
||||
return consuptionUnit;
|
||||
}
|
||||
|
||||
public void setConsuptionUnit(ConsuptionUnit consuptionUnit) {
|
||||
this.consuptionUnit = consuptionUnit;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "area_id")
|
||||
public Area getArea() {
|
||||
@@ -134,6 +133,16 @@ public class DataCenter extends BaseDomain {
|
||||
this.area = area;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "city_id")
|
||||
public City getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(City city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "sub_sector_id")
|
||||
public SubSector getSubSector() {
|
||||
@@ -144,16 +153,6 @@ public class DataCenter extends BaseDomain {
|
||||
this.subSector = subSector;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "emission_source_id")
|
||||
public EmissionSource getEmissionSource() {
|
||||
return emissionSource;
|
||||
}
|
||||
|
||||
public void setEmissionSource(EmissionSource emissionSource) {
|
||||
this.emissionSource = emissionSource;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "activity_sub_unit_id")
|
||||
public ActivitySubUnit getActivitySubUnit() {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.sgs.graphql.dataCenter.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import com.sgs.graphql.emissionSource.domain.EmissionSource;
|
||||
import com.sgs.graphql.consuptionUnit.domain.ConsuptionUnit;
|
||||
import com.sgs.lib.dao.domain.BaseDomain;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "data_center_emission_source")
|
||||
public class DataCenterEmissionSource extends BaseDomain {
|
||||
|
||||
private DataCenter dataCenter;
|
||||
private EmissionSource emissionSource;
|
||||
private ConsuptionUnit consuptionUnit;
|
||||
private Boolean isDefault = false; // To mark which one is the default emission source
|
||||
private Double percentage; // Percentage allocation for this emission source (optional)
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "data_center_id", nullable = false)
|
||||
@JsonBackReference
|
||||
public DataCenter getDataCenter() {
|
||||
return dataCenter;
|
||||
}
|
||||
|
||||
public void setDataCenter(DataCenter dataCenter) {
|
||||
this.dataCenter = dataCenter;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "emission_source_id", nullable = false)
|
||||
public EmissionSource getEmissionSource() {
|
||||
return emissionSource;
|
||||
}
|
||||
|
||||
public void setEmissionSource(EmissionSource emissionSource) {
|
||||
this.emissionSource = emissionSource;
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "consuption_unit_id", nullable = false)
|
||||
public ConsuptionUnit getConsuptionUnit() {
|
||||
return consuptionUnit;
|
||||
}
|
||||
|
||||
public void setConsuptionUnit(ConsuptionUnit consuptionUnit) {
|
||||
this.consuptionUnit = consuptionUnit;
|
||||
}
|
||||
|
||||
@Column(name = "is_default")
|
||||
public Boolean getIsDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
|
||||
public void setIsDefault(Boolean isDefault) {
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
@Column(name = "percentage")
|
||||
public Double getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
public void setPercentage(Double percentage) {
|
||||
this.percentage = percentage;
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,10 @@ public class PhysicalMachine extends BaseDomain {
|
||||
private String name;
|
||||
private String ip;
|
||||
private String tag;
|
||||
private String cloudSystem;
|
||||
private Double power;
|
||||
private List<VM> vms = new ArrayList<>();
|
||||
private Project project;
|
||||
private DataCenter dataCenter;
|
||||
|
||||
@Column(name = "name")
|
||||
public String getName() {
|
||||
@@ -45,6 +46,15 @@ public class PhysicalMachine extends BaseDomain {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@Column(name = "cloud_system")
|
||||
public String getCloudSystem() {
|
||||
return cloudSystem;
|
||||
}
|
||||
|
||||
public void setCloudSystem(String cloudSystem) {
|
||||
this.cloudSystem = cloudSystem;
|
||||
}
|
||||
|
||||
@Column(name = "power")
|
||||
public Double getPower() {
|
||||
return power;
|
||||
@@ -65,13 +75,13 @@ public class PhysicalMachine extends BaseDomain {
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "project_id")
|
||||
@JoinColumn(name = "data_center_id")
|
||||
@JsonBackReference
|
||||
public Project getProject() {
|
||||
return project;
|
||||
public DataCenter getDataCenter() {
|
||||
return dataCenter;
|
||||
}
|
||||
|
||||
public void setProject(Project project) {
|
||||
this.project = project;
|
||||
public void setDataCenter(DataCenter dataCenter) {
|
||||
this.dataCenter = dataCenter;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.sgs.graphql.dataCenter.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonBackReference;
|
||||
import com.fasterxml.jackson.annotation.JsonManagedReference;
|
||||
import com.sgs.lib.dao.domain.BaseDomain;
|
||||
|
||||
@Entity
|
||||
@Table(name = "project")
|
||||
public class Project extends BaseDomain{
|
||||
|
||||
private String name;
|
||||
|
||||
private List<PhysicalMachine> physicalMachines;
|
||||
|
||||
|
||||
@OneToMany(mappedBy = "project", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JsonManagedReference
|
||||
public List<PhysicalMachine> getPhysicalMachines() {
|
||||
return physicalMachines;
|
||||
}
|
||||
|
||||
public void setPhysicalMachines(List<PhysicalMachine> physicalMachines) {
|
||||
this.physicalMachines = physicalMachines;
|
||||
}
|
||||
|
||||
// This creates a foreign key in the `project` table
|
||||
private DataCenter dataCenter;
|
||||
|
||||
|
||||
@Column(name = "name")
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "data_center_id")
|
||||
@JsonBackReference
|
||||
public DataCenter getDataCenter() {
|
||||
return dataCenter;
|
||||
}
|
||||
public void setDataCenter(DataCenter dataCenter) {
|
||||
this.dataCenter = dataCenter;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class VM extends BaseDomain {
|
||||
private String host;
|
||||
private String flavorName;
|
||||
private String tag;
|
||||
private String project; // UUID of the project this VM belongs to
|
||||
private String emissionSourceData; // JSON string to store emission source map
|
||||
private Config config;
|
||||
private PhysicalMachine physicalMachine;
|
||||
@@ -100,6 +101,15 @@ public class VM extends BaseDomain {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@Column(name = "project")
|
||||
public String getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
public void setProject(String project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
@Column(name = "ip")
|
||||
public String getIp() {
|
||||
return ip;
|
||||
|
||||
@@ -17,8 +17,10 @@ public class DataCenterDto {
|
||||
private Integer externalId;
|
||||
private Integer number;
|
||||
private AreaDto area;
|
||||
private Map<String, ProjectDto> projects;
|
||||
|
||||
|
||||
@JsonProperty("physical_machines")
|
||||
private Map<String, PhysicalMachineDto> physicalMachine;
|
||||
|
||||
// Emission calculation fields
|
||||
private SectorDto sector;
|
||||
private SubSectorDto subSector;
|
||||
@@ -60,20 +62,20 @@ public class DataCenterDto {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public AreaDto getArea() {
|
||||
return area;
|
||||
}
|
||||
|
||||
public void setArea(AreaDto area) {
|
||||
this.area = area;
|
||||
public AreaDto getArea() {
|
||||
return area;
|
||||
}
|
||||
|
||||
public Map<String, ProjectDto> getProjects() {
|
||||
return projects;
|
||||
public void setArea(AreaDto area) {
|
||||
this.area = area;
|
||||
}
|
||||
|
||||
public void setProjects(Map<String, ProjectDto> projects) {
|
||||
this.projects = projects;
|
||||
public Map<String, PhysicalMachineDto> getPhysicalMachine() {
|
||||
return physicalMachine;
|
||||
}
|
||||
|
||||
public void setPhysicalMachine(Map<String, PhysicalMachineDto> physicalMachine) {
|
||||
this.physicalMachine = physicalMachine;
|
||||
}
|
||||
|
||||
public SectorDto getSector() {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.sgs.graphql.dataCenter.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class DataCenterEmissionSourceDto {
|
||||
|
||||
@JsonProperty("emission_source_id")
|
||||
private String emissionSourceId;
|
||||
|
||||
@JsonProperty("consuption_unit_id")
|
||||
private String consuptionUnitId;
|
||||
|
||||
@JsonProperty("is_default")
|
||||
private Boolean isDefault = false;
|
||||
|
||||
@JsonProperty("percentage")
|
||||
private Double percentage;
|
||||
|
||||
public String getEmissionSourceId() {
|
||||
return emissionSourceId;
|
||||
}
|
||||
|
||||
public void setEmissionSourceId(String emissionSourceId) {
|
||||
this.emissionSourceId = emissionSourceId;
|
||||
}
|
||||
|
||||
public String getConsuptionUnitId() {
|
||||
return consuptionUnitId;
|
||||
}
|
||||
|
||||
public void setConsuptionUnitId(String consuptionUnitId) {
|
||||
this.consuptionUnitId = consuptionUnitId;
|
||||
}
|
||||
|
||||
public Boolean getIsDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
|
||||
public void setIsDefault(Boolean isDefault) {
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
public Double getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
public void setPercentage(Double percentage) {
|
||||
this.percentage = percentage;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.sgs.graphql.dataCenter.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MainOptimizationSpaceDto {
|
||||
private Map<String, VMDto> vms;
|
||||
private Map<String, PhysicalMachineDto> pms;
|
||||
|
||||
public Map<String, VMDto> getVms() {
|
||||
return vms;
|
||||
}
|
||||
|
||||
public void setVms(Map<String, VMDto> vms) {
|
||||
this.vms = vms;
|
||||
}
|
||||
|
||||
public Map<String, PhysicalMachineDto> getPms() {
|
||||
return pms;
|
||||
}
|
||||
|
||||
public void setPms(Map<String, PhysicalMachineDto> pms) {
|
||||
this.pms = pms;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.sgs.graphql.dataCenter.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PhysicalMachineDto {
|
||||
@JsonProperty("name")
|
||||
@@ -10,11 +11,17 @@ public class PhysicalMachineDto {
|
||||
@JsonProperty("tag")
|
||||
private String tag;
|
||||
|
||||
@JsonProperty("cloud_system")
|
||||
private String cloudSystem;
|
||||
|
||||
@JsonProperty("power")
|
||||
private double power;
|
||||
|
||||
@JsonProperty("confg") // Note: keeping the typo from JSON
|
||||
private List<Object> confg;
|
||||
|
||||
@JsonProperty("vms")
|
||||
private Map<String, VMDto> vms;
|
||||
|
||||
private String ip; // IP address from the message key
|
||||
|
||||
@@ -34,6 +41,14 @@ public class PhysicalMachineDto {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String getCloudSystem() {
|
||||
return cloudSystem;
|
||||
}
|
||||
|
||||
public void setCloudSystem(String cloudSystem) {
|
||||
this.cloudSystem = cloudSystem;
|
||||
}
|
||||
|
||||
public double getPower() {
|
||||
return power;
|
||||
}
|
||||
@@ -57,4 +72,12 @@ public class PhysicalMachineDto {
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public Map<String, VMDto> getVms() {
|
||||
return vms;
|
||||
}
|
||||
|
||||
public void setVms(Map<String, VMDto> vms) {
|
||||
this.vms = vms;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.sgs.graphql.dataCenter.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class ProjectDto {
|
||||
@JsonProperty("main_optimization_space")
|
||||
private MainOptimizationSpaceDto mainOptimizationSpace;
|
||||
|
||||
@JsonProperty("sub_optimization_space")
|
||||
private SubOptimizationSpaceDto subOptimizationSpace;
|
||||
|
||||
// Legacy field - kept for backward compatibility with older message formats
|
||||
@JsonProperty("physical_machines")
|
||||
private List<PhysicalMachineDto> physicalMachines;
|
||||
|
||||
// These fields are derived from the map key and entity relationships, not from the message
|
||||
private String id; // This will be set from the map key
|
||||
private String name; // This will be derived or set separately
|
||||
|
||||
public MainOptimizationSpaceDto getMainOptimizationSpace() {
|
||||
return mainOptimizationSpace;
|
||||
}
|
||||
|
||||
public void setMainOptimizationSpace(MainOptimizationSpaceDto mainOptimizationSpace) {
|
||||
this.mainOptimizationSpace = mainOptimizationSpace;
|
||||
}
|
||||
|
||||
public SubOptimizationSpaceDto getSubOptimizationSpace() {
|
||||
return subOptimizationSpace;
|
||||
}
|
||||
|
||||
public void setSubOptimizationSpace(SubOptimizationSpaceDto subOptimizationSpace) {
|
||||
this.subOptimizationSpace = subOptimizationSpace;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public List<PhysicalMachineDto> getPhysicalMachines() {
|
||||
return physicalMachines;
|
||||
}
|
||||
public void setPhysicalMachines(List<PhysicalMachineDto> physicalMachines) {
|
||||
this.physicalMachines = physicalMachines;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.sgs.graphql.dataCenter.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
* DTO for sub_optimization_space in the message format.
|
||||
* Currently appears to be empty in the message structure.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SubOptimizationSpaceDto {
|
||||
// Currently empty based on the message format
|
||||
// Can be extended if needed in the future
|
||||
|
||||
// Add a placeholder field to make Jackson happy
|
||||
private boolean isEmpty = true;
|
||||
|
||||
public boolean isEmpty() {
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
public void setEmpty(boolean empty) {
|
||||
this.isEmpty = empty;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,9 @@ public class VMDto {
|
||||
@JsonProperty("tag")
|
||||
private String tag;
|
||||
|
||||
@JsonProperty("project")
|
||||
private String project;
|
||||
|
||||
@JsonProperty("confg") // Note: keeping the typo from JSON
|
||||
private List<Object> confg;
|
||||
|
||||
@@ -101,6 +104,14 @@ public class VMDto {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
public void setProject(String project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public List<Object> getConfg() {
|
||||
return confg;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package com.sgs.graphql.dataCenter.mutation;
|
||||
|
||||
import com.sgs.exception.GraphQLCustomException;
|
||||
import com.sgs.graphql.dataCenter.mutation.mapper.DataCenterMapper;
|
||||
import com.sgs.graphql.dataCenter.service.DataCenterService;
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenter;
|
||||
import com.sgs.graphql.dataCenter.mutation.input.DataCenterCreateInput;
|
||||
import com.sgs.graphql.dataCenter.mutation.input.DataCenterUpdateInput;
|
||||
import com.sgs.graphql.permission.domain.PermissionName;
|
||||
import com.sgs.graphql.systemHistory.enums.LogType;
|
||||
import com.sgs.graphql.systemHistory.mutation.SystemLogger;
|
||||
import com.sgs.graphql.userHistory.mutation.UserLogger;
|
||||
import com.sgs.graphql.userNotification.mutation.UserNotificationMutation;
|
||||
import graphql.kickstart.tools.GraphQLMutationResolver;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
import javax.validation.Valid;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.UUID;
|
||||
|
||||
@Validated
|
||||
@@ -21,57 +29,128 @@ public class DataCenterMutation implements GraphQLMutationResolver {
|
||||
|
||||
private final DataCenterService dataCenterService;
|
||||
private final DataCenterMapper dataCenterMapper;
|
||||
private final SystemLogger systemLogger;
|
||||
private final UserLogger userLogger;
|
||||
private final UserNotificationMutation notificationMutation;
|
||||
|
||||
@Autowired
|
||||
public DataCenterMutation(DataCenterService dataCenterService, DataCenterMapper dataCenterMapper) {
|
||||
public DataCenterMutation(DataCenterService dataCenterService, DataCenterMapper dataCenterMapper,
|
||||
SystemLogger systemLogger, UserLogger userLogger,
|
||||
UserNotificationMutation notificationMutation) {
|
||||
this.dataCenterService = dataCenterService;
|
||||
this.dataCenterMapper = dataCenterMapper;
|
||||
this.systemLogger = systemLogger;
|
||||
this.userLogger = userLogger;
|
||||
this.notificationMutation = notificationMutation;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_CREATE + "')")
|
||||
public DataCenter createDataCenter(@Valid DataCenterCreateInput input) {
|
||||
// Check for duplicate externalId
|
||||
if (input.getExternalId() != null && dataCenterService.existsByExternalId(input.getExternalId())) {
|
||||
throw new RuntimeException("This external id already exists: " + input.getExternalId());
|
||||
@Transactional
|
||||
public DataCenter createDataCenter(@Valid DataCenterCreateInput input, DataFetchingEnvironment environment) {
|
||||
try {
|
||||
// Check for duplicate externalId
|
||||
if (input.getExternalId() != null && dataCenterService.existsByExternalId(input.getExternalId())) {
|
||||
throw new GraphQLCustomException("2010", input.getExternalId().toString());
|
||||
}
|
||||
|
||||
DataCenter dataCenter = dataCenterMapper.toEntity(input);
|
||||
|
||||
// Set auto-generated number if not provided
|
||||
if (dataCenter.getNumber() == null) {
|
||||
Integer maxNumber = dataCenterService.findMaxNumber();
|
||||
dataCenter.setNumber((maxNumber == null) ? 1 : maxNumber + 1);
|
||||
}
|
||||
|
||||
DataCenter savedDataCenter = dataCenterService.save(dataCenter);
|
||||
|
||||
// Log and notify
|
||||
String dataCenterName = savedDataCenter.getDataCenter() != null ? savedDataCenter.getDataCenter()
|
||||
: "DataCenter #" + savedDataCenter.getNumber();
|
||||
systemLogger.createSystemLog(LogType.INFO, dataCenterName + " adlı veri merkezi oluşturuldu");
|
||||
userLogger.createUserLog(LogType.INFO, dataCenterName + " adlı veri merkezi oluşturuldu", environment);
|
||||
notificationMutation.createNotification(environment, "Veri Merkezi",
|
||||
dataCenterName + " adlı veri merkezi oluşturuldu", "Yeni kayıt");
|
||||
|
||||
return savedDataCenter;
|
||||
} catch (GraphQLCustomException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
systemLogger.createSystemLog(LogType.ERROR,
|
||||
"Yeni veri merkezi oluşturulurken hata oluştu: " + e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
|
||||
DataCenter dataCenter = dataCenterMapper.toEntity(input);
|
||||
|
||||
// Set auto-generated number if not provided
|
||||
if (dataCenter.getNumber() == null) {
|
||||
Integer maxNumber = dataCenterService.getRepository().findMaxNumber();
|
||||
dataCenter.setNumber((maxNumber == null) ? 1 : maxNumber + 1);
|
||||
}
|
||||
|
||||
return dataCenterService.save(dataCenter);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_UPDATE + "')")
|
||||
public DataCenter updateDataCenter(UUID id, @Valid DataCenterUpdateInput input) {
|
||||
return dataCenterService.findById(id)
|
||||
.map(dataCenter -> {
|
||||
// Check for duplicate externalId if it's being updated
|
||||
if (input.getExternalId() != null &&
|
||||
!input.getExternalId().equals(dataCenter.getExternalId()) &&
|
||||
dataCenterService.existsByExternalId(input.getExternalId())) {
|
||||
throw new RuntimeException("This external id already exists: " + input.getExternalId());
|
||||
}
|
||||
return dataCenterService.update(dataCenterMapper.updateEntity(dataCenter, input));
|
||||
})
|
||||
.orElseThrow(() -> new RuntimeException("Data center not found with ID: " + id));
|
||||
@Transactional
|
||||
public DataCenter updateDataCenter(UUID id, @Valid DataCenterUpdateInput input,
|
||||
DataFetchingEnvironment environment) {
|
||||
try {
|
||||
DataCenter dataCenter = dataCenterService.findById(id)
|
||||
.orElseThrow(() -> new NoSuchElementException("Data center not found with ID: " + id));
|
||||
|
||||
// Check for duplicate externalId if it's being updated
|
||||
if (input.getExternalId() != null &&
|
||||
!input.getExternalId().equals(dataCenter.getExternalId()) &&
|
||||
dataCenterService.existsByExternalId(input.getExternalId())) {
|
||||
throw new GraphQLCustomException("2010", input.getExternalId().toString());
|
||||
}
|
||||
|
||||
DataCenter updatedDataCenter = dataCenterService.update(dataCenterMapper.updateEntity(dataCenter, input));
|
||||
|
||||
// Log and notify
|
||||
String dataCenterName = updatedDataCenter.getDataCenter() != null ? updatedDataCenter.getDataCenter()
|
||||
: "DataCenter #" + updatedDataCenter.getNumber();
|
||||
systemLogger.createSystemLog(LogType.INFO, dataCenterName + " adlı veri merkezi güncellendi");
|
||||
userLogger.createUserLog(LogType.INFO, dataCenterName + " adlı veri merkezi güncellendi", environment);
|
||||
notificationMutation.createNotification(environment, "Veri Merkezi",
|
||||
dataCenterName + " adlı veri merkezi güncellendi", "Güncelleme");
|
||||
|
||||
return updatedDataCenter;
|
||||
} catch (GraphQLCustomException e) {
|
||||
throw e;
|
||||
} catch (NoSuchElementException e) {
|
||||
systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi bulunamadı: " + id);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi güncellenirken hata oluştu: " + e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_DELETE + "')")
|
||||
public Boolean deleteDataCenter(UUID id) {
|
||||
@Transactional
|
||||
public String deleteDataCenter(UUID id, DataFetchingEnvironment environment) {
|
||||
String dataCenterName = "";
|
||||
try {
|
||||
DataCenter dataCenter = dataCenterService.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Data center not found with ID: " + id));
|
||||
|
||||
.orElseThrow(() -> new NoSuchElementException("Data center not found with ID: " + id));
|
||||
|
||||
dataCenterName = dataCenter.getDataCenter() != null ? dataCenter.getDataCenter()
|
||||
: "DataCenter #" + dataCenter.getNumber();
|
||||
|
||||
// Check if data center has physical machines or other dependencies
|
||||
if (dataCenter.getPhysicalMachines() != null && !dataCenter.getPhysicalMachines().isEmpty()) {
|
||||
String message = "Silinmeye çalışılan veri merkezinin fiziksel makineleri olduğu için silinememektedir";
|
||||
systemLogger.createSystemLog(LogType.WARN, dataCenterName + ": " + message);
|
||||
return message;
|
||||
}
|
||||
|
||||
dataCenterService.delete(dataCenter);
|
||||
return true;
|
||||
|
||||
// Log and notify
|
||||
systemLogger.createSystemLog(LogType.INFO, dataCenterName + " adlı veri merkezi silindi");
|
||||
userLogger.createUserLog(LogType.INFO, dataCenterName + " adlı veri merkezi silindi", environment);
|
||||
notificationMutation.createNotification(environment, "Veri Merkezi",
|
||||
dataCenterName + " adlı veri merkezi silindi", "Silme");
|
||||
|
||||
return "true";
|
||||
} catch (NoSuchElementException e) {
|
||||
systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi bulunamadı: " + id);
|
||||
return "false";
|
||||
} catch (Exception e) {
|
||||
System.out.println("DataCenter deletion error: " + e.getMessage());
|
||||
return false;
|
||||
systemLogger.createSystemLog(LogType.ERROR, "Veri merkezi silinirken hata oluştu: " + e.getMessage());
|
||||
return "false";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Min;
|
||||
import java.util.UUID;
|
||||
import java.util.List;
|
||||
|
||||
public class DataCenterCreateInput extends BaseCreateInput {
|
||||
@NotBlank(message = "Data center adı gereklidir")
|
||||
@@ -17,15 +18,18 @@ public class DataCenterCreateInput extends BaseCreateInput {
|
||||
|
||||
private UUID areaId;
|
||||
|
||||
private UUID cityId;
|
||||
|
||||
@NotNull(message = "Sektör ID gereklidir")
|
||||
private UUID sectorId;
|
||||
|
||||
private UUID subSectorId;
|
||||
private UUID emissionSourceId;
|
||||
private UUID emissionScopeId;
|
||||
private UUID consuptionUnitId;
|
||||
private UUID activitySubUnitId;
|
||||
|
||||
// Multiple emission sources support - each with exactly one unit
|
||||
private List<DataCenterEmissionSourceInput> dataCenterEmissionSources;
|
||||
|
||||
// New attributes
|
||||
private String ayposURL;
|
||||
private String address;
|
||||
@@ -48,24 +52,24 @@ public class DataCenterCreateInput extends BaseCreateInput {
|
||||
public UUID getAreaId() { return areaId; }
|
||||
public void setAreaId(UUID areaId) { this.areaId = areaId; }
|
||||
|
||||
public UUID getCityId() { return cityId; }
|
||||
public void setCityId(UUID cityId) { this.cityId = cityId; }
|
||||
|
||||
public UUID getSectorId() { return sectorId; }
|
||||
public void setSectorId(UUID sectorId) { this.sectorId = sectorId; }
|
||||
|
||||
public UUID getSubSectorId() { return subSectorId; }
|
||||
public void setSubSectorId(UUID subSectorId) { this.subSectorId = subSectorId; }
|
||||
|
||||
public UUID getEmissionSourceId() { return emissionSourceId; }
|
||||
public void setEmissionSourceId(UUID emissionSourceId) { this.emissionSourceId = emissionSourceId; }
|
||||
|
||||
public UUID getEmissionScopeId() { return emissionScopeId; }
|
||||
public void setEmissionScopeId(UUID emissionScopeId) { this.emissionScopeId = emissionScopeId; }
|
||||
|
||||
public UUID getConsuptionUnitId() { return consuptionUnitId; }
|
||||
public void setConsuptionUnitId(UUID consuptionUnitId) { this.consuptionUnitId = consuptionUnitId; }
|
||||
|
||||
public UUID getActivitySubUnitId() { return activitySubUnitId; }
|
||||
public void setActivitySubUnitId(UUID activitySubUnitId) { this.activitySubUnitId = activitySubUnitId; }
|
||||
|
||||
public List<DataCenterEmissionSourceInput> getDataCenterEmissionSources() { return dataCenterEmissionSources; }
|
||||
public void setDataCenterEmissionSources(List<DataCenterEmissionSourceInput> dataCenterEmissionSources) { this.dataCenterEmissionSources = dataCenterEmissionSources; }
|
||||
|
||||
// New attribute getters and setters
|
||||
public String getAyposURL() { return ayposURL; }
|
||||
public void setAyposURL(String ayposURL) { this.ayposURL = ayposURL; }
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.sgs.graphql.dataCenter.mutation.input;
|
||||
|
||||
public class DataCenterEmissionSourceInput {
|
||||
private String emissionSourceId;
|
||||
private String consuptionUnitId;
|
||||
private Boolean isDefault = false;
|
||||
private Double percentage;
|
||||
|
||||
public String getEmissionSourceId() {
|
||||
return emissionSourceId;
|
||||
}
|
||||
|
||||
public void setEmissionSourceId(String emissionSourceId) {
|
||||
this.emissionSourceId = emissionSourceId;
|
||||
}
|
||||
|
||||
public String getConsuptionUnitId() {
|
||||
return consuptionUnitId;
|
||||
}
|
||||
|
||||
public void setConsuptionUnitId(String consuptionUnitId) {
|
||||
this.consuptionUnitId = consuptionUnitId;
|
||||
}
|
||||
|
||||
public Boolean getIsDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
|
||||
public void setIsDefault(Boolean isDefault) {
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
public Double getPercentage() {
|
||||
return percentage;
|
||||
}
|
||||
|
||||
public void setPercentage(Double percentage) {
|
||||
this.percentage = percentage;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.sgs.graphql.dataCenter.mutation.input;
|
||||
|
||||
import com.sgs.lib.dao.mutation.input.BaseUpdateInput;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class DataCenterUpdateInput extends BaseUpdateInput {
|
||||
private String dataCenter;
|
||||
private Integer externalId;
|
||||
@@ -11,14 +13,16 @@ public class DataCenterUpdateInput extends BaseUpdateInput {
|
||||
private Double consuptionAmount;
|
||||
|
||||
private UUID areaId;
|
||||
private UUID cityId;
|
||||
@NotNull(message = "Sektör ID gereklidir")
|
||||
private UUID sectorId;
|
||||
private UUID subSectorId;
|
||||
private UUID emissionSourceId;
|
||||
private UUID emissionScopeId;
|
||||
private UUID consuptionUnitId;
|
||||
private UUID activitySubUnitId;
|
||||
|
||||
// Multiple emission sources with units
|
||||
private List<DataCenterEmissionSourceInput> dataCenterEmissionSources;
|
||||
|
||||
// New attributes
|
||||
private String ayposURL;
|
||||
private String address;
|
||||
@@ -41,24 +45,28 @@ public class DataCenterUpdateInput extends BaseUpdateInput {
|
||||
public UUID getAreaId() { return areaId; }
|
||||
public void setAreaId(UUID areaId) { this.areaId = areaId; }
|
||||
|
||||
public UUID getCityId() { return cityId; }
|
||||
public void setCityId(UUID cityId) { this.cityId = cityId; }
|
||||
|
||||
public UUID getSectorId() { return sectorId; }
|
||||
public void setSectorId(UUID sectorId) { this.sectorId = sectorId; }
|
||||
|
||||
public UUID getSubSectorId() { return subSectorId; }
|
||||
public void setSubSectorId(UUID subSectorId) { this.subSectorId = subSectorId; }
|
||||
|
||||
public UUID getEmissionSourceId() { return emissionSourceId; }
|
||||
public void setEmissionSourceId(UUID emissionSourceId) { this.emissionSourceId = emissionSourceId; }
|
||||
|
||||
public UUID getEmissionScopeId() { return emissionScopeId; }
|
||||
public void setEmissionScopeId(UUID emissionScopeId) { this.emissionScopeId = emissionScopeId; }
|
||||
|
||||
public UUID getConsuptionUnitId() { return consuptionUnitId; }
|
||||
public void setConsuptionUnitId(UUID consuptionUnitId) { this.consuptionUnitId = consuptionUnitId; }
|
||||
|
||||
public UUID getActivitySubUnitId() { return activitySubUnitId; }
|
||||
public void setActivitySubUnitId(UUID activitySubUnitId) { this.activitySubUnitId = activitySubUnitId; }
|
||||
|
||||
public List<DataCenterEmissionSourceInput> getDataCenterEmissionSources() {
|
||||
return dataCenterEmissionSources;
|
||||
}
|
||||
public void setDataCenterEmissionSources(List<DataCenterEmissionSourceInput> dataCenterEmissionSources) {
|
||||
this.dataCenterEmissionSources = dataCenterEmissionSources;
|
||||
}
|
||||
|
||||
// New attribute getters and setters
|
||||
public String getAyposURL() { return ayposURL; }
|
||||
public void setAyposURL(String ayposURL) { this.ayposURL = ayposURL; }
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.sgs.graphql.dataCenter.mutation.mapper;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenter;
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource;
|
||||
import com.sgs.graphql.dataCenter.mutation.input.DataCenterCreateInput;
|
||||
import com.sgs.graphql.dataCenter.mutation.input.DataCenterUpdateInput;
|
||||
import com.sgs.graphql.dataCenter.mutation.input.DataCenterEmissionSourceInput;
|
||||
import com.sgs.graphql.area.service.AreaService;
|
||||
import com.sgs.graphql.sector.service.SectorService;
|
||||
import com.sgs.graphql.subSector.service.SubSectorService;
|
||||
@@ -10,9 +12,13 @@ import com.sgs.graphql.emissionSource.service.EmissionSourceService;
|
||||
import com.sgs.graphql.emissionScope.service.EmissionScopeService;
|
||||
import com.sgs.graphql.consuptionUnit.service.ConsuptionUnitService;
|
||||
import com.sgs.graphql.activitySubUnit.service.ActivitySubUnitService;
|
||||
import com.sgs.graphql.city.service.CityService;
|
||||
import com.sgs.lib.dao.mutation.mapper.BaseCreateUpdateMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class DataCenterMapper extends BaseCreateUpdateMapper<DataCenter, DataCenterCreateInput, DataCenterUpdateInput> {
|
||||
@@ -24,12 +30,13 @@ public class DataCenterMapper extends BaseCreateUpdateMapper<DataCenter, DataCen
|
||||
private final EmissionScopeService emissionScopeService;
|
||||
private final ConsuptionUnitService consuptionUnitService;
|
||||
private final ActivitySubUnitService activitySubUnitService;
|
||||
private final CityService cityService;
|
||||
|
||||
@Autowired
|
||||
public DataCenterMapper(AreaService areaService, SectorService sectorService,
|
||||
SubSectorService subSectorService, EmissionSourceService emissionSourceService,
|
||||
EmissionScopeService emissionScopeService, ConsuptionUnitService consuptionUnitService,
|
||||
ActivitySubUnitService activitySubUnitService) {
|
||||
ActivitySubUnitService activitySubUnitService, CityService cityService) {
|
||||
this.areaService = areaService;
|
||||
this.sectorService = sectorService;
|
||||
this.subSectorService = subSectorService;
|
||||
@@ -37,53 +44,80 @@ public class DataCenterMapper extends BaseCreateUpdateMapper<DataCenter, DataCen
|
||||
this.emissionScopeService = emissionScopeService;
|
||||
this.consuptionUnitService = consuptionUnitService;
|
||||
this.activitySubUnitService = activitySubUnitService;
|
||||
this.cityService = cityService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataCenter toEntity(DataCenterCreateInput input) {
|
||||
DataCenter entity = new DataCenter();
|
||||
|
||||
|
||||
// Basic fields
|
||||
entity.setDataCenter(input.getDataCenter());
|
||||
entity.setExternalId(input.getExternalId());
|
||||
entity.setNumber(input.getNumber());
|
||||
entity.setConsuptionAmount(input.getConsuptionAmount());
|
||||
|
||||
|
||||
// Convert ID references to entities
|
||||
if (input.getAreaId() != null) {
|
||||
entity.setArea(areaService.findById(input.getAreaId()).orElse(null));
|
||||
}
|
||||
if (input.getCityId() != null) {
|
||||
entity.setCity(cityService.findById(input.getCityId()).orElse(null));
|
||||
}
|
||||
|
||||
if (input.getSectorId() != null) {
|
||||
entity.setSector(sectorService.findById(input.getSectorId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
if (input.getSubSectorId() != null) {
|
||||
entity.setSubSector(subSectorService.findById(input.getSubSectorId()).orElse(null));
|
||||
}
|
||||
|
||||
if (input.getEmissionSourceId() != null) {
|
||||
entity.setEmissionSource(emissionSourceService.findById(input.getEmissionSourceId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
if (input.getEmissionScopeId() != null) {
|
||||
entity.setEmissionScope(emissionScopeService.findById(input.getEmissionScopeId()).orElse(null));
|
||||
}
|
||||
|
||||
if (input.getConsuptionUnitId() != null) {
|
||||
entity.setConsuptionUnit(consuptionUnitService.findById(input.getConsuptionUnitId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
if (input.getActivitySubUnitId() != null) {
|
||||
entity.setActivitySubUnit(activitySubUnitService.findById(input.getActivitySubUnitId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
// New attributes
|
||||
entity.setAyposURL(input.getAyposURL());
|
||||
entity.setAddress(input.getAddress());
|
||||
entity.setLatitude(input.getLatitude());
|
||||
entity.setLongitude(input.getLongitude());
|
||||
|
||||
|
||||
// Handle multiple emission sources if provided
|
||||
if (input.getDataCenterEmissionSources() != null && !input.getDataCenterEmissionSources().isEmpty()) {
|
||||
List<DataCenterEmissionSource> dataCenterEmissionSources = new ArrayList<>();
|
||||
|
||||
for (DataCenterEmissionSourceInput emissionSourceInput : input.getDataCenterEmissionSources()) {
|
||||
DataCenterEmissionSource dcEmissionSource = new DataCenterEmissionSource();
|
||||
dcEmissionSource.setDataCenter(entity);
|
||||
|
||||
// Set EmissionSource
|
||||
if (emissionSourceInput.getEmissionSourceId() != null) {
|
||||
dcEmissionSource.setEmissionSource(emissionSourceService
|
||||
.findById(UUID.fromString(emissionSourceInput.getEmissionSourceId())).orElse(null));
|
||||
}
|
||||
|
||||
// Set ConsuptionUnit
|
||||
if (emissionSourceInput.getConsuptionUnitId() != null) {
|
||||
dcEmissionSource.setConsuptionUnit(consuptionUnitService
|
||||
.findById(UUID.fromString(emissionSourceInput.getConsuptionUnitId())).orElse(null));
|
||||
}
|
||||
|
||||
// Set optional fields
|
||||
dcEmissionSource.setIsDefault(
|
||||
emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false);
|
||||
dcEmissionSource.setPercentage(emissionSourceInput.getPercentage());
|
||||
|
||||
dataCenterEmissionSources.add(dcEmissionSource);
|
||||
}
|
||||
|
||||
entity.setDataCenterEmissionSources(dataCenterEmissionSources);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -93,65 +127,89 @@ public class DataCenterMapper extends BaseCreateUpdateMapper<DataCenter, DataCen
|
||||
if (input.getDataCenter() != null) {
|
||||
entity.setDataCenter(input.getDataCenter());
|
||||
}
|
||||
|
||||
|
||||
if (input.getExternalId() != null) {
|
||||
entity.setExternalId(input.getExternalId());
|
||||
}
|
||||
|
||||
|
||||
if (input.getNumber() != null) {
|
||||
entity.setNumber(input.getNumber());
|
||||
}
|
||||
|
||||
|
||||
if (input.getConsuptionAmount() != null) {
|
||||
entity.setConsuptionAmount(input.getConsuptionAmount());
|
||||
}
|
||||
|
||||
|
||||
// Update relationships only if provided
|
||||
if (input.getAreaId() != null) {
|
||||
entity.setArea(areaService.findById(input.getAreaId()).orElse(null));
|
||||
}
|
||||
if (input.getCityId() != null) {
|
||||
entity.setCity(cityService.findById(input.getCityId()).orElse(null));
|
||||
}
|
||||
|
||||
if (input.getSectorId() != null) {
|
||||
entity.setSector(sectorService.findById(input.getSectorId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
if (input.getSubSectorId() != null) {
|
||||
entity.setSubSector(subSectorService.findById(input.getSubSectorId()).orElse(null));
|
||||
}
|
||||
|
||||
if (input.getEmissionSourceId() != null) {
|
||||
entity.setEmissionSource(emissionSourceService.findById(input.getEmissionSourceId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
if (input.getEmissionScopeId() != null) {
|
||||
entity.setEmissionScope(emissionScopeService.findById(input.getEmissionScopeId()).orElse(null));
|
||||
}
|
||||
|
||||
if (input.getConsuptionUnitId() != null) {
|
||||
entity.setConsuptionUnit(consuptionUnitService.findById(input.getConsuptionUnitId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
if (input.getActivitySubUnitId() != null) {
|
||||
entity.setActivitySubUnit(activitySubUnitService.findById(input.getActivitySubUnitId()).orElse(null));
|
||||
}
|
||||
|
||||
|
||||
// Handle multiple emission sources update if provided
|
||||
if (input.getDataCenterEmissionSources() != null) {
|
||||
// Clear existing emission sources from the managed collection
|
||||
entity.getDataCenterEmissionSources().clear();
|
||||
|
||||
// Add new emission sources to the same managed collection
|
||||
for (DataCenterEmissionSourceInput emissionSourceInput : input.getDataCenterEmissionSources()) {
|
||||
DataCenterEmissionSource dcEmissionSource = new DataCenterEmissionSource();
|
||||
dcEmissionSource.setDataCenter(entity);
|
||||
|
||||
if (emissionSourceInput.getEmissionSourceId() != null) {
|
||||
dcEmissionSource.setEmissionSource(emissionSourceService
|
||||
.findById(UUID.fromString(emissionSourceInput.getEmissionSourceId())).orElse(null));
|
||||
}
|
||||
|
||||
if (emissionSourceInput.getConsuptionUnitId() != null) {
|
||||
dcEmissionSource.setConsuptionUnit(consuptionUnitService
|
||||
.findById(UUID.fromString(emissionSourceInput.getConsuptionUnitId())).orElse(null));
|
||||
}
|
||||
|
||||
dcEmissionSource.setIsDefault(
|
||||
emissionSourceInput.getIsDefault() != null ? emissionSourceInput.getIsDefault() : false);
|
||||
dcEmissionSource.setPercentage(emissionSourceInput.getPercentage());
|
||||
|
||||
// Add to the existing managed collection instead of creating a new one
|
||||
entity.getDataCenterEmissionSources().add(dcEmissionSource);
|
||||
}
|
||||
}
|
||||
|
||||
// New attributes (partial update - only if provided)
|
||||
if (input.getAyposURL() != null) {
|
||||
entity.setAyposURL(input.getAyposURL());
|
||||
}
|
||||
|
||||
|
||||
if (input.getAddress() != null) {
|
||||
entity.setAddress(input.getAddress());
|
||||
}
|
||||
|
||||
|
||||
if (input.getLatitude() != null) {
|
||||
entity.setLatitude(input.getLatitude());
|
||||
}
|
||||
|
||||
|
||||
if (input.getLongitude() != null) {
|
||||
entity.setLongitude(input.getLongitude());
|
||||
}
|
||||
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,16 @@ import javax.transaction.Transactional;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.sgs.graphql.auth.service.AuthorizationService;
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenter;
|
||||
import com.sgs.graphql.dataCenter.domain.PhysicalMachine;
|
||||
import com.sgs.graphql.dataCenter.query.pagination.DataCenterPageable;
|
||||
import com.sgs.graphql.dataCenter.repo.DataCenterRepo;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.DataCenterCriteria;
|
||||
import com.sgs.graphql.dataCenter.service.DataCenterService;
|
||||
import com.sgs.graphql.systemHistory.mutation.SystemLogger;
|
||||
import com.sgs.graphql.dataCenter.service.PhysicalMachineService;
|
||||
import com.sgs.graphql.permission.domain.PermissionName;
|
||||
import com.sgs.lib.dao.query.pagination.Pagination;
|
||||
import com.sgs.lib.dao.query.sort.SortBy;
|
||||
|
||||
@@ -28,42 +29,46 @@ import graphql.kickstart.tools.GraphQLQueryResolver;
|
||||
@Component
|
||||
public class DataCenterQueryResolver implements GraphQLQueryResolver {
|
||||
|
||||
private final DataCenterService DataCenterService;
|
||||
private final DataCenterRepo dataCenterRepo;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final SystemLogger systemLogger;
|
||||
private final DataCenterService dataCenterService;
|
||||
private final PhysicalMachineService physicalMachineService;
|
||||
|
||||
@Autowired
|
||||
public DataCenterQueryResolver(AuthorizationService authorizationService, SystemLogger systemLogger, DataCenterService DataCenterService, DataCenterRepo dataCenterRepo) {
|
||||
this.DataCenterService = DataCenterService;
|
||||
this.dataCenterRepo = dataCenterRepo;
|
||||
this.authorizationService = authorizationService;
|
||||
this.systemLogger = systemLogger;
|
||||
public DataCenterQueryResolver(DataCenterService dataCenterService, PhysicalMachineService physicalMachineService) {
|
||||
this.dataCenterService = dataCenterService;
|
||||
this.physicalMachineService = physicalMachineService;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')")
|
||||
public DataCenter dataCenter(UUID id) {
|
||||
return DataCenterService.findById(id).orElse(null);
|
||||
return dataCenterService.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')")
|
||||
public List<DataCenter> dataCenters(DataCenterCriteria criteria, List<SortBy> sortBy) {
|
||||
List<DataCenter> dataCenters = DataCenterService.filterWithSort(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()),
|
||||
return dataCenterService.filterWithSort(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()),
|
||||
Sort.by(ObjectUtils.defaultIfNull(sortBy, new ArrayList<SortBy>())
|
||||
.stream()
|
||||
.map(SortBy::toOrder)
|
||||
.collect(Collectors.toList())));
|
||||
|
||||
return dataCenters;
|
||||
}
|
||||
|
||||
public DataCenterPageable paginateDataCenters(Pagination pagination, DataCenterCriteria criteria, List<SortBy> sortBy) {
|
||||
return new DataCenterPageable(DataCenterService.filterWithPaginate(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()),
|
||||
Pagination.toPageRequest(pagination, sortBy)));
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.PAGINATE_DATACENTERS_GET + "')")
|
||||
public DataCenterPageable paginateDataCenters(Pagination pagination, DataCenterCriteria criteria,
|
||||
List<SortBy> sortBy) {
|
||||
return new DataCenterPageable(
|
||||
dataCenterService.filterWithPaginate(ObjectUtils.defaultIfNull(criteria, new DataCenterCriteria()),
|
||||
Pagination.toPageRequest(pagination, sortBy)));
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')")
|
||||
public Optional<DataCenter> getByNumber(Integer number) {
|
||||
return dataCenterService.findByNumber(number);
|
||||
}
|
||||
|
||||
public Optional<DataCenter> getByNumber(Integer id) {
|
||||
return dataCenterRepo.findByNumber(id).stream().findFirst();
|
||||
@PreAuthorize("hasAuthority('" + PermissionName.DATA_CENTER_READ + "')")
|
||||
public List<PhysicalMachine> physicalMachines(UUID datacenterId) {
|
||||
return physicalMachineService.findByDatacenterId(datacenterId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.sgs.graphql.dataCenter.repo;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenter;
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource;
|
||||
import com.sgs.lib.dao.repo.BaseRepo;
|
||||
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface DataCenterEmissionSourceRepo extends BaseRepo<DataCenterEmissionSource> {
|
||||
|
||||
List<DataCenterEmissionSource> findByDataCenter(DataCenter dataCenter);
|
||||
|
||||
Optional<DataCenterEmissionSource> findByDataCenterAndIsDefaultTrue(DataCenter dataCenter);
|
||||
|
||||
@Query("SELECT dces FROM DataCenterEmissionSource dces WHERE dces.dataCenter.id = :dataCenterId")
|
||||
List<DataCenterEmissionSource> findByDataCenterId(@Param("dataCenterId") String dataCenterId);
|
||||
|
||||
void deleteByDataCenter(DataCenter dataCenter);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.sgs.graphql.dataCenter.repo;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.PhysicalMachine;
|
||||
import com.sgs.lib.dao.repo.BaseRepo;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface PhysicalMachineRepo extends BaseRepo<PhysicalMachine> {
|
||||
|
||||
/**
|
||||
* Find all physical machines by datacenter ID
|
||||
* @param datacenterId Datacenter ID
|
||||
* @return List of physical machines
|
||||
*/
|
||||
@Query("SELECT pm FROM PhysicalMachine pm WHERE pm.dataCenter.id = :datacenterId")
|
||||
List<PhysicalMachine> findByDatacenterId(@Param("datacenterId") UUID datacenterId);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.sgs.graphql.dataCenter.repo.criteria;
|
||||
|
||||
import com.sgs.lib.dao.repo.criteria.BaseCriteria;
|
||||
|
||||
public class DataCenterEmissionSourceCriteria extends BaseCriteria {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.sgs.graphql.dataCenter.repo.criteria;
|
||||
|
||||
import com.sgs.lib.dao.repo.criteria.BaseCriteria;
|
||||
|
||||
public class PhysicalMachineCriteria extends BaseCriteria {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.sgs.graphql.dataCenter.repo.criteria;
|
||||
|
||||
import com.sgs.lib.dao.repo.criteria.BaseCriteria;
|
||||
|
||||
public class VMCriteria extends BaseCriteria {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.sgs.graphql.dataCenter.repo.criteria.spec;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.DataCenterEmissionSourceCriteria;
|
||||
import com.sgs.lib.dao.repo.criteria.spec.BaseCriteriaSpec;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DataCenterEmissionSourceCriteriaSpec
|
||||
extends BaseCriteriaSpec<DataCenterEmissionSource, DataCenterEmissionSourceCriteria> {
|
||||
|
||||
@Override
|
||||
public Specification<DataCenterEmissionSource> createForAll(DataCenterEmissionSourceCriteria criteria) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.sgs.graphql.dataCenter.repo.criteria.spec;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.PhysicalMachine;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.PhysicalMachineCriteria;
|
||||
import com.sgs.lib.dao.repo.criteria.spec.BaseCriteriaSpec;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PhysicalMachineCriteriaSpec extends BaseCriteriaSpec<PhysicalMachine, PhysicalMachineCriteria> {
|
||||
|
||||
@Override
|
||||
public Specification<PhysicalMachine> createForAll(PhysicalMachineCriteria criteria) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.sgs.graphql.dataCenter.repo.criteria.spec;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.VM;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.VMCriteria;
|
||||
import com.sgs.lib.dao.repo.criteria.spec.BaseCriteriaSpec;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class VMCriteriaSpec extends BaseCriteriaSpec<VM, VMCriteria> {
|
||||
|
||||
@Override
|
||||
public Specification<VM> createForAll(VMCriteria criteria) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.sgs.graphql.dataCenter.service;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenter;
|
||||
import com.sgs.graphql.dataCenter.domain.DataCenterEmissionSource;
|
||||
import com.sgs.graphql.dataCenter.repo.DataCenterEmissionSourceRepo;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.DataCenterEmissionSourceCriteria;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.spec.DataCenterEmissionSourceCriteriaSpec;
|
||||
import com.sgs.lib.dao.service.BaseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class DataCenterEmissionSourceService extends
|
||||
BaseService<DataCenterEmissionSource, DataCenterEmissionSourceRepo, DataCenterEmissionSourceCriteria, DataCenterEmissionSourceCriteriaSpec> {
|
||||
|
||||
@Autowired
|
||||
public DataCenterEmissionSourceService(DataCenterEmissionSourceRepo repository,
|
||||
DataCenterEmissionSourceCriteriaSpec criteriaSpec) {
|
||||
super(repository, criteriaSpec);
|
||||
}
|
||||
|
||||
public List<DataCenterEmissionSource> findByDataCenter(DataCenter dataCenter) {
|
||||
return getRepository().findByDataCenter(dataCenter);
|
||||
}
|
||||
|
||||
public Optional<DataCenterEmissionSource> findByDataCenterAndIsDefaultTrue(DataCenter dataCenter) {
|
||||
return getRepository().findByDataCenterAndIsDefaultTrue(dataCenter);
|
||||
}
|
||||
|
||||
public List<DataCenterEmissionSource> findByDataCenterId(String dataCenterId) {
|
||||
return getRepository().findByDataCenterId(dataCenterId);
|
||||
}
|
||||
|
||||
public void deleteByDataCenter(DataCenter dataCenter) {
|
||||
getRepository().deleteByDataCenter(dataCenter);
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,26 @@ import com.sgs.lib.dao.service.BaseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class DataCenterService extends BaseService<DataCenter, DataCenterRepo, DataCenterCriteria, DataCenterCriteriaSpec> {
|
||||
public class DataCenterService
|
||||
extends BaseService<DataCenter, DataCenterRepo, DataCenterCriteria, DataCenterCriteriaSpec> {
|
||||
|
||||
@Autowired
|
||||
public DataCenterService(DataCenterRepo repository, DataCenterCriteriaSpec criteriaSpec) {
|
||||
super(repository, criteriaSpec);
|
||||
}
|
||||
|
||||
|
||||
public boolean existsByExternalId(Integer externalId) {
|
||||
return getRepository().existsByExternalId(externalId);
|
||||
}
|
||||
|
||||
public Optional<DataCenter> findByNumber(Integer number) {
|
||||
return getRepository().findByNumber(number);
|
||||
}
|
||||
|
||||
public Integer findMaxNumber() {
|
||||
return getRepository().findMaxNumber();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.sgs.graphql.dataCenter.service;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.PhysicalMachine;
|
||||
import com.sgs.graphql.dataCenter.repo.PhysicalMachineRepo;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.PhysicalMachineCriteria;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.spec.PhysicalMachineCriteriaSpec;
|
||||
import com.sgs.lib.dao.service.BaseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class PhysicalMachineService extends
|
||||
BaseService<PhysicalMachine, PhysicalMachineRepo, PhysicalMachineCriteria, PhysicalMachineCriteriaSpec> {
|
||||
|
||||
@Autowired
|
||||
public PhysicalMachineService(PhysicalMachineRepo repository, PhysicalMachineCriteriaSpec criteriaSpec) {
|
||||
super(repository, criteriaSpec);
|
||||
}
|
||||
|
||||
public List<PhysicalMachine> findByDatacenterId(UUID datacenterId) {
|
||||
return getRepository().findByDatacenterId(datacenterId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.sgs.graphql.dataCenter.service;
|
||||
|
||||
import com.sgs.graphql.dataCenter.domain.VM;
|
||||
import com.sgs.graphql.dataCenter.repo.VMRepo;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.VMCriteria;
|
||||
import com.sgs.graphql.dataCenter.repo.criteria.spec.VMCriteriaSpec;
|
||||
import com.sgs.lib.dao.service.BaseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class VMService extends BaseService<VM, VMRepo, VMCriteria, VMCriteriaSpec> {
|
||||
|
||||
@Autowired
|
||||
public VMService(VMRepo repository, VMCriteriaSpec criteriaSpec) {
|
||||
super(repository, criteriaSpec);
|
||||
}
|
||||
|
||||
public List<VM> findAllByVmName(String vmName) {
|
||||
return getRepository().findAllByVmName(vmName);
|
||||
}
|
||||
|
||||
public Optional<VM> findFirstByVmNameOrderByIdDesc(String vmName) {
|
||||
return getRepository().findFirstByVmNameOrderByIdDesc(vmName);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class VMEmissionSummary {
|
||||
private Double totalEmission; // Individual record's total emission
|
||||
private LocalDateTime createdDate; // When this specific record was created
|
||||
private String physicalMachine;
|
||||
private String project;
|
||||
private String cloudSystem; // From physical machine
|
||||
private String dataCenter;
|
||||
|
||||
// Individual emission values for this specific record
|
||||
@@ -31,7 +31,7 @@ public class VMEmissionSummary {
|
||||
|
||||
public VMEmissionSummary(UUID vmId, String vmName, Double vmPower, String vmStatus,
|
||||
Double totalEmission, LocalDateTime createdDate,
|
||||
String physicalMachine, String project, String dataCenter,
|
||||
String physicalMachine, String cloudSystem, String dataCenter,
|
||||
Double co2, Double ch4, Double n2o) {
|
||||
this.vmId = vmId;
|
||||
this.vmName = vmName;
|
||||
@@ -40,7 +40,7 @@ public class VMEmissionSummary {
|
||||
this.totalEmission = totalEmission;
|
||||
this.createdDate = createdDate;
|
||||
this.physicalMachine = physicalMachine;
|
||||
this.project = project;
|
||||
this.cloudSystem = cloudSystem;
|
||||
this.dataCenter = dataCenter;
|
||||
this.co2 = co2;
|
||||
this.ch4 = ch4;
|
||||
@@ -70,8 +70,8 @@ public class VMEmissionSummary {
|
||||
public String getPhysicalMachine() { return physicalMachine; }
|
||||
public void setPhysicalMachine(String physicalMachine) { this.physicalMachine = physicalMachine; }
|
||||
|
||||
public String getProject() { return project; }
|
||||
public void setProject(String project) { this.project = project; }
|
||||
public String getCloudSystem() { return cloudSystem; }
|
||||
public void setCloudSystem(String cloudSystem) { this.cloudSystem = cloudSystem; }
|
||||
|
||||
public String getDataCenter() { return dataCenter; }
|
||||
public void setDataCenter(String dataCenter) { this.dataCenter = dataCenter; }
|
||||
|
||||
@@ -51,9 +51,11 @@ public class MainDataTableQueryResolver implements GraphQLQueryResolver {
|
||||
|
||||
/**
|
||||
* GraphQL query to get VM emission summaries with hierarchy information
|
||||
* @param datacenterId Optional datacenter ID to filter VMs by datacenter
|
||||
* @param projectId Optional project ID to filter VMs by project
|
||||
* @return List of VM emission summaries including datacenter, project, aggregate, and physical machine info
|
||||
*/
|
||||
public List<VMEmissionSummary> vmEmissionSummary() {
|
||||
return mainDataTableService.getVMEmissionSummaries();
|
||||
public List<VMEmissionSummary> vmEmissionSummary(UUID datacenterId) {
|
||||
return mainDataTableService.getVMEmissionSummaries(datacenterId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.sgs.lib.dao.service.BaseService;
|
||||
import com.sgs.graphql.mainDataTable.dto.VMEmissionSummary;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -21,7 +22,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MainDataTableService extends BaseService<MainDataTable, MainDataTableRepo, MainDataTableCriteria, MainDataTableCriteriaSpec> {
|
||||
public class MainDataTableService
|
||||
extends BaseService<MainDataTable, MainDataTableRepo, MainDataTableCriteria, MainDataTableCriteriaSpec> {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
@@ -32,68 +34,86 @@ public class MainDataTableService extends BaseService<MainDataTable, MainDataTab
|
||||
}
|
||||
|
||||
public List<VMEmissionSummary> getVMEmissionSummaries() {
|
||||
return getVMEmissionSummaries(null);
|
||||
}
|
||||
|
||||
public List<VMEmissionSummary> getVMEmissionSummaries(UUID datacenterId) {
|
||||
List<String> whereConditions = new ArrayList<>();
|
||||
|
||||
if (datacenterId != null) {
|
||||
whereConditions.add("dc.id = decode(replace(:datacenterId, '-', ''), 'hex')");
|
||||
}
|
||||
|
||||
String whereClause = whereConditions.isEmpty() ? "" :
|
||||
"WHERE " + String.join(" AND ", whereConditions) + " ";
|
||||
|
||||
String sql = """
|
||||
SELECT
|
||||
CAST(v.id AS VARCHAR) as vm_id,
|
||||
v.name as vm_name,
|
||||
v.vm_name as vm_name,
|
||||
v.power as vm_power,
|
||||
v.status as vm_status,
|
||||
v.state as vm_status,
|
||||
mdt.total_emission,
|
||||
mdt.created_date,
|
||||
pm.name as physical_machine_name,
|
||||
p.name as project_name,
|
||||
pm.cloud_system as cloud_system,
|
||||
dc.data_center_name as datacenter_name,
|
||||
mdt.co2,
|
||||
mdt.ch4,
|
||||
mdt.n2o
|
||||
FROM main_data_table mdt
|
||||
JOIN vm v ON mdt.vm_id = v.id
|
||||
LEFT JOIN vms vms_active ON v.active_vms_id = vms_active.id
|
||||
LEFT JOIN vms vms_inactive ON v.inactive_vms_id = vms_inactive.id
|
||||
LEFT JOIN physical_machine pm ON (pm.vms_id = vms_active.id OR pm.vms_id = vms_inactive.id)
|
||||
LEFT JOIN project p ON pm.project_id = p.id
|
||||
LEFT JOIN data_center dc ON p.data_center_id = dc.id
|
||||
ORDER BY mdt.created_date DESC, v.name
|
||||
LEFT JOIN physical_machine pm ON v.physical_machine_id = pm.id
|
||||
LEFT JOIN data_center dc ON pm.data_center_id = dc.id
|
||||
""" + whereClause + """
|
||||
ORDER BY mdt.created_date DESC, v.vm_name
|
||||
""";
|
||||
|
||||
Query query = entityManager.createNativeQuery(sql);
|
||||
|
||||
// Add parameters if provided
|
||||
if (datacenterId != null) {
|
||||
query.setParameter("datacenterId", datacenterId.toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object[]> results = query.getResultList();
|
||||
|
||||
|
||||
return results.stream().map(row -> {
|
||||
VMEmissionSummary summary = new VMEmissionSummary();
|
||||
|
||||
|
||||
// Handle UUID conversion from hex format
|
||||
String uuidStr = (String) row[0];
|
||||
UUID vmId = convertHexToUUID(uuidStr);
|
||||
summary.setVmId(vmId);
|
||||
|
||||
|
||||
summary.setVmName((String) row[1]);
|
||||
summary.setVmPower((Double) row[2]);
|
||||
summary.setVmStatus((String) row[3]);
|
||||
summary.setTotalEmission((Double) row[4]);
|
||||
|
||||
|
||||
// Convert Timestamp to LocalDateTime for created_date
|
||||
Timestamp timestamp = (Timestamp) row[5];
|
||||
if (timestamp != null) {
|
||||
summary.setCreatedDate(timestamp.toLocalDateTime());
|
||||
}
|
||||
|
||||
|
||||
summary.setPhysicalMachine((String) row[6]);
|
||||
summary.setProject((String) row[7]);
|
||||
summary.setCloudSystem((String) row[7]);
|
||||
summary.setDataCenter((String) row[8]);
|
||||
|
||||
|
||||
// Individual emission values
|
||||
summary.setCo2((Double) row[9]);
|
||||
summary.setCh4((Double) row[10]);
|
||||
summary.setN2o((Double) row[11]);
|
||||
|
||||
|
||||
return summary;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts PostgreSQL hex format UUID to proper UUID format
|
||||
*
|
||||
* @param hexUuid UUID in hex format (e.g., \x6205c18b8d1e4f0fa5154212fb44050b)
|
||||
* @return UUID object
|
||||
*/
|
||||
@@ -103,11 +123,11 @@ public class MainDataTableService extends BaseService<MainDataTable, MainDataTab
|
||||
String hex = hexUuid.substring(2);
|
||||
// Insert hyphens to make it a proper UUID format
|
||||
// UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
String formatted = hex.substring(0, 8) + "-" +
|
||||
hex.substring(8, 12) + "-" +
|
||||
hex.substring(12, 16) + "-" +
|
||||
hex.substring(16, 20) + "-" +
|
||||
hex.substring(20);
|
||||
String formatted = hex.substring(0, 8) + "-" +
|
||||
hex.substring(8, 12) + "-" +
|
||||
hex.substring(12, 16) + "-" +
|
||||
hex.substring(16, 20) + "-" +
|
||||
hex.substring(20);
|
||||
return UUID.fromString(formatted);
|
||||
} else {
|
||||
// If it's already in proper format, parse directly
|
||||
|
||||
@@ -74,4 +74,5 @@ public class PermissionDescription {
|
||||
public static final String DATA_CENTER_DELETE = "Veri Merkezi Silme";
|
||||
public static final String DATA_CENTER_UPDATE = "Veri Merkezi Güncelleme";
|
||||
public static final String DATA_CENTER_READ = "Veri Merkezi Görüntüleme";
|
||||
public static final String PAGINATE_DATACENTERS_GET = "Veri Merkezleri Listesi Görüntüleme";
|
||||
}
|
||||
|
||||
@@ -84,5 +84,6 @@ public class PermissionName {
|
||||
public static final String DATA_CENTER_DELETE = "data_center_delete";
|
||||
public static final String DATA_CENTER_UPDATE = "data_center_update";
|
||||
public static final String DATA_CENTER_READ = "data_center_read";
|
||||
public static final String PAGINATE_DATACENTERS_GET = "paginate_datacenters_get";
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.sgs.lib.dao.service.BaseService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class PermissionService extends BaseService<Permission, PermissionRepo, PermissionCriteria, PermissionCriteriaSpec> {
|
||||
|
||||
@@ -16,4 +18,8 @@ public class PermissionService extends BaseService<Permission, PermissionRepo, P
|
||||
public PermissionService(PermissionRepo repository, PermissionCriteriaSpec criteriaSpec) {
|
||||
super(repository, criteriaSpec);
|
||||
}
|
||||
|
||||
public Optional<Permission> findByTag(String tag) {
|
||||
return getRepository().findByTag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,19 +74,16 @@ public class MessageListener {
|
||||
System.out.println(" External ID: " + dto.getId());
|
||||
System.out.println(" Number: " + dto.getNumber());
|
||||
System.out.println(" Consumption Amount: " + dto.getConsuptionAmount());
|
||||
System.out.println(" Projects Count: " + (dto.getProjects() != null ? dto.getProjects().size() : 0));
|
||||
System.out.println(" Physical Machines Count: " + (dto.getPhysicalMachine() != null ? dto.getPhysicalMachine().size() : 0));
|
||||
|
||||
// Log MainOptimizationSpace info for each project
|
||||
if (dto.getProjects() != null) {
|
||||
for (Map.Entry<String, ProjectDto> projectEntry : dto.getProjects().entrySet()) {
|
||||
String projectId = projectEntry.getKey();
|
||||
ProjectDto project = projectEntry.getValue();
|
||||
if (project.getMainOptimizationSpace() != null) {
|
||||
MainOptimizationSpaceDto mainOpt = project.getMainOptimizationSpace();
|
||||
System.out.println(" Project " + projectId + ":");
|
||||
System.out.println(" PMs: " + (mainOpt.getPms() != null ? mainOpt.getPms().size() : 0));
|
||||
System.out.println(" VMs: " + (mainOpt.getVms() != null ? mainOpt.getVms().size() : 0));
|
||||
}
|
||||
// Log Physical Machine info
|
||||
if (dto.getPhysicalMachine() != null) {
|
||||
for (Map.Entry<String, PhysicalMachineDto> pmEntry : dto.getPhysicalMachine().entrySet()) {
|
||||
String pmIp = pmEntry.getKey();
|
||||
PhysicalMachineDto pm = pmEntry.getValue();
|
||||
System.out.println(" Physical Machine " + pm.getName() + " (IP: " + pmIp + "):");
|
||||
System.out.println(" Power: " + pm.getPower());
|
||||
System.out.println(" VMs: " + (pm.getVms() != null ? pm.getVms().size() : 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,18 +100,18 @@ public class MessageListener {
|
||||
|
||||
// System.out.println(entity.toString());
|
||||
System.out.println("✅ Raw JSON message:\n" + message);
|
||||
System.out.println("✅ DTO parsed:\n" + objectMapper.writeValueAsString(dto));
|
||||
//System.out.println("✅ DTO parsed:\n" + objectMapper.writeValueAsString(dto));
|
||||
//System.out.println("✅ Entity:\n" + objectMapper.writeValueAsString(entity));
|
||||
System.out.println("🚨 DataCenter name: " + dataCenter.getDataCenter());
|
||||
System.out.println("🚨 External ID: " + dataCenter.getExternalId());
|
||||
System.out.println("🚨 Projects count: " + (dataCenter.getProjects() != null ? dataCenter.getProjects().size() : 0));
|
||||
if (dataCenter.getProjects() != null && !dataCenter.getProjects().isEmpty()) {
|
||||
Project firstProject = dataCenter.getProjects().get(0);
|
||||
System.out.println("🚨 PMs in first project: " + firstProject.getPhysicalMachines().size());
|
||||
System.out.println("🚨 Physical Machines count: " + (dataCenter.getPhysicalMachines() != null ? dataCenter.getPhysicalMachines().size() : 0));
|
||||
if (dataCenter.getPhysicalMachines() != null && !dataCenter.getPhysicalMachines().isEmpty()) {
|
||||
PhysicalMachine firstPM = dataCenter.getPhysicalMachines().get(0);
|
||||
System.out.println("🚨 VMs in first PM: " + firstPM.getVms().size());
|
||||
|
||||
// Show VM assignment summary
|
||||
int totalVMs = 0;
|
||||
for (PhysicalMachine pm : firstProject.getPhysicalMachines()) {
|
||||
for (PhysicalMachine pm : dataCenter.getPhysicalMachines()) {
|
||||
int vmCount = pm.getVms() != null ? pm.getVms().size() : 0;
|
||||
totalVMs += vmCount;
|
||||
System.out.println(" PM " + pm.getName() + " (IP: " + pm.getIp() + "): " + vmCount + " VMs");
|
||||
@@ -157,6 +154,7 @@ public class MessageListener {
|
||||
entity.setHost(dto.getHost());
|
||||
entity.setFlavorName(dto.getFlavorName());
|
||||
entity.setTag(dto.getTag());
|
||||
entity.setProject(dto.getProject());
|
||||
|
||||
// Set calcOn - you may want to derive this from state or other logic
|
||||
entity.setCalcOn(dto.isCalcOn());
|
||||
@@ -168,7 +166,7 @@ public class MessageListener {
|
||||
entity.setEmissionSource(dto.getEmissionSource());
|
||||
|
||||
// Debug logging
|
||||
System.out.println("🔍 VM Entity Created: " + dto.getVmName() + " - state = " + dto.getState() + " - calcOn = " + dto.isCalcOn());
|
||||
System.out.println("🔍 VM Entity Created: " + dto.getVmName() + " - state = " + dto.getState() + " - calcOn = " + dto.isCalcOn() + " - project = " + dto.getProject());
|
||||
if (dto.getEmissionSource() != null && !dto.getEmissionSource().isEmpty()) {
|
||||
System.out.println(" Emission Sources: " + dto.getEmissionSource());
|
||||
}
|
||||
@@ -184,6 +182,7 @@ public class MessageListener {
|
||||
entity.setName(dto.getName());
|
||||
entity.setIp(dto.getIp());
|
||||
entity.setTag(dto.getTag());
|
||||
entity.setCloudSystem(dto.getCloudSystem());
|
||||
entity.setPower(dto.getPower());
|
||||
|
||||
// VMs are now processed separately from the new message format
|
||||
@@ -207,112 +206,43 @@ public class MessageListener {
|
||||
// ConsuptionUnit, and ActivitySubUnit are no longer received in the message
|
||||
// These will need to be set via the DataCenter CRUD operations
|
||||
|
||||
// Convert Projects
|
||||
if (dto.getProjects() != null) {
|
||||
List<Project> projects = new ArrayList<>();
|
||||
for (Map.Entry<String, ProjectDto> projectEntry : dto.getProjects().entrySet()) {
|
||||
String projectId = projectEntry.getKey();
|
||||
ProjectDto projectDto = projectEntry.getValue();
|
||||
// Convert Physical Machines directly
|
||||
if (dto.getPhysicalMachine() != null) {
|
||||
List<PhysicalMachine> physicalMachines = new ArrayList<>();
|
||||
for (Map.Entry<String, PhysicalMachineDto> pmEntry : dto.getPhysicalMachine().entrySet()) {
|
||||
String pmIp = pmEntry.getKey();
|
||||
PhysicalMachineDto pmDto = pmEntry.getValue();
|
||||
|
||||
// Set the project ID and a default name if not provided
|
||||
projectDto.setId(projectId);
|
||||
if (projectDto.getName() == null || projectDto.getName().isEmpty()) {
|
||||
projectDto.setName("Project-" + projectId.substring(0, 8)); // Use first 8 chars of ID as name
|
||||
PhysicalMachine pm = new PhysicalMachine();
|
||||
pm.setName(pmDto.getName());
|
||||
pm.setIp(pmIp); // Use the IP from the map key
|
||||
pm.setTag(pmDto.getTag());
|
||||
pm.setCloudSystem(pmDto.getCloudSystem());
|
||||
pm.setPower(pmDto.getPower());
|
||||
pm.setDataCenter(entity);
|
||||
|
||||
// Process VMs for this PM
|
||||
if (pmDto.getVms() != null) {
|
||||
List<VM> vms = new ArrayList<>();
|
||||
for (Map.Entry<String, VMDto> vmEntry : pmDto.getVms().entrySet()) {
|
||||
String vmIp = vmEntry.getKey();
|
||||
VMDto vmDto = vmEntry.getValue();
|
||||
|
||||
VM vm = toVMEntity(vmDto);
|
||||
vm.setIp(vmIp); // Use the IP from the map key
|
||||
vm.setPhysicalMachine(pm);
|
||||
vms.add(vm);
|
||||
}
|
||||
pm.setVms(vms);
|
||||
}
|
||||
|
||||
Project project = toProjectEntity(projectDto);
|
||||
project.setDataCenter(entity);
|
||||
projects.add(project);
|
||||
physicalMachines.add(pm);
|
||||
}
|
||||
entity.setProjects(projects);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public Project toProjectEntity(ProjectDto dto){
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
Project entity = new Project();
|
||||
entity.setName(dto.getName());
|
||||
|
||||
// Process MainOptimizationSpace instead of physical machines list
|
||||
if (dto.getMainOptimizationSpace() != null) {
|
||||
List<PhysicalMachine> physicalMachines = processMainOptimizationSpace(dto.getMainOptimizationSpace());
|
||||
// Set project reference for each PM
|
||||
physicalMachines.forEach(pm -> pm.setProject(entity));
|
||||
entity.setPhysicalMachines(physicalMachines);
|
||||
}
|
||||
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process MainOptimizationSpaceDto and assign VMs to their hosting PMs
|
||||
*/
|
||||
private List<PhysicalMachine> processMainOptimizationSpace(MainOptimizationSpaceDto mainOptSpace) {
|
||||
List<PhysicalMachine> physicalMachines = new ArrayList<>();
|
||||
|
||||
if (mainOptSpace.getPms() == null || mainOptSpace.getVms() == null) {
|
||||
System.out.println("⚠️ MainOptimizationSpace has null PMs or VMs");
|
||||
return physicalMachines;
|
||||
}
|
||||
|
||||
System.out.println("🔍 Processing MainOptimizationSpace with " +
|
||||
mainOptSpace.getPms().size() + " PMs and " +
|
||||
mainOptSpace.getVms().size() + " VMs");
|
||||
|
||||
// Convert PMs from DTO to Entity
|
||||
for (Map.Entry<String, PhysicalMachineDto> pmEntry : mainOptSpace.getPms().entrySet()) {
|
||||
String pmIp = pmEntry.getKey();
|
||||
PhysicalMachineDto pmDto = pmEntry.getValue();
|
||||
|
||||
// Set the IP from the map key
|
||||
pmDto.setIp(pmIp);
|
||||
|
||||
PhysicalMachine pm = toPhysicalMachineEntity(pmDto);
|
||||
physicalMachines.add(pm);
|
||||
|
||||
System.out.println("✅ Created PM: " + pm.getName() + " (IP: " + pm.getIp() + ")");
|
||||
}
|
||||
|
||||
// Assign VMs to their hosting PMs
|
||||
for (Map.Entry<String, VMDto> vmEntry : mainOptSpace.getVms().entrySet()) {
|
||||
String vmIp = vmEntry.getKey();
|
||||
VMDto vmDto = vmEntry.getValue();
|
||||
|
||||
// Set the IP from the map key
|
||||
vmDto.setIp(vmIp);
|
||||
|
||||
VM vm = toVMEntity(vmDto);
|
||||
|
||||
// Find the hosting PM by IP
|
||||
String hostingPmIp = vmDto.getHostingPm();
|
||||
PhysicalMachine hostingPm = physicalMachines.stream()
|
||||
.filter(pm -> pm.getIp().equals(hostingPmIp))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (hostingPm != null) {
|
||||
// Assign VM to PM
|
||||
vm.setPhysicalMachine(hostingPm);
|
||||
if (hostingPm.getVms() == null) {
|
||||
hostingPm.setVms(new ArrayList<>());
|
||||
}
|
||||
hostingPm.getVms().add(vm);
|
||||
|
||||
System.out.println("✅ Assigned VM: " + vm.getVmName() + " (IP: " + vm.getIp() +
|
||||
") to PM: " + hostingPm.getName() + " (IP: " + hostingPm.getIp() + ")");
|
||||
} else {
|
||||
System.err.println("❌ Could not find hosting PM with IP: " + hostingPmIp +
|
||||
" for VM: " + vm.getVmName());
|
||||
}
|
||||
}
|
||||
|
||||
return physicalMachines;
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
public DataCenter createDataCenter(DataCenter newDc) {
|
||||
@@ -335,123 +265,117 @@ public class MessageListener {
|
||||
// Note: DataCenter name and emission-related fields are no longer updated from the message
|
||||
// These are now managed via DataCenter CRUD operations
|
||||
|
||||
for (Project newProject : newDc.getProjects()) {
|
||||
Optional<Project> existingProjOpt = dc.getProjects().stream()
|
||||
.filter(p -> p.getName().equalsIgnoreCase(newProject.getName()))
|
||||
.findFirst();
|
||||
// Ensure datacenter has initialized physical machines list
|
||||
if (dc.getPhysicalMachines() == null) {
|
||||
dc.setPhysicalMachines(new ArrayList<>());
|
||||
}
|
||||
|
||||
Project project;
|
||||
if (existingProjOpt.isPresent()) {
|
||||
project = existingProjOpt.get();
|
||||
project.setName(newProject.getName());
|
||||
System.out.println("✅ Updated existing project: " + project.getName());
|
||||
} else {
|
||||
project = newProject;
|
||||
project.setDataCenter(dc);
|
||||
dc.getProjects().add(project);
|
||||
System.out.println("✅ Created new project: " + project.getName());
|
||||
// Process Physical Machines directly from the message structure
|
||||
for (PhysicalMachine newPm : newDc.getPhysicalMachines()) {
|
||||
Optional<PhysicalMachine> existingPmOpt = dc.getPhysicalMachines().stream()
|
||||
.filter(pm -> pm.getIp().equals(newPm.getIp())) // Match by IP instead of name
|
||||
.findFirst();
|
||||
|
||||
PhysicalMachine pm;
|
||||
if (existingPmOpt.isPresent()) {
|
||||
// Update existing PM
|
||||
pm = existingPmOpt.get();
|
||||
pm.setName(newPm.getName());
|
||||
pm.setIp(newPm.getIp());
|
||||
pm.setTag(newPm.getTag());
|
||||
pm.setCloudSystem(newPm.getCloudSystem());
|
||||
pm.setPower(newPm.getPower());
|
||||
|
||||
System.out.println("✅ Updated existing PM: " + pm.getName() + " (IP: " + pm.getIp() + ") - CloudSystem: " + pm.getCloudSystem());
|
||||
} else {
|
||||
// Create new PM
|
||||
pm = newPm;
|
||||
pm.setDataCenter(dc);
|
||||
dc.getPhysicalMachines().add(pm);
|
||||
|
||||
System.out.println("✅ Created new PM: " + pm.getName() + " (IP: " + pm.getIp() + ") - CloudSystem: " + pm.getCloudSystem());
|
||||
}
|
||||
|
||||
// Process VMs that are already assigned to this PM
|
||||
if (newPm.getVms() != null && !newPm.getVms().isEmpty()) {
|
||||
System.out.println("🔍 Processing " + newPm.getVms().size() + " VMs for PM: " + newPm.getName());
|
||||
|
||||
// Ensure PM has VM list initialized
|
||||
if (pm.getVms() == null) {
|
||||
pm.setVms(new ArrayList<>());
|
||||
}
|
||||
|
||||
// Ensure project has initialized physical machines list
|
||||
if (project.getPhysicalMachines() == null) {
|
||||
project.setPhysicalMachines(new ArrayList<>());
|
||||
}
|
||||
|
||||
// Process Physical Machines (VMs are already assigned to PMs from MainOptimizationSpace)
|
||||
for (PhysicalMachine newPm : newProject.getPhysicalMachines()) {
|
||||
Optional<PhysicalMachine> existingPmOpt = project.getPhysicalMachines().stream()
|
||||
.filter(pm -> pm.getIp().equals(newPm.getIp())) // Match by IP instead of name
|
||||
.findFirst();
|
||||
|
||||
PhysicalMachine pm;
|
||||
if (existingPmOpt.isPresent()) {
|
||||
// Update existing PM
|
||||
pm = existingPmOpt.get();
|
||||
pm.setName(newPm.getName());
|
||||
pm.setIp(newPm.getIp());
|
||||
pm.setTag(newPm.getTag());
|
||||
pm.setPower(newPm.getPower());
|
||||
|
||||
System.out.println("✅ Updated existing PM: " + pm.getName() + " (IP: " + pm.getIp() + ")");
|
||||
} else {
|
||||
// Create new PM
|
||||
pm = newPm;
|
||||
pm.setProject(project);
|
||||
project.getPhysicalMachines().add(pm);
|
||||
|
||||
System.out.println("✅ Created new PM: " + pm.getName() + " (IP: " + pm.getIp() + ")");
|
||||
|
||||
// Clear existing VMs to replace with new ones
|
||||
pm.getVms().clear();
|
||||
|
||||
// Process each VM that's already assigned to this PM
|
||||
for (VM newVm : newPm.getVms()) {
|
||||
if (newVm == null) continue;
|
||||
|
||||
// Find existing VM by vmName
|
||||
Optional<VM> existingVmOpt = Optional.empty();
|
||||
String vmLookupName = newVm.getVmName();
|
||||
if (vmLookupName != null) {
|
||||
existingVmOpt = vmRepo.findFirstByVmNameOrderByIdDesc(vmLookupName);
|
||||
}
|
||||
|
||||
// Process VMs that are already assigned to this PM
|
||||
if (newPm.getVms() != null && !newPm.getVms().isEmpty()) {
|
||||
System.out.println("🔍 Processing " + newPm.getVms().size() + " VMs for PM: " + newPm.getName());
|
||||
|
||||
VM vm;
|
||||
if (existingVmOpt.isPresent()) {
|
||||
// Update existing VM
|
||||
vm = existingVmOpt.get();
|
||||
|
||||
// Ensure PM has VM list initialized
|
||||
if (pm.getVms() == null) {
|
||||
pm.setVms(new ArrayList<>());
|
||||
// IMPORTANT: Remove VM from its current Physical Machine first
|
||||
if (vm.getPhysicalMachine() != null) {
|
||||
PhysicalMachine currentPM = vm.getPhysicalMachine();
|
||||
if (currentPM.getVms() != null) {
|
||||
currentPM.getVms().remove(vm);
|
||||
System.out.println("🔄 Removed VM " + vm.getVmName() + " from previous PM: " + currentPM.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Clear existing VMs to replace with new ones
|
||||
pm.getVms().clear();
|
||||
vm.setState(newVm.getState());
|
||||
vm.setVmName(newVm.getVmName());
|
||||
vm.setIp(newVm.getIp());
|
||||
vm.setPower(newVm.getPower());
|
||||
vm.setCalcOn(newVm.getCalcOn());
|
||||
vm.setHostingPm(newVm.getHostingPm());
|
||||
vm.setHost(newVm.getHost());
|
||||
vm.setFlavorName(newVm.getFlavorName());
|
||||
vm.setTag(newVm.getTag());
|
||||
vm.setEmissionSource(newVm.getEmissionSource());
|
||||
vm.setProject(newVm.getProject());
|
||||
|
||||
// Process each VM that's already assigned to this PM
|
||||
for (VM newVm : newPm.getVms()) {
|
||||
if (newVm == null) continue;
|
||||
|
||||
// Find existing VM by vmName
|
||||
Optional<VM> existingVmOpt = Optional.empty();
|
||||
String vmLookupName = newVm.getVmName();
|
||||
if (vmLookupName != null) {
|
||||
existingVmOpt = vmRepo.findFirstByVmNameOrderByIdDesc(vmLookupName);
|
||||
}
|
||||
|
||||
VM vm;
|
||||
if (existingVmOpt.isPresent()) {
|
||||
// Update existing VM
|
||||
vm = existingVmOpt.get();
|
||||
vm.setState(newVm.getState());
|
||||
vm.setVmName(newVm.getVmName());
|
||||
vm.setIp(newVm.getIp());
|
||||
vm.setPower(newVm.getPower());
|
||||
vm.setCalcOn(newVm.getCalcOn());
|
||||
vm.setHostingPm(newVm.getHostingPm());
|
||||
vm.setHost(newVm.getHost());
|
||||
vm.setFlavorName(newVm.getFlavorName());
|
||||
vm.setTag(newVm.getTag());
|
||||
vm.setEmissionSource(newVm.getEmissionSource());
|
||||
|
||||
System.out.println("✅ Updated existing VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn());
|
||||
} else {
|
||||
// Use new VM
|
||||
vm = newVm;
|
||||
|
||||
System.out.println("✅ Created new VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn());
|
||||
}
|
||||
|
||||
// Set physical machine relationship
|
||||
vm.setPhysicalMachine(pm);
|
||||
pm.getVms().add(vm);
|
||||
System.out.println("✅ Updated existing VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn());
|
||||
} else {
|
||||
// Use new VM
|
||||
vm = newVm;
|
||||
|
||||
System.out.println("✅ Created new VM: " + vm.getVmName() + " (IP: " + vm.getIp() + ") - calcOn = " + vm.getCalcOn());
|
||||
}
|
||||
|
||||
// Set physical machine relationship
|
||||
vm.setPhysicalMachine(pm);
|
||||
pm.getVms().add(vm);
|
||||
|
||||
// Update config
|
||||
if (newVm.getConfig() != null) {
|
||||
if (vm.getConfig() == null) {
|
||||
vm.setConfig(newVm.getConfig());
|
||||
vm.getConfig().setVm(vm);
|
||||
} else {
|
||||
vm.getConfig().setCpu(newVm.getConfig().getCpu());
|
||||
vm.getConfig().setRam(newVm.getConfig().getRam());
|
||||
vm.getConfig().setDisk(newVm.getConfig().getDisk());
|
||||
}
|
||||
}
|
||||
// Update config
|
||||
if (newVm.getConfig() != null) {
|
||||
if (vm.getConfig() == null) {
|
||||
vm.setConfig(newVm.getConfig());
|
||||
vm.getConfig().setVm(vm);
|
||||
} else {
|
||||
vm.getConfig().setCpu(newVm.getConfig().getCpu());
|
||||
vm.getConfig().setRam(newVm.getConfig().getRam());
|
||||
vm.getConfig().setDisk(newVm.getConfig().getDisk());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Before Save: DataCenter=" + dc.getDataCenter());
|
||||
System.out.println("External ID=" + dc.getExternalId());
|
||||
System.out.println("Number=" + dc.getNumber());
|
||||
System.out.println("Projects=" + (dc.getProjects() != null ? dc.getProjects().size() : 0));
|
||||
System.out.println("Physical Machines=" + (dc.getPhysicalMachines() != null ? dc.getPhysicalMachines().size() : 0));
|
||||
|
||||
DataCenter saved = dataCenterService.save(dc);
|
||||
System.out.println("✅ Saved: ID=" + saved.getId());
|
||||
@@ -525,41 +449,29 @@ public class MessageListener {
|
||||
int failedCalculations = 0;
|
||||
int totalEmissionSources = 0;
|
||||
|
||||
for (Project project : dataCenter.getProjects()) {
|
||||
for (PhysicalMachine pm : project.getPhysicalMachines()) {
|
||||
if (pm.getVms() != null) {
|
||||
// Calculate for all VMs (only those with calcOn = true)
|
||||
for (VM vm : pm.getVms()) {
|
||||
totalVMs++;
|
||||
if (vm.getCalcOn() != null && vm.getCalcOn()) {
|
||||
eligibleVMs++;
|
||||
if (vm.getPower() != null && vm.getPower() > 0) {
|
||||
System.out.println("✅ Processing VM " + vm.getVmName() + " (calcOn = true)");
|
||||
processedVMs++;
|
||||
|
||||
// Check if VM has emission sources
|
||||
Map<String, Integer> emissionSources = vm.getEmissionSource();
|
||||
if (emissionSources != null && !emissionSources.isEmpty()) {
|
||||
// Create separate emission record for each emission source
|
||||
System.out.println("🔍 VM has " + emissionSources.size() + " emission sources");
|
||||
totalEmissionSources += emissionSources.size();
|
||||
for (Map.Entry<String, Integer> sourceEntry : emissionSources.entrySet()) {
|
||||
String sourceName = sourceEntry.getKey();
|
||||
Integer percentage = sourceEntry.getValue();
|
||||
System.out.println(" - " + sourceName + ": " + percentage + "%");
|
||||
|
||||
boolean success = createVMEmissionRecordForSource(dataCenter, vm, project, pm, sourceName, percentage);
|
||||
if (success) {
|
||||
successfulCalculations++;
|
||||
} else {
|
||||
failedCalculations++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback to default emission source if VM has no emission sources
|
||||
System.out.println("⚠️ VM has no emission sources, using default");
|
||||
totalEmissionSources++;
|
||||
boolean success = createVMEmissionRecord(dataCenter, vm, project, pm);
|
||||
for (PhysicalMachine pm : dataCenter.getPhysicalMachines()) {
|
||||
if (pm.getVms() != null) {
|
||||
// Calculate for all VMs (only those with calcOn = true)
|
||||
for (VM vm : pm.getVms()) {
|
||||
totalVMs++;
|
||||
if (vm.getCalcOn() != null && vm.getCalcOn()) {
|
||||
eligibleVMs++;
|
||||
if (vm.getPower() != null && vm.getPower() > 0) {
|
||||
System.out.println("✅ Processing VM " + vm.getVmName() + " (calcOn = true)");
|
||||
processedVMs++;
|
||||
|
||||
// Check if VM has emission sources
|
||||
Map<String, Integer> emissionSources = vm.getEmissionSource();
|
||||
if (emissionSources != null && !emissionSources.isEmpty()) {
|
||||
// Create separate emission record for each emission source
|
||||
System.out.println("🔍 VM has " + emissionSources.size() + " emission sources");
|
||||
totalEmissionSources += emissionSources.size();
|
||||
for (Map.Entry<String, Integer> sourceEntry : emissionSources.entrySet()) {
|
||||
String sourceName = sourceEntry.getKey();
|
||||
Integer percentage = sourceEntry.getValue();
|
||||
System.out.println(" - " + sourceName + ": " + percentage + "%");
|
||||
|
||||
boolean success = createVMEmissionRecordForSource(dataCenter, vm, pm, sourceName, percentage);
|
||||
if (success) {
|
||||
successfulCalculations++;
|
||||
} else {
|
||||
@@ -567,16 +479,26 @@ public class MessageListener {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("⚠️ Skipping VM " + vm.getVmName() + " (calcOn = true) - no power consumption data");
|
||||
// Fallback to default emission source if VM has no emission sources
|
||||
System.out.println("⚠️ VM has no emission sources, using default");
|
||||
totalEmissionSources++;
|
||||
boolean success = createVMEmissionRecord(dataCenter, vm, pm);
|
||||
if (success) {
|
||||
successfulCalculations++;
|
||||
} else {
|
||||
failedCalculations++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("⏭️ Skipping VM " + vm.getVmName() + " - calcOn = " + vm.getCalcOn());
|
||||
System.out.println("⚠️ Skipping VM " + vm.getVmName() + " (calcOn = true) - no power consumption data");
|
||||
}
|
||||
} else {
|
||||
System.out.println("⏭️ Skipping VM " + vm.getVmName() + " - calcOn = " + vm.getCalcOn());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
System.out.println("🎯 VM Emission Calculation Summary:");
|
||||
System.out.println(" Total VMs found: " + totalVMs);
|
||||
System.out.println(" VMs with calcOn = true: " + eligibleVMs);
|
||||
@@ -587,7 +509,7 @@ public class MessageListener {
|
||||
System.out.println(" Failed emission calculations: " + failedCalculations);
|
||||
}
|
||||
|
||||
private boolean createVMEmissionRecord(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm) {
|
||||
private boolean createVMEmissionRecord(DataCenter dataCenter, VM vm, PhysicalMachine pm) {
|
||||
try {
|
||||
// Check if VM has an ID (is persisted)
|
||||
if (vm.getId() == null) {
|
||||
@@ -595,7 +517,7 @@ public class MessageListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
MainDataTableCreateInput input = createVMMainDataTableInput(dataCenter, vm, project, pm);
|
||||
MainDataTableCreateInput input = createVMMainDataTableInput(dataCenter, vm, pm);
|
||||
System.out.println("🔍 Creating emission record for VM: " + vm.getVmName() + " (Power: " + vm.getPower() + "W)");
|
||||
|
||||
MainDataTable result = callMainDataTableMutation(input);
|
||||
@@ -619,7 +541,7 @@ public class MessageListener {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createVMEmissionRecordForSource(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm, String emissionSourceName, Integer percentage) {
|
||||
private boolean createVMEmissionRecordForSource(DataCenter dataCenter, VM vm, PhysicalMachine pm, String emissionSourceName, Integer percentage) {
|
||||
try {
|
||||
// Check if VM has an ID (is persisted)
|
||||
if (vm.getId() == null) {
|
||||
@@ -627,19 +549,56 @@ public class MessageListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the emission source by name/tag
|
||||
List<EmissionSource> emissionSources = emissionSourceRepo.findByTag(emissionSourceName);
|
||||
if (emissionSources.isEmpty()) {
|
||||
// Find the emission source by name/tag from datacenter's configured emission sources
|
||||
EmissionSource emissionSource = null;
|
||||
|
||||
// First, try to find the emission source from datacenter's configured sources
|
||||
if (dataCenter.getDataCenterEmissionSources() != null && !dataCenter.getDataCenterEmissionSources().isEmpty()) {
|
||||
for (DataCenterEmissionSource dces : dataCenter.getDataCenterEmissionSources()) {
|
||||
if (dces.getEmissionSource() != null &&
|
||||
emissionSourceName.equalsIgnoreCase(dces.getEmissionSource().getTag())) {
|
||||
emissionSource = dces.getEmissionSource();
|
||||
System.out.println("✅ Found emission source '" + emissionSourceName +
|
||||
"' in datacenter's configured sources (ID: " + emissionSource.getId() + ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not found in datacenter's sources, fall back to subsector-specific search
|
||||
if (emissionSource == null && dataCenter.getSubSector() != null) {
|
||||
emissionSource = emissionSourceRepo.findByTagAndSubSectorIgnoreCase(emissionSourceName, dataCenter.getSubSector());
|
||||
if (emissionSource != null) {
|
||||
System.out.println("⚠️ Using subsector fallback for emission source '" + emissionSourceName +
|
||||
"' (ID: " + emissionSource.getId() + ") - Consider configuring it for datacenter");
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: global search
|
||||
if (emissionSource == null) {
|
||||
List<EmissionSource> emissionSources = emissionSourceRepo.findByTag(emissionSourceName);
|
||||
if (!emissionSources.isEmpty()) {
|
||||
emissionSource = emissionSources.get(0);
|
||||
System.out.println("⚠️ Using global fallback for emission source '" + emissionSourceName +
|
||||
"' (ID: " + emissionSource.getId() + ") - This may cause incorrect calculations!");
|
||||
}
|
||||
}
|
||||
|
||||
if (emissionSource == null) {
|
||||
System.err.println("❌ Could not find emission source: " + emissionSourceName);
|
||||
return false;
|
||||
}
|
||||
|
||||
EmissionSource emissionSource = emissionSources.get(0);
|
||||
|
||||
// Calculate power consumption for this emission source (percentage of total VM power)
|
||||
double sourceSpecificPower = vm.getPower() * (percentage / 100.0);
|
||||
|
||||
MainDataTableCreateInput input = createVMMainDataTableInputForSource(dataCenter, vm, project, pm, emissionSource, sourceSpecificPower, percentage);
|
||||
MainDataTableCreateInput input = createVMMainDataTableInputForSource(dataCenter, vm, pm, emissionSource, sourceSpecificPower, percentage);
|
||||
|
||||
if (input == null) {
|
||||
System.err.println("❌ Failed to create input for VM emission calculation - skipping");
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out.println("🔍 Creating emission record for VM: " + vm.getVmName() +
|
||||
" - Source: " + emissionSourceName + " (" + percentage + "%) - Power: " + sourceSpecificPower + "W");
|
||||
|
||||
@@ -665,7 +624,7 @@ public class MessageListener {
|
||||
}
|
||||
}
|
||||
|
||||
private MainDataTableCreateInput createVMMainDataTableInput(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm) {
|
||||
private MainDataTableCreateInput createVMMainDataTableInput(DataCenter dataCenter, VM vm, PhysicalMachine pm) {
|
||||
MainDataTableCreateInput input = new MainDataTableCreateInput();
|
||||
|
||||
// Copy datacenter-level information (if available)
|
||||
@@ -692,8 +651,15 @@ public class MessageListener {
|
||||
input.setSubSector(dataCenter.getSubSector().getId());
|
||||
}
|
||||
|
||||
if (dataCenter.getEmissionSource() != null) {
|
||||
input.setEmissionSource(dataCenter.getEmissionSource().getId());
|
||||
// Handle multiple emission sources - use the default one for emission calculations
|
||||
if (dataCenter.getDataCenterEmissionSources() != null && !dataCenter.getDataCenterEmissionSources().isEmpty()) {
|
||||
// Find the default emission source or use the first one
|
||||
DataCenterEmissionSource defaultEmissionSource = dataCenter.getDataCenterEmissionSources().stream()
|
||||
.filter(DataCenterEmissionSource::getIsDefault)
|
||||
.findFirst()
|
||||
.orElse(dataCenter.getDataCenterEmissionSources().get(0));
|
||||
|
||||
input.setEmissionSource(defaultEmissionSource.getEmissionSource().getId());
|
||||
} else {
|
||||
// Fallback to default emission source for electricity
|
||||
try {
|
||||
@@ -711,8 +677,16 @@ public class MessageListener {
|
||||
input.setActivitySubUnit(dataCenter.getActivitySubUnit().getId());
|
||||
}
|
||||
|
||||
if (dataCenter.getConsuptionUnit() != null) {
|
||||
input.setConsuptionUnit(dataCenter.getConsuptionUnit().getId());
|
||||
// Handle consumption unit from emission sources - use the default one for VM calculations
|
||||
if (dataCenter.getDataCenterEmissionSources() != null && !dataCenter.getDataCenterEmissionSources().isEmpty()) {
|
||||
DataCenterEmissionSource defaultEmissionSource = dataCenter.getDataCenterEmissionSources().stream()
|
||||
.filter(DataCenterEmissionSource::getIsDefault)
|
||||
.findFirst()
|
||||
.orElse(dataCenter.getDataCenterEmissionSources().get(0));
|
||||
|
||||
if (defaultEmissionSource.getConsuptionUnit() != null) {
|
||||
input.setConsuptionUnit(defaultEmissionSource.getConsuptionUnit().getId());
|
||||
}
|
||||
}
|
||||
|
||||
// Default to Kapsam-3 if no emission scope is set
|
||||
@@ -739,69 +713,144 @@ public class MessageListener {
|
||||
System.out.println(" VM Name: " + vm.getVmName());
|
||||
System.out.println(" Power: " + vm.getPower() + "W");
|
||||
System.out.println(" Physical Machine: " + pm.getName());
|
||||
System.out.println(" Project: " + project.getName());
|
||||
System.out.println(" DataCenter Sector: " + (dataCenter.getSector() != null ? dataCenter.getSector().getTag() : "NOT SET"));
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
private MainDataTableCreateInput createVMMainDataTableInputForSource(DataCenter dataCenter, VM vm, Project project, PhysicalMachine pm, EmissionSource emissionSource, double sourceSpecificPower, Integer percentage) {
|
||||
private MainDataTableCreateInput createVMMainDataTableInputForSource(DataCenter dataCenter, VM vm, PhysicalMachine pm, EmissionSource emissionSource, double sourceSpecificPower, Integer percentage) {
|
||||
MainDataTableCreateInput input = new MainDataTableCreateInput();
|
||||
|
||||
// Copy datacenter-level information (if available)
|
||||
input.setYear("2025");
|
||||
input.setMonth("07");
|
||||
|
||||
// Validate required fields
|
||||
if (dataCenter == null) {
|
||||
System.err.println("❌ DataCenter is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (vm == null) {
|
||||
System.err.println("❌ VM is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (emissionSource == null) {
|
||||
System.err.println("❌ EmissionSource is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: These fields are no longer received in the message
|
||||
// They need to be set via DataCenter CRUD operations first
|
||||
if (dataCenter.getArea() != null && !dataCenter.getArea().getCities().isEmpty()) {
|
||||
input.setCity(dataCenter.getArea().getCities().get(0).getId());
|
||||
System.out.println("🔍 Setting City: " + dataCenter.getArea().getCities().get(0).getId());
|
||||
} else {
|
||||
System.out.println("⚠️ Warning: No city available for DataCenter");
|
||||
}
|
||||
|
||||
if (dataCenter.getArea() != null && !dataCenter.getArea().getDistricts().isEmpty()) {
|
||||
input.setDistrict(dataCenter.getArea().getDistricts().get(0).getId());
|
||||
System.out.println("🔍 Setting District: " + dataCenter.getArea().getDistricts().get(0).getId());
|
||||
} else {
|
||||
System.out.println("⚠️ Warning: No district available for DataCenter");
|
||||
}
|
||||
|
||||
if (dataCenter.getSector() != null) {
|
||||
input.setSector(dataCenter.getSector().getId());
|
||||
System.out.println("🔍 Setting Sector: " + dataCenter.getSector().getId() + " (" + dataCenter.getSector().getTag() + ")");
|
||||
} else {
|
||||
System.out.println("⚠️ Warning: DataCenter has no sector set - emission calculation may fail");
|
||||
System.err.println("❌ Error: DataCenter has no sector set - this is required for emission calculation");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dataCenter.getSubSector() != null) {
|
||||
input.setSubSector(dataCenter.getSubSector().getId());
|
||||
System.out.println("🔍 Setting SubSector: " + dataCenter.getSubSector().getId());
|
||||
} else {
|
||||
System.out.println("⚠️ Warning: DataCenter has no subsector set");
|
||||
}
|
||||
|
||||
// Use the specific emission source for this calculation
|
||||
input.setEmissionSource(emissionSource.getId());
|
||||
System.out.println("🔍 Setting EmissionSource: " + emissionSource.getId() + " (" + emissionSource.getTag() + ")");
|
||||
|
||||
if (dataCenter.getActivitySubUnit() != null) {
|
||||
input.setActivitySubUnit(dataCenter.getActivitySubUnit().getId());
|
||||
System.out.println("🔍 Setting ActivitySubUnit: " + dataCenter.getActivitySubUnit().getId());
|
||||
} else {
|
||||
System.out.println("⚠️ Warning: DataCenter has no activity sub unit set");
|
||||
}
|
||||
|
||||
if (dataCenter.getConsuptionUnit() != null) {
|
||||
input.setConsuptionUnit(dataCenter.getConsuptionUnit().getId());
|
||||
// Handle consumption unit from emission sources - use the emission source's unit
|
||||
if (emissionSource != null) {
|
||||
// Find the DataCenterEmissionSource that matches this emissionSource
|
||||
System.out.println("🔍 Looking for consumption unit for emission source: " + emissionSource.getTag());
|
||||
|
||||
if (dataCenter.getDataCenterEmissionSources() == null || dataCenter.getDataCenterEmissionSources().isEmpty()) {
|
||||
System.err.println("❌ Error: DataCenter has no emission sources configured - cannot find consumption unit");
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean foundUnit = false;
|
||||
for (DataCenterEmissionSource dces : dataCenter.getDataCenterEmissionSources()) {
|
||||
if (dces.getEmissionSource() != null && dces.getEmissionSource().getId().equals(emissionSource.getId())) {
|
||||
if (dces.getConsuptionUnit() != null) {
|
||||
input.setConsuptionUnit(dces.getConsuptionUnit().getId());
|
||||
System.out.println("🔍 Setting ConsumptionUnit: " + dces.getConsuptionUnit().getId() + " (" + dces.getConsuptionUnit().getTag() + ")");
|
||||
foundUnit = true;
|
||||
break;
|
||||
} else {
|
||||
System.err.println("❌ Error: DataCenterEmissionSource has no consumption unit set for emission source: " + emissionSource.getTag());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundUnit) {
|
||||
System.err.println("❌ Error: Could not find matching DataCenterEmissionSource for emission source: " + emissionSource.getTag());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to Kapsam-3 if no emission scope is set
|
||||
input.setScope(dataCenter.getEmissionScope() != null ?
|
||||
dataCenter.getEmissionScope().getTag().equals("Kapsam-3") : true);
|
||||
System.out.println("🔍 Setting Scope: " + (dataCenter.getEmissionScope() != null ?
|
||||
dataCenter.getEmissionScope().getTag() : "Kapsam-3 (default)"));
|
||||
|
||||
try {
|
||||
List<Organization> organizations = organizationRepo.findAll();
|
||||
if (!organizations.isEmpty()) {
|
||||
input.setOrganization(organizations.get(0).getId());
|
||||
System.out.println("🔍 Setting Organization: " + organizations.get(0).getId());
|
||||
} else {
|
||||
System.err.println("❌ Error: No organizations found in database - this is required");
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Error finding organization: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set VM-specific fields
|
||||
input.setVmId(vm.getId());
|
||||
System.out.println("🔍 Setting VM ID: " + vm.getId());
|
||||
|
||||
// Use the source-specific power consumption (percentage of total VM power)
|
||||
input.setConsuptionAmount(String.valueOf(sourceSpecificPower));
|
||||
// Use the source-specific power consumption (percentage of total VM power)
|
||||
// Format to 6 decimal places to avoid very long strings
|
||||
String formattedPower = String.format("%.6f", sourceSpecificPower);
|
||||
input.setConsuptionAmount(formattedPower);
|
||||
System.out.println("🔍 Setting Consumption Amount: " + formattedPower + "W");
|
||||
|
||||
// Validate field lengths to prevent database errors
|
||||
System.out.println("🔍 Field length validation:");
|
||||
System.out.println(" Year: " + (input.getYear() != null ? input.getYear().length() : "null"));
|
||||
System.out.println(" Month: " + (input.getMonth() != null ? input.getMonth().length() : "null"));
|
||||
System.out.println(" ConsuptionAmount: " + (input.getConsuptionAmount() != null ? input.getConsuptionAmount().length() : "null"));
|
||||
|
||||
|
||||
System.out.println("🔍 VM Emission Input for Source:");
|
||||
System.out.println(" VM ID: " + vm.getId());
|
||||
System.out.println(" VM Name: " + vm.getVmName());
|
||||
@@ -809,7 +858,6 @@ public class MessageListener {
|
||||
System.out.println(" Percentage: " + percentage + "%");
|
||||
System.out.println(" Source Power: " + sourceSpecificPower + "W");
|
||||
System.out.println(" Physical Machine: " + pm.getName());
|
||||
System.out.println(" Project: " + project.getName());
|
||||
System.out.println(" DataCenter Sector: " + (dataCenter.getSector() != null ? dataCenter.getSector().getTag() : "NOT SET"));
|
||||
|
||||
return input;
|
||||
@@ -828,6 +876,34 @@ public class MessageListener {
|
||||
try {
|
||||
System.out.println("🔄 Calling mainDataTableMutation.createMainDataTable...");
|
||||
|
||||
// Validate input fields before making the call
|
||||
if (input.getSector() == null) {
|
||||
System.err.println("❌ Sector is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.getEmissionSource() == null) {
|
||||
System.err.println("❌ EmissionSource is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.getConsuptionUnit() == null) {
|
||||
System.err.println("❌ ConsumptionUnit is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.getOrganization() == null) {
|
||||
System.err.println("❌ Organization is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input.getVmId() == null) {
|
||||
System.err.println("❌ VM ID is null - cannot create emission record");
|
||||
return null;
|
||||
}
|
||||
|
||||
System.out.println("🔍 Input validation passed - proceeding with mutation call");
|
||||
|
||||
// Call the mutation method - pass null environment since we've already patched the logging
|
||||
MainDataTable result = mainDataTableMutation.createMainDataTable(input, null);
|
||||
|
||||
|
||||
@@ -5,10 +5,13 @@ input DataCenterCreateInput {
|
||||
sectorId: ID
|
||||
subSectorId: ID
|
||||
activitySubUnitId: ID
|
||||
emissionSourceId: ID
|
||||
consuptionUnitId: ID
|
||||
|
||||
# Multiple emission sources support - each with exactly one unit
|
||||
dataCenterEmissionSources: [DataCenterEmissionSourceInput!]
|
||||
|
||||
consuptionAmount: Float
|
||||
areaId: ID
|
||||
cityId: ID
|
||||
number: Int
|
||||
ayposURL: String
|
||||
address: String
|
||||
@@ -23,13 +26,23 @@ input DataCenterUpdateInput {
|
||||
sectorId: ID
|
||||
subSectorId: ID
|
||||
activitySubUnitId: ID
|
||||
emissionSourceId: ID
|
||||
consuptionUnitId: ID
|
||||
|
||||
# Multiple emission sources support - each with exactly one unit
|
||||
dataCenterEmissionSources: [DataCenterEmissionSourceInput!]
|
||||
|
||||
consuptionAmount: Float
|
||||
areaId: ID
|
||||
cityId: ID
|
||||
number: Int
|
||||
ayposURL: String
|
||||
address: String
|
||||
latitude: Float
|
||||
longitude: Float
|
||||
}
|
||||
|
||||
input DataCenterEmissionSourceInput {
|
||||
emissionSourceId: ID!
|
||||
consuptionUnitId: ID!
|
||||
isDefault: Boolean
|
||||
percentage: Float
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ extend type Query{
|
||||
dataCenters(criteria: DataCenterCriteria, sortBy: [SortBy!]): [DataCenter!]
|
||||
paginateDataCenters(pagination : Pagination!, criteria: DataCenterCriteria, sortBy:[SortBy!] ) : DataCenterPageable!
|
||||
getByNumber(number: Int!): DataCenter
|
||||
physicalMachines(datacenterId: ID!): [PhysicalMachine!]!
|
||||
}
|
||||
@@ -7,12 +7,15 @@ type DataCenter {
|
||||
sector: Sector
|
||||
subSector: SubSector
|
||||
activitySubUnit: ActivitySubUnit
|
||||
emissionSource: EmissionSource
|
||||
consuptionUnit: ConsuptionUnit
|
||||
|
||||
# Multiple emission sources support - each with exactly one unit
|
||||
dataCenterEmissionSources: [DataCenterEmissionSource]
|
||||
|
||||
consuptionAmount: Float
|
||||
|
||||
projects: [Project]
|
||||
physicalMachines: [PhysicalMachine]
|
||||
area: Area
|
||||
city: City
|
||||
number: Int
|
||||
|
||||
ayposURL: String
|
||||
@@ -21,10 +24,12 @@ type DataCenter {
|
||||
longitude: Float
|
||||
}
|
||||
|
||||
type Project {
|
||||
type DataCenterEmissionSource {
|
||||
id: ID
|
||||
name: String
|
||||
physicalMachines: [PhysicalMachine]
|
||||
emissionSource: EmissionSource
|
||||
consuptionUnit: ConsuptionUnit
|
||||
isDefault: Boolean
|
||||
percentage: Float
|
||||
}
|
||||
|
||||
type PhysicalMachine {
|
||||
@@ -32,11 +37,13 @@ type PhysicalMachine {
|
||||
name: String
|
||||
ip: String
|
||||
tag: String
|
||||
cloudSystem: String
|
||||
power: Float
|
||||
vms: [Vm]
|
||||
vms: [VM]
|
||||
dataCenter: DataCenter
|
||||
}
|
||||
|
||||
type Vm {
|
||||
type VM {
|
||||
id: ID
|
||||
state: String
|
||||
vmName: String
|
||||
@@ -47,8 +54,10 @@ type Vm {
|
||||
host: String
|
||||
flavorName: String
|
||||
tag: String
|
||||
project: String
|
||||
emissionSource: EmissionSourceMap
|
||||
config: Config
|
||||
physicalMachine: PhysicalMachine
|
||||
}
|
||||
|
||||
scalar EmissionSourceMap
|
||||
|
||||
@@ -2,5 +2,5 @@ extend type Query{
|
||||
mainDataTable(id: ID!): MainDataTable!
|
||||
mainDataTables(criteria: MainDataTableCriteria, sortBy: [SortBy!]): [MainDataTable!]
|
||||
paginateMainDataTables(pagination : Pagination!, criteria: MainDataTableCriteria, sortBy:[SortBy!] ) : MainDataTablePageable!
|
||||
vmEmissionSummary: [VMEmissionSummary!]!
|
||||
vmEmissionSummary(datacenterId: ID): [VMEmissionSummary!]!
|
||||
}
|
||||
@@ -26,11 +26,11 @@ type MainDataTable {
|
||||
proteinAmount:Float
|
||||
burnOrOpenBurn:Boolean
|
||||
scopeCheck:Boolean
|
||||
vm: Vm
|
||||
vm: VM
|
||||
|
||||
}
|
||||
|
||||
type Vm {
|
||||
type VM {
|
||||
id: ID
|
||||
state: String
|
||||
vmName: String
|
||||
@@ -41,6 +41,7 @@ type Vm {
|
||||
host: String
|
||||
flavorName: String
|
||||
tag: String
|
||||
project: String
|
||||
config: Config
|
||||
}
|
||||
|
||||
@@ -51,22 +52,22 @@ type Config {
|
||||
disk: Int
|
||||
}
|
||||
|
||||
type VMEmissionSummary {
|
||||
vmId: ID!
|
||||
vmName: String!
|
||||
vmPower: Float
|
||||
vmStatus: String
|
||||
totalEmission: Float!
|
||||
createdDate: LocalDateTime!
|
||||
physicalMachine: String
|
||||
project: String
|
||||
dataCenter: String
|
||||
# Individual emission values per record
|
||||
co2: Float!
|
||||
ch4: Float!
|
||||
n2o: Float!
|
||||
reportGeneratedTime: LocalDateTime
|
||||
}
|
||||
type VMEmissionSummary {
|
||||
vmId: ID!
|
||||
vmName: String
|
||||
vmPower: Float
|
||||
vmStatus: String
|
||||
totalEmission: Float!
|
||||
createdDate: LocalDateTime!
|
||||
physicalMachine: String
|
||||
cloudSystem: String
|
||||
dataCenter: String
|
||||
# Individual emission values per record
|
||||
co2: Float!
|
||||
ch4: Float!
|
||||
n2o: Float!
|
||||
reportGeneratedTime: LocalDateTime
|
||||
}
|
||||
|
||||
type SolidWasteSupplement {
|
||||
id: ID!
|
||||
|
||||
@@ -1,104 +1,108 @@
|
||||
# SGE System Frontend
|
||||
|
||||
## Overview
|
||||
|
||||
The frontend of the SGE System is a modern web application built with **React**, providing an interactive and user-friendly interface. It communicates with the backend API to deliver a seamless experience for users.
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **React** – UI library
|
||||
- **React Router** – Routing
|
||||
- **i18n** – Internationalization
|
||||
- **Docker** – Containerization
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
SGE/
|
||||
├── sge-backend/ # Backend source code
|
||||
├── sge-frontend/ # Frontend source code
|
||||
├── config.conf # Centralized configuration file (see below)
|
||||
├── docker-compose.yml # Docker Compose file (see below)
|
||||
```
|
||||
|
||||
## Setup and Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js** (v14.0.0 required) and npm
|
||||
- **Docker** (for containerized deployment)
|
||||
|
||||
#### Node.js Version Management
|
||||
|
||||
If you need to switch Node versions, use nvm:
|
||||
|
||||
```sh
|
||||
nvm install v14.0.0
|
||||
nvm use v14.0.0
|
||||
unset NODE_OPTIONS
|
||||
```
|
||||
|
||||
### Frontend Setup
|
||||
|
||||
#### Local Development
|
||||
|
||||
1. Clone the repository and navigate to the frontend directory:
|
||||
|
||||
```sh
|
||||
git clone <repository-url>
|
||||
cd SGE/sge-frontend
|
||||
```
|
||||
|
||||
2. Ensure `config.conf` is in the project root (`SGE/`).
|
||||
You can either:
|
||||
|
||||
- Manually set environment variables from `config.conf`
|
||||
- Or configure your IDE to load variables from `config.conf`
|
||||
|
||||
3. Install dependencies:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
4. Start the development server:
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
#### Docker Deployment
|
||||
|
||||
1. Ensure both `config.conf` and `docker-compose.yml` are in the project root (`SGE/`).
|
||||
2. From the root directory, build and run the frontend (and backend) using Docker:
|
||||
```sh
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
## Configuration Management
|
||||
|
||||
The SGE System uses a centralized `config.conf` file for all environment variables.
|
||||
**Location:** Place `config.conf` in the root directory of your project, alongside `docker-compose.yml`.
|
||||
|
||||
This file contains environment variables for both backend and frontend components, including:
|
||||
|
||||
- API connection settings
|
||||
- Mail configuration
|
||||
- Application URLs
|
||||
|
||||
When running with Docker, environment variables are automatically loaded from `config.conf` via the `env_file` directive in `docker-compose.yml`.
|
||||
|
||||
For local development, you can either:
|
||||
|
||||
- Use the same `config.conf` file and manually set the environment variables
|
||||
- Configure your IDE to load these variables from the file
|
||||
|
||||
Refer to the backend README for more details on configuration options and structure.
|
||||
|
||||
## Integration with Backend
|
||||
|
||||
The frontend expects the backend API to be available at the host and port specified in `config.conf` (`API_PROTOCOL`, `API_HOST`, `SERVER_PORT`).
|
||||
Ensure both services are configured consistently.
|
||||
|
||||
## License
|
||||
|
||||
[Insert your license information here]
|
||||
# SGE System Frontend
|
||||
|
||||
## Overview
|
||||
|
||||
The frontend of the SGE System is a modern web application built with **React**, providing an interactive and user-friendly interface. It communicates with the backend API to deliver a seamless experience for users.
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **React** – UI library
|
||||
- **React Router** – Routing
|
||||
- **i18n** – Internationalization
|
||||
- **Docker** – Containerization
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
SGE/
|
||||
├── sge-backend/ # Backend source code
|
||||
├── sge-frontend/ # Frontend source code
|
||||
├── config.conf # Centralized configuration file (see below)
|
||||
├── docker-compose.yml # Docker Compose file (see below)
|
||||
```
|
||||
|
||||
## Setup and Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js** (v14.0.0 required) and npm
|
||||
- **Docker** (for containerized deployment)
|
||||
|
||||
#### Node.js Version Management
|
||||
|
||||
If you need to switch Node versions, use nvm:
|
||||
|
||||
```sh
|
||||
nvm install v14.0.0
|
||||
nvm use v14.0.0
|
||||
unset NODE_OPTIONS
|
||||
```
|
||||
|
||||
### Frontend Setup
|
||||
|
||||
#### Local Development
|
||||
|
||||
1. Clone the repository and navigate to the frontend directory:
|
||||
|
||||
```sh
|
||||
git clone <repository-url>
|
||||
cd SGE/sge-frontend
|
||||
```
|
||||
|
||||
2. Ensure `config.conf` is in the project root (`SGE/`).
|
||||
You can either:
|
||||
|
||||
- Manually set environment variables from `config.conf`
|
||||
- Or configure your IDE to load variables from `config.conf`
|
||||
|
||||
3. Install dependencies:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
4. Start the development server:
|
||||
```sh
|
||||
npm start
|
||||
```
|
||||
|
||||
#### Docker Deployment
|
||||
|
||||
1. Ensure both `config.conf` and `docker-compose.yml` are in the project root (`SGE/`).
|
||||
2. From the root directory, build and run the frontend (and backend) using Docker:
|
||||
```sh
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
## Configuration Management
|
||||
|
||||
The SGE System uses a centralized `config.conf` file for all environment variables.
|
||||
**Location:** Place `config.conf` in the root directory of your project, alongside `docker-compose.yml`.
|
||||
|
||||
This file contains environment variables for both backend and frontend components, including:
|
||||
|
||||
- API connection settings
|
||||
- Mail configuration
|
||||
- Application URLs
|
||||
|
||||
When running with Docker, environment variables are automatically loaded from `config.conf` via the `env_file` directive in `docker-compose.yml`.
|
||||
|
||||
For local development, you can either:
|
||||
|
||||
- Use the same `config.conf` file and manually set the environment variables
|
||||
- Configure your IDE to load these variables from the file
|
||||
|
||||
Refer to the backend README for more details on configuration options and structure.
|
||||
|
||||
## Integration with Backend
|
||||
|
||||
The frontend expects the backend API to be available at the host and port specified in `config.conf` (`API_PROTOCOL`, `API_HOST`, `SERVER_PORT`).
|
||||
Ensure both services are configured consistently.
|
||||
|
||||
## License
|
||||
|
||||
[Insert your license information here]
|
||||
|
||||
## CI/CD Testing v20
|
||||
|
||||
|
||||
|
||||
@@ -1,39 +1,32 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name bgreen.blc-css.com;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
|
||||
location /api/v1/graphql {
|
||||
proxy_pass http://bgreen-backend:8080/api/v1/graphql;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
location /api/v1/upload {
|
||||
proxy_pass http://bgreen-backend:8080/upload;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# location /api/v1/datacenter {
|
||||
# proxy_pass http://backend:8080/api/v1/datacenter;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# }
|
||||
|
||||
# Hata durumlarında da index.html'i sun
|
||||
error_page 404 /index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 10240;
|
||||
gzip_proxied any;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml application/rss+xml application/atom+xml image/svg+xml;
|
||||
gzip_comp_level 5;
|
||||
}
|
||||
}
|
||||
@@ -1,131 +1,131 @@
|
||||
{
|
||||
"name": "sgs-web",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.13.8",
|
||||
"@casl/react": "4.0.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.10.6",
|
||||
"@mui/icons-material": "^5.2.4",
|
||||
"@mui/material": "^5.2.4",
|
||||
"animate.css": "4.1.1",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "4.5.2",
|
||||
"chart.js": "^4.3.0",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"classnames": "2.2.6",
|
||||
"dotenv": "^14.3.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"graphql": "^16.11.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"i18next": "^22.4.14",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next-http-backend": "^2.2.0",
|
||||
"jquery": "^3.7.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"leaflet": "^1.6.0",
|
||||
"material-react-table": "^1.14.0",
|
||||
"moment": "2.29.1",
|
||||
"notistack": "^2.0.8",
|
||||
"postcss-rtl": "1.5.0",
|
||||
"prop-types": "15.7.2",
|
||||
"react": "17.0.1",
|
||||
"react-bootstrap": "^2.10.6",
|
||||
"react-chartjs-2": "^5.2.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-data-table-component": "^7.5.0",
|
||||
"react-datepicker": "^4.16.0",
|
||||
"react-dom": "17.0.1",
|
||||
"react-feather": "~2.0.3",
|
||||
"react-i18next": "^12.2.0",
|
||||
"react-intl": "6.0.5",
|
||||
"react-leaflet": "^3.2.5",
|
||||
"react-paginate": "8.2.0",
|
||||
"react-perfect-scrollbar": "^1.5.5",
|
||||
"react-redux": "7.2.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scroll-up": "1.3.7",
|
||||
"react-select": "4.0.2",
|
||||
"react-toastify": "^7.0.3",
|
||||
"reactstrap": "9.2.3",
|
||||
"redux": "4.0.5",
|
||||
"redux-debounced": "0.5.0",
|
||||
"redux-thunk": "2.3.0",
|
||||
"styled-components": "^5.3.6",
|
||||
"sweetalert2": "11.0.0",
|
||||
"sweetalert2-react-content": "4.2.0",
|
||||
"swiper": "6.0.4",
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^1.0.1",
|
||||
"websocket": "^1.0.34",
|
||||
"wnumb": "1.2.0",
|
||||
"xlsx": "^0.18.5",
|
||||
"yarn": "1.21.1",
|
||||
"yup": "0.32.8"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-app-rewired start",
|
||||
"build:dev": "react-app-rewired build --mode development",
|
||||
"build:prod": "react-app-rewired build --mode production",
|
||||
"build": "react-app-rewired build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "eslint src/**/*.js src/**/*.jsx",
|
||||
"lint:fix": "eslint src/**/*.js --fix"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.27.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/preset-env": "^7.27.1",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@swc/core": "^1.11.24",
|
||||
"@types/leaflet": "^1.7.5",
|
||||
"@types/sortablejs": "^1.10.6",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-styled-components": "^2.1.4",
|
||||
"core-js": "^3.42.0",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-react": "^7.33.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"react-app-rewire-postcss": "^3.0.2",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-error-overlay": "^6.0.9",
|
||||
"react-loadable": "^5.5.0",
|
||||
"react-scripts": "^5.0.1",
|
||||
"sass": "^1.81.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"swc-loader": "^0.2.6",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack": "^5.96.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"react-error-overlay": "6.0.9"
|
||||
},
|
||||
"homepage": ""
|
||||
}
|
||||
{
|
||||
"name": "sgs-web",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.13.8",
|
||||
"@casl/react": "4.0.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.10.6",
|
||||
"@mui/icons-material": "^5.2.4",
|
||||
"@mui/material": "^5.2.4",
|
||||
"animate.css": "4.1.1",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "4.5.2",
|
||||
"chart.js": "^4.3.0",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"classnames": "2.2.6",
|
||||
"dotenv": "^14.3.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"graphql": "^16.11.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"i18next": "^22.4.14",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next-http-backend": "^2.2.0",
|
||||
"jquery": "^3.7.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"leaflet": "^1.6.0",
|
||||
"material-react-table": "^1.14.0",
|
||||
"moment": "2.29.1",
|
||||
"notistack": "^2.0.8",
|
||||
"postcss-rtl": "1.5.0",
|
||||
"prop-types": "15.7.2",
|
||||
"react": "17.0.1",
|
||||
"react-bootstrap": "^2.10.6",
|
||||
"react-chartjs-2": "^5.2.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-data-table-component": "^7.5.0",
|
||||
"react-datepicker": "^4.16.0",
|
||||
"react-dom": "17.0.1",
|
||||
"react-feather": "~2.0.3",
|
||||
"react-i18next": "^12.2.0",
|
||||
"react-intl": "6.0.5",
|
||||
"react-leaflet": "^3.2.5",
|
||||
"react-paginate": "8.2.0",
|
||||
"react-perfect-scrollbar": "^1.5.5",
|
||||
"react-redux": "7.2.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scroll-up": "1.3.7",
|
||||
"react-select": "4.0.2",
|
||||
"react-toastify": "^7.0.3",
|
||||
"reactstrap": "9.2.3",
|
||||
"redux": "4.0.5",
|
||||
"redux-debounced": "0.5.0",
|
||||
"redux-thunk": "2.3.0",
|
||||
"styled-components": "^5.3.6",
|
||||
"sweetalert2": "11.0.0",
|
||||
"sweetalert2-react-content": "4.2.0",
|
||||
"swiper": "6.0.4",
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^1.0.1",
|
||||
"websocket": "^1.0.34",
|
||||
"wnumb": "1.2.0",
|
||||
"xlsx": "^0.18.5",
|
||||
"yarn": "1.21.1",
|
||||
"yup": "0.32.8"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-app-rewired start",
|
||||
"build:dev": "react-app-rewired build --mode development",
|
||||
"build:prod": "react-app-rewired build --mode production",
|
||||
"build": "react-app-rewired build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "eslint src/**/*.js src/**/*.jsx",
|
||||
"lint:fix": "eslint src/**/*.js --fix"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.27.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/preset-env": "^7.27.1",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@swc/core": "^1.11.24",
|
||||
"@types/leaflet": "^1.7.5",
|
||||
"@types/sortablejs": "^1.10.6",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-styled-components": "^2.1.4",
|
||||
"core-js": "^3.42.0",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-react": "^7.33.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"react-app-rewire-postcss": "^3.0.2",
|
||||
"react-app-rewired": "^2.2.1",
|
||||
"react-error-overlay": "^6.0.9",
|
||||
"react-loadable": "^5.5.0",
|
||||
"react-scripts": "^5.0.1",
|
||||
"sass": "^1.81.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"swc-loader": "^0.2.6",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack": "^5.96.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"react-error-overlay": "6.0.9"
|
||||
},
|
||||
"homepage": ""
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ export const getDataCenters = () => {
|
||||
name
|
||||
}
|
||||
}
|
||||
city {
|
||||
id
|
||||
name
|
||||
}
|
||||
emissionScope {
|
||||
id
|
||||
tag
|
||||
@@ -58,41 +62,42 @@ export const getDataCenters = () => {
|
||||
id
|
||||
tag
|
||||
}
|
||||
emissionSource {
|
||||
dataCenterEmissionSources {
|
||||
id
|
||||
tag
|
||||
}
|
||||
consuptionUnit {
|
||||
id
|
||||
description
|
||||
emissionSource {
|
||||
id
|
||||
tag
|
||||
}
|
||||
consuptionUnit {
|
||||
id
|
||||
description
|
||||
}
|
||||
isDefault
|
||||
percentage
|
||||
}
|
||||
activitySubUnit {
|
||||
id
|
||||
tag
|
||||
}
|
||||
projects {
|
||||
physicalMachines {
|
||||
id
|
||||
name
|
||||
physicalMachines {
|
||||
vms {
|
||||
id
|
||||
name
|
||||
vms {
|
||||
id
|
||||
vmName
|
||||
state
|
||||
power
|
||||
calcOn
|
||||
hostingPm
|
||||
host
|
||||
flavorName
|
||||
tag
|
||||
config {
|
||||
id
|
||||
cpu
|
||||
ram
|
||||
disk
|
||||
}
|
||||
}
|
||||
vmName
|
||||
state
|
||||
power
|
||||
calcOn
|
||||
hostingPm
|
||||
host
|
||||
flavorName
|
||||
tag
|
||||
config {
|
||||
id
|
||||
cpu
|
||||
ram
|
||||
disk
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +116,7 @@ export const getDataCenters = () => {
|
||||
console.log("GraphQL Response:", {
|
||||
status: response.status,
|
||||
data: response.data,
|
||||
errors: response.data?.errors
|
||||
errors: response.data?.errors,
|
||||
});
|
||||
|
||||
// Check for GraphQL errors
|
||||
@@ -125,7 +130,7 @@ export const getDataCenters = () => {
|
||||
}
|
||||
|
||||
const dataCenters = response.data.data.dataCenters;
|
||||
|
||||
|
||||
// Validate dataCenters is an array
|
||||
if (!Array.isArray(dataCenters)) {
|
||||
throw new Error("Invalid response: dataCenters is not an array");
|
||||
@@ -144,7 +149,7 @@ export const getDataCenters = () => {
|
||||
message: error.message,
|
||||
response: error.response?.data,
|
||||
status: error.response?.status,
|
||||
stack: error.stack
|
||||
stack: error.stack,
|
||||
});
|
||||
|
||||
// Check for specific error types
|
||||
@@ -200,6 +205,10 @@ export const createDataCenter = (dataCenterData) => {
|
||||
name
|
||||
}
|
||||
}
|
||||
city {
|
||||
id
|
||||
name
|
||||
}
|
||||
emissionScope {
|
||||
id
|
||||
tag
|
||||
@@ -213,13 +222,18 @@ export const createDataCenter = (dataCenterData) => {
|
||||
id
|
||||
tag
|
||||
}
|
||||
emissionSource {
|
||||
dataCenterEmissionSources {
|
||||
id
|
||||
tag
|
||||
}
|
||||
consuptionUnit {
|
||||
id
|
||||
description
|
||||
emissionSource {
|
||||
id
|
||||
tag
|
||||
}
|
||||
consuptionUnit {
|
||||
id
|
||||
description
|
||||
}
|
||||
isDefault
|
||||
percentage
|
||||
}
|
||||
activitySubUnit {
|
||||
id
|
||||
@@ -235,17 +249,22 @@ export const createDataCenter = (dataCenterData) => {
|
||||
ayposURL: dataCenterData.ayposURL || "",
|
||||
number: parseInt(dataCenterData.number) || 1,
|
||||
areaId: dataCenterData.areaId || null,
|
||||
cityId: dataCenterData.cityId || null,
|
||||
address: dataCenterData.address || "",
|
||||
latitude: dataCenterData.latitude ? parseFloat(dataCenterData.latitude) : null,
|
||||
longitude: dataCenterData.longitude ? parseFloat(dataCenterData.longitude) : null,
|
||||
latitude: dataCenterData.latitude
|
||||
? parseFloat(dataCenterData.latitude)
|
||||
: null,
|
||||
longitude: dataCenterData.longitude
|
||||
? parseFloat(dataCenterData.longitude)
|
||||
: null,
|
||||
emissionScopeId: dataCenterData.emissionScopeId || null,
|
||||
sectorId: dataCenterData.sectorId || null,
|
||||
subSectorId: dataCenterData.subSectorId || null,
|
||||
emissionSourceId: dataCenterData.emissionSourceId || null,
|
||||
consuptionUnitId: dataCenterData.consuptionUnitId || null,
|
||||
activitySubUnitId: dataCenterData.activitySubUnitId || null
|
||||
}
|
||||
}
|
||||
dataCenterEmissionSources:
|
||||
dataCenterData.dataCenterEmissionSources || [],
|
||||
activitySubUnitId: dataCenterData.activitySubUnitId || null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -258,7 +277,7 @@ export const createDataCenter = (dataCenterData) => {
|
||||
console.log("Create Response:", {
|
||||
status: response.status,
|
||||
data: response.data,
|
||||
errors: response.data?.errors
|
||||
errors: response.data?.errors,
|
||||
});
|
||||
|
||||
if (response.data?.errors) {
|
||||
@@ -321,6 +340,10 @@ export const updateDataCenter = (id, dataCenterData) => {
|
||||
name
|
||||
}
|
||||
}
|
||||
city {
|
||||
id
|
||||
name
|
||||
}
|
||||
emissionScope {
|
||||
id
|
||||
tag
|
||||
@@ -334,13 +357,18 @@ export const updateDataCenter = (id, dataCenterData) => {
|
||||
id
|
||||
tag
|
||||
}
|
||||
emissionSource {
|
||||
dataCenterEmissionSources {
|
||||
id
|
||||
tag
|
||||
}
|
||||
consuptionUnit {
|
||||
id
|
||||
description
|
||||
emissionSource {
|
||||
id
|
||||
tag
|
||||
}
|
||||
consuptionUnit {
|
||||
id
|
||||
description
|
||||
}
|
||||
isDefault
|
||||
percentage
|
||||
}
|
||||
activitySubUnit {
|
||||
id
|
||||
@@ -357,17 +385,22 @@ export const updateDataCenter = (id, dataCenterData) => {
|
||||
ayposURL: dataCenterData.ayposURL || "",
|
||||
number: parseInt(dataCenterData.number) || 1,
|
||||
areaId: dataCenterData.areaId || null,
|
||||
cityId: dataCenterData.cityId || null,
|
||||
address: dataCenterData.address || "",
|
||||
latitude: dataCenterData.latitude ? parseFloat(dataCenterData.latitude) : null,
|
||||
longitude: dataCenterData.longitude ? parseFloat(dataCenterData.longitude) : null,
|
||||
latitude: dataCenterData.latitude
|
||||
? parseFloat(dataCenterData.latitude)
|
||||
: null,
|
||||
longitude: dataCenterData.longitude
|
||||
? parseFloat(dataCenterData.longitude)
|
||||
: null,
|
||||
emissionScopeId: dataCenterData.emissionScopeId || null,
|
||||
sectorId: dataCenterData.sectorId || null,
|
||||
subSectorId: dataCenterData.subSectorId || null,
|
||||
emissionSourceId: dataCenterData.emissionSourceId || null,
|
||||
consuptionUnitId: dataCenterData.consuptionUnitId || null,
|
||||
activitySubUnitId: dataCenterData.activitySubUnitId || null
|
||||
}
|
||||
}
|
||||
dataCenterEmissionSources:
|
||||
dataCenterData.dataCenterEmissionSources || [],
|
||||
activitySubUnitId: dataCenterData.activitySubUnitId || null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -382,7 +415,7 @@ export const updateDataCenter = (id, dataCenterData) => {
|
||||
|
||||
dispatch({
|
||||
type: "UPDATE_DATA_CENTER_SUCCESS",
|
||||
payload: response.data.data.updateDataCenter
|
||||
payload: response.data.data.updateDataCenter,
|
||||
});
|
||||
|
||||
return response.data.data.updateDataCenter;
|
||||
@@ -415,8 +448,8 @@ export const deleteDataCenter = (id) => {
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: id
|
||||
}
|
||||
id: id,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -431,7 +464,7 @@ export const deleteDataCenter = (id) => {
|
||||
|
||||
dispatch({
|
||||
type: "DELETE_DATA_CENTER_SUCCESS",
|
||||
payload: id
|
||||
payload: id,
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -466,7 +499,7 @@ export const getEmissionScopes = () => {
|
||||
description
|
||||
}
|
||||
}
|
||||
`
|
||||
`,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -481,7 +514,7 @@ export const getEmissionScopes = () => {
|
||||
|
||||
dispatch({
|
||||
type: "GET_EMISSION_SCOPES_SUCCESS",
|
||||
payload: response.data.data.emissionScopes
|
||||
payload: response.data.data.emissionScopes,
|
||||
});
|
||||
|
||||
return response.data.data.emissionScopes;
|
||||
@@ -502,65 +535,59 @@ export const getDataCenterVMs = (dataCenterId) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// Don't make the request if dataCenterId is undefined, null, or empty
|
||||
if (!dataCenterId || dataCenterId === "undefined") {
|
||||
console.log('getDataCenterVMs: No dataCenterId provided');
|
||||
console.log("getDataCenterVMs: No dataCenterId provided");
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('getDataCenterVMs: Fetching VMs for data center:', dataCenterId);
|
||||
const response = await ApplicationService.http()
|
||||
.post(
|
||||
"/graphql",
|
||||
{
|
||||
query: `
|
||||
console.log(
|
||||
"getDataCenterVMs: Fetching VMs for data center:",
|
||||
dataCenterId
|
||||
);
|
||||
const response = await ApplicationService.http().post(
|
||||
"/graphql",
|
||||
{
|
||||
query: `
|
||||
{
|
||||
dataCenter(id: "${dataCenterId}") {
|
||||
id
|
||||
dataCenter
|
||||
projects {
|
||||
physicalMachines {
|
||||
id
|
||||
name
|
||||
physicalMachines {
|
||||
vms {
|
||||
id
|
||||
name
|
||||
vms {
|
||||
id
|
||||
name
|
||||
status
|
||||
power
|
||||
}
|
||||
vmName
|
||||
state
|
||||
power
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer " + localStorage.getItem("accessToken"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer " + localStorage.getItem("accessToken"),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const dataCenter = response?.data?.data?.dataCenter;
|
||||
console.log('getDataCenterVMs: Data center response:', dataCenter);
|
||||
|
||||
console.log("getDataCenterVMs: Data center response:", dataCenter);
|
||||
|
||||
let allVMs = [];
|
||||
|
||||
if (dataCenter && dataCenter.projects) {
|
||||
dataCenter.projects.forEach(project => {
|
||||
if (project.physicalMachines) {
|
||||
project.physicalMachines.forEach(pm => {
|
||||
if (pm.vms) {
|
||||
allVMs = allVMs.concat(pm.vms);
|
||||
}
|
||||
});
|
||||
|
||||
if (dataCenter && dataCenter.physicalMachines) {
|
||||
dataCenter.physicalMachines.forEach((pm) => {
|
||||
if (pm.vms) {
|
||||
allVMs = allVMs.concat(pm.vms);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('getDataCenterVMs: Found VMs:', allVMs);
|
||||
|
||||
console.log("getDataCenterVMs: Found VMs:", allVMs);
|
||||
resolve(allVMs);
|
||||
} catch (error) {
|
||||
console.error("Error fetching VMs by data center:", error);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import ApplicationService from "../../../services/ApplicationService";
|
||||
|
||||
export const getVMEmissionSummary = () => {
|
||||
export const getVMEmissionSummary = (datacenterId) => {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const response = await ApplicationService.http()
|
||||
@@ -8,8 +8,8 @@ export const getVMEmissionSummary = () => {
|
||||
"/graphql",
|
||||
{
|
||||
query: `
|
||||
{
|
||||
vmEmissionSummary {
|
||||
query GetVMEmissions($datacenterId: ID) {
|
||||
vmEmissionSummary(datacenterId: $datacenterId) {
|
||||
vmId
|
||||
vmName
|
||||
vmPower
|
||||
@@ -17,7 +17,7 @@ export const getVMEmissionSummary = () => {
|
||||
totalEmission
|
||||
createdDate
|
||||
physicalMachine
|
||||
project
|
||||
cloudSystem
|
||||
dataCenter
|
||||
co2
|
||||
ch4
|
||||
@@ -25,7 +25,10 @@ export const getVMEmissionSummary = () => {
|
||||
reportGeneratedTime
|
||||
}
|
||||
}
|
||||
`
|
||||
`,
|
||||
variables: {
|
||||
datacenterId: datacenterId
|
||||
}
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,218 +1,218 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Col,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalHeader,
|
||||
Row,
|
||||
UncontrolledTooltip, // Add UncontrolledTooltip import
|
||||
} from "reactstrap";
|
||||
import { Edit, Mail, Phone, MapPin } from "react-feather"; // Import Mail, Phone, MapPin
|
||||
|
||||
import { useSnackbar } from "notistack";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { permissionCheck } from "../components/permission-check";
|
||||
import {
|
||||
getMailSettings,
|
||||
clearMailSuccess,
|
||||
clearMailError,
|
||||
} from "../redux/actions/mailSettings";
|
||||
import MailSettings from "./MailSettings";
|
||||
import SpinnerComponent from "../@core/components/spinner/Fallback-spinner";
|
||||
|
||||
// Import custom styles for the communication page
|
||||
import "../assets/scss/pages/communication.scss";
|
||||
|
||||
function Communication() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const { currentSettings, loading, success, error } = useSelector(
|
||||
// Add loading to useSelector
|
||||
(state) => state.mailSettings
|
||||
);
|
||||
const [showMailModal, setShowMailModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Load mail settings once when component mounts
|
||||
dispatch(getMailSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (success) {
|
||||
enqueueSnackbar(t("Warnings.updatedSuccessfully"), {
|
||||
variant: "success",
|
||||
});
|
||||
dispatch(clearMailSuccess());
|
||||
}
|
||||
}, [success, enqueueSnackbar, t, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
const errorMessage =
|
||||
error?.graphQLErrors?.[0]?.message ||
|
||||
error?.message ||
|
||||
t("Warnings.genericUpdateFailed");
|
||||
|
||||
enqueueSnackbar(errorMessage, {
|
||||
variant: "error",
|
||||
});
|
||||
dispatch(clearMailError());
|
||||
}
|
||||
}, [error, enqueueSnackbar, t, dispatch]);
|
||||
|
||||
// Get email address from settings or use default from environment variable
|
||||
const defaultEmail = process.env.REACT_APP_DEFAULT_EMAIL || "";
|
||||
// Use default email if currentSettings is null or emailAddress is null
|
||||
const emailAddress = currentSettings?.emailAddress || defaultEmail;
|
||||
const handleEmailClick = () => {
|
||||
if (emailAddress) {
|
||||
window.location.href = `mailto:${emailAddress}`;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setShowMailModal(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: "2%" }}>
|
||||
{loading && !currentSettings ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
minHeight: 200,
|
||||
}}
|
||||
>
|
||||
<SpinnerComponent />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{permissionCheck("settings_access") && (
|
||||
<>
|
||||
<Modal
|
||||
isOpen={showMailModal}
|
||||
toggle={handleCloseModal}
|
||||
className="modal-dialog-centered"
|
||||
size="lg"
|
||||
>
|
||||
<ModalHeader toggle={handleCloseModal}>
|
||||
{t("MailSettings.editMailInfo")}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<MailSettings closeModal={handleCloseModal} />
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
<Card className="border-bottom">
|
||||
<CardHeader className="border-bottom">
|
||||
<CardTitle tag="h2" className="row ml-md-2 align-items-center">
|
||||
{t("Contact.contactInfo")}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<Row>
|
||||
<Col className="pl-md-5 pl-2 col-md-6 col-12">
|
||||
<Row className="mx-0 mt-1">
|
||||
<Col sm="6" md="5" className="mr-2">
|
||||
<div className="email-container d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h4>
|
||||
<Mail size={16} className="mr-1" />{" "}
|
||||
{t("Contact.contactEmail")}
|
||||
</h4>{" "}
|
||||
{/* Add Mail icon */}
|
||||
<p
|
||||
style={{
|
||||
color: emailAddress ? "blue" : "inherit",
|
||||
textDecoration: emailAddress ? "underline" : "none",
|
||||
cursor: emailAddress ? "pointer" : "default",
|
||||
}}
|
||||
onClick={handleEmailClick}
|
||||
>
|
||||
{emailAddress ||
|
||||
defaultEmail ||
|
||||
t("MailSettings.notConfigured")}
|
||||
</p>
|
||||
</div>
|
||||
{permissionCheck("settings_access") && (
|
||||
<>
|
||||
{" "}
|
||||
{/* Wrap button and tooltip in fragment */}
|
||||
<Button
|
||||
color="flat-primary"
|
||||
className="btn-icon"
|
||||
onClick={() => setShowMailModal(true)}
|
||||
id="edit-email-btn" // Add id for tooltip
|
||||
>
|
||||
<Edit size={18} />
|
||||
</Button>
|
||||
<UncontrolledTooltip
|
||||
placement="top"
|
||||
target="edit-email-btn" // Target the button id
|
||||
timeout={150} // Add timeout prop to fix the warning
|
||||
>
|
||||
{t("MailSettings.editMailInfo")}{" "}
|
||||
{/* Tooltip text */}
|
||||
</UncontrolledTooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
<Col sm="6" md="5" className="mr-2">
|
||||
<div className="telephone-container">
|
||||
<h4>
|
||||
<Phone size={16} className="mr-1" />{" "}
|
||||
{t("Contact.contactPhoneNumber")}
|
||||
</h4>{" "}
|
||||
{/* Add Phone icon */}
|
||||
<p>+90 507 750 00 41</p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mx-0 mt-4">
|
||||
<Col sm="6" md="5" className="mr-2">
|
||||
<div className="address-container">
|
||||
<h4>
|
||||
<MapPin size={16} className="mr-1" />{" "}
|
||||
{t("Contact.contactAddress")}
|
||||
</h4>{" "}
|
||||
{/* Add MapPin icon */}
|
||||
<address>
|
||||
Central Office: 4995 sokak no:3, Alacaatlı Mahallesi,
|
||||
Daire No: A2 06810 Çankaya/Ankara
|
||||
</address>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col className="pl-md-5 pl-2 col-md-6 col-12 pb-2 border-left">
|
||||
<Row className="mx-0 mt-1">
|
||||
<div className="address-map w-100">
|
||||
<div className="responsive-map-container">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3063.1752741150003!2d32.658217075858445!3d39.847904971536735!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x14d33eef6ee44755%3A0x77faea5f08f32c60!2zTUVBIEfDnFpFTEJBSMOHRU0!5e0!3m2!1sen!2str!4v1741165773414!5m2!1sen!2str"
|
||||
allowFullScreen=""
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer-when-downgrade"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Communication;
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Col,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalHeader,
|
||||
Row,
|
||||
UncontrolledTooltip, // Add UncontrolledTooltip import
|
||||
} from "reactstrap";
|
||||
import { Edit, Mail, Phone, MapPin } from "react-feather"; // Import Mail, Phone, MapPin
|
||||
|
||||
import { useSnackbar } from "notistack";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { permissionCheck } from "../components/permission-check";
|
||||
import {
|
||||
getMailSettings,
|
||||
clearMailSuccess,
|
||||
clearMailError,
|
||||
} from "../redux/actions/mailSettings";
|
||||
import MailSettings from "./MailSettings";
|
||||
import SpinnerComponent from "../@core/components/spinner/Fallback-spinner";
|
||||
|
||||
// Import custom styles for the communication page
|
||||
import "../assets/scss/pages/communication.scss";
|
||||
|
||||
function Communication() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const { currentSettings, loading, success, error } = useSelector(
|
||||
// Add loading to useSelector
|
||||
(state) => state.mailSettings
|
||||
);
|
||||
const [showMailModal, setShowMailModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Load mail settings once when component mounts
|
||||
dispatch(getMailSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (success) {
|
||||
enqueueSnackbar(t("Warnings.updatedSuccessfully"), {
|
||||
variant: "success",
|
||||
});
|
||||
dispatch(clearMailSuccess());
|
||||
}
|
||||
}, [success, enqueueSnackbar, t, dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
const errorMessage =
|
||||
error?.graphQLErrors?.[0]?.message ||
|
||||
error?.message ||
|
||||
t("Warnings.genericUpdateFailed");
|
||||
|
||||
enqueueSnackbar(errorMessage, {
|
||||
variant: "error",
|
||||
});
|
||||
dispatch(clearMailError());
|
||||
}
|
||||
}, [error, enqueueSnackbar, t, dispatch]);
|
||||
|
||||
// Get email address from settings or use default from environment variable
|
||||
const defaultEmail = process.env.REACT_APP_DEFAULT_EMAIL || "";
|
||||
// Use default email if currentSettings is null or emailAddress is null
|
||||
const emailAddress = currentSettings?.emailAddress || defaultEmail;
|
||||
const handleEmailClick = () => {
|
||||
if (emailAddress) {
|
||||
window.location.href = `mailto:${emailAddress}`;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setShowMailModal(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: "2%" }}>
|
||||
{loading && !currentSettings ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
minHeight: 200,
|
||||
}}
|
||||
>
|
||||
<SpinnerComponent />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{permissionCheck("settings_access") && (
|
||||
<>
|
||||
<Modal
|
||||
isOpen={showMailModal}
|
||||
toggle={handleCloseModal}
|
||||
className="modal-dialog-centered"
|
||||
size="lg"
|
||||
>
|
||||
<ModalHeader toggle={handleCloseModal}>
|
||||
{t("MailSettings.editMailInfo")}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<MailSettings closeModal={handleCloseModal} />
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
<Card className="border-bottom">
|
||||
<CardHeader className="border-bottom">
|
||||
<CardTitle tag="h2" className="row ml-md-2 align-items-center">
|
||||
{t("Contact.contactInfo")}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<Row>
|
||||
<Col className="pl-md-5 pl-2 col-md-6 col-12">
|
||||
<Row className="mx-0 mt-1">
|
||||
<Col sm="6" md="5" className="mr-2">
|
||||
<div className="email-container d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h4>
|
||||
<Mail size={16} className="mr-1" />{" "}
|
||||
{t("Contact.contactEmail")}
|
||||
</h4>{" "}
|
||||
{/* Add Mail icon */}
|
||||
<p
|
||||
style={{
|
||||
color: emailAddress ? "blue" : "inherit",
|
||||
textDecoration: emailAddress ? "underline" : "none",
|
||||
cursor: emailAddress ? "pointer" : "default",
|
||||
}}
|
||||
onClick={handleEmailClick}
|
||||
>
|
||||
{emailAddress ||
|
||||
defaultEmail ||
|
||||
t("MailSettings.notConfigured")}
|
||||
</p>
|
||||
</div>
|
||||
{permissionCheck("settings_access") && (
|
||||
<>
|
||||
{" "}
|
||||
{/* Wrap button and tooltip in fragment */}
|
||||
<Button
|
||||
color="flat-primary"
|
||||
className="btn-icon"
|
||||
onClick={() => setShowMailModal(true)}
|
||||
id="edit-email-btn" // Add id for tooltip
|
||||
>
|
||||
<Edit size={18} />
|
||||
</Button>
|
||||
<UncontrolledTooltip
|
||||
placement="top"
|
||||
target="edit-email-btn" // Target the button id
|
||||
timeout={150} // Add timeout prop to fix the warning
|
||||
>
|
||||
{t("MailSettings.editMailInfo")}{" "}
|
||||
{/* Tooltip text */}
|
||||
</UncontrolledTooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
<Col sm="6" md="5" className="mr-2">
|
||||
<div className="telephone-container">
|
||||
<h4>
|
||||
<Phone size={16} className="mr-1" />{" "}
|
||||
{t("Contact.contactPhoneNumber")}
|
||||
</h4>{" "}
|
||||
{/* Add Phone icon */}
|
||||
<p>+90 507 750 00 41</p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mx-0 mt-4">
|
||||
<Col sm="6" md="5" className="mr-2">
|
||||
<div className="address-container">
|
||||
<h4>
|
||||
<MapPin size={16} className="mr-1" />{" "}
|
||||
{t("Contact.contactAddress")}
|
||||
</h4>{" "}
|
||||
{/* Add MapPin icon */}
|
||||
<address>
|
||||
Central Office: 4995 sokak no:3, Alacaatlı Mahallesi,
|
||||
Daire No: A2 06820 Çankaya/Ankara
|
||||
</address>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col className="pl-md-5 pl-2 col-md-6 col-12 pb-2 border-left">
|
||||
<Row className="mx-0 mt-1">
|
||||
<div className="address-map w-100">
|
||||
<div className="responsive-map-container">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3063.1752741150003!2d32.658217075858445!3d39.847904971536735!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x14d33eef6ee44755%3A0x77faea5f08f32c60!2zTUVBIEfDnFpFTEJBSMOHRU0!5e0!3m2!1sen!2str!4v1741165773414!5m2!1sen!2str"
|
||||
allowFullScreen=""
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer-when-downgrade"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Communication;
|
||||
|
||||
@@ -18,17 +18,16 @@ const DataCenter = () => {
|
||||
const [refreshInterval, setRefreshInterval] = useState(null);
|
||||
|
||||
const getAllPhysicalMachines = (dataCenter) => {
|
||||
if (!dataCenter.projects) return [];
|
||||
return dataCenter.projects.flatMap(project =>
|
||||
project.physicalMachines || []
|
||||
);
|
||||
// Physical machines are directly in the dataCenter object, not in projects
|
||||
const pms = dataCenter.physicalMachines || [];
|
||||
return pms;
|
||||
};
|
||||
|
||||
// Table columns following your pattern
|
||||
const initialColumns = [
|
||||
{
|
||||
name: "Number",
|
||||
selector: (row) => row.number,
|
||||
name: "External ID",
|
||||
selector: (row) => row.externalId,
|
||||
sortable: true,
|
||||
minWidth: "100px",
|
||||
},
|
||||
@@ -38,28 +37,33 @@ const DataCenter = () => {
|
||||
sortable: true,
|
||||
minWidth: "200px",
|
||||
},
|
||||
// Projects
|
||||
{
|
||||
name: "Projects",
|
||||
selector: (row) => (row.projects || []).length,
|
||||
sortable: true,
|
||||
minWidth: "200px",
|
||||
cell: (row) => (
|
||||
<div>
|
||||
{(row.projects || []).length > 0 ? (
|
||||
<div className="d-flex flex-column">
|
||||
{row.projects.map((project, index) => (
|
||||
<div key={project.id} className={`badge badge-light-primary ${index > 0 ? 'mt-1' : ''}`}>
|
||||
{project.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-muted">-</span>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
// Projects - Based on API response, this field might not exist or be structured differently
|
||||
// {
|
||||
// name: "Projects",
|
||||
// selector: (row) => row.projects?.length || 0,
|
||||
// sortable: true,
|
||||
// minWidth: "200px",
|
||||
// cell: (row) => (
|
||||
// <div>
|
||||
// {row.projects && row.projects.length > 0 ? (
|
||||
// <div className="d-flex flex-column">
|
||||
// {row.projects.map((project, index) => (
|
||||
// <div
|
||||
// key={project.id}
|
||||
// className={`badge badge-light-primary ${
|
||||
// index > 0 ? "mt-1" : ""
|
||||
// }`}
|
||||
// >
|
||||
// {project.name}
|
||||
// </div>
|
||||
// ))}
|
||||
// </div>
|
||||
// ) : (
|
||||
// <span className="text-muted">-</span>
|
||||
// )}
|
||||
// </div>
|
||||
// ),
|
||||
// },
|
||||
// Physical Machines
|
||||
{
|
||||
name: "Physical Machines",
|
||||
@@ -80,27 +84,39 @@ const DataCenter = () => {
|
||||
name: "Virtual Machines",
|
||||
selector: (row) => {
|
||||
const pms = getAllPhysicalMachines(row);
|
||||
const vms = pms.reduce((acc, pm) => {
|
||||
if (!pm.vms) return acc;
|
||||
return {
|
||||
active: acc.active + pm.vms.filter(vm => vm.state?.toLowerCase() === "active").length,
|
||||
total: acc.total + pm.vms.length
|
||||
};
|
||||
}, { active: 0, total: 0 });
|
||||
const vms = pms.reduce(
|
||||
(acc, pm) => {
|
||||
if (!pm.vms) return acc;
|
||||
return {
|
||||
active:
|
||||
acc.active +
|
||||
pm.vms.filter((vm) => vm.state?.toLowerCase() === "active")
|
||||
.length,
|
||||
total: acc.total + pm.vms.length,
|
||||
};
|
||||
},
|
||||
{ active: 0, total: 0 }
|
||||
);
|
||||
return vms.total;
|
||||
},
|
||||
sortable: true,
|
||||
minWidth: "200px",
|
||||
cell: (row) => {
|
||||
const pms = getAllPhysicalMachines(row);
|
||||
const vms = pms.reduce((acc, pm) => {
|
||||
if (!pm.vms) return acc;
|
||||
return {
|
||||
active: acc.active + pm.vms.filter(vm => vm.state?.toLowerCase() === "active").length,
|
||||
total: acc.total + pm.vms.length
|
||||
};
|
||||
}, { active: 0, total: 0 });
|
||||
|
||||
const vms = pms.reduce(
|
||||
(acc, pm) => {
|
||||
if (!pm.vms) return acc;
|
||||
return {
|
||||
active:
|
||||
acc.active +
|
||||
pm.vms.filter((vm) => vm.state?.toLowerCase() === "active")
|
||||
.length,
|
||||
total: acc.total + pm.vms.length,
|
||||
};
|
||||
},
|
||||
{ active: 0, total: 0 }
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="d-flex align-items-center">
|
||||
<Monitor size={16} className="mr-2" />
|
||||
@@ -109,7 +125,9 @@ const DataCenter = () => {
|
||||
<div className="small">
|
||||
<span className="text-success">{vms.active} Active</span>
|
||||
<span className="text-muted mx-1">•</span>
|
||||
<span className="text-warning">{vms.total - vms.active} Inactive</span>
|
||||
<span className="text-warning">
|
||||
{vms.total - vms.active} Inactive
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -183,14 +201,23 @@ const DataCenter = () => {
|
||||
</thead>
|
||||
<tbody>
|
||||
{pm.vms.map((vm) => {
|
||||
const isActive = vm.state && ["ACTIVE", "active"].includes(vm.state);
|
||||
const isActive =
|
||||
vm.state && ["ACTIVE", "active"].includes(vm.state);
|
||||
return (
|
||||
<tr key={vm.id}>
|
||||
<td>
|
||||
<span className="font-weight-bold">{vm.vmName || vm.vm_name}</span>
|
||||
<span className="font-weight-bold">
|
||||
{vm.vmName || vm.vm_name}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div className={`d-inline-block px-2 py-1 rounded-pill ${isActive ? 'bg-light-success text-success' : 'bg-light-warning text-warning'}`}>
|
||||
<div
|
||||
className={`d-inline-block px-2 py-1 rounded-pill ${
|
||||
isActive
|
||||
? "bg-light-success text-success"
|
||||
: "bg-light-warning text-warning"
|
||||
}`}
|
||||
>
|
||||
{vm.state}
|
||||
</div>
|
||||
</td>
|
||||
@@ -204,23 +231,48 @@ const DataCenter = () => {
|
||||
<td>
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="mr-3">
|
||||
<small className="text-muted d-block">CPU</small>
|
||||
<span>{(vm.config?.cpu || (vm.confg && vm.confg[1])) || '-'}</span>
|
||||
<small className="text-muted d-block">
|
||||
CPU
|
||||
</small>
|
||||
<span>
|
||||
{vm.config?.cpu ||
|
||||
(vm.confg && vm.confg[1]) ||
|
||||
"-"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mr-3">
|
||||
<small className="text-muted d-block">RAM</small>
|
||||
<span>{(vm.config?.ram || (vm.confg && vm.confg[2])) || '-'} GB</span>
|
||||
<small className="text-muted d-block">
|
||||
RAM
|
||||
</small>
|
||||
<span>
|
||||
{vm.config?.ram ||
|
||||
(vm.confg && vm.confg[2]) ||
|
||||
"-"}{" "}
|
||||
GB
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<small className="text-muted d-block">Disk</small>
|
||||
<span>{(vm.config?.disk || (vm.confg && vm.confg[3])) || '-'} GB</span>
|
||||
<small className="text-muted d-block">
|
||||
Disk
|
||||
</small>
|
||||
<span>
|
||||
{vm.config?.disk ||
|
||||
(vm.confg && vm.confg[3]) ||
|
||||
"-"}{" "}
|
||||
GB
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="d-flex align-items-center">
|
||||
<Server size={14} className="mr-1" />
|
||||
<span>{vm.host || vm.hostingPm || (vm.confg && vm.confg[4]) || '-'}</span>
|
||||
<span>
|
||||
{vm.host ||
|
||||
vm.hostingPm ||
|
||||
(vm.confg && vm.confg[4]) ||
|
||||
"-"}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -241,8 +293,6 @@ const DataCenter = () => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: "2%" }}>
|
||||
<Card>
|
||||
|
||||
@@ -28,26 +28,37 @@ import { Edit } from "@mui/icons-material";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { default as SweetAlert } from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { getDataCenters, createDataCenter, updateDataCenter, deleteDataCenter, getEmissionScopes } from "../redux/actions/dataCenter";
|
||||
import {
|
||||
getDataCenters,
|
||||
createDataCenter,
|
||||
updateDataCenter,
|
||||
deleteDataCenter,
|
||||
getEmissionScopes,
|
||||
} from "../redux/actions/dataCenter";
|
||||
import { getAreas, getAreasWithCriteria } from "../redux/actions/areas";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getSectors, getSectorById, getSubSectorById, getConsuptionUnits } from "../redux/actions/datas";
|
||||
import {
|
||||
getSectors,
|
||||
getSectorById,
|
||||
getSubSectorById,
|
||||
getConsuptionUnits,
|
||||
} from "../redux/actions/datas";
|
||||
import { getAllEmissionSources } from "../redux/actions/emissionSources";
|
||||
import { permissionCheck } from "../components/permission-check";
|
||||
import { customFilterForSelect } from "../utility/Utils";
|
||||
import { MapContainer, TileLayer, Marker, useMapEvents } from 'react-leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import L from 'leaflet';
|
||||
import axios from 'axios';
|
||||
import { debounce } from 'lodash';
|
||||
import { MapContainer, TileLayer, Marker, useMapEvents } from "react-leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import L from "leaflet";
|
||||
import axios from "axios";
|
||||
import { debounce } from "lodash";
|
||||
|
||||
// Add Nominatim service configuration
|
||||
const NOMINATIM_BASE_URL = 'https://nominatim.openstreetmap.org';
|
||||
const NOMINATIM_BASE_URL = "https://nominatim.openstreetmap.org";
|
||||
const nominatimAxios = axios.create({
|
||||
baseURL: NOMINATIM_BASE_URL,
|
||||
headers: {
|
||||
'User-Agent': 'SGE-DataCenter-Management' // Required by Nominatim's usage policy
|
||||
}
|
||||
"User-Agent": "SGE-DataCenter-Management", // Required by Nominatim's usage policy
|
||||
},
|
||||
});
|
||||
|
||||
const Swal = withReactContent(SweetAlert);
|
||||
@@ -55,9 +66,9 @@ const Swal = withReactContent(SweetAlert);
|
||||
// Fix Leaflet marker icon issue
|
||||
delete L.Icon.Default.prototype._getIconUrl;
|
||||
L.Icon.Default.mergeOptions({
|
||||
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
|
||||
iconUrl: require('leaflet/dist/images/marker-icon.png'),
|
||||
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
|
||||
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
|
||||
iconUrl: require("leaflet/dist/images/marker-icon.png"),
|
||||
shadowUrl: require("leaflet/dist/images/marker-shadow.png"),
|
||||
});
|
||||
|
||||
// Map marker component that handles clicks
|
||||
@@ -66,30 +77,33 @@ const MapMarker = ({ position, setPosition, setSelectedDataCenter }) => {
|
||||
click(e) {
|
||||
setPosition([e.latlng.lat, e.latlng.lng]);
|
||||
// Use Nominatim reverse geocoding directly
|
||||
nominatimAxios.get(`/reverse?format=json&lat=${e.latlng.lat}&lon=${e.latlng.lng}`)
|
||||
.then(response => {
|
||||
nominatimAxios
|
||||
.get(`/reverse?format=json&lat=${e.latlng.lat}&lon=${e.latlng.lng}`)
|
||||
.then((response) => {
|
||||
const address = response.data.display_name;
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
address,
|
||||
latitude: e.latlng.lat,
|
||||
longitude: e.latlng.lng
|
||||
longitude: e.latlng.lng,
|
||||
}));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error getting address:', error);
|
||||
.catch((error) => {
|
||||
console.error("Error getting address:", error);
|
||||
// Still update coordinates even if address lookup fails
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
latitude: e.latlng.lat,
|
||||
longitude: e.latlng.lng
|
||||
longitude: e.latlng.lng,
|
||||
}));
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Only render marker if position exists and has valid coordinates
|
||||
return position && position[0] && position[1] ? <Marker position={position} /> : null;
|
||||
return position && position[0] && position[1] ? (
|
||||
<Marker position={position} />
|
||||
) : null;
|
||||
};
|
||||
|
||||
const DataCenterManagement = () => {
|
||||
@@ -115,15 +129,14 @@ const DataCenterManagement = () => {
|
||||
emissionScopeId: null,
|
||||
sectorId: null,
|
||||
subSectorId: null,
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
activitySubUnitId: null
|
||||
dataCenterEmissionSources: [], // Array of emission sources with consumption units
|
||||
activitySubUnitId: null,
|
||||
});
|
||||
|
||||
const [mapPosition, setMapPosition] = useState(null);
|
||||
|
||||
const dataCenterStore = useSelector((state) => {
|
||||
console.log('DataCenter Store:', state.dataCenter);
|
||||
console.log("DataCenter Store:", state.dataCenter);
|
||||
return state.dataCenter;
|
||||
});
|
||||
const emissionScopeStore = useSelector((state) => state.emissionScope);
|
||||
@@ -138,7 +151,7 @@ const DataCenterManagement = () => {
|
||||
const [emissionScopesOptions, setEmissionScopesOptions] = useState([]);
|
||||
const [areasOptions, setAreasOptions] = useState([]);
|
||||
const [citiesOptions, setCitiesOptions] = useState([]);
|
||||
|
||||
|
||||
// Add state for selected sector and sub sector like in data input
|
||||
const [selectedSector, setSelectedSector] = useState(null);
|
||||
const [selectedSubSector, setSelectedSubSector] = useState(null);
|
||||
@@ -170,7 +183,7 @@ const DataCenterManagement = () => {
|
||||
<Button
|
||||
color="primary"
|
||||
size="sm"
|
||||
onClick={() => window.open(row.ayposURL, '_blank')}
|
||||
onClick={() => window.open(row.ayposURL, "_blank")}
|
||||
>
|
||||
Dashboard
|
||||
</Button>
|
||||
@@ -200,7 +213,9 @@ const DataCenterManagement = () => {
|
||||
onClick={() => handleEditDataCenter(row)}
|
||||
>
|
||||
<Edit size={15} />
|
||||
<span className="align-middle ml-50">{t("Cruds.edit")}</span>
|
||||
<span className="align-middle ml-50">
|
||||
{t("Cruds.edit")}
|
||||
</span>
|
||||
</DropdownItem>
|
||||
)}
|
||||
{permissionCheck("data_center_delete") && (
|
||||
@@ -210,7 +225,9 @@ const DataCenterManagement = () => {
|
||||
onClick={() => handleDeleteDataCenter(row)}
|
||||
>
|
||||
<Trash size={15} />
|
||||
<span className="align-middle ml-50">{t("Cruds.delete")}</span>
|
||||
<span className="align-middle ml-50">
|
||||
{t("Cruds.delete")}
|
||||
</span>
|
||||
</DropdownItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
@@ -269,16 +286,41 @@ const DataCenterManagement = () => {
|
||||
);
|
||||
}, [emissionSourceStore?.emissionSources]);
|
||||
|
||||
// Remove the old emission source effect since we now handle it in the emission sources component
|
||||
// useEffect(() => {
|
||||
// if (selectedDataCenter?.emissionSourceId) {
|
||||
// dispatch(
|
||||
// getConsuptionUnits({
|
||||
// id: selectedDataCenter?.emissionSourceId,
|
||||
// sector: selectedDataCenter?.sectorId,
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// }, [selectedDataCenter?.emissionSourceId]);
|
||||
|
||||
// Ensure there's always at least one emission source entry for new data centers
|
||||
useEffect(() => {
|
||||
if (selectedDataCenter?.emissionSourceId) {
|
||||
dispatch(
|
||||
getConsuptionUnits({
|
||||
id: selectedDataCenter?.emissionSourceId,
|
||||
sector: selectedDataCenter?.sectorId,
|
||||
})
|
||||
);
|
||||
if (
|
||||
showAddModal &&
|
||||
!editingDataCenter &&
|
||||
selectedDataCenter.dataCenterEmissionSources.length === 0
|
||||
) {
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
dataCenterEmissionSources: [
|
||||
{
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
isDefault: true,
|
||||
},
|
||||
],
|
||||
}));
|
||||
}
|
||||
}, [selectedDataCenter?.emissionSourceId]);
|
||||
}, [
|
||||
showAddModal,
|
||||
editingDataCenter,
|
||||
selectedDataCenter.dataCenterEmissionSources.length,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedSubSector != null) {
|
||||
@@ -352,33 +394,63 @@ const DataCenterManagement = () => {
|
||||
}, [selectedDataCenter.areaId, areasStore?.areas]);
|
||||
|
||||
const handleEditDataCenter = (row) => {
|
||||
console.log('Editing data center:', row);
|
||||
console.log("Editing data center:", row);
|
||||
setEditingDataCenter(row);
|
||||
|
||||
// Convert dataCenterEmissionSources to the format expected by the form
|
||||
const emissionSources = row.dataCenterEmissionSources
|
||||
? row.dataCenterEmissionSources.map((dces) => ({
|
||||
emissionSourceId: dces.emissionSource?.id,
|
||||
consuptionUnitId: dces.consuptionUnit?.id,
|
||||
isDefault: dces.isDefault || false,
|
||||
}))
|
||||
: [];
|
||||
|
||||
setSelectedDataCenter({
|
||||
name: row.dataCenter,
|
||||
externalId: row.externalId?.toString(),
|
||||
number: row.number?.toString(),
|
||||
address: row.address || "",
|
||||
areaId: row.area?.id || null,
|
||||
cityId: null,
|
||||
cityId: row.city?.id || null,
|
||||
latitude: row.latitude,
|
||||
longitude: row.longitude,
|
||||
ayposURL: row.ayposURL || "",
|
||||
city: row.city || "",
|
||||
city: row.city?.name || "",
|
||||
emissionScopeId: row.emissionScope?.id || null,
|
||||
sectorId: row.sector?.id || null,
|
||||
subSectorId: row.subSector?.id || null,
|
||||
emissionSourceId: row.emissionSource?.id || null,
|
||||
consuptionUnitId: row.consuptionUnit?.id || null,
|
||||
activitySubUnitId: row.activitySubUnit?.id || null
|
||||
dataCenterEmissionSources: emissionSources,
|
||||
activitySubUnitId: row.activitySubUnit?.id || null,
|
||||
});
|
||||
|
||||
|
||||
// Set the selected sector and sub sector for cascading dropdowns
|
||||
setSelectedSector(row.sector?.id);
|
||||
setSelectedSubSector(row.subSector?.id);
|
||||
|
||||
|
||||
// If there are existing emission sources, fetch consumption units for each
|
||||
if (
|
||||
row.dataCenterEmissionSources &&
|
||||
row.dataCenterEmissionSources.length > 0
|
||||
) {
|
||||
row.dataCenterEmissionSources.forEach((dces) => {
|
||||
if (dces.emissionSource && dces.emissionSource.id) {
|
||||
dispatch(
|
||||
getConsuptionUnits({
|
||||
id: dces.emissionSource.id,
|
||||
sector: row.sector?.id,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Only set map position if we have both address and valid coordinates
|
||||
setMapPosition(row.address && row.latitude && row.longitude ? [row.latitude, row.longitude] : null);
|
||||
setMapPosition(
|
||||
row.address && row.latitude && row.longitude
|
||||
? [row.latitude, row.longitude]
|
||||
: null
|
||||
);
|
||||
setShowAddModal(true);
|
||||
};
|
||||
|
||||
@@ -403,17 +475,16 @@ const DataCenterManagement = () => {
|
||||
enqueueSnackbar(t("DataCenter.deleteSuccess"), { variant: "success" });
|
||||
} catch (error) {
|
||||
console.error("Delete error:", error);
|
||||
enqueueSnackbar(
|
||||
error?.message || t("DataCenter.deleteError"),
|
||||
{ variant: "error" }
|
||||
);
|
||||
enqueueSnackbar(error?.message || t("DataCenter.deleteError"), {
|
||||
variant: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errors = [];
|
||||
|
||||
|
||||
// Required fields validation
|
||||
if (!selectedDataCenter.name?.trim()) {
|
||||
errors.push(t("DataCenter.nameRequired"));
|
||||
@@ -448,7 +519,7 @@ const DataCenterManagement = () => {
|
||||
try {
|
||||
const lat = parseFloat(selectedDataCenter.latitude);
|
||||
const lng = parseFloat(selectedDataCenter.longitude);
|
||||
|
||||
|
||||
if (isNaN(lat) || lat < -90 || lat > 90) {
|
||||
errors.push(t("DataCenter.invalidLatitude"));
|
||||
}
|
||||
@@ -473,23 +544,60 @@ const DataCenterManagement = () => {
|
||||
if (selectedDataCenter.subSectorId && !selectedDataCenter.sectorId) {
|
||||
errors.push(t("DataCenter.sectorRequired"));
|
||||
}
|
||||
if (selectedDataCenter.emissionSourceId && !selectedDataCenter.subSectorId) {
|
||||
errors.push(t("DataCenter.subSectorRequired"));
|
||||
}
|
||||
if (selectedDataCenter.consuptionUnitId && !selectedDataCenter.emissionSourceId) {
|
||||
errors.push(t("DataCenter.emissionSourceRequired"));
|
||||
}
|
||||
if (selectedDataCenter.activitySubUnitId && !selectedDataCenter.subSectorId) {
|
||||
if (
|
||||
selectedDataCenter.activitySubUnitId &&
|
||||
!selectedDataCenter.subSectorId
|
||||
) {
|
||||
errors.push(t("DataCenter.subSectorRequiredForActivity"));
|
||||
}
|
||||
|
||||
// Emission sources validations
|
||||
if (selectedDataCenter.dataCenterEmissionSources.length > 0) {
|
||||
const validSources = selectedDataCenter.dataCenterEmissionSources.filter(
|
||||
(source) => source.emissionSourceId && source.consuptionUnitId
|
||||
);
|
||||
|
||||
if (validSources.length === 0) {
|
||||
errors.push(t("DataCenter.atLeastOneEmissionSource"));
|
||||
}
|
||||
|
||||
// Check for incomplete emission sources
|
||||
selectedDataCenter.dataCenterEmissionSources.forEach((source, index) => {
|
||||
if (
|
||||
(source.emissionSourceId && !source.consuptionUnitId) ||
|
||||
(!source.emissionSourceId && source.consuptionUnitId)
|
||||
) {
|
||||
errors.push(
|
||||
t("DataCenter.incompleteEmissionSource", { index: index + 1 })
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for duplicate emission sources
|
||||
const sourceIds = validSources.map((s) => s.emissionSourceId);
|
||||
const duplicates = sourceIds.filter(
|
||||
(id, index) => sourceIds.indexOf(id) !== index
|
||||
);
|
||||
if (duplicates.length > 0) {
|
||||
errors.push(t("DataCenter.duplicateEmissionSources"));
|
||||
}
|
||||
|
||||
// Ensure exactly one default emission source
|
||||
const defaultSources = validSources.filter((s) => s.isDefault);
|
||||
if (defaultSources.length === 0) {
|
||||
errors.push(t("DataCenter.oneDefaultEmissionSourceRequired"));
|
||||
} else if (defaultSources.length > 1) {
|
||||
errors.push(t("DataCenter.onlyOneDefaultEmissionSourceAllowed"));
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const validationErrors = validateForm();
|
||||
if (validationErrors.length > 0) {
|
||||
validationErrors.forEach(error => {
|
||||
validationErrors.forEach((error) => {
|
||||
enqueueSnackbar(error, { variant: "error" });
|
||||
});
|
||||
return;
|
||||
@@ -498,20 +606,27 @@ const DataCenterManagement = () => {
|
||||
try {
|
||||
// Format data according to GraphQL input type
|
||||
const dataToSubmit = {
|
||||
dataCenter: selectedDataCenter.name,
|
||||
name: selectedDataCenter.name,
|
||||
externalId: parseInt(selectedDataCenter.externalId),
|
||||
number: parseInt(selectedDataCenter.number || "1"),
|
||||
address: selectedDataCenter.address,
|
||||
areaId: selectedDataCenter.areaId,
|
||||
latitude: selectedDataCenter.latitude ? parseFloat(selectedDataCenter.latitude) : null,
|
||||
longitude: selectedDataCenter.longitude ? parseFloat(selectedDataCenter.longitude) : null,
|
||||
cityId: selectedDataCenter.cityId,
|
||||
latitude: selectedDataCenter.latitude
|
||||
? parseFloat(selectedDataCenter.latitude)
|
||||
: null,
|
||||
longitude: selectedDataCenter.longitude
|
||||
? parseFloat(selectedDataCenter.longitude)
|
||||
: null,
|
||||
ayposURL: selectedDataCenter.ayposURL,
|
||||
emissionScopeId: selectedDataCenter.emissionScopeId,
|
||||
sectorId: selectedDataCenter.sectorId,
|
||||
subSectorId: selectedDataCenter.subSectorId,
|
||||
emissionSourceId: selectedDataCenter.emissionSourceId,
|
||||
consuptionUnitId: selectedDataCenter.consuptionUnitId,
|
||||
activitySubUnitId: selectedDataCenter.activitySubUnitId
|
||||
dataCenterEmissionSources:
|
||||
selectedDataCenter.dataCenterEmissionSources.filter(
|
||||
(source) => source.emissionSourceId && source.consuptionUnitId
|
||||
),
|
||||
activitySubUnitId: selectedDataCenter.activitySubUnitId,
|
||||
};
|
||||
|
||||
if (editingDataCenter) {
|
||||
@@ -523,26 +638,27 @@ const DataCenterManagement = () => {
|
||||
await dispatch(createDataCenter(dataToSubmit));
|
||||
enqueueSnackbar(t("DataCenter.createSuccess"), { variant: "success" });
|
||||
}
|
||||
|
||||
|
||||
// Refresh the data centers list
|
||||
await dispatch(getDataCenters());
|
||||
|
||||
handleCloseModal();
|
||||
} catch (error) {
|
||||
console.error("Submit error:", error);
|
||||
|
||||
|
||||
// Handle specific error cases
|
||||
if (error.message?.includes("duplicate")) {
|
||||
enqueueSnackbar(t("DataCenter.duplicateExternalId"), { variant: "error" });
|
||||
enqueueSnackbar(t("DataCenter.duplicateExternalId"), {
|
||||
variant: "error",
|
||||
});
|
||||
} else if (error.message?.includes("permission")) {
|
||||
enqueueSnackbar(t("Common.noPermission"), { variant: "error" });
|
||||
} else if (error.message?.includes("not found")) {
|
||||
enqueueSnackbar(t("DataCenter.resourceNotFound"), { variant: "error" });
|
||||
} else {
|
||||
enqueueSnackbar(
|
||||
error?.message || t("DataCenter.submitError"),
|
||||
{ variant: "error" }
|
||||
);
|
||||
enqueueSnackbar(error?.message || t("DataCenter.submitError"), {
|
||||
variant: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -554,13 +670,28 @@ const DataCenterManagement = () => {
|
||||
externalId: "",
|
||||
number: "",
|
||||
address: "",
|
||||
areaId: null,
|
||||
cityId: null,
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
ayposURL: "",
|
||||
city: ""
|
||||
city: "",
|
||||
emissionScopeId: null,
|
||||
sectorId: null,
|
||||
subSectorId: null,
|
||||
dataCenterEmissionSources: [
|
||||
{
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
isDefault: true,
|
||||
},
|
||||
],
|
||||
activitySubUnitId: null,
|
||||
});
|
||||
setMapPosition(null);
|
||||
setEditingDataCenter(null);
|
||||
setSelectedSector(null);
|
||||
setSelectedSubSector(null);
|
||||
};
|
||||
|
||||
const handleFilter = (e) => {
|
||||
@@ -594,45 +725,47 @@ const DataCenterManagement = () => {
|
||||
const geocodeAddress = useCallback(async (address) => {
|
||||
if (!address || !address.trim()) {
|
||||
setMapPosition(null);
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
latitude: null,
|
||||
longitude: null
|
||||
longitude: null,
|
||||
}));
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await nominatimAxios.get(`/search?format=json&q=${encodeURIComponent(address)}`);
|
||||
const response = await nominatimAxios.get(
|
||||
`/search?format=json&q=${encodeURIComponent(address)}`
|
||||
);
|
||||
|
||||
if (response.data && response.data[0]) {
|
||||
const { lat, lon } = response.data[0];
|
||||
const newPosition = [parseFloat(lat), parseFloat(lon)];
|
||||
setMapPosition(newPosition);
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
latitude: parseFloat(lat),
|
||||
longitude: parseFloat(lon)
|
||||
longitude: parseFloat(lon),
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
// If no results found, clear the coordinates
|
||||
setMapPosition(null);
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
latitude: null,
|
||||
longitude: null
|
||||
longitude: null,
|
||||
}));
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error('Error geocoding address:', error);
|
||||
console.error("Error geocoding address:", error);
|
||||
// On error, clear the coordinates
|
||||
setMapPosition(null);
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
latitude: null,
|
||||
longitude: null
|
||||
longitude: null,
|
||||
}));
|
||||
return false;
|
||||
}
|
||||
@@ -641,18 +774,18 @@ const DataCenterManagement = () => {
|
||||
// Update the address input handler
|
||||
const handleAddressChange = (e) => {
|
||||
const newAddress = e.target.value;
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
address: newAddress
|
||||
address: newAddress,
|
||||
}));
|
||||
|
||||
// If address is empty, clear the coordinates
|
||||
if (!newAddress.trim()) {
|
||||
setMapPosition(null);
|
||||
setSelectedDataCenter(prev => ({
|
||||
setSelectedDataCenter((prev) => ({
|
||||
...prev,
|
||||
latitude: null,
|
||||
longitude: null
|
||||
longitude: null,
|
||||
}));
|
||||
} else {
|
||||
// If address is not empty, try to geocode it after a delay
|
||||
@@ -675,9 +808,7 @@ const DataCenterManagement = () => {
|
||||
size="lg"
|
||||
>
|
||||
<ModalHeader toggle={handleCloseModal}>
|
||||
{editingDataCenter
|
||||
? t("DataCenter.edit")
|
||||
: t("DataCenter.add")}
|
||||
{editingDataCenter ? t("DataCenter.edit") : t("DataCenter.add")}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Form>
|
||||
@@ -685,7 +816,8 @@ const DataCenterManagement = () => {
|
||||
<Col sm="12">
|
||||
<FormGroup>
|
||||
<Label for="name">
|
||||
{t("DataCenter.name")} <span className="text-danger">*</span>
|
||||
{t("DataCenter.name")}{" "}
|
||||
<span className="text-danger">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
type="text"
|
||||
@@ -705,7 +837,8 @@ const DataCenterManagement = () => {
|
||||
<Col sm="6">
|
||||
<FormGroup>
|
||||
<Label for="externalId">
|
||||
{t("DataCenter.externalId")} <span className="text-danger">*</span>
|
||||
{t("DataCenter.externalId")}{" "}
|
||||
<span className="text-danger">*</span>
|
||||
</Label>
|
||||
<Input
|
||||
type="text"
|
||||
@@ -814,10 +947,12 @@ const DataCenterManagement = () => {
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
|
||||
{/* Emission Scope Section */}
|
||||
<Col sm="12">
|
||||
<h5 className="mt-3 mb-2 text-primary">Emission Scope Configuration</h5>
|
||||
<h5 className="mt-3 mb-2 text-primary">
|
||||
Emission Scope Configuration
|
||||
</h5>
|
||||
</Col>
|
||||
<Col sm="6">
|
||||
<FormGroup>
|
||||
@@ -828,7 +963,8 @@ const DataCenterManagement = () => {
|
||||
placeholder="Select emission scope"
|
||||
options={emissionScopesOptions}
|
||||
value={emissionScopesOptions?.find(
|
||||
(option) => option.value === selectedDataCenter.emissionScopeId
|
||||
(option) =>
|
||||
option.value === selectedDataCenter.emissionScopeId
|
||||
)}
|
||||
onChange={(option) =>
|
||||
setSelectedDataCenter({
|
||||
@@ -843,7 +979,9 @@ const DataCenterManagement = () => {
|
||||
</Col>
|
||||
<Col sm="6">
|
||||
<FormGroup>
|
||||
<Label for="sector">Sector <span className="text-danger">*</span></Label>
|
||||
<Label for="sector">
|
||||
Sector <span className="text-danger">*</span>
|
||||
</Label>
|
||||
<Select
|
||||
id="sector"
|
||||
name="sector"
|
||||
@@ -858,8 +996,13 @@ const DataCenterManagement = () => {
|
||||
...selectedDataCenter,
|
||||
sectorId: option?.value,
|
||||
subSectorId: null,
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
dataCenterEmissionSources: [
|
||||
{
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
isDefault: true,
|
||||
},
|
||||
],
|
||||
activitySubUnitId: null,
|
||||
});
|
||||
}}
|
||||
@@ -877,15 +1020,21 @@ const DataCenterManagement = () => {
|
||||
placeholder="Select sub sector"
|
||||
options={subSectorsOptions}
|
||||
value={subSectorsOptions?.find(
|
||||
(option) => option.value === selectedDataCenter.subSectorId
|
||||
(option) =>
|
||||
option.value === selectedDataCenter.subSectorId
|
||||
)}
|
||||
onChange={(option) => {
|
||||
setSelectedSubSector(option?.value);
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
subSectorId: option?.value,
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
dataCenterEmissionSources: [
|
||||
{
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
isDefault: true,
|
||||
},
|
||||
],
|
||||
activitySubUnitId: null,
|
||||
});
|
||||
}}
|
||||
@@ -895,51 +1044,236 @@ const DataCenterManagement = () => {
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col sm="6">
|
||||
<Col sm="12">
|
||||
<FormGroup>
|
||||
<Label for="emissionSource">Emission Source</Label>
|
||||
<Select
|
||||
id="emissionSource"
|
||||
name="emissionSource"
|
||||
placeholder="Select emission source"
|
||||
options={emissionSourcesOptions}
|
||||
value={emissionSourcesOptions?.find(
|
||||
(option) => option.value === selectedDataCenter.emissionSourceId
|
||||
<Label>Emission Sources & Consumption Units</Label>
|
||||
<div className="border rounded p-3 bg-light">
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<small className="text-muted">
|
||||
Configure emission sources for this data center. At
|
||||
least one emission source with consumption unit is
|
||||
required.
|
||||
</small>
|
||||
<Button
|
||||
color="primary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
dataCenterEmissionSources: [
|
||||
...selectedDataCenter.dataCenterEmissionSources,
|
||||
{
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
isDefault: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
}}
|
||||
disabled={!selectedDataCenter.subSectorId}
|
||||
className="d-none d-sm-flex"
|
||||
>
|
||||
<Plus size={14} className="me-1" />
|
||||
Add Source
|
||||
</Button>
|
||||
</div>
|
||||
{selectedDataCenter.dataCenterEmissionSources.map(
|
||||
(source, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border rounded p-3 mb-3 bg-white"
|
||||
>
|
||||
<Row className="g-3">
|
||||
<Col xs="12" md="6">
|
||||
<Label
|
||||
for={`emissionSource-${index}`}
|
||||
className="form-label fw-bold"
|
||||
>
|
||||
Emission Source *
|
||||
</Label>
|
||||
<Select
|
||||
id={`emissionSource-${index}`}
|
||||
placeholder="Select emission source..."
|
||||
options={emissionSourcesOptions}
|
||||
value={emissionSourcesOptions?.find(
|
||||
(option) =>
|
||||
option.value === source.emissionSourceId
|
||||
)}
|
||||
onChange={(option) => {
|
||||
const updatedSources = [
|
||||
...selectedDataCenter.dataCenterEmissionSources,
|
||||
];
|
||||
updatedSources[index] = {
|
||||
...updatedSources[index],
|
||||
emissionSourceId: option?.value,
|
||||
consuptionUnitId: null, // Reset consumption unit when emission source changes
|
||||
};
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
dataCenterEmissionSources: updatedSources,
|
||||
});
|
||||
// Fetch consumption units for the selected emission source
|
||||
if (option?.value) {
|
||||
dispatch(
|
||||
getConsuptionUnits({
|
||||
id: option.value,
|
||||
sector: selectedDataCenter?.sectorId,
|
||||
})
|
||||
);
|
||||
}
|
||||
}}
|
||||
isClearable
|
||||
filterOption={customFilterForSelect}
|
||||
isDisabled={!selectedDataCenter.subSectorId}
|
||||
styles={{
|
||||
placeholder: (provided) => ({
|
||||
...provided,
|
||||
color: "#6e6b7b",
|
||||
}),
|
||||
}}
|
||||
menuPlacement="auto"
|
||||
/>
|
||||
</Col>
|
||||
<Col xs="12" md="6">
|
||||
<Label
|
||||
for={`consuptionUnit-${index}`}
|
||||
className="form-label fw-bold"
|
||||
>
|
||||
Consumption Unit *
|
||||
</Label>
|
||||
<Select
|
||||
id={`consuptionUnit-${index}`}
|
||||
placeholder="Select consumption unit..."
|
||||
options={consuptionUnitsOptions}
|
||||
value={consuptionUnitsOptions?.find(
|
||||
(option) =>
|
||||
option.value === source.consuptionUnitId
|
||||
)}
|
||||
onChange={(option) => {
|
||||
const updatedSources = [
|
||||
...selectedDataCenter.dataCenterEmissionSources,
|
||||
];
|
||||
updatedSources[index] = {
|
||||
...updatedSources[index],
|
||||
consuptionUnitId: option?.value,
|
||||
};
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
dataCenterEmissionSources: updatedSources,
|
||||
});
|
||||
}}
|
||||
isClearable
|
||||
filterOption={customFilterForSelect}
|
||||
isDisabled={!source.emissionSourceId}
|
||||
styles={{
|
||||
placeholder: (provided) => ({
|
||||
...provided,
|
||||
color: "#6e6b7b",
|
||||
}),
|
||||
}}
|
||||
menuPlacement="auto"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<Col xs="12">
|
||||
<div className="d-flex flex-column flex-sm-row gap-2 justify-content-between align-items-start align-items-sm-center">
|
||||
<div className="d-flex gap-2 flex-wrap">
|
||||
<Button
|
||||
color={
|
||||
source.isDefault
|
||||
? "primary"
|
||||
: "outline-secondary"
|
||||
}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const updatedSources = [
|
||||
...selectedDataCenter.dataCenterEmissionSources,
|
||||
];
|
||||
// First, set all sources to not default
|
||||
updatedSources.forEach(
|
||||
(s) => (s.isDefault = false)
|
||||
);
|
||||
// Then set the selected one as default
|
||||
updatedSources[index].isDefault = true;
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
dataCenterEmissionSources:
|
||||
updatedSources,
|
||||
});
|
||||
}}
|
||||
title="Set as default emission source"
|
||||
>
|
||||
{source.isDefault
|
||||
? "★ Default"
|
||||
: "☆ Set Default"}
|
||||
</Button>
|
||||
{source.isDefault && (
|
||||
<span className="badge bg-success align-self-center">
|
||||
Default Source
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
color="outline-danger"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const updatedSources =
|
||||
selectedDataCenter.dataCenterEmissionSources.filter(
|
||||
(_, i) => i !== index
|
||||
);
|
||||
// If we're removing the default source and there are other sources, make the first one default
|
||||
if (
|
||||
source.isDefault &&
|
||||
updatedSources.length > 0
|
||||
) {
|
||||
updatedSources[0].isDefault = true;
|
||||
}
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
dataCenterEmissionSources: updatedSources,
|
||||
});
|
||||
}}
|
||||
disabled={
|
||||
selectedDataCenter.dataCenterEmissionSources
|
||||
.length === 1
|
||||
}
|
||||
title="Remove emission source"
|
||||
>
|
||||
<Trash size={14} className="me-1" />
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
onChange={(option) => {
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
emissionSourceId: option?.value,
|
||||
consuptionUnitId: null,
|
||||
});
|
||||
}}
|
||||
isClearable
|
||||
filterOption={customFilterForSelect}
|
||||
isDisabled={!selectedDataCenter.subSectorId}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col sm="6">
|
||||
<FormGroup>
|
||||
<Label for="consuptionUnit">Consumption Unit</Label>
|
||||
<Select
|
||||
id="consuptionUnit"
|
||||
name="consuptionUnit"
|
||||
placeholder="Select consumption unit"
|
||||
options={consuptionUnitsOptions}
|
||||
value={consuptionUnitsOptions?.find(
|
||||
(option) => option.value === selectedDataCenter.consuptionUnitId
|
||||
)}
|
||||
onChange={(option) => {
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
consuptionUnitId: option?.value,
|
||||
});
|
||||
}}
|
||||
isClearable
|
||||
filterOption={customFilterForSelect}
|
||||
isDisabled={!selectedDataCenter.emissionSourceId}
|
||||
/>
|
||||
<div className="text-center mt-3">
|
||||
<Button
|
||||
color="primary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
dataCenterEmissionSources: [
|
||||
...selectedDataCenter.dataCenterEmissionSources,
|
||||
{
|
||||
emissionSourceId: null,
|
||||
consuptionUnitId: null,
|
||||
isDefault: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
}}
|
||||
disabled={!selectedDataCenter.subSectorId}
|
||||
className="w-100 d-sm-none"
|
||||
>
|
||||
<Plus size={14} className="me-1" />
|
||||
Add Another Emission Source
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col sm="12">
|
||||
@@ -948,10 +1282,11 @@ const DataCenterManagement = () => {
|
||||
<Select
|
||||
id="activitySubUnit"
|
||||
name="activitySubUnit"
|
||||
placeholder="Select activity sub unit"
|
||||
placeholder="Select activity sub unit..."
|
||||
options={activitySubUnitsOptions}
|
||||
value={activitySubUnitsOptions?.find(
|
||||
(option) => option.value === selectedDataCenter.activitySubUnitId
|
||||
(option) =>
|
||||
option.value === selectedDataCenter.activitySubUnitId
|
||||
)}
|
||||
onChange={(option) => {
|
||||
setSelectedDataCenter({
|
||||
@@ -962,10 +1297,11 @@ const DataCenterManagement = () => {
|
||||
isClearable
|
||||
filterOption={customFilterForSelect}
|
||||
isDisabled={!selectedDataCenter.subSectorId}
|
||||
menuPlacement="top"
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
|
||||
<Col sm="12">
|
||||
<FormGroup>
|
||||
<Label>{t("DataCenter.location")}</Label>
|
||||
@@ -986,7 +1322,7 @@ const DataCenterManagement = () => {
|
||||
setSelectedDataCenter({
|
||||
...selectedDataCenter,
|
||||
latitude: pos[0],
|
||||
longitude: pos[1]
|
||||
longitude: pos[1],
|
||||
});
|
||||
}}
|
||||
setSelectedDataCenter={setSelectedDataCenter}
|
||||
@@ -1002,14 +1338,13 @@ const DataCenterManagement = () => {
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={handleSubmit}
|
||||
disabled={!selectedDataCenter.name || !selectedDataCenter.externalId}
|
||||
disabled={
|
||||
!selectedDataCenter.name || !selectedDataCenter.externalId
|
||||
}
|
||||
>
|
||||
{t("Common.save")}
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
onClick={handleCloseModal}
|
||||
>
|
||||
<Button color="secondary" onClick={handleCloseModal}>
|
||||
{t("Common.cancel")}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
@@ -1083,9 +1418,7 @@ const DataCenterManagement = () => {
|
||||
progressPending={dataCenterStore?.loading}
|
||||
progressComponent={<div className="text-center p-3">Loading...</div>}
|
||||
noDataComponent={
|
||||
<div className="p-2 text-center">
|
||||
{t("Common.noDataAvailable")}
|
||||
</div>
|
||||
<div className="p-2 text-center">{t("Common.noDataAvailable")}</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -1094,4 +1427,4 @@ const DataCenterManagement = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(DataCenterManagement);
|
||||
export default memo(DataCenterManagement);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,56 +1,157 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import { MaterialReactTable } from "material-react-table";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, CardHeader, CardTitle, Alert } from "reactstrap";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Alert,
|
||||
Row,
|
||||
Col,
|
||||
Label,
|
||||
} from "reactstrap";
|
||||
import { getVMEmissionSummary } from "../../redux/actions/mainDataTables/index";
|
||||
import { getDataCenters } from "../../redux/actions/dataCenter";
|
||||
import { editNumbers } from "../../components/edit-numbers";
|
||||
import Select from "react-select";
|
||||
|
||||
function MainDataTables() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const mainDataTablesStore = useSelector((state) => state.mainDataTables);
|
||||
const dataCenterStore = useSelector((state) => state.dataCenter);
|
||||
const [error, setError] = useState(null);
|
||||
const [selectedDataCenter, setSelectedDataCenter] = useState(null);
|
||||
const [dataCenterOptions, setDataCenterOptions] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Fetch datacenters on component mount
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
await dispatch(getVMEmissionSummary());
|
||||
} catch (err) {
|
||||
console.error('Error in MainDataTables:', err);
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
dispatch(getDataCenters());
|
||||
}, [dispatch]);
|
||||
|
||||
// Debug log for store data
|
||||
// Update datacenter options when datacenters are loaded
|
||||
useEffect(() => {
|
||||
console.log('Current store data:', mainDataTablesStore);
|
||||
}, [mainDataTablesStore]);
|
||||
if (dataCenterStore?.dataCenters?.length > 0) {
|
||||
const options = dataCenterStore.dataCenters.map((dataCenter) => ({
|
||||
value: dataCenter.id,
|
||||
label: dataCenter.dataCenter,
|
||||
externalId: dataCenter.externalId,
|
||||
}));
|
||||
setDataCenterOptions(options);
|
||||
}
|
||||
}, [dataCenterStore?.dataCenters]);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
// Fetch VM emission data when datacenter is selected
|
||||
useEffect(() => {
|
||||
if (selectedDataCenter?.value) {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
await dispatch(getVMEmissionSummary(selectedDataCenter.value));
|
||||
} catch (err) {
|
||||
console.error("Error in MainDataTables:", err);
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}
|
||||
}, [selectedDataCenter, dispatch]);
|
||||
|
||||
const columns = [
|
||||
{ header: t("VM ID"), accessorKey: "vmId", Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span> },
|
||||
{ header: t("VM Name"), accessorKey: "vmName", Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span> },
|
||||
{ header: t("VM Power"), accessorKey: "vmPower", Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span> },
|
||||
{ header: t("VM Status"), accessorKey: "vmStatus", Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span> },
|
||||
{ header: t("Total Emission"), accessorKey: "totalEmission", Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span> },
|
||||
{ header: t("Created Date"), accessorKey: "createdDate", Cell: ({ cell }) => (<span>{cell.getValue() ? new Date(cell.getValue()).toLocaleString() : "-"}</span>), sortable: true },
|
||||
{ header: t("Physical Machine"), accessorKey: "physicalMachine", Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span> },
|
||||
{ header: t("Project"), accessorKey: "project", Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span> },
|
||||
{ header: t("Data Center"), accessorKey: "dataCenter", Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span> },
|
||||
{ header: "CO2", accessorKey: "co2", Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span> },
|
||||
{ header: "CH4", accessorKey: "ch4", Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span> },
|
||||
{ header: "N2O", accessorKey: "n2o", Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span> },
|
||||
];
|
||||
// Memoize columns to prevent re-renders
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
header: t("VM ID"),
|
||||
accessorKey: "vmId",
|
||||
size: 150,
|
||||
Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("VM Name"),
|
||||
accessorKey: "vmName",
|
||||
size: 200,
|
||||
Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("VM Power"),
|
||||
accessorKey: "vmPower",
|
||||
size: 120,
|
||||
Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("VM Status"),
|
||||
accessorKey: "vmStatus",
|
||||
size: 100,
|
||||
Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("Total Emission"),
|
||||
accessorKey: "totalEmission",
|
||||
size: 150,
|
||||
Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("Physical Machine"),
|
||||
accessorKey: "physicalMachine",
|
||||
size: 150,
|
||||
Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("Cloud System"),
|
||||
accessorKey: "cloudSystem",
|
||||
size: 150,
|
||||
Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("Data Center"),
|
||||
accessorKey: "dataCenter",
|
||||
size: 150,
|
||||
Cell: ({ cell }) => <span>{cell.getValue() || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: "CO2",
|
||||
accessorKey: "co2",
|
||||
size: 100,
|
||||
Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: "CH4",
|
||||
accessorKey: "ch4",
|
||||
size: 100,
|
||||
Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: "N2O",
|
||||
accessorKey: "n2o",
|
||||
size: 100,
|
||||
Cell: ({ cell }) => <span>{editNumbers(cell.getValue()) || "-"}</span>,
|
||||
},
|
||||
{
|
||||
header: t("Created Date"),
|
||||
accessorKey: "createdDate",
|
||||
size: 180,
|
||||
Cell: ({ cell }) => (
|
||||
<span>
|
||||
{cell.getValue() ? new Date(cell.getValue()).toLocaleString() : "-"}
|
||||
</span>
|
||||
),
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const tableData = mainDataTablesStore?.vmEmissionSummary || [];
|
||||
console.log('VM Emission data:', tableData);
|
||||
// Memoize table data to prevent unnecessary re-renders
|
||||
const tableData = useMemo(() => {
|
||||
const data = mainDataTablesStore?.vmEmissionSummary || [];
|
||||
console.log("VM Emission data count:", data.length);
|
||||
return data;
|
||||
}, [mainDataTablesStore?.vmEmissionSummary]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@@ -65,38 +166,108 @@ function MainDataTables() {
|
||||
<Card>
|
||||
<CardHeader className="border-bottom">
|
||||
<CardTitle tag="h4">{t("Raw Data")}</CardTitle>
|
||||
{tableData.length > 0 && (
|
||||
<small className="text-muted">
|
||||
{tableData.length} records loaded
|
||||
</small>
|
||||
)}
|
||||
</CardHeader>
|
||||
<MaterialReactTable
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
enableColumnFilters={true}
|
||||
enableFilters={true}
|
||||
enableGlobalFilter={true}
|
||||
enablePagination={true}
|
||||
enableColumnResizing={true}
|
||||
enableStickyHeader={true}
|
||||
muiTableContainerProps={{ sx: { maxHeight: 'calc(100vh - 180px)' } }}
|
||||
muiTableProps={{
|
||||
sx: {
|
||||
tableLayout: 'auto',
|
||||
},
|
||||
}}
|
||||
initialState={{
|
||||
pagination: {
|
||||
pageSize: 100,
|
||||
pageIndex: 0
|
||||
},
|
||||
sorting: [
|
||||
{ id: 'createdDate', desc: true }
|
||||
],
|
||||
density: 'compact'
|
||||
}}
|
||||
state={{
|
||||
isLoading: loading,
|
||||
showProgressBars: true,
|
||||
showSkeletons: true,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Datacenter Selection */}
|
||||
<div className="p-3 border-bottom">
|
||||
<Row>
|
||||
<Col md="6">
|
||||
<Label for="datacenter-select">{t("Data Center")}</Label>
|
||||
<Select
|
||||
id="datacenter-select"
|
||||
value={selectedDataCenter}
|
||||
onChange={setSelectedDataCenter}
|
||||
options={dataCenterOptions}
|
||||
placeholder={t("Select a data center...")}
|
||||
isClearable
|
||||
isSearchable
|
||||
isLoading={dataCenterStore?.loading}
|
||||
noOptionsMessage={() => t("No data centers available")}
|
||||
styles={{
|
||||
menu: (provided) => ({
|
||||
...provided,
|
||||
zIndex: 9999, // Ensure dropdown appears above other elements
|
||||
}),
|
||||
}}
|
||||
menuPortalTarget={document.body} // Render dropdown in body to avoid container overflow
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
{selectedDataCenter ? (
|
||||
<MaterialReactTable
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
// Performance optimizations for large datasets
|
||||
enableColumnFilters={true}
|
||||
enableFilters={true}
|
||||
enableGlobalFilter={true}
|
||||
enablePagination={true}
|
||||
enableColumnResizing={true} // Disable resizing for better performance
|
||||
enableStickyHeader={true}
|
||||
enableRowVirtualization={true} // Enable virtualization for large datasets
|
||||
enableColumnVirtualization={false} // Keep columns visible
|
||||
// Pagination settings for large datasets
|
||||
initialState={{
|
||||
pagination: {
|
||||
pageSize: 100, // Reduce page size for better performance
|
||||
pageIndex: 0,
|
||||
},
|
||||
sorting: [{ id: "createdDate", desc: true }],
|
||||
density: "compact",
|
||||
}}
|
||||
// Performance-optimized table props
|
||||
muiTableContainerProps={{
|
||||
sx: {
|
||||
maxHeight: "calc(100vh - 250px)",
|
||||
minHeight: "400px",
|
||||
},
|
||||
}}
|
||||
muiTableProps={{
|
||||
sx: {
|
||||
tableLayout: "fixed", // Better performance with fixed layout
|
||||
},
|
||||
}}
|
||||
// Pagination options
|
||||
muiTablePaginationProps={{
|
||||
rowsPerPageOptions: [10, 25, 50, 100],
|
||||
showFirstButton: true,
|
||||
showLastButton: true,
|
||||
}}
|
||||
// Loading and error states
|
||||
state={{
|
||||
isLoading: loading || mainDataTablesStore?.loading,
|
||||
showProgressBars: loading || mainDataTablesStore?.loading,
|
||||
showSkeletons: loading || mainDataTablesStore?.loading,
|
||||
}}
|
||||
// Disable features that can slow down large tables
|
||||
enableRowSelection={false}
|
||||
enableColumnOrdering={true}
|
||||
enableColumnDragging={false}
|
||||
enableDensityToggle={false}
|
||||
enableFullScreenToggle={false}
|
||||
// Custom loading overlay
|
||||
renderProgressBarCell={({ cell }) => (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "20px",
|
||||
backgroundColor: "#f0f0f0",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<div className="p-4 text-center text-muted">
|
||||
{t("Please select a data center to view raw data")}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -32,15 +32,87 @@ import { getCities } from "../redux/actions/cities";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { getCity } from "../redux/actions/city";
|
||||
import { getDistrict } from "../redux/actions/district";
|
||||
import {
|
||||
getOrganisations,
|
||||
getOrganisationById,
|
||||
} from "../redux/actions/organisations";
|
||||
// import {
|
||||
// getOrganisations,
|
||||
// getOrganisationById,
|
||||
// } from "../redux/actions/organisations";
|
||||
import { getAreasWithCriteria } from "../redux/actions/areas";
|
||||
import { ChromePicker } from "react-color";
|
||||
import { customFilterForSelect } from "../utility/Utils";
|
||||
import { permissionCheck } from "../components/permission-check";
|
||||
import { getDataCenters } from "../redux/actions/dataCenter";
|
||||
import L from "leaflet";
|
||||
|
||||
// Custom data center icon
|
||||
const dataCenterIcon = new L.Icon({
|
||||
iconUrl:
|
||||
"data:image/svg+xml;base64," +
|
||||
btoa(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<defs>
|
||||
<linearGradient id="serverGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#2E5BBA;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="rackGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#F5F5F5;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#E0E0E0;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Main server rack -->
|
||||
<rect x="8" y="4" width="32" height="40" rx="2" ry="2" fill="url(#rackGradient)" stroke="#B0B0B0" stroke-width="1"/>
|
||||
|
||||
<!-- Server units -->
|
||||
<rect x="10" y="6" width="28" height="4" rx="1" fill="url(#serverGradient)" stroke="#2E5BBA" stroke-width="0.5"/>
|
||||
<rect x="10" y="12" width="28" height="4" rx="1" fill="url(#serverGradient)" stroke="#2E5BBA" stroke-width="0.5"/>
|
||||
<rect x="10" y="18" width="28" height="4" rx="1" fill="url(#serverGradient)" stroke="#2E5BBA" stroke-width="0.5"/>
|
||||
<rect x="10" y="24" width="28" height="4" rx="1" fill="url(#serverGradient)" stroke="#2E5BBA" stroke-width="0.5"/>
|
||||
<rect x="10" y="30" width="28" height="4" rx="1" fill="url(#serverGradient)" stroke="#2E5BBA" stroke-width="0.5"/>
|
||||
<rect x="10" y="36" width="28" height="4" rx="1" fill="url(#serverGradient)" stroke="#2E5BBA" stroke-width="0.5"/>
|
||||
|
||||
<!-- LED indicators -->
|
||||
<circle cx="13" cy="8" r="0.8" fill="#00FF00"/>
|
||||
<circle cx="13" cy="14" r="0.8" fill="#00FF00"/>
|
||||
<circle cx="13" cy="20" r="0.8" fill="#FFFF00"/>
|
||||
<circle cx="13" cy="26" r="0.8" fill="#00FF00"/>
|
||||
<circle cx="13" cy="32" r="0.8" fill="#FF0000"/>
|
||||
<circle cx="13" cy="38" r="0.8" fill="#00FF00"/>
|
||||
|
||||
<!-- Power indicators -->
|
||||
<circle cx="35" cy="8" r="0.6" fill="#0080FF"/>
|
||||
<circle cx="35" cy="14" r="0.6" fill="#0080FF"/>
|
||||
<circle cx="35" cy="20" r="0.6" fill="#0080FF"/>
|
||||
<circle cx="35" cy="26" r="0.6" fill="#0080FF"/>
|
||||
<circle cx="35" cy="32" r="0.6" fill="#0080FF"/>
|
||||
<circle cx="35" cy="38" r="0.6" fill="#0080FF"/>
|
||||
|
||||
<!-- Ventilation grilles -->
|
||||
<rect x="16" y="7" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="8.5" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="13" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="14.5" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="19" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="20.5" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="25" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="26.5" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="31" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="32.5" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="37" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
<rect x="16" y="38.5" width="16" height="0.5" fill="#1A4A8A"/>
|
||||
|
||||
<!-- Base/feet -->
|
||||
<rect x="6" y="42" width="4" height="2" rx="1" fill="#808080"/>
|
||||
<rect x="38" y="42" width="4" height="2" rx="1" fill="#808080"/>
|
||||
|
||||
<!-- Shadow -->
|
||||
<ellipse cx="24" cy="45" rx="18" ry="2" fill="#000000" opacity="0.2"/>
|
||||
</svg>
|
||||
`),
|
||||
iconSize: [48, 48],
|
||||
iconAnchor: [18, 36],
|
||||
popupAnchor: [0, -36],
|
||||
});
|
||||
|
||||
const ColorPicker = ({ selectedColors, setSelectedColors, index }) => {
|
||||
const [showColorPicker, setShowColorPicker] = useState(false);
|
||||
@@ -117,20 +189,20 @@ const Map = () => {
|
||||
const citiesStore = useSelector((state) => state.cities);
|
||||
const cityStore = useSelector((state) => state.city);
|
||||
const districtStore = useSelector((state) => state.district);
|
||||
const OrganisationsStore = useSelector((state) => state.organizations);
|
||||
// const OrganisationsStore = useSelector((state) => state.organizations);
|
||||
const areasStore = useSelector((state) => state.areas);
|
||||
const dataCenterStore = useSelector((state) => state.dataCenter);
|
||||
|
||||
const [cities, setCities] = useState([]);
|
||||
const [districts, setDistricts] = useState([]);
|
||||
const [neighborhoods, setNeighborhoods] = useState([]);
|
||||
const [organizationOptions, setOrganizationOptions] = useState([]);
|
||||
const organizationId = localStorage.getItem("organizationId");
|
||||
const roleTag = localStorage.getItem("roleTag");
|
||||
const [selectedOrganization, setSelectedOrganization] = useState({
|
||||
label: localStorage.getItem("organizationName"),
|
||||
value: organizationId,
|
||||
});
|
||||
// const [organizationOptions, setOrganizationOptions] = useState([]);
|
||||
// const organizationId = localStorage.getItem("organizationId");
|
||||
// const roleTag = localStorage.getItem("roleTag");
|
||||
// const [selectedOrganization, setSelectedOrganization] = useState({
|
||||
// label: localStorage.getItem("organizationName"),
|
||||
// value: organizationId,
|
||||
// });
|
||||
const [areasOptions, setAreasOptions] = useState([]);
|
||||
|
||||
const [done, setDone] = useState(false);
|
||||
@@ -146,7 +218,7 @@ const Map = () => {
|
||||
|
||||
const [showDataInputModal, setShowDataInputModal] = useState(false);
|
||||
const [inputData, setInputData] = useState({
|
||||
organization: selectedOrganization,
|
||||
// organization: selectedOrganization,
|
||||
});
|
||||
const [referance, setReferance] = useState(
|
||||
Number(localStorage.getItem("referance")) || 1000
|
||||
@@ -176,15 +248,17 @@ const Map = () => {
|
||||
dispatch(getDataCenters());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedOrganization?.value != "undefined") {
|
||||
dispatch(getAreasWithCriteria(selectedOrganization.value));
|
||||
}
|
||||
}, [selectedOrganization]);
|
||||
// useEffect(() => {
|
||||
// if (selectedOrganization?.value != "undefined") {
|
||||
// dispatch(getAreasWithCriteria(selectedOrganization.value));
|
||||
// }
|
||||
// }, [selectedOrganization]);
|
||||
|
||||
useEffect(() => {
|
||||
setAreasOptions([]);
|
||||
|
||||
// Load all areas without organization filter for now
|
||||
// You may want to add organization filtering back later if needed
|
||||
const citiesOptions =
|
||||
areasStore?.areasWithCriteria
|
||||
?.map((area) =>
|
||||
@@ -235,13 +309,13 @@ const Map = () => {
|
||||
]);
|
||||
}, [areasStore]);
|
||||
|
||||
useEffect(() => {
|
||||
if (roleTag === "SUPER_ADMIN") {
|
||||
dispatch(getOrganisations());
|
||||
} else {
|
||||
dispatch(getOrganisationById(organizationId));
|
||||
}
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// if (roleTag === "SUPER_ADMIN") {
|
||||
// dispatch(getOrganisations());
|
||||
// } else {
|
||||
// dispatch(getOrganisationById(organizationId));
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
const handleDataInputButtonPressed = ({ area, type }) => {
|
||||
const areaName =
|
||||
@@ -348,38 +422,38 @@ const Map = () => {
|
||||
});
|
||||
}, [selectedDistrict, districtStore?.district, year.value]);
|
||||
|
||||
useEffect(() => {
|
||||
let organizationOptions = [];
|
||||
// useEffect(() => {
|
||||
// let organizationOptions = [];
|
||||
|
||||
if (
|
||||
OrganisationsStore.organization &&
|
||||
OrganisationsStore.organization.length !== 0
|
||||
) {
|
||||
organizationOptions.push({
|
||||
value: OrganisationsStore.organization.id,
|
||||
label: OrganisationsStore.organization.tag,
|
||||
});
|
||||
// if (
|
||||
// OrganisationsStore.organization &&
|
||||
// OrganisationsStore.organization.length !== 0
|
||||
// ) {
|
||||
// organizationOptions.push({
|
||||
// value: OrganisationsStore.organization.id,
|
||||
// label: OrganisationsStore.organization.tag,
|
||||
// });
|
||||
|
||||
if (OrganisationsStore.organization.children) {
|
||||
organizationOptions = [
|
||||
...organizationOptions,
|
||||
...OrganisationsStore.organization.children.map((organization) => ({
|
||||
value: organization.child.id,
|
||||
label: organization.child.tag,
|
||||
})),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
organizationOptions = OrganisationsStore.dataOrganization.map(
|
||||
(organization) => ({
|
||||
value: organization.id,
|
||||
label: organization.tag,
|
||||
})
|
||||
);
|
||||
}
|
||||
// if (OrganisationsStore.organization.children) {
|
||||
// organizationOptions = [
|
||||
// ...organizationOptions,
|
||||
// ...OrganisationsStore.organization.children.map((organization) => ({
|
||||
// value: organization.child.id,
|
||||
// label: organization.child.tag,
|
||||
// })),
|
||||
// ];
|
||||
// }
|
||||
// } else {
|
||||
// organizationOptions = OrganisationsStore.dataOrganization.map(
|
||||
// (organization) => ({
|
||||
// value: organization.id,
|
||||
// label: organization.tag,
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
setOrganizationOptions(organizationOptions);
|
||||
}, [OrganisationsStore]);
|
||||
// setOrganizationOptions(organizationOptions);
|
||||
// }, [OrganisationsStore]);
|
||||
|
||||
const renderDataInputModal = () => {
|
||||
return (
|
||||
@@ -623,29 +697,60 @@ const Map = () => {
|
||||
<LayerGroup>
|
||||
{dataCenterStore.dataCenters.map((dc) => {
|
||||
if (!dc.latitude || !dc.longitude) return null;
|
||||
|
||||
|
||||
return (
|
||||
<Marker
|
||||
key={dc.id}
|
||||
<Marker
|
||||
key={dc.id}
|
||||
position={[dc.latitude, dc.longitude]}
|
||||
icon={dataCenterIcon}
|
||||
>
|
||||
<Popup>
|
||||
<div className="data-center-popup">
|
||||
<h5 className="mb-2">{dc.dataCenter}</h5>
|
||||
<p className="mb-1"><strong>{t('DataCenter.number')}:</strong> {dc.number}</p>
|
||||
<p className="mb-1"><strong>{t('DataCenter.externalId')}:</strong> {dc.externalId}</p>
|
||||
<p className="mb-1"><strong>{t('DataCenter.city')}:</strong> {dc.area?.cities?.map(city => city.name).join(', ') || "-"}</p>
|
||||
{dc.area && <p className="mb-1"><strong>{t('Area')}:</strong> {dc.area.tag}</p>}
|
||||
{dc.projects?.length > 0 && (
|
||||
<h5 className="mb-2 text-primary">{dc.dataCenter}</h5>
|
||||
<div className="mb-2">
|
||||
<p className="mb-1">
|
||||
<strong>{t('Projects')}:</strong> {dc.projects.length}
|
||||
<strong>{t("DataCenter.city")}:</strong>{" "}
|
||||
<span>{dc.city?.name || "-"}</span>
|
||||
</p>
|
||||
{dc.address && (
|
||||
<p className="mb-1 small text-muted">
|
||||
<strong>{t("Address")}:</strong> {dc.address}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<p className="mb-1">
|
||||
<strong>{t("DataCenter.number")}:</strong> {dc.number}
|
||||
</p>
|
||||
<p className="mb-1">
|
||||
<strong>{t("DataCenter.externalId")}:</strong>{" "}
|
||||
{dc.externalId}
|
||||
</p>
|
||||
{dc.area && (
|
||||
<p className="mb-1">
|
||||
<strong>{t("Area")}:</strong> {dc.area.tag}
|
||||
</p>
|
||||
)}
|
||||
{dc.physicalMachines?.length > 0 && (
|
||||
<p className="mb-1">
|
||||
<strong>{t("Physical Machines")}:</strong>{" "}
|
||||
<span className="badge badge-secondary">
|
||||
{dc.physicalMachines.length}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{dc.dataCenterEmissionSources?.length > 0 && (
|
||||
<p className="mb-1">
|
||||
<strong>{t("EmissionSources.emissionSources")}:</strong>{" "}
|
||||
<span className="badge badge-info">
|
||||
{dc.dataCenterEmissionSources.length}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{dc.ayposURL && (
|
||||
<Button
|
||||
className="w-100 mb-1"
|
||||
color="primary"
|
||||
onClick={() => window.open(dc.ayposURL, '_blank')}
|
||||
onClick={() => window.open(dc.ayposURL, "_blank")}
|
||||
>
|
||||
Dashboard
|
||||
</Button>
|
||||
@@ -699,7 +804,9 @@ const Map = () => {
|
||||
setDistrictView(true);
|
||||
setZoom(8.0);
|
||||
|
||||
let convertCordinates = convertCoordinates(city.coordinates);
|
||||
let convertCordinates = convertCoordinates(
|
||||
city.coordinates
|
||||
);
|
||||
let length = convertCordinates[0][0][0].length;
|
||||
let mlength = ((length + 1) / 2).toFixed(0);
|
||||
let lat1 = convertCordinates[0][0][0][0];
|
||||
@@ -710,7 +817,7 @@ const Map = () => {
|
||||
lat: ((lat1 + lat2) / 2).toFixed(2),
|
||||
lng: ((lng1 + lng2) / 2).toFixed(2),
|
||||
});
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tooltip permanent direction="center">
|
||||
|
||||
Reference in New Issue
Block a user