diff --git a/backend/config/application.tmpl b/backend/config/application.tmpl
index e22578c..6239247 100644
--- a/backend/config/application.tmpl
+++ b/backend/config/application.tmpl
@@ -46,16 +46,12 @@ spring:
default-property-inclusion: non_null
security:
oauth2:
- resourceserver:
- jwt:
- issuer-uri: {{ .Env.KEYCLOAK_AUTH_URL }}realms/{{ .Env.KEYCLOAK_REALM }}
client:
registration:
keycloak:
authorization-grant-type: authorization_code
client-id: {{ .Env.KEYCLOAK_CLIENT_ID }}
client-secret: {{ .Env.KEYCLOAK_CLIENT_SECRET }}
- redirect-uri: "http://localhost:8090/services/login/oauth2/code/keycloak"
scope: openid
provider:
keycloak:
@@ -63,8 +59,8 @@ spring:
user-name-attribute: preferred_username
app:
frontend:
- auth-callback-url: {{ .Env.FRONTEND_URL }}/auth-callback
- base-url: {{ .Env.FRONTEND_URL }}
+ auth-callback-url: {{ .Env.PUBLIC_HOST }}/auth-callback
+ base-url: {{ .Env.PUBLIC_HOST }}
data_quality_tool:
json-to-excel-url: {{ .Env.DQT_URL }}/json-to-excel
excel-to-json-url: {{ .Env.DQT_URL }}/excel-to-json
\ No newline at end of file
diff --git a/compose.yaml b/compose.yaml
index 8e9fd89..3d42f08 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -1,5 +1,3 @@
-
-
services:
data_quality_tool:
build:
@@ -41,7 +39,7 @@ services:
DB_USER: postgres
DB_PASSWORD: test
### Frontend ###
- FRONTEND_URL: http://localhost
+ PUBLIC_HOST: http://localhost
DQT_URL: http://data_quality_tool:8000
### Keycloak ###
AUTHENTICATION: 1
diff --git a/frontend/src/app/pages/account-page/account-page.component.css b/frontend/src/app/pages/account-page/account-page.component.css
index 93e5c52..bf0812c 100644
--- a/frontend/src/app/pages/account-page/account-page.component.css
+++ b/frontend/src/app/pages/account-page/account-page.component.css
@@ -3,98 +3,65 @@
/* General Styling */
.account-page {
display: flex;
+ justify-content: center;
+ align-items: center;
height: 100vh;
- margin-top: 20px;
font-family: 'Roboto', sans-serif;
-}
-
-.sidebar {
- width: 250px;
- background-color: #f5f5f5;
- transition: width 0.3s ease-in-out;
- overflow: hidden;
- position: relative;
-}
-
-.sidebar.collapsed {
- width: 4%; /* Collapsed width for smaller sidebar */
-}
-
-.sidebar-toggle {
- cursor: pointer;
- padding: 10px;
- text-align: center;
- background-color: #18a2b8;
- color: white;
-}
-
-.sidebar-nav {
- list-style: none;
- padding: 20px 0;
+ background-color: #f4f6f8;
margin: 0;
}
-.sidebar-nav li {
- margin: 10px 0;
-}
-
-.sidebar-nav li a {
- display: flex;
- align-items: center;
- padding: 10px 15px;
- text-decoration: none;
- color: #333;
- transition: background-color 0.2s;
-}
-
-.sidebar-nav li a i {
- font-size: 1.5em;
-}
-
-.sidebar-nav li a span {
- margin-left: 10px;
-}
-
-.sidebar-nav li a:hover, .sidebar-nav li a.active {
- background-color: #18a2b8;
- color: white;
-}
-
/* Main Content Styling */
.content {
- flex-grow: 1;
+ width: 90%;
+ max-width: 600px;
padding: 20px;
background-color: #ffffff;
- overflow-y: auto;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+ border-radius: 10px;
+ text-align: center;
}
.account-header {
display: flex;
justify-content: space-between;
align-items: center;
+ margin-bottom: 20px;
+}
+
+h1 {
+ font-size: 1.8rem;
+ color: #333333;
}
.sign-out-btn {
background-color: #18a2b8;
- color: white;
+ color: #ffffff;
border: none;
- padding: 10px 15px;
+ padding: 10px 20px;
+ font-size: 1rem;
cursor: pointer;
border-radius: 5px;
+ transition: background-color 0.3s;
+}
+
+.sign-out-btn:hover {
+ background-color: #12798e;
}
.account-details {
display: flex;
+ flex-direction: column;
align-items: center;
- margin: 20px 0;
+ gap: 15px;
}
.profile-picture {
- width: 100px;
- height: 100px;
+ width: 120px;
+ height: 120px;
border-radius: 50%;
overflow: hidden;
- margin-right: 20px;
+ border: 2px solid #18a2b8;
}
.profile-picture img {
@@ -103,66 +70,36 @@
}
.user-info h2 {
- margin: 0;
-}
-
-.personal-info {
- margin-top: 30px;
-}
-
-.info-cards {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 20px;
- margin-top: 20px;
+ margin: 10px 0 5px;
+ font-size: 1.5rem;
+ color: #333333;
}
-.info-card {
- background-color: #f9f9f9;
- border: 1px solid #eee;
- border-radius: 8px;
- padding: 15px;
-}
-
-.info-card h3 {
- margin: 0 0 10px 0;
+.user-info p {
+ margin: 0;
+ font-size: 1rem;
+ color: #666666;
}
/* Responsive Design */
@media (max-width: 768px) {
- .sidebar {
- position: absolute;
- width: 80px;
- }
-
- .sidebar.collapsed {
- width: 0;
- }
-
.content {
- margin-left: 80px;
- }
-
- .sidebar-nav li a span {
- display: none;
- }
-
- .info-cards {
- grid-template-columns: 1fr; /* One column for mobile view */
+ width: 100%;
+ padding: 15px;
}
}
@media (max-width: 480px) {
.content {
- margin-left: 0;
padding: 10px;
}
- .sidebar {
- width: 60px;
+ h1 {
+ font-size: 1.5rem;
}
- .sidebar.collapsed {
- width: 0;
+ .sign-out-btn {
+ padding: 8px 15px;
+ font-size: 0.9rem;
}
}
diff --git a/frontend/src/app/pages/account-page/account-page.component.html b/frontend/src/app/pages/account-page/account-page.component.html
index cf7c77c..a62b530 100644
--- a/frontend/src/app/pages/account-page/account-page.component.html
+++ b/frontend/src/app/pages/account-page/account-page.component.html
@@ -1,38 +1,5 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/app/pages/data-models-page/data-models-page.component.css b/frontend/src/app/pages/data-models-page/data-models-page.component.css
index 639efe7..8865987 100644
--- a/frontend/src/app/pages/data-models-page/data-models-page.component.css
+++ b/frontend/src/app/pages/data-models-page/data-models-page.component.css
@@ -18,14 +18,9 @@
/* Dropdown container styling */
.dropdown-container {
- margin: 0; /* Ensure no extra space */
display: flex;
gap: 8px;
flex-wrap: wrap;
+ margin-bottom: 4px;
}
-
-/* Node info container */
-.node-info-container {
- margin-top: 10px; /* Add spacing above node info */
-}
diff --git a/frontend/src/app/pages/data-models-page/data-models-page.component.html b/frontend/src/app/pages/data-models-page/data-models-page.component.html
index 229eef3..6db7fc1 100644
--- a/frontend/src/app/pages/data-models-page/data-models-page.component.html
+++ b/frontend/src/app/pages/data-models-page/data-models-page.component.html
@@ -1,55 +1,57 @@
+
-
-
-
-
- @if(selectedDataModel && (crossSectionalModels.length > 0 || longitudinalModels.length > 0)){
-
- }
+
+
+
+
-
- @if(isDomainExpert){
-
+ @if(selectedDataModel && (crossSectionalModels.length > 0 || longitudinalModels.length > 0)){
+
}
-
-
+
+
- @if(!menuVisible()){
-
- @if(selectedNode){
-
- }
-
+ @if(isDomainExpert){
+
}
+
+
+
+ @if(!menuVisible()){
+
+ @if(selectedNode){
+
+ }
+
+ }
+
}
diff --git a/frontend/src/app/pages/data-models-page/data-models-page.component.ts b/frontend/src/app/pages/data-models-page/data-models-page.component.ts
index df27f38..aad14e4 100644
--- a/frontend/src/app/pages/data-models-page/data-models-page.component.ts
+++ b/frontend/src/app/pages/data-models-page/data-models-page.component.ts
@@ -20,6 +20,7 @@ import {ErrorService} from "./services/error.service";
import {ConfirmationDialogComponent} from "./confirmation-dialog/confirmation-dialog.component";
import {MatDialog} from "@angular/material/dialog";
import {DataModelFormComponent} from "./data-model-form/data-model-form.component";
+import {GuidePopupComponent} from "./guide-popup/guide-popup.component";
@Component({
@@ -40,7 +41,8 @@ import {DataModelFormComponent} from "./data-model-form/data-model-form.componen
NodeInfoComponent,
DataModelSelectorComponent,
ExportOptionsComponent,
- RouterOutlet
+ RouterOutlet,
+ GuidePopupComponent,
],
standalone: true
})
@@ -60,7 +62,6 @@ export class DataModelsPageComponent implements OnInit{
longitudinalModels: DataModel[] = [];
menuVisible = signal(false);
-
constructor(
private federationService: FederationService,
private dataModelService: DataModelService,
diff --git a/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.css b/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.css
new file mode 100644
index 0000000..bb8c479
--- /dev/null
+++ b/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.css
@@ -0,0 +1,70 @@
+.guide-popup {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 400px;
+ background: #fff;
+ border-radius: 10px;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
+ animation: fadeIn 0.3s ease-in-out;
+ z-index: 1000;
+}
+.popup-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 15px 20px;
+ background: linear-gradient(90deg, #0056b3, #007bff);
+ color: #ffffff;
+ font-size: 18px;
+ font-weight: 600;
+ border-radius: 10px 10px 0 0;
+}
+
+.popup-header h2 {
+ margin: 0;
+ font-size: 20px;
+ font-weight: bold;
+}
+
+.popup-body {
+ padding: 20px;
+ font-size: 14px;
+ line-height: 1.6;
+ color: #333;
+}
+
+.popup-footer {
+ padding: 10px 20px;
+ border-top: 1px solid #ddd;
+ text-align: right;
+}
+
+.close-btn {
+ font-size: 18px;
+ cursor: pointer;
+ background: none;
+ border: none;
+ color: #ffffff;
+}
+
+.close-btn:hover {
+ color: #ffcccc;
+}
+
+.highlight {
+ color: #ffcc00;
+ font-weight: bold;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translate(-50%, -60%);
+ }
+ to {
+ opacity: 1;
+ transform: translate(-50%, -50%);
+ }
+}
diff --git a/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.html b/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.html
new file mode 100644
index 0000000..8d85a1a
--- /dev/null
+++ b/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.html
@@ -0,0 +1,23 @@
+
diff --git a/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.ts b/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.ts
new file mode 100644
index 0000000..01913b5
--- /dev/null
+++ b/frontend/src/app/pages/data-models-page/guide-popup/guide-popup.component.ts
@@ -0,0 +1,33 @@
+import {Component, OnInit} from '@angular/core';
+import {FormsModule} from "@angular/forms";
+import {NgIf} from "@angular/common";
+
+@Component({
+ selector: 'app-guide-popup',
+ templateUrl: './guide-popup.component.html',
+ styleUrls: ['./guide-popup.component.css'],
+ imports: [
+ FormsModule,
+ NgIf
+ ],
+ standalone: true
+})
+export class GuidePopupComponent implements OnInit{
+ showPopup = true;
+ dontShowAgain = false;
+
+ closePopup() {
+ this.showPopup = false;
+
+ if (this.dontShowAgain) {
+ localStorage.setItem('hideGuidePopup', 'true');
+ }
+ }
+
+ ngOnInit() {
+ const hideGuidePopup = localStorage.getItem('hideGuidePopup');
+ if (hideGuidePopup === 'true') {
+ this.showPopup = false;
+ }
+ }
+}
diff --git a/frontend/src/app/pages/data-models-page/visualization/tidy-tree.ts b/frontend/src/app/pages/data-models-page/visualization/tidy-tree.ts
index f514103..b3e5105 100644
--- a/frontend/src/app/pages/data-models-page/visualization/tidy-tree.ts
+++ b/frontend/src/app/pages/data-models-page/visualization/tidy-tree.ts
@@ -70,9 +70,25 @@ export function createTidyTree(
const dynamicHeight = Math.max(baseHeight, x1 - x0 + dx * 2);
const dynamicWidth = y1 - y0 + dy * 2;
- const offsetX = Math.max(0, (baseWidth - dynamicWidth) / 3) - y0;
+ const offsetFactor = dynamicWidth / baseWidth; // Calculate a ratio based on size
+ const leftBias = Math.min(0.25, offsetFactor / 2); // Bias more for larger charts
+
+ const offsetX = (baseWidth - dynamicWidth) / 2 * (1 - leftBias) - y0;
+
+ // Ensure offset doesn't push too far left for large diagrams
+ const adjustedOffsetX = Math.max(offsetX, -y0) + 50;
+
+
+ console.log("offsetX", offsetX)
+
+ const paddingX = 10, paddingY = 10;
+ const verticalOffset = (2 * baseHeight - dynamicHeight)/ 2;
+ console.log(dynamicHeight)
+ console.log(baseHeight)
+
+ // Ensure the offset is non-negative (only for smaller diagrams)
+ const adjustedPaddingY = Math.max(paddingY, verticalOffset);
- const paddingX = 50, paddingY = 20;
const viewBoxWidth = dynamicWidth + paddingX * 2;
const viewBoxHeight = dynamicHeight + paddingY * 2;
@@ -83,7 +99,7 @@ export function createTidyTree(
.attr('style', 'max-width: 100%; height: auto; font: 10px sans-serif;');
const g = svg.append('g')
- .attr('transform', `translate(${offsetX}, ${paddingY})`);
+ .attr('transform', `translate(${adjustedOffsetX}, ${adjustedPaddingY})`);
// Render links
g.append('g')
@@ -122,7 +138,7 @@ export function createTidyTree(
const newAvailableDepths = calculateMaxDepth(d); // Calculate available depths for the new root
- if (newAvailableDepths <= 0) {
+ if (newAvailableDepths <= 1) {
console.log('Double-clicked node has no depth, breadcrumb unchanged.');
return; // If the node has no depth, do nothing and leave the breadcrumb unchanged
}
@@ -134,20 +150,20 @@ export function createTidyTree(
});
node.append('circle')
- .attr('filter', d => (d.depth === 0 ? null : (d.depth > 0 && calculateMaxDepth(d) > 0 ? 'url(#glow)' : null))) // No glow for root
+ .attr('filter', d => (d.depth === 0 ? null : (d.depth > 0 && calculateMaxDepth(d) > 1 ? 'url(#glow)' : null))) // No glow for root
.attr('title', d => `Name: ${d.data.name}\nDepth: ${d.depth}`)
- .attr('fill', d => (d.depth === 0 ? '#4caf50' : (d.depth > 0 && calculateMaxDepth(d) > 0 ? '#007acc' : '#555'))) // Root gets green color
- .attr('stroke', d => (d.depth === 0 ? '#2e7d32' : (d.depth > 0 && calculateMaxDepth(d) > 0 ? '#ffcc00' : null))) // Root gets dark green stroke
- .attr('r', d => (d.depth === 0 ? 8 : (d.depth > 0 && calculateMaxDepth(d) > 0 ? 5 : 2.5))); // Root has a larger radius
+ .attr('fill', d => (d.depth === 0 ? '#4caf50' : (d.depth > 0 && calculateMaxDepth(d) > 1 ? '#007acc' : '#555'))) // Root gets green color
+ .attr('stroke', d => (d.depth === 0 ? '#2e7d32' : (d.depth > 0 && calculateMaxDepth(d) > 1 ? '#ffcc00' : null))) // Root gets dark green stroke
+ .attr('r', d => (d.depth === 0 ? 8 : (d.depth > 0 && calculateMaxDepth(d) > 1 ? 5 : 2.5))); // Root has a larger radius
node.append('text')
.attr('dy', '0.31em')
.attr('x', d => (d.children ? -6 : 6))
.attr('text-anchor', d => (d.children ? 'end' : 'start'))
.text(d => d.data.name)
- .attr('stroke', d => (d.depth === 0 ? '#ffffff' : (d.depth > 0 && calculateMaxDepth(d) > 0 ? 'yellow' : 'white'))) // Root text gets white stroke
+ .attr('stroke', d => (d.depth === 0 ? '#ffffff' : (d.depth > 0 && calculateMaxDepth(d) > 1 ? 'yellow' : 'white'))) // Root text gets white stroke
.attr('paint-order', 'stroke')
- .style('font-size', d => (d.depth === 0 ? `${14}px` : (d.depth > 0 && calculateMaxDepth(d) > 0 ? `${12}px` : `${10}px`))) // Root has larger font size
+ .style('font-size', d => (d.depth === 0 ? `${14}px` : (d.depth > 0 && calculateMaxDepth(d) > 1 ? `${12}px` : `${10}px`))) // Root has larger font size
.style('font-weight', d => (d.depth === 0 ? 'bold' : 'normal')); // Bold font for root
if (svg.node() !== null) {
diff --git a/frontend/src/app/pages/data-models-page/visualization/visualization.component.css b/frontend/src/app/pages/data-models-page/visualization/visualization.component.css
index b2b1280..e2b6cca 100644
--- a/frontend/src/app/pages/data-models-page/visualization/visualization.component.css
+++ b/frontend/src/app/pages/data-models-page/visualization/visualization.component.css
@@ -7,7 +7,7 @@
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow-x: auto;
overflow-y: auto;
- height: calc(100vh - 280px); /* Reduced height */
+ height: calc(100vh - 239px); /* Reduced height */
width: calc(87% - 30px);
display: inline-block;
vertical-align: top;
@@ -38,7 +38,6 @@
color: #555;
}
-
@media (max-width: 768px) {
#chart-container {
height: auto; /* Adjust height dynamically */
@@ -48,14 +47,3 @@
font-size: 12px;
}
}
-
-
-.disclaimer {
- margin-top: 5px; /* Reduce top margin of the disclaimer */
- padding: 8px; /* Reduce padding */
- background-color: #f9f9f9;
- border: 1px solid #ddd;
- border-radius: 5px;
- font-size: 14px;
- color: #333;
-}
diff --git a/frontend/src/app/pages/data-models-page/visualization/visualization.component.html b/frontend/src/app/pages/data-models-page/visualization/visualization.component.html
index 71aea1f..fa51321 100644
--- a/frontend/src/app/pages/data-models-page/visualization/visualization.component.html
+++ b/frontend/src/app/pages/data-models-page/visualization/visualization.component.html
@@ -1,11 +1,3 @@
-
- Note: All nodes can be clicked to access their information. Only nodes with the
-
- yellow effect
-
- can be double-clicked to become the new root. Additionally, the node must have a depth of at least 3 to be eligible as a root.
-
-