Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cff84c448 |
@@ -1,67 +1,61 @@
|
|||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
||||||
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
<li><a href="javascript://"><clr-icon shape="dashboard"></clr-icon> {{ 'Dashboard_builder' | translate }}</a></li>
|
<li><a href="javascript://"><clr-icon shape="dashboard"></clr-icon> {{ 'Dashboard_builder' | translate }}</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="dg-wrapper">
|
<div class="dg-wrapper">
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="clr-col-8">x
|
<div class="clr-col-8">
|
||||||
<h3>{{ 'Dashboard_builder' | translate }}</h3>
|
<h3>{{ 'Dashboard_builder' | translate }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-4" style="text-align: right;">
|
<div class="clr-col-4" style="text-align: right;">
|
||||||
<button class="btn btn-success" [routerLink]="['/cns-portal/shield-dashboard']">
|
<button class="btn btn-success" [routerLink]="['/cns-portal/shield-dashboard']">
|
||||||
<clr-icon shape="shield"></clr-icon>Shield Dashboard
|
<clr-icon shape="shield"></clr-icon>Shield Dashboard
|
||||||
</button>
|
</button>
|
||||||
<button id="add" class="btn btn-primary" (click)="gotorunner()">
|
<button id="add" class="btn btn-primary" (click)="gotorunner()">
|
||||||
<clr-icon shape="grid-view"></clr-icon>{{ 'Dashboard_runner' | translate }}
|
<clr-icon shape="grid-view"></clr-icon>{{ 'Dashboard_runner' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<!-- Add Chart Config button -->
|
<button class="btn btn-outline" (click)="onExport()">
|
||||||
<button class="btn btn-primary" (click)="openChartConfig()">
|
<clr-icon shape="export"></clr-icon> {{ 'EXPORT_XLSX' | translate }}
|
||||||
<clr-icon shape="cog"></clr-icon> Chart Config
|
</button>
|
||||||
</button>
|
<button id="add" class="btn btn-primary" (click)="gotoadd()">
|
||||||
<button class="btn btn-outline" (click)="onExport()">
|
<clr-icon shape="plus"></clr-icon> {{ 'ADD' | translate }}
|
||||||
<clr-icon shape="export"></clr-icon> {{ 'EXPORT_XLSX' | translate }}
|
</button>
|
||||||
</button>
|
</div>
|
||||||
<button id="add" class="btn btn-primary" (click)="gotoadd()">
|
</div>
|
||||||
<clr-icon shape="plus"></clr-icon> {{ 'ADD' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>{{ 'Loading' | translate }} ......
|
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>{{ 'Loading' | translate }} ...... </clr-spinner></ng-template>
|
||||||
</clr-spinner></ng-template>
|
<div *ngIf="error;else loadingSpinner">{{error}}</div></clr-dg-placeholder>
|
||||||
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
|
||||||
</clr-dg-placeholder>
|
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="''">
|
<clr-dg-column [clrDgField]="''">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Go_to' | translate }}
|
{{ 'Go_to' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'dashboard_name'">
|
<clr-dg-column [clrDgField]="'dashboard_name'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Dashboard_Name' | translate }}
|
{{ 'Dashboard_Name' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'description'">
|
<clr-dg-column [clrDgField]="'description'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Description' | translate }}
|
{{ 'Description' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column >
|
||||||
<clr-dg-column [clrDgField]="'secuirity_profile'">
|
<clr-dg-column [clrDgField]="'secuirity_profile'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Security_Profile' | translate }}
|
{{ 'Security_Profile' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column >
|
||||||
<clr-dg-column [clrDgField]="'add_to_home'">
|
<clr-dg-column [clrDgField]="'add_to_home'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Add_to_home' | translate }}
|
{{ 'Add_to_home' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column >
|
||||||
<!-- <clr-dg-column [clrDgField]="'formType'">
|
<!-- <clr-dg-column [clrDgField]="'formType'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
Build Status
|
Build Status
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -71,86 +65,81 @@
|
|||||||
Testing
|
Testing
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column > -->
|
</clr-dg-column > -->
|
||||||
<clr-dg-column [clrDgField]="'action'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-column [clrDgField]="'action'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Action' | translate }}
|
{{ 'Action' | translate }}
|
||||||
</ng-container></clr-dg-column>
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of data?.slice()?.reverse();" [clrDgItem]="user">
|
<clr-dg-row *clrDgItems="let user of data?.slice()?.reverse();" [clrDgItem]="user">
|
||||||
<clr-dg-cell><span class="label label-light-blue" style="display: inline;margin-left: 10px; cursor: pointer;"
|
<clr-dg-cell><span class="label label-light-blue" style="display: inline;margin-left: 10px; cursor: pointer;" (click)="goToEdit(user.id)"> {{ 'SET_UP' | translate }}</span></clr-dg-cell>
|
||||||
(click)="goToEdit(user.id)"> {{ 'SET_UP' | translate }}</span></clr-dg-cell>
|
<clr-dg-cell>{{user.dashboard_name}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.dashboard_name}}</clr-dg-cell>
|
<clr-dg-cell>{{user.description}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.description}}</clr-dg-cell>
|
<clr-dg-cell>{{user.secuirity_profile}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.secuirity_profile}}</clr-dg-cell>
|
<clr-dg-cell>{{user.add_to_home}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.add_to_home}}</clr-dg-cell>
|
|
||||||
|
|
||||||
<!-- <clr-dg-cell><input type="radio" id="cb1" class="dots" [ngStyle]="{'background-color': user.build == true ? 'green' : 'red'}"></clr-dg-cell>
|
<!-- <clr-dg-cell><input type="radio" id="cb1" class="dots" [ngStyle]="{'background-color': user.build == true ? 'green' : 'red'}"></clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.testing}}</clr-dg-cell> -->
|
<clr-dg-cell>{{user.testing}}</clr-dg-cell> -->
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<!-- <span style="cursor: pointer; padding: 10px; "><clr-icon shape="form" (click)="goToEdit(user.id)" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span> -->
|
<!-- <span style="cursor: pointer; padding: 10px; "><clr-icon shape="form" (click)="goToEdit(user.id)" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span> -->
|
||||||
<a href="javascript:void(0)" style="padding-right: 10px;" role="tooltip" aria-haspopup="true"
|
<a href="javascript:void(0)" style="padding-right: 10px;" role="tooltip" aria-haspopup="true" class="tooltip tooltip-sm tooltip-top-left">
|
||||||
class="tooltip tooltip-sm tooltip-top-left">
|
<span style="cursor: pointer;"><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error" style="color: red;"></clr-icon></span>
|
||||||
<span style="cursor: pointer;"><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error"
|
<span class="tooltip-content">{{ 'Delete' | translate }}</span>
|
||||||
style="color: red;"></clr-icon></span>
|
</a>
|
||||||
<span class="tooltip-content">{{ 'Delete' | translate }}</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<clr-signpost>
|
<clr-signpost>
|
||||||
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success"
|
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span>
|
||||||
style="color: rgb(0, 130, 236);"></clr-icon></span>
|
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
||||||
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
<h5 style="margin-top: 0">{{ 'Who_Column' | translate }}</h5>
|
||||||
<h5 style="margin-top: 0">{{ 'Who_Column' | translate }}</h5>
|
<div>{{ 'Account_ID' | translate }}: <code class="clr-code">{{ user.accountId }}</code></div>
|
||||||
<div>{{ 'Account_ID' | translate }}: <code class="clr-code">{{ user.accountId }}</code></div>
|
<div>{{ 'Created_At' | translate }}: <code class="clr-code">{{ user.createdAt | date }}</code></div>
|
||||||
<div>{{ 'Created_At' | translate }}: <code class="clr-code">{{ user.createdAt | date }}</code></div>
|
<div>{{ 'Created_By' | translate }}: <code class="clr-code">{{ user.createdBy }}</code></div>
|
||||||
<div>{{ 'Created_By' | translate }}: <code class="clr-code">{{ user.createdBy }}</code></div>
|
<div>{{ 'Updated_At' | translate }}: <code class="clr-code">{{ user.updatedAt | date }}</code></div>
|
||||||
<div>{{ 'Updated_At' | translate }}: <code class="clr-code">{{ user.updatedAt | date }}</code></div>
|
<div>{{ 'Updated_By' | translate }}: <code class="clr-code">{{ user.updatedBy }}</code></div>
|
||||||
<div>{{ 'Updated_By' | translate }}: <code class="clr-code">{{ user.updatedBy }}</code></div>
|
</clr-signpost-content>
|
||||||
</clr-signpost-content>
|
</clr-signpost>
|
||||||
</clr-signpost>
|
|
||||||
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-action-overflow>
|
<clr-dg-action-overflow>
|
||||||
<button class="action-item" (click)="goToEditData(user.id)">{{ 'Edit' | translate }} <clr-icon shape="edit"
|
<button class="action-item" (click)="goToEditData(user.id)">{{ 'Edit' | translate }} <clr-icon shape="edit" class="is-error"></clr-icon></button>
|
||||||
class="is-error"></clr-icon></button>
|
<!-- <button class="action-item" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button> -->
|
||||||
<!-- <button class="action-item" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button> -->
|
</clr-dg-action-overflow>
|
||||||
</clr-dg-action-overflow>
|
</clr-dg-row>
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{ 'Users_per_page' | translate }}</clr-dg-page-size>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{ 'Users_per_page' | translate }}</clr-dg-page-size>
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
{{ 'of' | translate }} {{pagination.totalItems}} {{ 'users' | translate }}
|
{{ 'of' | translate }} {{pagination.totalItems}} {{ 'users' | translate }}
|
||||||
</clr-dg-pagination>
|
</clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
</div>
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="addModall" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="s-order-dash-pg">
|
|
||||||
<div class="chart-box" id="word1" (click)="gotoadd()"><br>
|
|
||||||
<img style="margin: auto; display: block;" src="/assets/images/fromscratch.png" height="90" width="90">
|
|
||||||
<h5 class="center"> <b>{{ 'Start_from_scratch' | translate }}</b> </h5>
|
|
||||||
</div>
|
|
||||||
<div class="chart-box" id="word1"><br>
|
|
||||||
<img style="margin: auto; display: block;" src="/assets/images/copytemplate.png" height="90" width="90">
|
|
||||||
<h5 class="center"> <b>{{ 'Import_from_template' | translate }}</b> </h5>
|
|
||||||
</div>
|
|
||||||
<div class="chart-box" id="word1"><br>
|
|
||||||
<img style="margin: auto; display: block;" src="/assets/images/database.png" height="90" width="90">
|
|
||||||
<h5 class="center"> <b>{{ 'Import_from_public_project' | translate }}</b> </h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalDelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
<clr-modal [(clrModalOpen)]="addModall" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
<div class="modal-body" *ngIf="rowSelected.id">
|
<div class="modal-body">
|
||||||
<h1 class="delete">{{ 'Are_you_sure_want_to_delete' | translate }}</h1>
|
<div class="s-order-dash-pg">
|
||||||
<h2 class="heading">{{rowSelected.id}}</h2>
|
<div class="chart-box" id="word1" (click)="gotoadd()"><br>
|
||||||
<div class="modal-footer">
|
<img style="margin: auto; display: block;" src="/assets/images/fromscratch.png" height="90" width="90">
|
||||||
<button type="button" class="btn btn-outline" (click)="modalDelete = false">{{ 'Cancel' | translate }}</button>
|
<h5 class="center"> <b>{{ 'Start_from_scratch' | translate }}</b> </h5>
|
||||||
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary">{{ 'Delete' | translate }}</button>
|
</div>
|
||||||
</div>
|
<div class="chart-box" id="word1" ><br>
|
||||||
</div>
|
<img style="margin: auto; display: block;" src="/assets/images/copytemplate.png" height="90" width="90">
|
||||||
</clr-modal>
|
<h5 class="center"> <b>{{ 'Import_from_template' | translate }}</b> </h5>
|
||||||
|
</div>
|
||||||
|
<div class="chart-box" id="word1"><br>
|
||||||
|
<img style="margin: auto; display: block;" src="/assets/images/database.png" height="90" width="90">
|
||||||
|
<h5 class="center"> <b>{{ 'Import_from_public_project' | translate }}</b> </h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
|
<clr-modal [(clrModalOpen)]="modalDelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
<div class="modal-body" *ngIf="rowSelected.id">
|
||||||
|
<h1 class="delete">{{ 'Are_you_sure_want_to_delete' | translate }}</h1>
|
||||||
|
<h2 class="heading">{{rowSelected.id}}</h2>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline" (click)="modalDelete = false">{{ 'Cancel' | translate }}</button>
|
||||||
|
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary" >{{ 'Delete' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
@@ -24,7 +24,6 @@ export class AllnewdashComponent implements OnInit {
|
|||||||
projectname;
|
projectname;
|
||||||
projectId;
|
projectId;
|
||||||
error;
|
error;
|
||||||
chartConfigManagerOpen = false;
|
|
||||||
constructor(
|
constructor(
|
||||||
private router : Router,
|
private router : Router,
|
||||||
private route: ActivatedRoute,private dashboardService : DashboardService,
|
private route: ActivatedRoute,private dashboardService : DashboardService,
|
||||||
@@ -122,9 +121,4 @@ export class AllnewdashComponent implements OnInit {
|
|||||||
// this.router.navigate(['../editdashn'],{relativeTo:this.route});
|
// this.router.navigate(['../editdashn'],{relativeTo:this.route});
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Add method to open chart configuration manager
|
|
||||||
openChartConfig(): void {
|
|
||||||
this.router.navigate(['../chart-types'],{relativeTo:this.route});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,630 +0,0 @@
|
|||||||
<div class="chart-config-manager">
|
|
||||||
<h2>Chart Configuration Manager</h2>
|
|
||||||
|
|
||||||
<!-- Chart Types Section -->
|
|
||||||
<clr-tabs>
|
|
||||||
<clr-tab>
|
|
||||||
<button clrTabLink>Chart Types</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Chart Types</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTypeForm = true">
|
|
||||||
Add Chart Type
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTypeForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartType.name" name="chartTypeName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartType.description" name="chartTypeDescription"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newChartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createChartType()" [disabled]="!newChartType.name">Save</button>
|
|
||||||
<button class="btn" (click)="showAddChartTypeForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Description</clr-dg-column>
|
|
||||||
<clr-dg-column>Status</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartType of chartTypes" [clrDgItem]="chartType">
|
|
||||||
<clr-dg-cell>{{chartType.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.displayName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.description}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{chartType.isActive ? 'Active' : 'Inactive'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTypeForEdit(chartType)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartType(chartType.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="onChartTypeSelect(chartType)">
|
|
||||||
<cds-icon shape="eye"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTypes.length}} chart types
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartType">
|
|
||||||
<h4>Edit Chart Type</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartType.name" name="editChartTypeName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartType.displayName" name="editChartTypeDisplayName" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartType.description" name="editChartTypeDescription"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedChartType.isActive" name="editChartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateChartType()" [disabled]="!selectedChartType.name">Update</button>
|
|
||||||
<button class="btn" (click)="selectedChartType = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- UI Components Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>UI Components</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>UI Components for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddUiComponentForm = true">
|
|
||||||
Add UI Component
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddUiComponentForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentName" name="componentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentType" name="componentType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.displayLabel" name="displayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.placeholder" name="placeholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newUiComponent.sortOrder" name="sortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newUiComponent.isRequired" name="isRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createUiComponent()" [disabled]="!newUiComponent.componentName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddUiComponentForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- UI Components Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Component Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Component Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Placeholder</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let uiComponent of uiComponents" [clrDgItem]="uiComponent">
|
|
||||||
<clr-dg-cell>{{uiComponent.componentName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.componentType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.displayLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.placeholder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="uiComponent.isRequired" [class.label-danger]="!uiComponent.isRequired">
|
|
||||||
{{uiComponent.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectUiComponentForEdit(uiComponent)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteUiComponent(uiComponent.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{uiComponents.length}} UI components
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedUiComponent">
|
|
||||||
<h4>Edit UI Component</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentName" name="editComponentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentType" name="editComponentType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.displayLabel" name="editDisplayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.placeholder" name="editPlaceholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedUiComponent.sortOrder" name="editSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedUiComponent.isRequired" name="editIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateUiComponent()" [disabled]="!selectedUiComponent.componentName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedUiComponent = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Component Properties Section -->
|
|
||||||
<clr-tab *ngIf="selectedUiComponent">
|
|
||||||
<button clrTabLink>Component Properties</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Properties for {{selectedUiComponent?.componentName}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddComponentPropertyForm = true">
|
|
||||||
Add Property
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddComponentPropertyForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyName" name="propertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyValue" name="propertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyType" name="propertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createComponentProperty()" [disabled]="!newComponentProperty.propertyName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddComponentPropertyForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Property Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Value</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let property of componentProperties" [clrDgItem]="property">
|
|
||||||
<clr-dg-cell>{{property.propertyName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyValue}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectComponentPropertyForEdit(property)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteComponentProperty(property.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{componentProperties.length}} properties
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedComponentProperty">
|
|
||||||
<h4>Edit Component Property</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyName" name="editPropertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyValue" name="editPropertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyType" name="editPropertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateComponentProperty()" [disabled]="!selectedComponentProperty.propertyName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedComponentProperty = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Chart Templates Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Chart Templates</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Templates for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTemplateForm = true">
|
|
||||||
Add Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTemplateForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartTemplate.templateName" name="templateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateHtml" name="templateHtml" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateCss" name="templateCss" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newChartTemplate.isDefault" name="isDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createChartTemplate()" [disabled]="!newChartTemplate.templateName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddChartTemplateForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Templates Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Template Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Is Default</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let template of chartTemplates" [clrDgItem]="template">
|
|
||||||
<clr-dg-cell>{{template.templateName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="template.isDefault" [class.label-danger]="!template.isDefault">
|
|
||||||
{{template.isDefault ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTemplateForEdit(template)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartTemplate(template.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTemplates.length}} templates
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartTemplate">
|
|
||||||
<h4>Edit Chart Template</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartTemplate.templateName" name="editTemplateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateHtml" name="editTemplateHtml" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateCss" name="editTemplateCss" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedChartTemplate.isDefault" name="editIsDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateChartTemplate()" [disabled]="!selectedChartTemplate.templateName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedChartTemplate = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Dynamic Fields</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Dynamic Fields for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddDynamicFieldForm = true">
|
|
||||||
Add Field
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddDynamicFieldForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldName" name="fieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldType" name="fieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newDynamicField.fieldOptions" name="fieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newDynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createDynamicField()" [disabled]="!newDynamicField.fieldName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddDynamicFieldForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Field Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Show in UI</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let field of dynamicFields" [clrDgItem]="field">
|
|
||||||
<clr-dg-cell>{{field.fieldName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.isRequired" [class.label-danger]="!field.isRequired">
|
|
||||||
{{field.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.showInUi" [class.label-danger]="!field.showInUi">
|
|
||||||
{{field.showInUi ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectDynamicFieldForEdit(field)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteDynamicField(field.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{dynamicFields.length}} fields
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedDynamicField">
|
|
||||||
<h4>Edit Dynamic Field</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldName" name="editFieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldLabel" name="editFieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldType" name="editFieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedDynamicField.fieldOptions" name="editFieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedDynamicField.sortOrder" name="editFieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.isRequired" name="editFieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.showInUi" name="editFieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateDynamicField()" [disabled]="!selectedDynamicField.fieldName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedDynamicField = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
</clr-tabs>
|
|
||||||
</div>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
.chart-config-manager {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
min-height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-success {
|
|
||||||
background-color: #318700;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-danger {
|
|
||||||
background-color: #e62200;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,658 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { environment } from 'src/environments/environment';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
|
|
||||||
interface ChartType {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
isActive: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UiComponent {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
componentName: string;
|
|
||||||
componentType: string;
|
|
||||||
displayLabel: string;
|
|
||||||
placeholder: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ComponentProperty {
|
|
||||||
id: number;
|
|
||||||
component: UiComponent;
|
|
||||||
propertyName: string;
|
|
||||||
propertyValue: string;
|
|
||||||
propertyType: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChartTemplate {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
templateName: string;
|
|
||||||
templateHtml: string;
|
|
||||||
templateCss: string;
|
|
||||||
isDefault: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynamicField {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
fieldName: string;
|
|
||||||
fieldLabel: string;
|
|
||||||
fieldType: string;
|
|
||||||
fieldOptions: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
showInUi: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-config-manager',
|
|
||||||
templateUrl: './chart-config-manager.component.html',
|
|
||||||
styleUrls: ['./chart-config-manager.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartConfigManagerComponent implements OnInit {
|
|
||||||
// Chart Types
|
|
||||||
chartTypes: ChartType[] = [];
|
|
||||||
selectedChartType: ChartType | null = null;
|
|
||||||
newChartType: Partial<ChartType> = {};
|
|
||||||
showAddChartTypeForm = false;
|
|
||||||
chartTypeLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
uiComponents: UiComponent[] = [];
|
|
||||||
selectedUiComponent: UiComponent | null = null;
|
|
||||||
newUiComponent: Partial<UiComponent> = {};
|
|
||||||
showAddUiComponentForm = false;
|
|
||||||
uiComponentLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Component Properties
|
|
||||||
componentProperties: ComponentProperty[] = [];
|
|
||||||
selectedComponentProperty: ComponentProperty | null = null;
|
|
||||||
newComponentProperty: Partial<ComponentProperty> = {};
|
|
||||||
showAddComponentPropertyForm = false;
|
|
||||||
componentPropertyLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Chart Templates
|
|
||||||
chartTemplates: ChartTemplate[] = [];
|
|
||||||
selectedChartTemplate: ChartTemplate | null = null;
|
|
||||||
newChartTemplate: Partial<ChartTemplate> = {};
|
|
||||||
showAddChartTemplateForm = false;
|
|
||||||
chartTemplateLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Dynamic Fields
|
|
||||||
dynamicFields: DynamicField[] = [];
|
|
||||||
selectedDynamicField: DynamicField | null = null;
|
|
||||||
newDynamicField: Partial<DynamicField> = {};
|
|
||||||
showAddDynamicFieldForm = false;
|
|
||||||
dynamicFieldLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// API base URL
|
|
||||||
private apiUrl = environment.apiUrl || 'http://localhost:8080/api';
|
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.loadChartTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Type Methods
|
|
||||||
loadChartTypes(): void {
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<ChartType[]>(`${this.apiUrl}/chart-types`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTypes = data;
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
this.showError('Error loading chart types: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartType(): void {
|
|
||||||
if (!this.newChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.post<ChartType>(`${this.apiUrl}/chart-types`, this.newChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTypes.push(data);
|
|
||||||
this.newChartType = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showSuccess('Chart type created successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart type:', error);
|
|
||||||
this.showError('Error creating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartType(): void {
|
|
||||||
if (!this.selectedChartType || !this.selectedChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<ChartType>(`${this.apiUrl}/chart-types/${this.selectedChartType.id}`, this.selectedChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTypes.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTypes[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.showSuccess('Chart type updated successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart type:', error);
|
|
||||||
this.showError('Error updating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartType(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart type?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/chart-types/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTypes = this.chartTypes.filter(ct => ct.id !== id);
|
|
||||||
// Also clear related data if the deleted chart type was selected
|
|
||||||
if (this.selectedChartType && this.selectedChartType.id === id) {
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.uiComponents = [];
|
|
||||||
this.chartTemplates = [];
|
|
||||||
this.dynamicFields = [];
|
|
||||||
}
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTypeForEdit(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = { ...chartType };
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI Component Methods
|
|
||||||
loadUiComponents(chartTypeId: number): void {
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<UiComponent[]>(`${this.apiUrl}/ui-components/chart-type/${chartTypeId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents = data;
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading UI components:', error);
|
|
||||||
this.showError('Error loading UI components: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createUiComponent(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const uiComponentData = {
|
|
||||||
...this.newUiComponent,
|
|
||||||
chartType: { id: this.selectedChartType.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<UiComponent>(`${this.apiUrl}/ui-components`, uiComponentData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents.push(data);
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showSuccess('UI component created successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating UI component:', error);
|
|
||||||
this.showError('Error creating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUiComponent(): void {
|
|
||||||
if (!this.selectedUiComponent || !this.selectedUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<UiComponent>(`${this.apiUrl}/ui-components/${this.selectedUiComponent.id}`, this.selectedUiComponent).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.uiComponents.findIndex(uc => uc.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.uiComponents[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.showSuccess('UI component updated successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating UI component:', error);
|
|
||||||
this.showError('Error updating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUiComponent(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this UI component?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/ui-components/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.uiComponents = this.uiComponents.filter(uc => uc.id !== id);
|
|
||||||
if (this.selectedUiComponent && this.selectedUiComponent.id === id) {
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('UI component deleted successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting UI component:', error);
|
|
||||||
this.showError('Error deleting UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectUiComponentForEdit(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = { ...uiComponent };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Property Methods
|
|
||||||
loadComponentProperties(componentId: number): void {
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<ComponentProperty[]>(`${this.apiUrl}/component-properties/component/${componentId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties = data;
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading component properties:', error);
|
|
||||||
this.showError('Error loading component properties: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createComponentProperty(): void {
|
|
||||||
if (!this.selectedUiComponent) {
|
|
||||||
this.showError('Please select a UI component first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const componentPropertyData = {
|
|
||||||
...this.newComponentProperty,
|
|
||||||
component: { id: this.selectedUiComponent.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<ComponentProperty>(`${this.apiUrl}/component-properties`, componentPropertyData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties.push(data);
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showSuccess('Component property created successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating component property:', error);
|
|
||||||
this.showError('Error creating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateComponentProperty(): void {
|
|
||||||
if (!this.selectedComponentProperty || !this.selectedComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<ComponentProperty>(`${this.apiUrl}/component-properties/${this.selectedComponentProperty.id}`, this.selectedComponentProperty).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.componentProperties.findIndex(cp => cp.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.componentProperties[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.showSuccess('Component property updated successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating component property:', error);
|
|
||||||
this.showError('Error updating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComponentProperty(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this component property?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/component-properties/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.componentProperties = this.componentProperties.filter(cp => cp.id !== id);
|
|
||||||
if (this.selectedComponentProperty && this.selectedComponentProperty.id === id) {
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('Component property deleted successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting component property:', error);
|
|
||||||
this.showError('Error deleting component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectComponentPropertyForEdit(componentProperty: ComponentProperty): void {
|
|
||||||
this.selectedComponentProperty = { ...componentProperty };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Template Methods
|
|
||||||
loadChartTemplates(chartTypeId: number): void {
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<ChartTemplate[]>(`${this.apiUrl}/chart-templates/chart-type/${chartTypeId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates = data;
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart templates:', error);
|
|
||||||
this.showError('Error loading chart templates: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartTemplate(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const chartTemplateData = {
|
|
||||||
...this.newChartTemplate,
|
|
||||||
chartType: { id: this.selectedChartType.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<ChartTemplate>(`${this.apiUrl}/chart-templates`, chartTemplateData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates.push(data);
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showSuccess('Chart template created successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart template:', error);
|
|
||||||
this.showError('Error creating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartTemplate(): void {
|
|
||||||
if (!this.selectedChartTemplate || !this.selectedChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<ChartTemplate>(`${this.apiUrl}/chart-templates/${this.selectedChartTemplate.id}`, this.selectedChartTemplate).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTemplates.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTemplates[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.showSuccess('Chart template updated successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart template:', error);
|
|
||||||
this.showError('Error updating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartTemplate(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart template?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/chart-templates/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTemplates = this.chartTemplates.filter(ct => ct.id !== id);
|
|
||||||
if (this.selectedChartTemplate && this.selectedChartTemplate.id === id) {
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('Chart template deleted successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart template:', error);
|
|
||||||
this.showError('Error deleting chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTemplateForEdit(chartTemplate: ChartTemplate): void {
|
|
||||||
this.selectedChartTemplate = { ...chartTemplate };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamic Field Methods
|
|
||||||
loadDynamicFields(chartTypeId: number): void {
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<DynamicField[]>(`${this.apiUrl}/dynamic-fields/chart-type/${chartTypeId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields = data;
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading dynamic fields:', error);
|
|
||||||
this.showError('Error loading dynamic fields: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createDynamicField(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const dynamicFieldData = {
|
|
||||||
...this.newDynamicField,
|
|
||||||
chartType: { id: this.selectedChartType.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<DynamicField>(`${this.apiUrl}/dynamic-fields`, dynamicFieldData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields.push(data);
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.showSuccess('Dynamic field created successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating dynamic field:', error);
|
|
||||||
this.showError('Error creating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDynamicField(): void {
|
|
||||||
if (!this.selectedDynamicField || !this.selectedDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<DynamicField>(`${this.apiUrl}/dynamic-fields/${this.selectedDynamicField.id}`, this.selectedDynamicField).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.dynamicFields.findIndex(df => df.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.dynamicFields[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
this.showSuccess('Dynamic field updated successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating dynamic field:', error);
|
|
||||||
this.showError('Error updating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteDynamicField(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this dynamic field?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/dynamic-fields/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.dynamicFields = this.dynamicFields.filter(df => df.id !== id);
|
|
||||||
if (this.selectedDynamicField && this.selectedDynamicField.id === id) {
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('Dynamic field deleted successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting dynamic field:', error);
|
|
||||||
this.showError('Error deleting dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectDynamicFieldForEdit(dynamicField: DynamicField): void {
|
|
||||||
this.selectedDynamicField = { ...dynamicField };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
onChartTypeSelect(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = chartType;
|
|
||||||
this.loadUiComponents(chartType.id);
|
|
||||||
this.loadChartTemplates(chartType.id);
|
|
||||||
this.loadDynamicFields(chartType.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetForms(): void {
|
|
||||||
this.newChartType = {};
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
<div class="chart-config-manager">
|
|
||||||
<h2>Chart Configuration Manager</h2>
|
|
||||||
|
|
||||||
<!-- Test message to verify component is loading -->
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">Chart Config Manager Component Loaded Successfully</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Section -->
|
|
||||||
<clr-tab>
|
|
||||||
<button clrTabLink>Chart Types</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Chart Types</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTypeForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Chart Type
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTypeForm">
|
|
||||||
<app-chart-type-form
|
|
||||||
[chartType]="newChartType"
|
|
||||||
(save)="createChartType()"
|
|
||||||
(cancel)="showAddChartTypeForm = false">
|
|
||||||
</app-chart-type-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Description</clr-dg-column>
|
|
||||||
<clr-dg-column>Status</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartType of chartTypes" [clrDgItem]="chartType">
|
|
||||||
<clr-dg-cell>{{chartType.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.displayName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.description}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{chartType.isActive ? 'Active' : 'Inactive'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTypeForEdit(chartType)" title="Edit" [disabled]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTypeLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTypeLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartType(chartType.id)" title="Delete" [disabled]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTypeLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTypeLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="onChartTypeSelect(chartType)" title="View Details" [disabled]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTypeLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTypeLoadingState !== ClrLoadingState.LOADING" shape="eye"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTypes.length}} chart type(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartType">
|
|
||||||
<h4>Edit Chart Type</h4>
|
|
||||||
<app-chart-type-form
|
|
||||||
[chartType]="selectedChartType"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateChartType()"
|
|
||||||
(cancel)="selectedChartType = null">
|
|
||||||
</app-chart-type-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- UI Components Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>UI Components</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>UI Components for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddUiComponentForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add UI Component
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddUiComponentForm">
|
|
||||||
<app-ui-component-form
|
|
||||||
[uiComponent]="newUiComponent"
|
|
||||||
(save)="createUiComponent()"
|
|
||||||
(cancel)="showAddUiComponentForm = false">
|
|
||||||
</app-ui-component-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- UI Components Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Component Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Component Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let uiComponent of uiComponents" [clrDgItem]="uiComponent">
|
|
||||||
<clr-dg-cell>{{uiComponent.componentName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.componentType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.displayLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="uiComponent.isRequired" [class.label-danger]="!uiComponent.isRequired">
|
|
||||||
{{uiComponent.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell class="action-cell">
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectUiComponentForEdit(uiComponent)" title="Edit" [disabled]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="uiComponentLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="uiComponentLoadingState !== ClrLoadingState.LOADING" shape="pencil" aria-label="Edit"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteUiComponent(uiComponent.id)" title="Delete" [disabled]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="uiComponentLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="uiComponentLoadingState !== ClrLoadingState.LOADING" shape="trash" aria-label="Delete"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="onUiComponentSelect(uiComponent)" title="View Properties" [disabled]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="uiComponentLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="uiComponentLoadingState !== ClrLoadingState.LOADING" shape="eye" aria-label="View Properties"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{uiComponents.length}} UI component(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedUiComponent">
|
|
||||||
<h4>Edit UI Component</h4>
|
|
||||||
<app-ui-component-form
|
|
||||||
[uiComponent]="selectedUiComponent"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateUiComponent()"
|
|
||||||
(cancel)="selectedUiComponent = null">
|
|
||||||
</app-ui-component-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Component Properties Section -->
|
|
||||||
<clr-tab *ngIf="selectedUiComponent">
|
|
||||||
<button clrTabLink>Component Properties</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Properties for {{selectedUiComponent?.componentName}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddComponentPropertyForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Property
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddComponentPropertyForm">
|
|
||||||
<app-component-property-form
|
|
||||||
[componentProperty]="newComponentProperty"
|
|
||||||
(save)="createComponentProperty()"
|
|
||||||
(cancel)="showAddComponentPropertyForm = false">
|
|
||||||
</app-component-property-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Property Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Value</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let property of componentProperties" [clrDgItem]="property">
|
|
||||||
<clr-dg-cell>{{property.propertyName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyValue}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectComponentPropertyForEdit(property)" title="Edit" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteComponentProperty(property.id)" title="Delete" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{componentProperties.length}} propertie(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedComponentProperty">
|
|
||||||
<h4>Edit Component Property</h4>
|
|
||||||
<app-component-property-form
|
|
||||||
[componentProperty]="selectedComponentProperty"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateComponentProperty()"
|
|
||||||
(cancel)="selectedComponentProperty = null">
|
|
||||||
</app-component-property-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Chart Templates Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Chart Templates</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Templates for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTemplateForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTemplateForm">
|
|
||||||
<app-chart-template-form
|
|
||||||
[chartTemplate]="newChartTemplate"
|
|
||||||
(save)="createChartTemplate()"
|
|
||||||
(cancel)="showAddChartTemplateForm = false">
|
|
||||||
</app-chart-template-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Templates Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="chartTemplateLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Template Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Is Default</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let template of chartTemplates" [clrDgItem]="template">
|
|
||||||
<clr-dg-cell>{{template.templateName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="template.isDefault" [class.label-danger]="!template.isDefault">
|
|
||||||
{{template.isDefault ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{template.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{template.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTemplateForEdit(template)" title="Edit" [disabled]="chartTemplateLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTemplateLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTemplateLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartTemplate(template.id)" title="Delete" [disabled]="chartTemplateLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTemplateLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTemplateLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTemplates.length}} template(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartTemplate">
|
|
||||||
<h4>Edit Chart Template</h4>
|
|
||||||
<app-chart-template-form
|
|
||||||
[chartTemplate]="selectedChartTemplate"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateChartTemplate()"
|
|
||||||
(cancel)="selectedChartTemplate = null">
|
|
||||||
</app-chart-template-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Dynamic Fields</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Dynamic Fields for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddDynamicFieldForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Field
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddDynamicFieldForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldName" name="fieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldType" name="fieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newDynamicField.fieldOptions" name="fieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newDynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createDynamicField()" [disabled]="!newDynamicField.fieldName || dynamicFieldLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="dynamicFieldLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
<button class="btn" (click)="showAddDynamicFieldForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="dynamicFieldLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Field Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Show in UI</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let field of dynamicFields" [clrDgItem]="field">
|
|
||||||
<clr-dg-cell>{{field.fieldName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.isRequired" [class.label-danger]="!field.isRequired">
|
|
||||||
{{field.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.showInUi" [class.label-danger]="!field.showInUi">
|
|
||||||
{{field.showInUi ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectDynamicFieldForEdit(field)" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteDynamicField(field.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{dynamicFields.length}} field(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedDynamicField">
|
|
||||||
<h4>Edit Dynamic Field</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldName" name="editFieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldLabel" name="editFieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldType" name="editFieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedDynamicField.fieldOptions" name="editFieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedDynamicField.sortOrder" name="editFieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.isRequired" name="editFieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.showInUi" name="editFieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateDynamicField()" [disabled]="!selectedDynamicField.fieldName || dynamicFieldLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="dynamicFieldLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
<button class="btn" (click)="selectedDynamicField = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
</div>
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
.chart-config-manager {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
min-height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-success {
|
|
||||||
background-color: #318700;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-danger {
|
|
||||||
background-color: #e62200;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-tab-content {
|
|
||||||
padding: 15px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-cell {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons .btn-icon {
|
|
||||||
min-width: 36px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure icons are visible
|
|
||||||
cds-icon {
|
|
||||||
display: inline-block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-spinner {
|
|
||||||
margin: 0;
|
|
||||||
}cds-icon {
|
|
||||||
display: inline-block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-spinner {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
@@ -1,710 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartTypeService } from '../chart-type-manager/chart-type.service';
|
|
||||||
import { UiComponentService } from './ui-component.service';
|
|
||||||
import { ComponentPropertyService } from './component-property.service';
|
|
||||||
import { ChartTemplateService } from './chart-template.service';
|
|
||||||
import { DynamicFieldService } from './dynamic-field.service';
|
|
||||||
|
|
||||||
export interface ChartType {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
isActive: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UiComponent {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
componentName: string;
|
|
||||||
componentType: string;
|
|
||||||
displayLabel: string;
|
|
||||||
placeholder: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ComponentProperty {
|
|
||||||
id: number;
|
|
||||||
component: UiComponent;
|
|
||||||
propertyName: string;
|
|
||||||
propertyValue: string;
|
|
||||||
propertyType: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChartTemplate {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
templateName: string;
|
|
||||||
templateHtml: string;
|
|
||||||
templateCss: string;
|
|
||||||
isDefault: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DynamicField {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
fieldName: string;
|
|
||||||
fieldLabel: string;
|
|
||||||
fieldType: string;
|
|
||||||
fieldOptions: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
showInUi: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-config-manager',
|
|
||||||
templateUrl: './chart-config-manager.component.html',
|
|
||||||
styleUrls: ['./chart-config-manager.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartConfigManagerComponent implements OnInit {
|
|
||||||
// Chart Types
|
|
||||||
chartTypes: ChartType[] = [];
|
|
||||||
selectedChartType: ChartType | null = null;
|
|
||||||
newChartType: Partial<ChartType> = {};
|
|
||||||
showAddChartTypeForm = false;
|
|
||||||
chartTypeLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
uiComponents: UiComponent[] = [];
|
|
||||||
selectedUiComponent: UiComponent | null = null;
|
|
||||||
newUiComponent: Partial<UiComponent> = {};
|
|
||||||
showAddUiComponentForm = false;
|
|
||||||
uiComponentLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Component Properties
|
|
||||||
componentProperties: ComponentProperty[] = [];
|
|
||||||
selectedComponentProperty: ComponentProperty | null = null;
|
|
||||||
newComponentProperty: Partial<ComponentProperty> = {};
|
|
||||||
showAddComponentPropertyForm = false;
|
|
||||||
componentPropertyLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Chart Templates
|
|
||||||
chartTemplates: ChartTemplate[] = [];
|
|
||||||
selectedChartTemplate: ChartTemplate | null = null;
|
|
||||||
newChartTemplate: Partial<ChartTemplate> = {};
|
|
||||||
showAddChartTemplateForm = false;
|
|
||||||
chartTemplateLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Dynamic Fields
|
|
||||||
dynamicFields: DynamicField[] = [];
|
|
||||||
selectedDynamicField: DynamicField | null = null;
|
|
||||||
newDynamicField: Partial<DynamicField> = {};
|
|
||||||
showAddDynamicFieldForm = false;
|
|
||||||
dynamicFieldLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private uiComponentService: UiComponentService,
|
|
||||||
private componentPropertyService: ComponentPropertyService,
|
|
||||||
private chartTemplateService: ChartTemplateService,
|
|
||||||
private dynamicFieldService: DynamicFieldService
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
console.log('ChartConfigManagerComponent initialized');
|
|
||||||
this.loadChartTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Type Methods
|
|
||||||
loadChartTypes(): void {
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
console.log('Loading chart types...');
|
|
||||||
this.chartTypeService.getAllChartTypes().subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
console.log('Chart types loaded:', data);
|
|
||||||
this.chartTypes = data;
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
this.showError('Error loading chart types: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartType(): void {
|
|
||||||
if (!this.newChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.createChartType(this.newChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTypes.push(data);
|
|
||||||
this.newChartType = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showSuccess('Chart type created successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart type:', error);
|
|
||||||
this.showError('Error creating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartType(): void {
|
|
||||||
if (!this.selectedChartType || !this.selectedChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.updateChartType(this.selectedChartType.id, this.selectedChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTypes.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTypes[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.showSuccess('Chart type updated successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart type:', error);
|
|
||||||
this.showError('Error updating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartType(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart type? This will also delete all related UI components, templates, and fields.')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.deleteChartType(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTypes = this.chartTypes.filter(ct => ct.id !== id);
|
|
||||||
// Clear related data if the deleted chart type was selected
|
|
||||||
if (this.selectedChartType && this.selectedChartType.id === id) {
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.uiComponents = [];
|
|
||||||
this.chartTemplates = [];
|
|
||||||
this.dynamicFields = [];
|
|
||||||
}
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTypeForEdit(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = { ...chartType };
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI Component Methods
|
|
||||||
loadUiComponents(chartTypeId: number): void {
|
|
||||||
if (!chartTypeId) {
|
|
||||||
this.uiComponents = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.getUiComponentsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents = data;
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading UI components:', error);
|
|
||||||
this.showError('Error loading UI components: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createUiComponent(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete chartType object with only the ID
|
|
||||||
const chartTypeWithId = {
|
|
||||||
id: this.selectedChartType.id,
|
|
||||||
name: '',
|
|
||||||
displayName: '',
|
|
||||||
description: '',
|
|
||||||
isActive: true,
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
const uiComponentData = {
|
|
||||||
...this.newUiComponent,
|
|
||||||
chartType: chartTypeWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.uiComponentService.createUiComponent(uiComponentData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents.push(data);
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showSuccess('UI component created successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating UI component:', error);
|
|
||||||
this.showError('Error creating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUiComponent(): void {
|
|
||||||
if (!this.selectedUiComponent || !this.selectedUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.updateUiComponent(this.selectedUiComponent.id, this.selectedUiComponent).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.uiComponents.findIndex(uc => uc.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.uiComponents[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.showSuccess('UI component updated successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating UI component:', error);
|
|
||||||
this.showError('Error updating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUiComponent(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this UI component?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.deleteUiComponent(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.uiComponents = this.uiComponents.filter(uc => uc.id !== id);
|
|
||||||
this.showSuccess('UI component deleted successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting UI component:', error);
|
|
||||||
this.showError('Error deleting UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectUiComponentForEdit(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = { ...uiComponent };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Property Methods
|
|
||||||
loadComponentProperties(componentId: number): void {
|
|
||||||
if (!componentId) {
|
|
||||||
this.componentProperties = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.getComponentPropertiesByComponent(componentId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties = data;
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading component properties:', error);
|
|
||||||
this.showError('Error loading component properties: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createComponentProperty(): void {
|
|
||||||
if (!this.selectedUiComponent) {
|
|
||||||
this.showError('Please select a UI component first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete component object with only the ID
|
|
||||||
const componentWithId = {
|
|
||||||
id: this.selectedUiComponent.id
|
|
||||||
} as UiComponent;
|
|
||||||
|
|
||||||
const componentPropertyData = {
|
|
||||||
...this.newComponentProperty,
|
|
||||||
component: componentWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.componentPropertyService.createComponentProperty(componentPropertyData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties.push(data);
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showSuccess('Component property created successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating component property:', error);
|
|
||||||
this.showError('Error creating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateComponentProperty(): void {
|
|
||||||
if (!this.selectedComponentProperty || !this.selectedComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.updateComponentProperty(this.selectedComponentProperty.id, this.selectedComponentProperty).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.componentProperties.findIndex(cp => cp.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.componentProperties[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.showSuccess('Component property updated successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating component property:', error);
|
|
||||||
this.showError('Error updating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComponentProperty(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this component property?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.deleteComponentProperty(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.componentProperties = this.componentProperties.filter(cp => cp.id !== id);
|
|
||||||
this.showSuccess('Component property deleted successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting component property:', error);
|
|
||||||
this.showError('Error deleting component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectComponentPropertyForEdit(componentProperty: ComponentProperty): void {
|
|
||||||
this.selectedComponentProperty = { ...componentProperty };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Template Methods
|
|
||||||
loadChartTemplates(chartTypeId: number): void {
|
|
||||||
if (!chartTypeId) {
|
|
||||||
this.chartTemplates = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.getChartTemplatesByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates = data;
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart templates:', error);
|
|
||||||
this.showError('Error loading chart templates: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartTemplate(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Remove the chartType from the template data since we're passing it as a parameter
|
|
||||||
const chartTemplateData = { ...this.newChartTemplate };
|
|
||||||
// Remove chartType property if it exists
|
|
||||||
delete (chartTemplateData as any).chartType;
|
|
||||||
|
|
||||||
this.chartTemplateService.createChartTemplate(chartTemplateData, this.selectedChartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates.push(data);
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showSuccess('Chart template created successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart template:', error);
|
|
||||||
this.showError('Error creating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartTemplate(): void {
|
|
||||||
if (!this.selectedChartTemplate || !this.selectedChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.updateChartTemplate(this.selectedChartTemplate.id, this.selectedChartTemplate).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTemplates.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTemplates[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.showSuccess('Chart template updated successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart template:', error);
|
|
||||||
this.showError('Error updating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartTemplate(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart template?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.deleteChartTemplate(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTemplates = this.chartTemplates.filter(ct => ct.id !== id);
|
|
||||||
this.showSuccess('Chart template deleted successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart template:', error);
|
|
||||||
this.showError('Error deleting chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTemplateForEdit(chartTemplate: ChartTemplate): void {
|
|
||||||
this.selectedChartTemplate = { ...chartTemplate };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamic Field Methods
|
|
||||||
loadDynamicFields(chartTypeId: number): void {
|
|
||||||
if (!chartTypeId) {
|
|
||||||
this.dynamicFields = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.getDynamicFieldsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields = data;
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading dynamic fields:', error);
|
|
||||||
this.showError('Error loading dynamic fields: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createDynamicField(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Remove the chartType from the dynamic field data since we're passing it as a parameter
|
|
||||||
const dynamicFieldData = { ...this.newDynamicField };
|
|
||||||
// Remove chartType property if it exists
|
|
||||||
delete (dynamicFieldData as any).chartType;
|
|
||||||
|
|
||||||
this.dynamicFieldService.createDynamicField(dynamicFieldData, this.selectedChartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields.push(data);
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.showSuccess('Dynamic field created successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating dynamic field:', error);
|
|
||||||
this.showError('Error creating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDynamicField(): void {
|
|
||||||
if (!this.selectedDynamicField || !this.selectedDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.updateDynamicField(this.selectedDynamicField.id, this.selectedDynamicField).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.dynamicFields.findIndex(df => df.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.dynamicFields[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
this.showSuccess('Dynamic field updated successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating dynamic field:', error);
|
|
||||||
this.showError('Error updating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteDynamicField(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this dynamic field?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.deleteDynamicField(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.dynamicFields = this.dynamicFields.filter(df => df.id !== id);
|
|
||||||
this.showSuccess('Dynamic field deleted successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting dynamic field:', error);
|
|
||||||
this.showError('Error deleting dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectDynamicFieldForEdit(dynamicField: DynamicField): void {
|
|
||||||
this.selectedDynamicField = { ...dynamicField };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
onChartTypeSelect(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = chartType;
|
|
||||||
this.loadUiComponents(chartType.id);
|
|
||||||
this.loadChartTemplates(chartType.id);
|
|
||||||
this.loadDynamicFields(chartType.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
onUiComponentSelect(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = uiComponent;
|
|
||||||
this.loadComponentProperties(uiComponent.id);
|
|
||||||
// Scroll to the Component Properties tab
|
|
||||||
setTimeout(() => {
|
|
||||||
const element = document.querySelector('clr-tab[ng-reflect-ng-if="true"] button[clr-tab-link]:nth-child(3)');
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetForms(): void {
|
|
||||||
this.newChartType = {};
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { ChartTemplate } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ChartTemplateService {
|
|
||||||
private chartTemplatesUrl = 'api/chart-templates';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all chart templates for a chart type
|
|
||||||
getChartTemplatesByChartType(chartTypeId: number): Observable<ChartTemplate[]> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/chart-type/${chartTypeId}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get chart template by ID
|
|
||||||
getChartTemplateById(id: number): Observable<ChartTemplate> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/${id}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new chart template with optional chart type ID as parameter
|
|
||||||
createChartTemplate(chartTemplate: Partial<ChartTemplate>, chartTypeId?: number): Observable<ChartTemplate> {
|
|
||||||
let url = this.chartTemplatesUrl;
|
|
||||||
if (chartTypeId) {
|
|
||||||
url = `${this.chartTemplatesUrl}?chartTypeId=${chartTypeId}`;
|
|
||||||
}
|
|
||||||
return this.apiRequest.post(url, chartTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update chart template
|
|
||||||
updateChartTemplate(id: number, chartTemplate: ChartTemplate): Observable<ChartTemplate> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/${id}`;
|
|
||||||
return this.apiRequest.put(url, chartTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete chart template
|
|
||||||
deleteChartTemplate(id: number): Observable<void> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/${id}`;
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { ComponentProperty } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ComponentPropertyService {
|
|
||||||
private componentPropertiesUrl = 'api/component-properties';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all component properties for a component
|
|
||||||
getComponentPropertiesByComponent(componentId: number): Observable<ComponentProperty[]> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/component/${componentId}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get component property by ID
|
|
||||||
getComponentPropertyById(id: number): Observable<ComponentProperty> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/${id}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new component property
|
|
||||||
createComponentProperty(componentProperty: Partial<ComponentProperty>): Observable<ComponentProperty> {
|
|
||||||
return this.apiRequest.post(this.componentPropertiesUrl, componentProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update component property
|
|
||||||
updateComponentProperty(id: number, componentProperty: ComponentProperty): Observable<ComponentProperty> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/${id}`;
|
|
||||||
return this.apiRequest.put(url, componentProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete component property
|
|
||||||
deleteComponentProperty(id: number): Observable<void> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/${id}`;
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { DynamicField } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class DynamicFieldService {
|
|
||||||
private dynamicFieldsUrl = 'api/dynamic-fields';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all dynamic fields for a chart type
|
|
||||||
getDynamicFieldsByChartType(chartTypeId: number): Observable<DynamicField[]> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/chart-type/${chartTypeId}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get dynamic field by ID
|
|
||||||
getDynamicFieldById(id: number): Observable<DynamicField> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/${id}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new dynamic field with optional chart type ID as parameter
|
|
||||||
createDynamicField(dynamicField: Partial<DynamicField>, chartTypeId?: number): Observable<DynamicField> {
|
|
||||||
let url = this.dynamicFieldsUrl;
|
|
||||||
if (chartTypeId) {
|
|
||||||
url = `${this.dynamicFieldsUrl}?chartTypeId=${chartTypeId}`;
|
|
||||||
}
|
|
||||||
return this.apiRequest.post(url, dynamicField);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update dynamic field
|
|
||||||
updateDynamicField(id: number, dynamicField: DynamicField): Observable<DynamicField> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/${id}`;
|
|
||||||
return this.apiRequest.put(url, dynamicField);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete dynamic field
|
|
||||||
deleteDynamicField(id: number): Observable<void> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/${id}`;
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<div class="chart-template-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #chartTemplateForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartTemplate.templateName" name="templateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartTemplate.templateHtml" name="templateHtml" rows="5" placeholder="<div class='chart-container'>...</div>"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartTemplate.templateCss" name="templateCss" rows="5" placeholder=".chart-container { ... }"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartTemplate.isDefault" name="isDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartTemplate.templateName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
.chart-template-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { ChartTemplate } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-template-form',
|
|
||||||
templateUrl: './chart-template-form.component.html',
|
|
||||||
styleUrls: ['./chart-template-form.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTemplateFormComponent {
|
|
||||||
@Input() chartTemplate: Partial<ChartTemplate> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<ChartTemplate>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.chartTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<div class="chart-type-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #chartTypeForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.name" name="chartTypeName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartType.description" name="chartTypeDescription" rows="3"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartType.name">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
.chart-type-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { ChartType } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-form',
|
|
||||||
templateUrl: './chart-type-form.component.html',
|
|
||||||
styleUrls: ['./chart-type-form.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeFormComponent {
|
|
||||||
@Input() chartType: Partial<ChartType> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<ChartType>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.chartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<div class="component-property-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #componentPropertyForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="componentProperty.propertyName" name="propertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="componentProperty.propertyValue" name="propertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-select-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<select clrSelect [(ngModel)]="componentProperty.propertyType" name="propertyType">
|
|
||||||
<option value="">Select a type</option>
|
|
||||||
<option *ngFor="let type of propertyTypes" [value]="type">{{ type | titlecase }}</option>
|
|
||||||
</select>
|
|
||||||
</clr-select-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!componentProperty.propertyName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
.component-property-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { ComponentProperty } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-component-property-form',
|
|
||||||
templateUrl: './component-property-form.component.html',
|
|
||||||
styleUrls: ['./component-property-form.component.scss']
|
|
||||||
})
|
|
||||||
export class ComponentPropertyFormComponent {
|
|
||||||
@Input() componentProperty: Partial<ComponentProperty> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<ComponentProperty>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
propertyTypes = [
|
|
||||||
'string',
|
|
||||||
'number',
|
|
||||||
'boolean',
|
|
||||||
'array',
|
|
||||||
'object',
|
|
||||||
'function'
|
|
||||||
];
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.componentProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<div class="dynamic-field-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #dynamicFieldForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="dynamicField.fieldName" name="fieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="dynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-select-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<select clrSelect [(ngModel)]="dynamicField.fieldType" name="fieldType">
|
|
||||||
<option value="">Select a type</option>
|
|
||||||
<option *ngFor="let type of fieldTypes" [value]="type">{{ type | titlecase }}</option>
|
|
||||||
</select>
|
|
||||||
</clr-select-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options (JSON format)</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="dynamicField.fieldOptions" name="fieldOptions" rows="3" placeholder='{"option1": "Option 1", "option2": "Option 2"}'></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="dynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="dynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="dynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!dynamicField.fieldName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
.dynamic-field-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { DynamicField } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-dynamic-field-form',
|
|
||||||
templateUrl: './dynamic-field-form.component.html',
|
|
||||||
styleUrls: ['./dynamic-field-form.component.scss']
|
|
||||||
})
|
|
||||||
export class DynamicFieldFormComponent {
|
|
||||||
@Input() dynamicField: Partial<DynamicField> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<DynamicField>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
fieldTypes = [
|
|
||||||
'text',
|
|
||||||
'number',
|
|
||||||
'email',
|
|
||||||
'password',
|
|
||||||
'date',
|
|
||||||
'select',
|
|
||||||
'checkbox',
|
|
||||||
'radio',
|
|
||||||
'textarea'
|
|
||||||
];
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.dynamicField);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<div class="ui-component-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #uiComponentForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="uiComponent.componentName" name="componentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-select-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<select clrSelect [(ngModel)]="uiComponent.componentType" name="componentType">
|
|
||||||
<option value="">Select a type</option>
|
|
||||||
<option *ngFor="let type of componentTypes" [value]="type">{{ type | titlecase }}</option>
|
|
||||||
</select>
|
|
||||||
</clr-select-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="uiComponent.displayLabel" name="displayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="uiComponent.placeholder" name="placeholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="uiComponent.sortOrder" name="sortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="uiComponent.isRequired" name="isRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!uiComponent.componentName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
.ui-component-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { UiComponent } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-ui-component-form',
|
|
||||||
templateUrl: './ui-component-form.component.html',
|
|
||||||
styleUrls: ['./ui-component-form.component.scss']
|
|
||||||
})
|
|
||||||
export class UiComponentFormComponent {
|
|
||||||
@Input() uiComponent: Partial<UiComponent> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<UiComponent>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
componentTypes = [
|
|
||||||
'input',
|
|
||||||
'select',
|
|
||||||
'checkbox',
|
|
||||||
'radio',
|
|
||||||
'textarea',
|
|
||||||
'datepicker',
|
|
||||||
'slider',
|
|
||||||
'toggle'
|
|
||||||
];
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.uiComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { UiComponent } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class UiComponentService {
|
|
||||||
private uiComponentsUrl = 'api/ui-components';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all UI components for a chart type
|
|
||||||
getUiComponentsByChartType(chartTypeId: number): Observable<UiComponent[]> {
|
|
||||||
const url = `${this.uiComponentsUrl}/chart-type/${chartTypeId}`;
|
|
||||||
console.log(`Fetching UI components for chart type ${chartTypeId} from ${url}`);
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get UI component by ID
|
|
||||||
getUiComponentById(id: number): Observable<UiComponent> {
|
|
||||||
const url = `${this.uiComponentsUrl}/${id}`;
|
|
||||||
console.log(`Fetching UI component ${id} from ${url}`);
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new UI component
|
|
||||||
createUiComponent(uiComponent: Partial<UiComponent>): Observable<UiComponent> {
|
|
||||||
console.log('Creating UI component:', uiComponent);
|
|
||||||
return this.apiRequest.post(this.uiComponentsUrl, uiComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update UI component
|
|
||||||
updateUiComponent(id: number, uiComponent: UiComponent): Observable<UiComponent> {
|
|
||||||
const url = `${this.uiComponentsUrl}/${id}`;
|
|
||||||
console.log(`Updating UI component ${id}:`, uiComponent);
|
|
||||||
return this.apiRequest.put(url, uiComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete UI component
|
|
||||||
deleteUiComponent(id: number): Observable<void> {
|
|
||||||
const url = `${this.uiComponentsUrl}/${id}`;
|
|
||||||
console.log(`Deleting UI component ${id}`);
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<div class="add-chart-type-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>Add New Chart Type</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createChartType()" #addChartTypeForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.name" name="chartTypeName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the chart type (e.g., "line-chart", "bar-chart")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
<clr-control-helper>This is the user-friendly name shown in the UI</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartType.description" name="chartTypeDescription" rows="3"></textarea>
|
|
||||||
<clr-control-helper>Provide a detailed description of this chart type and when to use it</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Deactivate chart types that should not be available for selection</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartType.name || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Chart Type
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Chart Types</h4>
|
|
||||||
<p>Chart types define the different visualization options available in the dashboard builder. Each chart type can have:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Associated UI components that define the configuration form</li>
|
|
||||||
<li>Templates that define how the chart is rendered</li>
|
|
||||||
<li>Dynamic fields that capture specific configuration parameters</li>
|
|
||||||
</ul>
|
|
||||||
<p>After creating a chart type, you can configure its components, templates, and fields in the chart configuration manager.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
.add-chart-type-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-add-chart-type',
|
|
||||||
templateUrl: './add-chart-type.component.html',
|
|
||||||
styleUrls: ['./add-chart-type.component.scss']
|
|
||||||
})
|
|
||||||
export class AddChartTypeComponent {
|
|
||||||
chartType: Partial<ChartType> = {
|
|
||||||
isActive: true
|
|
||||||
};
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartType(): void {
|
|
||||||
if (!this.chartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.createChartType(this.chartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.showSuccess('Chart type created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
// Redirect to chart types list after a short delay
|
|
||||||
setTimeout(() => {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}, 1500);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart type:', error);
|
|
||||||
this.showError('Error creating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
<div class="chart-type-fields-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>
|
|
||||||
<button class="btn btn-link back-button" (click)="goBack()">
|
|
||||||
<cds-icon shape="arrow" direction="left"></cds-icon>
|
|
||||||
</button>
|
|
||||||
Dynamic Fields for {{ chartType?.name || 'Chart Type' }}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Dynamic Field Form -->
|
|
||||||
<div class="card" *ngIf="showAddForm">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Add New Dynamic Field</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createDynamicField()" #addDynamicFieldForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldName" name="fieldName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the dynamic field (e.g., "chart-title", "x-axis-label")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
<clr-control-helper>User-friendly label shown in the configuration form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldType" name="fieldType" />
|
|
||||||
<clr-control-helper>Type of the field (e.g., "string", "number", "boolean")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newDynamicField.fieldOptions" name="fieldOptions" rows="3"></textarea>
|
|
||||||
<clr-control-helper>JSON options for the field (for select fields, etc.)</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newDynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
<clr-control-helper>Order in which fields appear in the form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Mark as required if this field must be filled</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Visible</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Uncheck to hide this field in the configuration form</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newDynamicField.fieldName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Dynamic Field
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Dynamic Fields</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Dynamic Field
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Field Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Visible</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let dynamicField of dynamicFields" [clrDgItem]="dynamicField">
|
|
||||||
<clr-dg-cell>{{dynamicField.fieldName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{dynamicField.fieldLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{dynamicField.fieldType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="dynamicField.isRequired" [class.label-danger]="!dynamicField.isRequired">
|
|
||||||
{{dynamicField.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="dynamicField.showInUi" [class.label-danger]="!dynamicField.showInUi">
|
|
||||||
{{dynamicField.showInUi ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{dynamicField.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectDynamicFieldForEdit(dynamicField)" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteDynamicField(dynamicField.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{dynamicFields.length}} dynamic field(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Dynamic Field Form -->
|
|
||||||
<div class="card" *ngIf="selectedDynamicField">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Edit Dynamic Field</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateDynamicField()" #editDynamicFieldForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldName" name="editFieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldLabel" name="editFieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldType" name="editFieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedDynamicField.fieldOptions" name="editFieldOptions" rows="3"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedDynamicField.sortOrder" name="editFieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.isRequired" name="editFieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.showInUi" name="editFieldShowInUi" />
|
|
||||||
<label>Visible</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedDynamicField.fieldName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Dynamic Field
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedDynamicField = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Dynamic Fields</h4>
|
|
||||||
<p>Dynamic fields define the configurable parameters for a chart type. Each field represents:</p>
|
|
||||||
<ul>
|
|
||||||
<li>A configuration option that can be set when creating or editing a chart</li>
|
|
||||||
<li>Metadata like type, label, and validation rules</li>
|
|
||||||
<li>Visibility and requirement settings</li>
|
|
||||||
</ul>
|
|
||||||
<p>Dynamic fields allow you to create flexible chart configurations that can be customized per instance.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
.chart-type-fields-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
padding: 0;
|
|
||||||
cds-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
import { DynamicField } from '../chart-config/chart-config-manager.component';
|
|
||||||
import { DynamicFieldService } from '../chart-config/dynamic-field.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-fields',
|
|
||||||
templateUrl: './chart-type-fields.component.html',
|
|
||||||
styleUrls: ['./chart-type-fields.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeFieldsComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
dynamicFields: DynamicField[] = [];
|
|
||||||
newDynamicField: Partial<DynamicField> = {
|
|
||||||
isRequired: false,
|
|
||||||
showInUi: true
|
|
||||||
};
|
|
||||||
selectedDynamicField: DynamicField | null = null;
|
|
||||||
showAddForm = false;
|
|
||||||
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private dynamicFieldService: DynamicFieldService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const chartTypeId = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (chartTypeId) {
|
|
||||||
this.loadChartType(chartTypeId);
|
|
||||||
this.loadDynamicFields(chartTypeId);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartType = data;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadDynamicFields(chartTypeId: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.getDynamicFieldsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields = data;
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading dynamic fields:', error);
|
|
||||||
this.showError('Error loading dynamic fields: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createDynamicField(): void {
|
|
||||||
if (!this.chartType || !this.newDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy without the chartType property
|
|
||||||
const fieldData: Partial<DynamicField> = {
|
|
||||||
fieldName: this.newDynamicField.fieldName,
|
|
||||||
fieldLabel: this.newDynamicField.fieldLabel,
|
|
||||||
fieldType: this.newDynamicField.fieldType,
|
|
||||||
fieldOptions: this.newDynamicField.fieldOptions,
|
|
||||||
isRequired: this.newDynamicField.isRequired,
|
|
||||||
showInUi: this.newDynamicField.showInUi,
|
|
||||||
sortOrder: this.newDynamicField.sortOrder
|
|
||||||
};
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.createDynamicField(fieldData, this.chartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields.push(data);
|
|
||||||
this.newDynamicField = { isRequired: false, showInUi: true };
|
|
||||||
this.showAddForm = false;
|
|
||||||
this.showSuccess('Dynamic field created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating dynamic field:', error);
|
|
||||||
this.showError('Error creating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDynamicField(): void {
|
|
||||||
if (!this.selectedDynamicField || !this.selectedDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.updateDynamicField(this.selectedDynamicField.id, this.selectedDynamicField).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.dynamicFields.findIndex(df => df.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.dynamicFields[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
this.showSuccess('Dynamic field updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating dynamic field:', error);
|
|
||||||
this.showError('Error updating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteDynamicField(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this dynamic field?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.deleteDynamicField(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.dynamicFields = this.dynamicFields.filter(df => df.id !== id);
|
|
||||||
this.showSuccess('Dynamic field deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting dynamic field:', error);
|
|
||||||
this.showError('Error deleting dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectDynamicFieldForEdit(dynamicField: DynamicField): void {
|
|
||||||
this.selectedDynamicField = { ...dynamicField };
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
if (this.chartType) {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types/edit', this.chartType.id]);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
<div class="chart-type-manager">
|
|
||||||
<h2>Chart Type Management</h2>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Chart Types</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" [routerLink]="['/cns-portal/dashboardbuilder/chart-types/add']">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Chart Type
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Description</clr-dg-column>
|
|
||||||
<clr-dg-column>Status</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartType of chartTypes" [clrDgItem]="chartType">
|
|
||||||
<clr-dg-cell>{{chartType.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.displayName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.description}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{chartType.isActive ? 'Active' : 'Inactive'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.createdAt ? (chartType.createdAt | date:'short') : 'N/A'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.updatedAt ? (chartType.updatedAt | date:'short') : 'N/A'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" [routerLink]="['/cns-portal/dashboardbuilder/chart-types/edit', chartType.id]" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartType(chartType.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTypes.length}} chart type(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
.chart-type-manager {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-manager',
|
|
||||||
templateUrl: './chart-type-manager.component.html',
|
|
||||||
styleUrls: ['./chart-type-manager.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeManagerComponent implements OnInit {
|
|
||||||
chartTypes: ChartType[] = [];
|
|
||||||
chartTypeLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
constructor(private chartTypeService: ChartTypeService) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.loadChartTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Type Methods
|
|
||||||
loadChartTypes(): void {
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.getAllChartTypes().subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
// Process the data to ensure dates are properly formatted
|
|
||||||
this.chartTypes = data.map(chartType => ({
|
|
||||||
...chartType,
|
|
||||||
createdAt: this.formatDate(chartType.createdAt),
|
|
||||||
updatedAt: this.formatDate(chartType.updatedAt)
|
|
||||||
}));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
this.showError('Error loading chart types: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format date to handle both string and object formats
|
|
||||||
private formatDate(date: any): string {
|
|
||||||
if (!date) return '';
|
|
||||||
|
|
||||||
// If it's already a string, return as is
|
|
||||||
if (typeof date === 'string') {
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's an object, try to convert to string
|
|
||||||
if (typeof date === 'object') {
|
|
||||||
// Handle various date object formats
|
|
||||||
if (date instanceof Date) {
|
|
||||||
return date.toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle ISO string within object
|
|
||||||
if (date.date) {
|
|
||||||
return date.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle other object formats
|
|
||||||
try {
|
|
||||||
return new Date(date).toISOString();
|
|
||||||
} catch {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartType(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart type?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.deleteChartType(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTypes = this.chartTypes.filter(ct => ct.id !== id);
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-page',
|
|
||||||
template: `
|
|
||||||
<div class="chart-type-page">
|
|
||||||
<app-chart-type-manager></app-chart-type-manager>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
styles: [`
|
|
||||||
.chart-type-page {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.chart-type-page {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`]
|
|
||||||
})
|
|
||||||
export class ChartTypePageComponent { }
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
<div class="chart-type-templates-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>
|
|
||||||
<button class="btn btn-link back-button" (click)="goBack()">
|
|
||||||
<cds-icon shape="arrow" direction="left"></cds-icon>
|
|
||||||
</button>
|
|
||||||
Chart Templates for {{ chartType?.name || 'Chart Type' }}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Template Form -->
|
|
||||||
<div class="card" *ngIf="showAddForm">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Add New Chart Template</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createChartTemplate()" #addChartTemplateForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartTemplate.templateName" name="templateName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the chart template</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateHtml" name="templateHtml" rows="5"></textarea>
|
|
||||||
<clr-control-helper>HTML structure for rendering the chart</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateCss" name="templateCss" rows="5"></textarea>
|
|
||||||
<clr-control-helper>CSS styling for the chart template</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newChartTemplate.isDefault" name="isDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Mark as default template for this chart type</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newChartTemplate.templateName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Chart Template
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Templates Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Chart Templates</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Chart Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Template Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Default</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartTemplate of chartTemplates" [clrDgItem]="chartTemplate">
|
|
||||||
<clr-dg-cell>{{chartTemplate.templateName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartTemplate.isDefault" [class.label-danger]="!chartTemplate.isDefault">
|
|
||||||
{{chartTemplate.isDefault ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartTemplate.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartTemplate.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTemplateForEdit(chartTemplate)" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartTemplate(chartTemplate.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTemplates.length}} chart template(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Template Form -->
|
|
||||||
<div class="card" *ngIf="selectedChartTemplate">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Edit Chart Template</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateChartTemplate()" #editChartTemplateForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartTemplate.templateName" name="editTemplateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateHtml" name="editTemplateHtml" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateCss" name="editTemplateCss" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedChartTemplate.isDefault" name="editIsDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedChartTemplate.templateName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Chart Template
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedChartTemplate = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Chart Templates</h4>
|
|
||||||
<p>Chart templates define how a chart of this type is rendered. Each template includes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>HTML structure that defines the chart layout</li>
|
|
||||||
<li>CSS styling that controls the appearance</li>
|
|
||||||
<li>A default flag to indicate the primary template</li>
|
|
||||||
</ul>
|
|
||||||
<p>Templates allow you to have multiple visual representations for the same chart type.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
.chart-type-templates-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
padding: 0;
|
|
||||||
cds-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
import { ChartTemplateService } from '../chart-config/chart-template.service';
|
|
||||||
import { ChartTemplate } from '../chart-config/chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-templates',
|
|
||||||
templateUrl: './chart-type-templates.component.html',
|
|
||||||
styleUrls: ['./chart-type-templates.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeTemplatesComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
chartTemplates: ChartTemplate[] = [];
|
|
||||||
newChartTemplate: Partial<ChartTemplate> = {
|
|
||||||
isDefault: false
|
|
||||||
};
|
|
||||||
selectedChartTemplate: ChartTemplate | null = null;
|
|
||||||
showAddForm = false;
|
|
||||||
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private chartTemplateService: ChartTemplateService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const chartTypeId = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (chartTypeId) {
|
|
||||||
this.loadChartType(chartTypeId);
|
|
||||||
this.loadChartTemplates(chartTypeId);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartType = data;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartTemplates(chartTypeId: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.getChartTemplatesByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates = data;
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart templates:', error);
|
|
||||||
this.showError('Error loading chart templates: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartTemplate(): void {
|
|
||||||
if (!this.chartType || !this.newChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy without the chartType property
|
|
||||||
const templateData: Partial<ChartTemplate> = {
|
|
||||||
templateName: this.newChartTemplate.templateName,
|
|
||||||
templateHtml: this.newChartTemplate.templateHtml,
|
|
||||||
templateCss: this.newChartTemplate.templateCss,
|
|
||||||
isDefault: this.newChartTemplate.isDefault
|
|
||||||
};
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.createChartTemplate(templateData, this.chartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates.push(data);
|
|
||||||
this.newChartTemplate = { isDefault: false };
|
|
||||||
this.showAddForm = false;
|
|
||||||
this.showSuccess('Chart template created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart template:', error);
|
|
||||||
this.showError('Error creating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartTemplate(): void {
|
|
||||||
if (!this.selectedChartTemplate || !this.selectedChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.updateChartTemplate(this.selectedChartTemplate.id, this.selectedChartTemplate).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTemplates.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTemplates[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.showSuccess('Chart template updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart template:', error);
|
|
||||||
this.showError('Error updating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartTemplate(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart template?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.deleteChartTemplate(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTemplates = this.chartTemplates.filter(ct => ct.id !== id);
|
|
||||||
this.showSuccess('Chart template deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart template:', error);
|
|
||||||
this.showError('Error deleting chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTemplateForEdit(chartTemplate: ChartTemplate): void {
|
|
||||||
this.selectedChartTemplate = { ...chartTemplate };
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
if (this.chartType) {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types/edit', this.chartType.id]);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,312 +0,0 @@
|
|||||||
<div class="chart-type-ui-components-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>
|
|
||||||
<button class="btn btn-link back-button" (click)="goBack()">
|
|
||||||
<cds-icon shape="arrow" direction="left"></cds-icon>
|
|
||||||
</button>
|
|
||||||
UI Components for {{ chartType?.name || 'Chart Type' }}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add UI Component Form -->
|
|
||||||
<div class="card" *ngIf="showAddForm">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Add New UI Component</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createUiComponent()" #addUiComponentForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentName" name="componentName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the UI component (e.g., "title-config", "axis-config")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentType" name="componentType" />
|
|
||||||
<clr-control-helper>Type of the component (e.g., "input", "select", "checkbox")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.displayLabel" name="displayLabel" />
|
|
||||||
<clr-control-helper>User-friendly label shown in the configuration form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.placeholder" name="placeholder" />
|
|
||||||
<clr-control-helper>Placeholder text for input fields</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newUiComponent.sortOrder" name="sortOrder" />
|
|
||||||
<clr-control-helper>Order in which components appear in the form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newUiComponent.isRequired" name="isRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Mark as required if this component must be filled</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newUiComponent.componentName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create UI Component
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- UI Components Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>UI Components</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add UI Component
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Component Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Component Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let uiComponent of uiComponents" [clrDgItem]="uiComponent">
|
|
||||||
<clr-dg-cell>{{uiComponent.componentName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.componentType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.displayLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="uiComponent.isRequired" [class.label-danger]="!uiComponent.isRequired">
|
|
||||||
{{uiComponent.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell class="action-cell">
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectUiComponentForEdit(uiComponent)" title="Edit" [disabled]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="loadingState !== ClrLoadingState.LOADING" shape="pencil" aria-label="Edit"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteUiComponent(uiComponent.id)" title="Delete" [disabled]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="loadingState !== ClrLoadingState.LOADING" shape="trash" aria-label="Delete"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon btn-primary" (click)="onViewComponentProperties(uiComponent)" title="View Properties" [disabled]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="loadingState !== ClrLoadingState.LOADING" shape="eye" aria-label="View Properties"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{uiComponents.length}} UI component(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit UI Component Form -->
|
|
||||||
<div class="card" *ngIf="selectedUiComponent && !showAddComponentPropertyForm && !selectedComponentProperty">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Edit UI Component</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateUiComponent()" #editUiComponentForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentName" name="editComponentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentType" name="editComponentType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.displayLabel" name="editDisplayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.placeholder" name="editPlaceholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedUiComponent.sortOrder" name="editSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedUiComponent.isRequired" name="editIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedUiComponent.componentName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update UI Component
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedUiComponent = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Section -->
|
|
||||||
<div class="card" *ngIf="selectedUiComponent">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Properties for {{selectedUiComponent?.componentName}}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddComponentPropertyForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Property
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddComponentPropertyForm">
|
|
||||||
<form clrForm (ngSubmit)="createComponentProperty()" #addComponentPropertyForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyName" name="propertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyValue" name="propertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyType" name="propertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newComponentProperty.propertyName || componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Property
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddComponentPropertyForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Property Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Value</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let property of componentProperties" [clrDgItem]="property">
|
|
||||||
<clr-dg-cell>{{property.propertyName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyValue}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectComponentPropertyForEdit(property)" title="Edit" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteComponentProperty(property.id)" title="Delete" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{componentProperties.length}} propertie(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedComponentProperty">
|
|
||||||
<h4>Edit Component Property</h4>
|
|
||||||
<form clrForm (ngSubmit)="updateComponentProperty()" #editComponentPropertyForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyName" name="editPropertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyValue" name="editPropertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyType" name="editPropertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedComponentProperty.propertyName || componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Property
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedComponentProperty = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About UI Components</h4>
|
|
||||||
<p>UI components define the configuration form elements for a chart type. Each component represents:</p>
|
|
||||||
<ul>
|
|
||||||
<li>A form field that appears when configuring a chart of this type</li>
|
|
||||||
<li>Metadata like label, placeholder, and validation rules</li>
|
|
||||||
<li>An order in which they appear in the configuration form</li>
|
|
||||||
</ul>
|
|
||||||
<p>After creating UI components, you can define their properties using the "View Properties" button.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
.chart-type-ui-components-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
padding: 0;
|
|
||||||
cds-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Properties Section Styles
|
|
||||||
.action-cell {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons .btn-icon {
|
|
||||||
min-width: 36px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure icons are visible
|
|
||||||
cds-icon {
|
|
||||||
display: inline-block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-spinner {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
import { UiComponentService } from '../chart-config/ui-component.service';
|
|
||||||
import { ComponentPropertyService } from '../chart-config/component-property.service';
|
|
||||||
import { UiComponent, ComponentProperty } from '../chart-config/chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-ui-components',
|
|
||||||
templateUrl: './chart-type-ui-components.component.html',
|
|
||||||
styleUrls: ['./chart-type-ui-components.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeUiComponentsComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
uiComponents: UiComponent[] = [];
|
|
||||||
newUiComponent: Partial<UiComponent> = {
|
|
||||||
isRequired: false
|
|
||||||
};
|
|
||||||
selectedUiComponent: UiComponent | null = null;
|
|
||||||
showAddForm = false;
|
|
||||||
|
|
||||||
// Component Properties
|
|
||||||
componentProperties: ComponentProperty[] = [];
|
|
||||||
selectedComponentProperty: ComponentProperty | null = null;
|
|
||||||
newComponentProperty: Partial<ComponentProperty> = {};
|
|
||||||
showAddComponentPropertyForm = false;
|
|
||||||
componentPropertyLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private uiComponentService: UiComponentService,
|
|
||||||
private componentPropertyService: ComponentPropertyService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const chartTypeId = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (chartTypeId) {
|
|
||||||
this.loadChartType(chartTypeId);
|
|
||||||
this.loadUiComponents(chartTypeId);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartType = data;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadUiComponents(chartTypeId: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.getUiComponentsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents = data;
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading UI components:', error);
|
|
||||||
this.showError('Error loading UI components: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createUiComponent(): void {
|
|
||||||
if (!this.chartType || !this.newUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete chartType object with only the ID (following the pattern in chart-config-manager)
|
|
||||||
const chartTypeWithId = {
|
|
||||||
id: this.chartType.id,
|
|
||||||
name: '',
|
|
||||||
displayName: '',
|
|
||||||
description: '',
|
|
||||||
isActive: true,
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
const componentData = {
|
|
||||||
...this.newUiComponent,
|
|
||||||
chartType: chartTypeWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.uiComponentService.createUiComponent(componentData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents.push(data);
|
|
||||||
this.newUiComponent = { isRequired: false };
|
|
||||||
this.showAddForm = false;
|
|
||||||
this.showSuccess('UI component created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating UI component:', error);
|
|
||||||
this.showError('Error creating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUiComponent(): void {
|
|
||||||
if (!this.selectedUiComponent || !this.selectedUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.updateUiComponent(this.selectedUiComponent.id, this.selectedUiComponent).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.uiComponents.findIndex(uc => uc.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.uiComponents[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.showSuccess('UI component updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating UI component:', error);
|
|
||||||
this.showError('Error updating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUiComponent(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this UI component?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.deleteUiComponent(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.uiComponents = this.uiComponents.filter(uc => uc.id !== id);
|
|
||||||
this.showSuccess('UI component deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting UI component:', error);
|
|
||||||
this.showError('Error deleting UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectUiComponentForEdit(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = { ...uiComponent };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Property Methods
|
|
||||||
loadComponentProperties(componentId: number): void {
|
|
||||||
if (!componentId) {
|
|
||||||
this.componentProperties = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.getComponentPropertiesByComponent(componentId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties = data;
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading component properties:', error);
|
|
||||||
this.showError('Error loading component properties: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createComponentProperty(): void {
|
|
||||||
if (!this.selectedUiComponent) {
|
|
||||||
this.showError('Please select a UI component first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete component object with only the ID
|
|
||||||
const componentWithId = {
|
|
||||||
id: this.selectedUiComponent.id
|
|
||||||
} as UiComponent;
|
|
||||||
|
|
||||||
const componentPropertyData = {
|
|
||||||
...this.newComponentProperty,
|
|
||||||
component: componentWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.componentPropertyService.createComponentProperty(componentPropertyData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties.push(data);
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showSuccess('Component property created successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating component property:', error);
|
|
||||||
this.showError('Error creating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateComponentProperty(): void {
|
|
||||||
if (!this.selectedComponentProperty || !this.selectedComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.updateComponentProperty(this.selectedComponentProperty.id, this.selectedComponentProperty).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.componentProperties.findIndex(cp => cp.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.componentProperties[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.showSuccess('Component property updated successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating component property:', error);
|
|
||||||
this.showError('Error updating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComponentProperty(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this component property?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.deleteComponentProperty(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.componentProperties = this.componentProperties.filter(cp => cp.id !== id);
|
|
||||||
this.showSuccess('Component property deleted successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting component property:', error);
|
|
||||||
this.showError('Error deleting component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectComponentPropertyForEdit(componentProperty: ComponentProperty): void {
|
|
||||||
this.selectedComponentProperty = { ...componentProperty };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to view properties for a UI component
|
|
||||||
onViewComponentProperties(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = uiComponent;
|
|
||||||
this.loadComponentProperties(uiComponent.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
if (this.chartType) {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types/edit', this.chartType.id]);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
|
|
||||||
export interface ChartType {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
isActive: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ChartTypeService {
|
|
||||||
private chartTypesUrl = 'api/chart-types';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all chart types
|
|
||||||
getAllChartTypes(): Observable<ChartType[]> {
|
|
||||||
console.log('Fetching all chart types from', this.chartTypesUrl);
|
|
||||||
return this.apiRequest.get(this.chartTypesUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get chart type by ID
|
|
||||||
getChartTypeById(id: number): Observable<ChartType> {
|
|
||||||
const url = `${this.chartTypesUrl}/${id}`;
|
|
||||||
console.log(`Fetching chart type ${id} from ${url}`);
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new chart type
|
|
||||||
createChartType(chartType: Partial<ChartType>): Observable<ChartType> {
|
|
||||||
console.log('Creating chart type:', chartType);
|
|
||||||
return this.apiRequest.post(this.chartTypesUrl, chartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update chart type
|
|
||||||
updateChartType(id: number, chartType: ChartType): Observable<ChartType> {
|
|
||||||
const url = `${this.chartTypesUrl}/${id}`;
|
|
||||||
console.log(`Updating chart type ${id}:`, chartType);
|
|
||||||
return this.apiRequest.put(url, chartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete chart type
|
|
||||||
deleteChartType(id: number): Observable<void> {
|
|
||||||
const url = `${this.chartTypesUrl}/${id}`;
|
|
||||||
console.log(`Deleting chart type ${id}`);
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
<div class="edit-chart-type-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>Edit Chart Type</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Loading State -->
|
|
||||||
<div *ngIf="loadingState === ClrLoadingState.LOADING" class="loading-spinner">
|
|
||||||
<clr-spinner clrMedium></clr-spinner>
|
|
||||||
<span>Loading chart type...</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Type Form -->
|
|
||||||
<div class="card" *ngIf="chartType && loadingState !== ClrLoadingState.LOADING">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="chart-type-header">
|
|
||||||
<h3>{{ chartType.name }}</h3>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{ chartType.isActive ? 'Active' : 'Inactive' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateChartType()" #editChartTypeForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.name" name="chartTypeName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the chart type (e.g., "line-chart", "bar-chart")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
<clr-control-helper>This is the user-friendly name shown in the UI</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartType.description" name="chartTypeDescription" rows="3"></textarea>
|
|
||||||
<clr-control-helper>Provide a detailed description of this chart type and when to use it</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Deactivate chart types that should not be available for selection</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartType.name || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Chart Type
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
<button class="btn btn-danger" type="button" (click)="onDelete()" style="margin-left: auto;">
|
|
||||||
<cds-icon shape="trash"></cds-icon> Delete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Related Entities Management -->
|
|
||||||
<div class="card" *ngIf="chartType && loadingState !== ClrLoadingState.LOADING">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Related Configuration</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<div class="related-entities-grid">
|
|
||||||
<div class="entity-card">
|
|
||||||
<div class="entity-header">
|
|
||||||
<cds-icon shape="view-list" size="24"></cds-icon>
|
|
||||||
<h4>UI Components</h4>
|
|
||||||
</div>
|
|
||||||
<p>Manage the UI components that define the configuration form for this chart type.</p>
|
|
||||||
<button class="btn btn-sm btn-link" [routerLink]="['/cns-portal/dashboardbuilder/chart-types', chartType.id, 'ui-components']">
|
|
||||||
Manage UI Components
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="entity-card">
|
|
||||||
<div class="entity-header">
|
|
||||||
<cds-icon shape="template" size="24"></cds-icon>
|
|
||||||
<h4>Chart Templates</h4>
|
|
||||||
</div>
|
|
||||||
<p>Manage the templates that define how this chart type is rendered.</p>
|
|
||||||
<button class="btn btn-sm btn-link" [routerLink]="['/cns-portal/dashboardbuilder/chart-types', chartType.id, 'templates']">
|
|
||||||
Manage Templates
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="entity-card">
|
|
||||||
<div class="entity-header">
|
|
||||||
<cds-icon shape="form" size="24"></cds-icon>
|
|
||||||
<h4>Dynamic Fields</h4>
|
|
||||||
</div>
|
|
||||||
<p>Manage the dynamic fields that capture specific configuration parameters.</p>
|
|
||||||
<button class="btn btn-sm btn-link" [routerLink]="['/cns-portal/dashboardbuilder/chart-types', chartType.id, 'fields']">
|
|
||||||
Manage Fields
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Type Details -->
|
|
||||||
<div class="card" *ngIf="chartType && loadingState !== ClrLoadingState.LOADING">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Chart Type Details</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<div class="details-grid">
|
|
||||||
<div class="detail-item">
|
|
||||||
<label>ID:</label>
|
|
||||||
<span>{{ chartType.id }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<label>Created At:</label>
|
|
||||||
<span>{{ chartType.createdAt ? (chartType.createdAt | date:'medium') : 'N/A' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<label>Updated At:</label>
|
|
||||||
<span>{{ chartType.updatedAt ? (chartType.updatedAt | date:'medium') : 'N/A' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Chart Types</h4>
|
|
||||||
<p>Chart types define the different visualization options available in the dashboard builder. Each chart type can have:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Associated UI components that define the configuration form</li>
|
|
||||||
<li>Templates that define how the chart is rendered</li>
|
|
||||||
<li>Dynamic fields that capture specific configuration parameters</li>
|
|
||||||
</ul>
|
|
||||||
<p>After creating a chart type, you can configure its components, templates, and fields using the management links above.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
.edit-chart-type-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-spinner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 20px;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.chart-type-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related-entities-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
|
|
||||||
.entity-card {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
|
|
||||||
.entity-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
cds-icon {
|
|
||||||
color: #0079b8;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
min-height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-link {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
||||||
gap: 15px;
|
|
||||||
|
|
||||||
.detail-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
padding: 8px;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-type-header {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start !important;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.related-entities-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-edit-chart-type',
|
|
||||||
templateUrl: './edit-chart-type.component.html',
|
|
||||||
styleUrls: ['./edit-chart-type.component.scss']
|
|
||||||
})
|
|
||||||
export class EditChartTypeComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const id = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (id) {
|
|
||||||
this.loadChartType(id);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
// Process the data to ensure dates are properly formatted
|
|
||||||
this.chartType = {
|
|
||||||
...data,
|
|
||||||
createdAt: this.formatDate(data.createdAt),
|
|
||||||
updatedAt: this.formatDate(data.updatedAt)
|
|
||||||
};
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format date to handle both string and object formats
|
|
||||||
private formatDate(date: any): string {
|
|
||||||
if (!date) return '';
|
|
||||||
|
|
||||||
// If it's already a string, return as is
|
|
||||||
if (typeof date === 'string') {
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's an object, try to convert to string
|
|
||||||
if (typeof date === 'object') {
|
|
||||||
// Handle various date object formats
|
|
||||||
if (date instanceof Date) {
|
|
||||||
return date.toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle ISO string within object
|
|
||||||
if (date.date) {
|
|
||||||
return date.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle other object formats
|
|
||||||
try {
|
|
||||||
return new Date(date).toISOString();
|
|
||||||
} catch {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartType(): void {
|
|
||||||
if (!this.chartType || !this.chartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.updateChartType(this.chartType.id, this.chartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
// Process the data to ensure dates are properly formatted
|
|
||||||
this.chartType = {
|
|
||||||
...data,
|
|
||||||
createdAt: this.formatDate(data.createdAt),
|
|
||||||
updatedAt: this.formatDate(data.updatedAt)
|
|
||||||
};
|
|
||||||
this.showSuccess('Chart type updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
// Redirect to chart types list after a short delay
|
|
||||||
setTimeout(() => {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}, 1500);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart type:', error);
|
|
||||||
this.showError('Error updating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
|
|
||||||
onDelete(): void {
|
|
||||||
if (!this.chartType) return;
|
|
||||||
|
|
||||||
if (confirm('Are you sure you want to delete this chart type? This action cannot be undone.')) {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.deleteChartType(this.chartType.id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
// Redirect to chart types list after a short delay
|
|
||||||
setTimeout(() => {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}, 1500);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<div class="chart-config-modal">
|
|
||||||
<h3>Chart Configuration Manager</h3>
|
|
||||||
<app-chart-config-manager></app-chart-config-manager>
|
|
||||||
</div>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
.chart-config-modal {
|
|
||||||
padding: 20px;
|
|
||||||
max-height: 80vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-config-modal',
|
|
||||||
templateUrl: './chart-config-modal.component.html',
|
|
||||||
styleUrls: ['./chart-config-modal.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartConfigModalComponent {
|
|
||||||
// This component will be used to display the chart configuration manager in a modal
|
|
||||||
}
|
|
||||||
@@ -393,6 +393,19 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle charts that use UnifiedChartComponent but have different names
|
||||||
|
// After serialization, these charts have component = "Unified Chart" but their name is still "Line Chart", "Pie Chart", etc.
|
||||||
|
const unifiedChartNames = [
|
||||||
|
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||||
|
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||||
|
'Dynamic Chart', 'Financial Chart'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (dashboard.component === "Unified Chart" && unifiedChartNames.includes(dashboard.name)) {
|
||||||
|
// Restore the actual UnifiedChartComponent reference
|
||||||
|
dashboard.component = UnifiedChartComponent;
|
||||||
|
}
|
||||||
|
|
||||||
// Map chart names to unified chart types
|
// Map chart names to unified chart types
|
||||||
const chartTypeMap = {
|
const chartTypeMap = {
|
||||||
'Radar Chart': 'radar',
|
'Radar Chart': 'radar',
|
||||||
@@ -460,6 +473,14 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
// The name is already correct, no need to change it
|
// The name is already correct, no need to change it
|
||||||
dashboard.component = "Unified Chart";
|
dashboard.component = "Unified Chart";
|
||||||
}
|
}
|
||||||
|
// Handle charts that use UnifiedChartComponent but have different names
|
||||||
|
else if (dashboard.component === UnifiedChartComponent && dashboard.chartType) {
|
||||||
|
// Preserve the chartType property for UnifiedChartComponent
|
||||||
|
// The name should already be correct (e.g., "Line Chart", "Pie Chart", etc.)
|
||||||
|
// Instead of changing the component reference, we preserve it and only change the component name for serialization
|
||||||
|
// But we need to ensure the component name is set correctly for proper deserialization
|
||||||
|
dashboard.component = "Unified Chart";
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure compact filter configuration properties are preserved
|
// Ensure compact filter configuration properties are preserved
|
||||||
if (dashboard.name === 'Compact Filter') {
|
if (dashboard.name === 'Compact Filter') {
|
||||||
@@ -1055,7 +1076,14 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For unified chart, preserve chart configuration properties
|
// For unified chart, preserve chart configuration properties
|
||||||
if (item.name === 'Unified Chart') {
|
// Check if this item uses UnifiedChartComponent
|
||||||
|
const unifiedChartNames = [
|
||||||
|
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||||
|
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||||
|
'Dynamic Chart', 'Financial Chart', 'Unified Chart'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (unifiedChartNames.includes(item.name)) {
|
||||||
xyz.chartType = this.gadgetsEditdata.chartType || 'bar';
|
xyz.chartType = this.gadgetsEditdata.chartType || 'bar';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1348,6 +1376,18 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
this.gadgetsEditdata.filterOptions = updatedItem.filterOptions;
|
this.gadgetsEditdata.filterOptions = updatedItem.filterOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For unified chart, preserve chart configuration properties
|
||||||
|
// Check if this item uses UnifiedChartComponent
|
||||||
|
const unifiedChartNames = [
|
||||||
|
'Radar Chart', 'Line Chart', 'Doughnut Chart', 'Bar Chart',
|
||||||
|
'Pie Chart', 'Polar Area Chart', 'Bubble Chart', 'Scatter Chart',
|
||||||
|
'Dynamic Chart', 'Financial Chart', 'Unified Chart'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (unifiedChartNames.includes(item.name)) {
|
||||||
|
updatedItem.chartType = this.gadgetsEditdata.chartType || 'bar';
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Updated item:', updatedItem);
|
console.log('Updated item:', updatedItem);
|
||||||
return updatedItem;
|
return updatedItem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filter toggle icon -->
|
<!-- Filter toggle icon -->
|
||||||
<div class="filter-toggle-icon" *ngIf="hasFilters()" (click)="toggleFilters()">
|
<div class="filter-toggle-icon" *ngIf="baseFilters && baseFilters.length > 0"
|
||||||
|
(click)="toggleFilters()"
|
||||||
|
style="cursor: pointer; text-align: right; padding: 5px;">
|
||||||
<clr-icon shape="filter" size="24"
|
<clr-icon shape="filter" size="24"
|
||||||
[style.color]="showFilters ? '#007cba' : '#666'"
|
[style.color]="showFilters ? '#007cba' : '#666'"
|
||||||
title="Toggle Filters">
|
title="Toggle Filters">
|
||||||
</clr-icon>
|
</clr-icon>
|
||||||
<span>
|
<span style="margin-left: 5px; font-size: 12px; color: #666;">
|
||||||
{{ showFilters ? 'Hide Filters' : 'Show Filters' }}
|
{{ showFilters ? 'Hide Filters' : 'Show Filters' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +35,7 @@
|
|||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'bar'"
|
[type]="'bar'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -46,7 +48,7 @@
|
|||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'line'"
|
[type]="'line'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -59,7 +61,7 @@
|
|||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'pie'"
|
[type]="'pie'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -72,7 +74,7 @@
|
|||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'doughnut'"
|
[type]="'doughnut'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -84,7 +86,7 @@
|
|||||||
[datasets]="bubbleChartData"
|
[datasets]="bubbleChartData"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'bubble'"
|
[type]="'bubble'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -97,7 +99,7 @@
|
|||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'radar'"
|
[type]="'radar'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -105,12 +107,15 @@
|
|||||||
|
|
||||||
<!-- Polar Area Chart -->
|
<!-- Polar Area Chart -->
|
||||||
<div *ngIf="chartType === 'polar'" class="chart-canvas-container">
|
<div *ngIf="chartType === 'polar'" class="chart-canvas-container">
|
||||||
|
<div style="position: absolute; top: 0; left: 0; background: magenta; color: white; padding: 5px; z-index: 1000; font-size: 12px;">
|
||||||
|
POLAR CHART RENDERED (chartType: {{chartType}})
|
||||||
|
</div>
|
||||||
<canvas baseChart
|
<canvas baseChart
|
||||||
[datasets]="chartData"
|
[datasets]="chartData"
|
||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'polarArea'"
|
[type]="'polarArea'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -118,11 +123,14 @@
|
|||||||
|
|
||||||
<!-- Scatter Chart -->
|
<!-- Scatter Chart -->
|
||||||
<div *ngIf="chartType === 'scatter'" class="chart-canvas-container">
|
<div *ngIf="chartType === 'scatter'" class="chart-canvas-container">
|
||||||
|
<div style="position: absolute; top: 0; left: 0; background: brown; color: white; padding: 5px; z-index: 1000; font-size: 12px;">
|
||||||
|
SCATTER CHART RENDERED (chartType: {{chartType}})
|
||||||
|
</div>
|
||||||
<canvas baseChart
|
<canvas baseChart
|
||||||
[datasets]="chartData"
|
[datasets]="chartData"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'scatter'"
|
[type]="'scatter'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
@@ -130,12 +138,15 @@
|
|||||||
|
|
||||||
<!-- Default/Unknown Chart Type -->
|
<!-- Default/Unknown Chart Type -->
|
||||||
<div *ngIf="!['bar', 'line', 'pie', 'doughnut', 'bubble', 'radar', 'polar', 'scatter'].includes(chartType)" class="chart-canvas-container">
|
<div *ngIf="!['bar', 'line', 'pie', 'doughnut', 'bubble', 'radar', 'polar', 'scatter'].includes(chartType)" class="chart-canvas-container">
|
||||||
|
<div style="position: absolute; top: 0; left: 0; background: gray; color: white; padding: 5px; z-index: 1000; font-size: 12px;">
|
||||||
|
DEFAULT CHART RENDERED (chartType: {{chartType}})
|
||||||
|
</div>
|
||||||
<canvas baseChart
|
<canvas baseChart
|
||||||
[datasets]="chartData"
|
[datasets]="chartData"
|
||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="'bar'"
|
[type]="'bar'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { ChartConfiguration, ChartDataset } from 'chart.js';
|
|||||||
styleUrls: ['./unified-chart.component.scss']
|
styleUrls: ['./unified-chart.component.scss']
|
||||||
})
|
})
|
||||||
export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
@Input() chartType: string;
|
@Input() chartType: string = 'bar';
|
||||||
@Input() xAxis: string;
|
@Input() xAxis: string;
|
||||||
@Input() yAxis: string | string[];
|
@Input() yAxis: string | string[];
|
||||||
@Input() table: string;
|
@Input() table: string;
|
||||||
@@ -88,29 +88,16 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Log initial input values for debugging
|
|
||||||
console.log('UnifiedChartComponent ngOnInit - initial input values:', {
|
|
||||||
chartType: this.chartType,
|
|
||||||
xAxis: this.xAxis,
|
|
||||||
yAxis: this.yAxis,
|
|
||||||
table: this.table,
|
|
||||||
baseFilters: this.baseFilters,
|
|
||||||
drilldownFilters: this.drilldownFilters,
|
|
||||||
drilldownLayers: this.drilldownLayers
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if filters are available
|
|
||||||
console.log('Has filters in ngOnInit:', this.hasFilters());
|
|
||||||
|
|
||||||
// Initialize filter values if they haven't been initialized yet
|
// Initialize filter values if they haven't been initialized yet
|
||||||
if (!this.filtersInitialized) {
|
if (!this.filtersInitialized) {
|
||||||
this.initializeFilterValues();
|
this.initializeFilterValues();
|
||||||
this.filtersInitialized = true;
|
this.filtersInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize chart options with default structure
|
// Don't initialize chart options here since inputs might not be set yet
|
||||||
this.initializeChartOptions();
|
// Chart options will be initialized in ngOnChanges when inputs are available
|
||||||
this.fetchChartData();
|
// this.initializeChartOptions();
|
||||||
|
// this.fetchChartData();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
@@ -127,6 +114,11 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
drilldownLayers: this.drilldownLayers
|
drilldownLayers: this.drilldownLayers
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Special logging for chartType changes
|
||||||
|
if (changes.chartType) {
|
||||||
|
console.log('Chart type changed from', changes.chartType.previousValue, 'to', changes.chartType.currentValue);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize filter values if they haven't been initialized yet
|
// Initialize filter values if they haven't been initialized yet
|
||||||
if (!this.filtersInitialized && (changes.baseFilters || changes.drilldownFilters || changes.drilldownLayers)) {
|
if (!this.filtersInitialized && (changes.baseFilters || changes.drilldownFilters || changes.drilldownLayers)) {
|
||||||
this.initializeFilterValues();
|
this.initializeFilterValues();
|
||||||
@@ -380,6 +372,7 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeBarChartOptions(): void {
|
private initializeBarChartOptions(): void {
|
||||||
|
console.log('Initializing bar chart options');
|
||||||
this.chartOptions = {
|
this.chartOptions = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
@@ -462,6 +455,7 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeLineChartOptions(): void {
|
private initializeLineChartOptions(): void {
|
||||||
|
console.log('Initializing line chart options');
|
||||||
this.chartOptions = {
|
this.chartOptions = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
@@ -708,6 +702,14 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
console.log('Starting fetchChartData for chart type:', this.chartType);
|
console.log('Starting fetchChartData for chart type:', this.chartType);
|
||||||
|
|
||||||
|
// Log current state for debugging
|
||||||
|
console.log('Current component state:', {
|
||||||
|
chartType: this.chartType,
|
||||||
|
chartData: this.chartData,
|
||||||
|
chartLabels: this.chartLabels,
|
||||||
|
chartOptions: this.chartOptions
|
||||||
|
});
|
||||||
|
|
||||||
// If we're in drilldown mode, fetch the appropriate drilldown data
|
// If we're in drilldown mode, fetch the appropriate drilldown data
|
||||||
if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) {
|
if (this.currentDrilldownLevel > 0 && this.drilldownStack.length > 0) {
|
||||||
console.log('Fetching drilldown data');
|
console.log('Fetching drilldown data');
|
||||||
@@ -792,13 +794,23 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.xAxis,
|
this.xAxis,
|
||||||
yAxisString,
|
yAxisString,
|
||||||
this.connection,
|
this.connection,
|
||||||
'',
|
|
||||||
'',
|
|
||||||
filterParams
|
filterParams
|
||||||
).subscribe(
|
).subscribe(
|
||||||
(data: any) => {
|
(data: any) => {
|
||||||
console.log('Received chart data:', data);
|
console.log('Received chart data:', data);
|
||||||
|
|
||||||
|
// Log data structure for debugging
|
||||||
|
console.log('Data structure analysis:', {
|
||||||
|
hasChartLabels: !!(data && data.chartLabels),
|
||||||
|
hasChartData: !!(data && data.chartData),
|
||||||
|
hasLabels: !!(data && data.labels),
|
||||||
|
hasDatasets: !!(data && data.datasets),
|
||||||
|
chartLabelsLength: data && data.chartLabels ? data.chartLabels.length : 0,
|
||||||
|
chartDataLength: data && data.chartData ? data.chartData.length : 0,
|
||||||
|
labelsLength: data && data.labels ? data.labels.length : 0,
|
||||||
|
datasetsLength: data && data.datasets ? data.datasets.length : 0
|
||||||
|
});
|
||||||
|
|
||||||
if (data === null || data === undefined) {
|
if (data === null || data === undefined) {
|
||||||
console.warn('Chart API returned null/undefined data.');
|
console.warn('Chart API returned null/undefined data.');
|
||||||
this.noDataAvailable = true;
|
this.noDataAvailable = true;
|
||||||
@@ -829,6 +841,14 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.noDataAvailable = true;
|
this.noDataAvailable = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log final data state
|
||||||
|
console.log('Final data state:', {
|
||||||
|
chartLabels: this.chartLabels,
|
||||||
|
chartData: this.chartData,
|
||||||
|
bubbleChartData: this.bubbleChartData,
|
||||||
|
noDataAvailable: this.noDataAvailable
|
||||||
|
});
|
||||||
|
|
||||||
// Reset flags after fetching
|
// Reset flags after fetching
|
||||||
this.isFetchingData = false;
|
this.isFetchingData = false;
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
@@ -1702,8 +1722,6 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Clear all filters
|
// Clear all filters
|
||||||
clearAllFilters(): void {
|
clearAllFilters(): void {
|
||||||
// Clear base filters
|
// Clear base filters
|
||||||
|
|||||||
@@ -126,7 +126,6 @@ import { OauthComponent } from './builder/dashboardnew/sureconnect/oauth/oauth.c
|
|||||||
import { QueryComponent } from './superadmin/query/query.component';
|
import { QueryComponent } from './superadmin/query/query.component';
|
||||||
import { QueryaddComponent } from './superadmin/queryadd/queryadd.component';
|
import { QueryaddComponent } from './superadmin/queryadd/queryadd.component';
|
||||||
import { QueryeditComponent } from './superadmin/queryedit/queryedit.component';
|
import { QueryeditComponent } from './superadmin/queryedit/queryedit.component';
|
||||||
import { ChartTypePageComponent } from './builder/dashboardnew/chart-type-manager/chart-type-page.component';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -223,7 +222,6 @@ const routes: Routes = [
|
|||||||
{ path: 'editdata/:id', component: EditformnewdashComponent },
|
{ path: 'editdata/:id', component: EditformnewdashComponent },
|
||||||
{ path: 'editdashn/:id', component: EditnewdashComponent },
|
{ path: 'editdashn/:id', component: EditnewdashComponent },
|
||||||
{ path: 'schedule/:id', component: ScheduleComponent },
|
{ path: 'schedule/:id', component: ScheduleComponent },
|
||||||
{ path: 'chart-types', component: ChartTypePageComponent },
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -162,14 +162,6 @@ import { OauthComponent } from './builder/dashboardnew/sureconnect/oauth/oauth.c
|
|||||||
|
|
||||||
// Import Shield Dashboard Module
|
// Import Shield Dashboard Module
|
||||||
import { ShieldDashboardModule } from './builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.module';
|
import { ShieldDashboardModule } from './builder/dashboardnew/gadgets/shield-dashboard/shield-dashboard.module';
|
||||||
// Import UnifiedChartComponent
|
|
||||||
import { UnifiedChartComponent } from './builder/dashboardnew/gadgets/unified-chart/unified-chart.component';
|
|
||||||
// Import ChartConfigManagerComponent
|
|
||||||
import { ChartConfigManagerComponent } from './builder/dashboardnew/chart-config-manager/chart-config-manager.component';
|
|
||||||
// Import ChartTypeManagerComponent
|
|
||||||
import { ChartTypeManagerComponent } from './builder/dashboardnew/chart-type-manager/chart-type-manager.component';
|
|
||||||
// Import ChartTypePageComponent
|
|
||||||
import { ChartTypePageComponent } from './builder/dashboardnew/chart-type-manager/chart-type-page.component';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -181,14 +173,6 @@ import { ChartTypePageComponent } from './builder/dashboardnew/chart-type-manage
|
|||||||
SubmenuComponent, ModulesComponent, SessionloggerComponent,
|
SubmenuComponent, ModulesComponent, SessionloggerComponent,
|
||||||
DashboardnewComponent, EditformnewdashComponent, EditnewdashComponent, ScheduleComponent,
|
DashboardnewComponent, EditformnewdashComponent, EditnewdashComponent, ScheduleComponent,
|
||||||
CommonFilterComponent, ChartWrapperComponent, CompactFilterComponent, DoughnutChartComponent, LineChartComponent, RadarChartComponent, BarChartComponent, BubbleChartComponent, DynamicChartComponent, ScatterChartComponent, PolarChartComponent, PieChartComponent, FinancialChartComponent, ToDoChartComponent, GridViewComponent,
|
CommonFilterComponent, ChartWrapperComponent, CompactFilterComponent, DoughnutChartComponent, LineChartComponent, RadarChartComponent, BarChartComponent, BubbleChartComponent, DynamicChartComponent, ScatterChartComponent, PolarChartComponent, PieChartComponent, FinancialChartComponent, ToDoChartComponent, GridViewComponent,
|
||||||
// Add UnifiedChartComponent to declarations
|
|
||||||
UnifiedChartComponent,
|
|
||||||
// Add ChartConfigManagerComponent to declarations
|
|
||||||
ChartConfigManagerComponent,
|
|
||||||
// Add ChartTypeManagerComponent to declarations
|
|
||||||
ChartTypeManagerComponent,
|
|
||||||
// Add ChartTypePageComponent to declarations
|
|
||||||
ChartTypePageComponent,
|
|
||||||
DashrunnerlineComponent, BarRunnerComponent, LineRunnerComponent, DoughnutRunnerComponent, GridRunnerComponent, PieRunnerComponent, PolarRunnerComponent, RadarRunnerComponent, ScatterRunnerComponent, TodoRunnerComponent, BubbleRunnerComponent,
|
DashrunnerlineComponent, BarRunnerComponent, LineRunnerComponent, DoughnutRunnerComponent, GridRunnerComponent, PieRunnerComponent, PolarRunnerComponent, RadarRunnerComponent, ScatterRunnerComponent, TodoRunnerComponent, BubbleRunnerComponent,
|
||||||
// Add CompactFilterRunnerComponent to declarations
|
// Add CompactFilterRunnerComponent to declarations
|
||||||
CompactFilterRunnerComponent,
|
CompactFilterRunnerComponent,
|
||||||
|
|||||||
Reference in New Issue
Block a user