diff --git a/frontend/src/app/modules/common/common-components.module.ts b/frontend/src/app/modules/common/common-components.module.ts
index ab0385c238..3d29f50b23 100644
--- a/frontend/src/app/modules/common/common-components.module.ts
+++ b/frontend/src/app/modules/common/common-components.module.ts
@@ -34,6 +34,7 @@ import { StatusDropdown } from './status-dropdown/status-dropdown.component';
import { CustomConfirmDialogComponent } from './custom-confirm-dialog/custom-confirm-dialog.component';
import { TreeGraphComponent } from './tree-graph/tree-graph.component';
import { GuardianSwitchButton } from './guardian-switch-button/guardian-switch-button.component';
+import { GuardianTabsSwitch } from './guardian-tabs-switch/guardian-tabs-switch.component';
import { ImportEntityDialog } from './import-entity-dialog/import-entity-dialog.component';
import { DialogService, DynamicDialogModule } from 'primeng/dynamicdialog';
import { GuardianDialogService } from '../../services/guardian-dialog.service';
@@ -53,6 +54,8 @@ import { PluralizePipe } from './pipes/pluralize.pipe';
import { PagesControl } from './pages-control/pages-control.component';
import { GridDialogComponent } from './grid-dialog/grid-dialog.component';
import { ContentSearchComponent } from './content-search/content-search.component';
+import { CommonDialogComponent, DialogHeaderDirective, DialogFooterDirective } from './common-dialog/common-dialog.component';
+import { ProgressSpinnerModule } from 'primeng/progressspinner';
@NgModule({
declarations: [
@@ -79,6 +82,7 @@ import { ContentSearchComponent } from './content-search/content-search.componen
CustomConfirmDialogComponent,
TreeGraphComponent,
GuardianSwitchButton,
+ GuardianTabsSwitch,
ImportEntityDialog,
MathLiveComponent,
MenuButton,
@@ -93,6 +97,9 @@ import { ContentSearchComponent } from './content-search/content-search.componen
PagesControl,
GridDialogComponent,
ContentSearchComponent,
+ CommonDialogComponent,
+ DialogHeaderDirective,
+ DialogFooterDirective,
],
imports: [
CommonModule,
@@ -112,6 +119,7 @@ import { ContentSearchComponent } from './content-search/content-search.componen
TooltipModule,
DynamicDialogModule,
AgGridModule,
+ ProgressSpinnerModule,
],
providers: [
provideNgxMask(),
@@ -141,6 +149,7 @@ import { ContentSearchComponent } from './content-search/content-search.componen
CustomConfirmDialogComponent,
TreeGraphComponent,
GuardianSwitchButton,
+ GuardianTabsSwitch,
ImportEntityDialog,
MathLiveComponent,
MenuButton,
@@ -155,6 +164,9 @@ import { ContentSearchComponent } from './content-search/content-search.componen
PagesControl,
GridDialogComponent,
ContentSearchComponent,
+ CommonDialogComponent,
+ DialogHeaderDirective,
+ DialogFooterDirective,
]
})
export class CommonComponentsModule {
diff --git a/frontend/src/app/modules/common/common-dialog/common-dialog.component.html b/frontend/src/app/modules/common/common-dialog/common-dialog.component.html
new file mode 100644
index 0000000000..276afbda71
--- /dev/null
+++ b/frontend/src/app/modules/common/common-dialog/common-dialog.component.html
@@ -0,0 +1,47 @@
+
+ @if (showHeader) {
+
+ }
+
+
+ @if (!loading) {
+
+ } @else {
+
+ }
+
+
+ @if (showFooter) {
+
+ }
+
diff --git a/frontend/src/app/modules/common/common-dialog/common-dialog.component.scss b/frontend/src/app/modules/common/common-dialog/common-dialog.component.scss
new file mode 100644
index 0000000000..6a3799f97f
--- /dev/null
+++ b/frontend/src/app/modules/common/common-dialog/common-dialog.component.scss
@@ -0,0 +1,32 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ min-height: 0;
+}
+
+.guardian-common-dialog {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 auto;
+ min-height: 0;
+
+ .dialog-body {
+ flex: 1 1 auto;
+ min-height: 0;
+ margin-top: 16px;
+ }
+
+ .dialog-footer {
+ flex-shrink: 0;
+ }
+}
+
+.loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ align-content: center;
+ min-height: 120px;
+ background: var(--guardian-background, #fff);
+}
diff --git a/frontend/src/app/modules/common/common-dialog/common-dialog.component.ts b/frontend/src/app/modules/common/common-dialog/common-dialog.component.ts
new file mode 100644
index 0000000000..e644e1018d
--- /dev/null
+++ b/frontend/src/app/modules/common/common-dialog/common-dialog.component.ts
@@ -0,0 +1,57 @@
+import {
+ AfterContentInit,
+ Component,
+ ContentChild,
+ Directive,
+ EventEmitter,
+ Input,
+ Output,
+} from '@angular/core';
+
+@Directive({
+ selector: '[appDialogHeader]',
+ standalone: false,
+})
+export class DialogHeaderDirective {}
+
+@Directive({
+ selector: '[appDialogFooter]',
+ standalone: false,
+})
+export class DialogFooterDirective {}
+
+@Component({
+ selector: 'app-common-dialog',
+ templateUrl: './common-dialog.component.html',
+ styleUrls: ['./common-dialog.component.scss'],
+ standalone: false,
+})
+export class CommonDialogComponent implements AfterContentInit {
+ @Input() title: string;
+ @Input() confirmLabel: string = 'Confirm';
+ @Input() cancelLabel: string = 'Close';
+ @Input() confirmDisabled: boolean = false;
+ @Input() confirmVisible: boolean = true;
+ @Input() cancelVisible: boolean = true;
+ @Input() showHeader: boolean = true;
+ @Input() showFooter: boolean = true;
+ @Input() loading: boolean = false;
+ @Input() width: string;
+ @Input() height: string;
+ @Input() bodyMaxHeight: string;
+
+ @Output() confirm: EventEmitter = new EventEmitter();
+ @Output() cancel: EventEmitter = new EventEmitter();
+ @Output() close: EventEmitter = new EventEmitter();
+
+ @ContentChild(DialogHeaderDirective) private headerContent?: DialogHeaderDirective;
+ @ContentChild(DialogFooterDirective) private footerContent?: DialogFooterDirective;
+
+ public hasCustomHeader: boolean = false;
+ public hasCustomFooter: boolean = false;
+
+ public ngAfterContentInit(): void {
+ this.hasCustomHeader = !!this.headerContent;
+ this.hasCustomFooter = !!this.footerContent;
+ }
+}
diff --git a/frontend/src/app/modules/common/data-input-dialog/data-input-dialog.component.html b/frontend/src/app/modules/common/data-input-dialog/data-input-dialog.component.html
index 3e6ee70a3a..15e0c51de0 100644
--- a/frontend/src/app/modules/common/data-input-dialog/data-input-dialog.component.html
+++ b/frontend/src/app/modules/common/data-input-dialog/data-input-dialog.component.html
@@ -1,13 +1,11 @@
-
-
-
-
-
\ No newline at end of file
+
diff --git a/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.html b/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.html
new file mode 100644
index 0000000000..c52fb43dd0
--- /dev/null
+++ b/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.html
@@ -0,0 +1,11 @@
+
+
+
+ @for (tab of options; track tab.value) {
+
+ {{ tab.label }}
+
+ }
+
+
+
diff --git a/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.scss b/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.scss
new file mode 100644
index 0000000000..931e0b3e17
--- /dev/null
+++ b/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.scss
@@ -0,0 +1,33 @@
+:host ::ng-deep {
+ .p-tab {
+ padding: 0 !important;
+ color: var(--color-grey-5) !important;
+ }
+
+ .p-tabs,
+ .p-tablist,
+ .p-tablist-content,
+ .p-tablist-tab-list {
+ background: transparent;
+ }
+
+ .p-tabpanels {
+ background: transparent;
+ }
+
+ .p-tabs .p-tab.p-tab-active,
+ .p-tabs .p-tab[data-p-active='true'] {
+ color: var(--color-primary) !important;
+ border-color: var(--color-primary);
+ }
+}
+
+.tabview-label {
+ padding: 10px 25px 10px 25px;
+ background: var(--guardian-grey-background, #f9fafc);
+ text-align: center;
+ font-family: Inter, sans-serif;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 600;
+}
diff --git a/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.ts b/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.ts
new file mode 100644
index 0000000000..819ce2ecc2
--- /dev/null
+++ b/frontend/src/app/modules/common/guardian-tabs-switch/guardian-tabs-switch.component.ts
@@ -0,0 +1,28 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+/**
+ * Reusable tab-style switcher matching the Schemas page tabs.
+ * Renders a list of options as PrimeNG tabs and emits the selected value.
+ */
+@Component({
+ selector: 'guardian-tabs-switch',
+ templateUrl: './guardian-tabs-switch.component.html',
+ styleUrls: ['./guardian-tabs-switch.component.scss'],
+ standalone: false
+})
+export class GuardianTabsSwitch {
+ @Input('options') options: { label: string; value: any }[] = [];
+ @Input('value') value: any;
+
+ @Output('valueChange') valueChange = new EventEmitter();
+ @Output('onChange') change = new EventEmitter();
+
+ onSelect(value: any): void {
+ if (value === this.value) {
+ return;
+ }
+ this.value = value;
+ this.valueChange.emit(value);
+ this.change.emit(value);
+ }
+}
diff --git a/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.css b/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.css
index 479894b18a..97e1f78211 100644
--- a/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.css
+++ b/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.css
@@ -1,54 +1,6 @@
.actions__tabs {
position: relative;
- margin-bottom: 40px;
-
- &::after {
- content: "";
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- height: 2px;
- background: var(--color-grey-3);
- z-index: 0;
- }
-
- ::ng-deep .p-selectbutton .p-button {
- background: transparent !important;
- border: none !important;
- color: #848FA9 !important;
- position: relative;
- padding: 0.75rem 2rem 0.5rem 0 !important;
- margin: 0 0.5rem 0 0 !important;
- border-radius: 0 !important;
- }
-
- ::ng-deep .p-selectbutton .p-button.p-highlight {
- color: var(--color-primary, #0b73f8) !important;
- pointer-events: none !important;
- cursor: default !important;
- }
-
- ::ng-deep .p-selectbutton .p-button.p-highlight::after {
- display: none !important;
- }
-
- ::ng-deep .p-selectbutton .p-button .p-button-label {
- position: relative;
- padding: 0 !important;
- margin: 0 !important;
- }
-
- ::ng-deep .p-selectbutton .p-button.p-highlight .p-button-label::after {
- content: "";
- position: absolute;
- bottom: -8px;
- left: 0;
- width: 100%;
- height: 3px;
- background: var(--color-primary, #0b73f8);
- z-index: 2;
- }
+ margin-bottom: 28px;
}
.button-group {
diff --git a/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.html b/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.html
index c0fe946f33..0ffc558631 100644
--- a/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.html
+++ b/frontend/src/app/modules/contract-engine/configs/contract-config/contract-config.component.html
@@ -15,14 +15,12 @@