Commit fec4de97 by Ooh-Ao

config tab

parent ad76d94b
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, OnInit, AfterViewInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
......@@ -7,6 +7,7 @@ import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTabsModule } from '@angular/material/tabs';
@Component({
selector: 'app-base-config',
......@@ -19,11 +20,22 @@ import { MatIconModule } from '@angular/material/icon';
MatSelectModule,
MatCheckboxModule,
MatButtonModule,
MatIconModule
MatIconModule,
MatTabsModule
],
template: `
<div class="base-config">
<ng-content></ng-content>
<mat-tab-group [(selectedIndex)]="selectedTabIndex" (selectedIndexChange)="onTabChange($event)">
<mat-tab *ngFor="let tab of availableTabs" [label]="tab.label">
<ng-template mat-tab-label>
<mat-icon *ngIf="tab.icon" class="tab-icon">{{ tab.icon }}</mat-icon>
{{ tab.label }}
</ng-template>
<div class="tab-content">
<ng-content></ng-content>
</div>
</mat-tab>
</mat-tab-group>
</div>
`,
styles: [`
......@@ -31,6 +43,37 @@ import { MatIconModule } from '@angular/material/icon';
padding: 16px;
}
.tab-content {
padding: 16px 0;
min-height: 400px;
}
.tab-icon {
margin-right: 8px;
font-size: 18px;
width: 18px;
height: 18px;
}
::ng-deep .mat-tab-group {
width: 100%;
}
::ng-deep .mat-tab-header {
border-bottom: 1px solid #e5e7eb;
}
::ng-deep .mat-tab-label {
min-width: 120px;
padding: 0 16px;
}
::ng-deep .mat-tab-label-content {
display: flex;
align-items: center;
gap: 8px;
}
.config-section {
border: 1px solid #e5e7eb;
border-radius: 8px;
......@@ -176,17 +219,132 @@ import { MatIconModule } from '@angular/material/icon';
width: 100%;
}
.config-tabs {
display: flex;
border-bottom: 1px solid #e5e7eb;
margin-bottom: 16px;
overflow-x: auto;
}
.tab-button {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
border: none;
background: none;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.2s;
white-space: nowrap;
font-size: 14px;
font-weight: 500;
color: #6b7280;
}
.tab-button:hover {
background-color: #f3f4f6;
color: #374151;
}
.tab-button.active {
color: #2563eb;
border-bottom-color: #2563eb;
background-color: #eff6ff;
}
.config-content {
min-height: 400px;
}
.size-config {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.size-option {
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 12px;
cursor: pointer;
transition: all 0.2s;
text-align: center;
}
.size-option:hover {
border-color: #2563eb;
background-color: #eff6ff;
}
.size-option.selected {
border-color: #2563eb;
background-color: #eff6ff;
}
.size-option h4 {
margin: 0 0 8px 0;
font-size: 14px;
font-weight: 600;
}
.size-option p {
margin: 0;
font-size: 12px;
color: #6b7280;
}
.column-config-item {
background: #f9fafb;
border: 1px solid #e5e7eb;
}
`]
})
export class BaseConfigComponent {
export class BaseConfigComponent implements OnInit, AfterViewInit {
@Input() currentConfig: any;
@Input() availableColumns: string[] = [];
@Output() configChange = new EventEmitter<any>();
// Tab management
selectedTabIndex: number = 0;
availableTabs: any[] = [
{ id: 'basic', label: 'Basic', icon: 'settings' },
{ id: 'columns', label: 'Columns', icon: 'view_column' },
{ id: 'rows', label: 'Rows', icon: 'view_list' },
{ id: 'style', label: 'Style', icon: 'palette' },
{ id: 'filter', label: 'Filter', icon: 'filter_list' },
{ id: 'aggregate', label: 'Aggregate', icon: 'functions' },
{ id: 'icon', label: 'Icon', icon: 'image' },
{ id: 'trend', label: 'Trend', icon: 'trending_up' },
{ id: 'animation', label: 'Animation', icon: 'animation' },
{ id: 'condition', label: 'Condition', icon: 'rule' }
];
constructor(protected cdr: ChangeDetectorRef) {
// Initialize selectedTabIndex
this.selectedTabIndex = 0;
}
ngOnInit() {
// Ensure selectedTabIndex is set during initialization
this.selectedTabIndex = 0;
}
ngAfterViewInit() {
// Ensure selectedTabIndex is properly set after view initialization
setTimeout(() => {
this.selectedTabIndex = 0;
this.cdr.detectChanges();
}, 0);
}
// Size options
sizeOptions = [
{ id: 'auto', label: 'Auto', description: 'Auto fit content' },
{ id: '100%', label: '100%', description: 'Full width/height' },
{ id: 'custom', label: 'Custom', description: 'Custom dimensions' }
];
// Common methods that can be used by child components
protected updateConfig(key: string, value: any): void {
this.currentConfig[key] = value;
......@@ -207,4 +365,55 @@ export class BaseConfigComponent {
this.configChange.emit(this.currentConfig);
}
}
// Tab management
onTabChange(index: number): void {
this.selectedTabIndex = index;
this.cdr.detectChanges();
}
setActiveTab(tabId: string): void {
const index = this.availableTabs.findIndex(tab => tab.id === tabId);
if (index !== -1) {
this.selectedTabIndex = index;
this.cdr.detectChanges();
}
}
// Size management
setSizeOption(option: string): void {
this.updateConfig('sizeOption', option);
if (option === '100%') {
this.updateConfig('width', '100%');
this.updateConfig('height', '100%');
} else if (option === 'auto') {
this.updateConfig('width', 'auto');
this.updateConfig('height', 'auto');
}
}
// Initialize default config
protected initializeDefaultConfig(): void {
if (!this.currentConfig) {
this.currentConfig = {};
}
// Set default size
if (!this.currentConfig.sizeOption) {
this.currentConfig.sizeOption = 'auto';
}
// Set default dimensions
if (!this.currentConfig.width) {
this.currentConfig.width = 'auto';
}
if (!this.currentConfig.height) {
this.currentConfig.height = 'auto';
}
// Initialize default color values to prevent color format errors
if (!this.currentConfig.backgroundColor) this.currentConfig.backgroundColor = '#FFFFFF';
if (!this.currentConfig.textColor) this.currentConfig.textColor = '#374151';
if (!this.currentConfig.borderColor) this.currentConfig.borderColor = '#E5E7EB';
}
}
......@@ -2,6 +2,11 @@
<mat-dialog-content>
<form>
<!-- Dynamic Config Component Container -->
<div #configContainer></div>
<!-- Fallback Config for widgets without specific config components -->
<div *ngIf="!widgetConfigRegistry.hasConfigComponent(widgetType)">
<div *ngIf="widgetType === 'CompanyInfoWidgetComponent' || widgetType === 'CompanyInfoSubfolderWidgetComponent'">
<mat-form-field appearance="fill">
<mat-label>Title</mat-label>
......@@ -1446,6 +1451,434 @@
</div>
</div>
<div *ngIf="widgetType === 'SyncfusionPivotWidgetComponent'">
<!-- Basic Configuration -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-blue-600">Basic Configuration</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Title</mat-label>
<input matInput [(ngModel)]="currentConfig.title" name="title" aria-label="Widget title">
<mat-hint>Widget title displayed in header</mat-hint>
</mat-form-field>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Display As</mat-label>
<mat-select [(ngModel)]="currentConfig.displayOptionView" name="displayOptionView">
<mat-option value="Both">Both (Grid & Chart)</mat-option>
<mat-option value="Grid">Grid Only</mat-option>
<mat-option value="Chart">Chart Only</mat-option>
</mat-select>
<mat-hint>What to display</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Chart Type</mat-label>
<mat-select [(ngModel)]="currentConfig.chartType" name="chartType">
<mat-option value="Column">Column</mat-option>
<mat-option value="Bar">Bar</mat-option>
<mat-option value="Line">Line</mat-option>
<mat-option value="Area">Area</mat-option>
<mat-option value="Pie">Pie</mat-option>
<mat-option value="Doughnut">Doughnut</mat-option>
</mat-select>
<mat-hint>Chart type when displaying chart</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Pivot Functionality -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-green-600">Pivot Functionality</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showFieldList" name="showFieldList" class="mr-2">
Show Field List
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showToolbar" name="showToolbar" class="mr-2">
Show Toolbar
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showGroupingBar" name="showGroupingBar" class="mr-2">
Show Grouping Bar
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowCalculatedField" name="allowCalculatedField" class="mr-2">
Allow Calculated Fields
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowConditionalFormatting" name="allowConditionalFormatting" class="mr-2">
Allow Conditional Formatting
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowNumberFormatting" name="allowNumberFormatting" class="mr-2">
Allow Number Formatting
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowSubTotal" name="allowSubTotal" class="mr-2">
Allow Sub Total
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowGrandTotal" name="allowGrandTotal" class="mr-2">
Allow Grand Total
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowExcelExport" name="allowExcelExport" class="mr-2">
Allow Excel Export
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowPdfExport" name="allowPdfExport" class="mr-2">
Allow PDF Export
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.allowCsvExport" name="allowCsvExport" class="mr-2">
Allow CSV Export
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showChart" name="showChart" class="mr-2">
Show Chart
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showGrid" name="showGrid" class="mr-2">
Show Grid
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableDrillThrough" name="enableDrillThrough" class="mr-2">
Enable Drill Through
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableSorting" name="enableSorting" class="mr-2">
Enable Sorting
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableFiltering" name="enableFiltering" class="mr-2">
Enable Filtering
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableGrouping" name="enableGrouping" class="mr-2">
Enable Grouping
</mat-checkbox>
</div>
</div>
</div>
<!-- Style Configuration -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-purple-600">Style & Colors</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Background Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.backgroundColor" name="backgroundColor" class="h-[40px]" aria-label="Background color">
<mat-hint>Widget background color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Header Background Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.headerBackgroundColor" name="headerBackgroundColor" class="h-[40px]" aria-label="Header background color">
<mat-hint>Header background color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Text Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.textColor" name="textColor" class="h-[40px]" aria-label="Text color">
<mat-hint>Text color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Header Text Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.headerTextColor" name="headerTextColor" class="h-[40px]" aria-label="Header text color">
<mat-hint>Header text color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Border Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor" class="h-[40px]" aria-label="Border color">
<mat-hint>Widget border color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Accent Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.accentColor" name="accentColor" class="h-[40px]" aria-label="Accent color">
<mat-hint>Selection and hover color</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<mat-form-field appearance="fill">
<mat-label>Border Radius (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="20" aria-label="Border radius">
<mat-hint>Widget border radius</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Padding (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.padding" name="padding" min="0" max="50" aria-label="Padding">
<mat-hint>Internal spacing</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.fontSize" name="fontSize" min="10" max="20" aria-label="Font size">
<mat-hint>Font size for content</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Font Weight</mat-label>
<mat-select [(ngModel)]="currentConfig.fontWeight" name="fontWeight">
<mat-option value="normal">Normal</mat-option>
<mat-option value="bold">Bold</mat-option>
<mat-option value="lighter">Lighter</mat-option>
<mat-option value="bolder">Bolder</mat-option>
</mat-select>
<mat-hint>Font weight</mat-hint>
</mat-form-field>
</div>
<!-- Animation Configuration -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-orange-600">Animation Settings</h3>
<div class="flex items-center mb-3">
<mat-checkbox [(ngModel)]="currentConfig.enableAnimations" name="enableAnimations" class="mr-2">
Enable Animations
</mat-checkbox>
</div>
<div *ngIf="currentConfig.enableAnimations" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Animation Type</mat-label>
<mat-select [(ngModel)]="currentConfig.animationType" name="animationType">
<mat-option value="fade">Fade</mat-option>
<mat-option value="slide">Slide</mat-option>
<mat-option value="bounce">Bounce</mat-option>
<mat-option value="pulse">Pulse</mat-option>
<mat-option value="none">None</mat-option>
</mat-select>
<mat-hint>Animation effect type</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Animation Duration (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.animationDuration" name="animationDuration" min="100" max="2000" step="100" aria-label="Animation duration">
<mat-hint>Duration of animation</mat-hint>
</mat-form-field>
</div>
<div *ngIf="currentConfig.enableAnimations" class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.hoverEffects" name="hoverEffects" class="mr-2">
Enable Hover Effects
</mat-checkbox>
</div>
</div>
<!-- Interaction Settings -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-indigo-600">Interaction Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableTooltip" name="enableTooltip" class="mr-2">
Enable Tooltips
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableClick" name="enableClick" class="mr-2">
Enable Click Events
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableHover" name="enableHover" class="mr-2">
Enable Hover Effects
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableExport" name="enableExport" class="mr-2">
Enable Export
</mat-checkbox>
</div>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Click Action</mat-label>
<mat-select [(ngModel)]="currentConfig.clickAction" name="clickAction">
<mat-option value="none">None</mat-option>
<mat-option value="drill_down">Drill Down</mat-option>
<mat-option value="open_modal">Open Modal</mat-option>
<mat-option value="navigate">Navigate</mat-option>
<mat-option value="custom">Custom</mat-option>
</mat-select>
<mat-hint>Action when widget is clicked</mat-hint>
</mat-form-field>
<mat-form-field *ngIf="currentConfig.clickAction === 'custom'" appearance="fill" class="w-full">
<mat-label>Custom Click Handler</mat-label>
<textarea matInput [(ngModel)]="currentConfig.customClickHandler" name="customClickHandler" rows="3"
placeholder="function(event) {&#10; console.log('Widget clicked:', event);&#10;}"></textarea>
<mat-hint>Custom JavaScript function for click events</mat-hint>
</mat-form-field>
</div>
<!-- Layout Configuration -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-teal-600">Layout Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.width" name="width" min="600" max="1400" aria-label="Widget width">
<mat-hint>Widget width</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.height" name="height" min="400" max="1000" aria-label="Widget height">
<mat-hint>Widget height</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.responsive" name="responsive" class="mr-2">
Responsive Layout
</mat-checkbox>
</div>
</div>
<!-- Data Configuration -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-red-600">Data Settings</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Source</mat-label>
<mat-select [(ngModel)]="currentConfig.dataSource" name="dataSource">
<mat-option value="static">Static Data</mat-option>
<mat-option value="api">API Endpoint</mat-option>
<mat-option value="websocket">WebSocket</mat-option>
<mat-option value="file">File Upload</mat-option>
</mat-select>
<mat-hint>Data source type</mat-hint>
</mat-form-field>
<mat-form-field *ngIf="currentConfig.dataSource === 'api'" appearance="fill" class="w-full">
<mat-label>API Endpoint</mat-label>
<input matInput [(ngModel)]="currentConfig.apiEndpoint" name="apiEndpoint" placeholder="/api/pivot-data" aria-label="API endpoint">
<mat-hint>API endpoint URL</mat-hint>
</mat-form-field>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Refresh Interval (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.refreshInterval" name="refreshInterval" min="0" step="1000" aria-label="Refresh interval">
<mat-hint>0 = No auto-refresh</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.cacheEnabled" name="cacheEnabled" class="mr-2">
Enable Data Caching
</mat-checkbox>
</div>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Transform Function</mat-label>
<textarea matInput [(ngModel)]="currentConfig.dataTransform" name="dataTransform" rows="3"
placeholder="data => data.map(item => ({ ...item, formattedValue: formatCurrency(item.value) }))"></textarea>
<mat-hint>JavaScript function to transform data</mat-hint>
</mat-form-field>
</div>
<!-- Security Configuration -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-gray-600">Security Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.requireAuth" name="requireAuth" class="mr-2">
Require Authentication
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.dataEncryption" name="dataEncryption" class="mr-2">
Enable Data Encryption
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.auditLog" name="auditLog" class="mr-2">
Enable Audit Logging
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Rate Limit (requests/min)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.rateLimit" name="rateLimit" min="0" aria-label="Rate limit">
<mat-hint>0 = No limit</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Allowed Roles (comma-separated)</mat-label>
<input matInput [(ngModel)]="currentConfig.allowedRoles" name="allowedRoles" placeholder="admin, analyst, manager" aria-label="Allowed roles">
<mat-hint>Roles that can access this widget</mat-hint>
</mat-form-field>
</div>
<!-- Custom CSS -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-purple-600">Custom Styling</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Custom CSS</mat-label>
<textarea matInput [(ngModel)]="currentConfig.customCSS" name="customCSS" rows="4"
placeholder="/* Add custom CSS rules here */&#10;.syncfusion-pivot-widget:hover {&#10; transform: translateY(-4px);&#10;}"></textarea>
<mat-hint>Add custom CSS for advanced styling</mat-hint>
</mat-form-field>
</div>
</div>
<!-- End Fallback Config -->
</div>
</form>
</mat-dialog-content>
......
......@@ -201,7 +201,7 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
`
})
export class AttendanceConfigComponent extends BaseConfigComponent implements OnInit {
ngOnInit(): void {
override ngOnInit(): void {
// Initialize default values for attendance widget
if (!this.currentConfig.layout) this.currentConfig.layout = 'cards';
if (!this.currentConfig.chartType) this.currentConfig.chartType = 'pie';
......
......@@ -7,6 +7,7 @@ import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTabsModule } from '@angular/material/tabs';
import { BaseConfigComponent } from '../../../widget-config/base-config/base-config.component';
@Component({
......@@ -21,17 +22,18 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
MatCheckboxModule,
MatButtonModule,
MatIconModule,
MatTabsModule,
BaseConfigComponent
],
template: `
<app-base-config>
<!-- Basic Configuration -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-blue-600">Basic Configuration</h3>
<!-- Basic Configuration Tab -->
<div class="config-section">
<h3 class="text-blue-600">Basic Configuration</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Title</mat-label>
<input matInput [(ngModel)]="currentConfig.title" name="title" aria-label="Widget title">
<input matInput [(ngModel)]="currentConfig.title" name="title" aria-label="Chart title">
<mat-hint>Chart title displayed in header</mat-hint>
</mat-form-field>
......@@ -50,11 +52,13 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<mat-option value="bar">Bar Chart</mat-option>
<mat-option value="line">Line Chart</mat-option>
<mat-option value="pie">Pie Chart</mat-option>
<mat-option value="doughnut">Doughnut Chart</mat-option>
<mat-option value="area">Area Chart</mat-option>
<mat-option value="doughnut">Doughnut Chart</mat-option>
<mat-option value="scatter">Scatter Chart</mat-option>
<mat-option value="bubble">Bubble Chart</mat-option>
<mat-option value="funnel">Funnel Chart</mat-option>
<mat-option value="waterfall">Waterfall Chart</mat-option>
<mat-option value="combo">Combo Chart</mat-option>
<mat-option value="gauge">Gauge Chart</mat-option>
<mat-option value="treemap">Treemap</mat-option>
</mat-select>
......@@ -62,168 +66,371 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
</mat-form-field>
</div>
<!-- Size Configuration -->
<div class="config-section">
<h3 class="text-blue-600">Size Configuration</h3>
<div class="size-config">
<div
*ngFor="let option of sizeOptions"
class="size-option"
[class.selected]="currentConfig.sizeOption === option.id"
(click)="setSizeOption(option.id)">
<h4>{{ option.label }}</h4>
<p>{{ option.description }}</p>
</div>
</div>
<div *ngIf="currentConfig.sizeOption === 'custom'" class="grid grid-cols-2 gap-4 mt-4">
<mat-form-field appearance="fill">
<mat-label>Width</mat-label>
<input matInput [(ngModel)]="currentConfig.width" name="width" placeholder="e.g., 100%, 600px">
<mat-hint>Chart width</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height</mat-label>
<input matInput [(ngModel)]="currentConfig.height" name="height" placeholder="e.g., 100%, 400px">
<mat-hint>Chart height</mat-hint>
</mat-form-field>
</div>
</div>
</div>
<!-- Columns Tab -->
<div class="config-section">
<h3 class="text-blue-600">Data Series Configuration</h3>
<div class="flex items-center justify-between mb-4">
<h4>Y-Axis Fields</h4>
<button mat-raised-button color="primary" (click)="addYField()">
<mat-icon>add</mat-icon>
Add Series
</button>
</div>
<div *ngFor="let field of currentConfig.yFields; let i = index" class="column-config-item p-4 mb-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<mat-form-field appearance="fill">
<mat-label>Field Name</mat-label>
<mat-select [(ngModel)]="field.field" name="yField{{i}}">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Series Name</mat-label>
<input matInput [(ngModel)]="field.name" name="yFieldName{{i}}" placeholder="Series display name">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Chart Type</mat-label>
<mat-select [(ngModel)]="field.type" name="yFieldType{{i}}">
<mat-option value="bar">Bar</mat-option>
<mat-option value="line">Line</mat-option>
<mat-option value="area">Area</mat-option>
<mat-option value="scatter">Scatter</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Color</mat-label>
<input matInput type="color" [(ngModel)]="field.color" name="yFieldColor{{i}}">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Y-Axis</mat-label>
<mat-select [(ngModel)]="field.yAxis" name="yFieldAxis{{i}}">
<mat-option value="primary">Primary</mat-option>
<mat-option value="secondary">Secondary</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="flex items-center justify-between mt-4">
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="field.visible" name="yFieldVisible{{i}}">
Visible
</mat-checkbox>
<mat-checkbox [(ngModel)]="field.showDataLabels" name="yFieldDataLabels{{i}}">
Show Data Labels
</mat-checkbox>
</div>
<button mat-icon-button color="warn" (click)="removeYField(i)">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</div>
<!-- Rows Tab -->
<div class="config-section">
<h3 class="text-blue-600">Data Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>X-Axis Title</mat-label>
<input matInput [(ngModel)]="currentConfig.xAxisTitle" name="xAxisTitle">
<mat-hint>X-axis label</mat-hint>
<mat-label>Data Limit</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.dataLimit" name="dataLimit" min="10" max="1000">
<mat-hint>Maximum number of data points</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Y-Axis Title</mat-label>
<input matInput [(ngModel)]="currentConfig.yAxisTitle" name="yAxisTitle">
<mat-hint>Y-axis label</mat-hint>
<mat-label>Data Sort Field</mat-label>
<mat-select [(ngModel)]="currentConfig.dataSortField" name="dataSortField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field to sort data by</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Y-Axis Fields -->
<div class="config-section border p-3 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-2">Y-Axis Fields</h3>
<div *ngFor="let yField of currentConfig.yFields; let i = index" class="flex items-center gap-2 mb-2">
<mat-form-field appearance="fill" class="flex-grow">
<mat-label>Field {{i + 1}}</mat-label>
<mat-select [(ngModel)]="currentConfig.yFields[i]" [name]="'yfield_' + i">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Sort Order</mat-label>
<mat-select [(ngModel)]="currentConfig.dataSortOrder" name="dataSortOrder">
<mat-option value="asc">Ascending</mat-option>
<mat-option value="desc">Descending</mat-option>
</mat-select>
<mat-hint>Sort order for data</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Group By Field</mat-label>
<mat-select [(ngModel)]="currentConfig.groupByField" name="groupByField">
<mat-option value="">None</mat-option>
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field to group data by</mat-hint>
</mat-form-field>
<button mat-icon-button color="warn" (click)="removeChartYField(i)" type="button"><mat-icon>delete</mat-icon></button>
</div>
<button mat-stroked-button color="primary" (click)="addChartYField()" type="button"><mat-icon>add</mat-icon> Add Y-Axis Field</button>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.showDataTable" name="showDataTable">
Show Data Table
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.enableDataZoom" name="enableDataZoom">
Enable Data Zoom
</mat-checkbox>
</div>
</div>
<!-- Chart Options -->
<div class="config-section border p-4 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-3 text-green-600">Chart Options</h3>
<!-- Style Tab -->
<div class="config-section">
<h3 class="text-blue-600">Style Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showLegend" name="showLegend" class="mr-2">
Show Legend
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Background Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.backgroundColor" name="backgroundColor">
<mat-hint>Chart background color</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showDataLabels" name="showDataLabels" class="mr-2">
Show Data Labels
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Text Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.textColor" name="textColor">
<mat-hint>Text color</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showTooltip" name="showTooltip" class="mr-2">
Show Tooltip
</mat-checkbox>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Grid Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.gridColor" name="gridColor">
<mat-hint>Grid line color</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableAnimation" name="enableAnimation" class="mr-2">
Enable Animation
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Border Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor">
<mat-hint>Chart border color</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.fontSize" name="fontSize" min="8" max="24">
<mat-hint>Base font size</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Legend Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.legendFontSize" name="legendFontSize" min="8" max="20">
<mat-hint>Legend font size</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Aggregation</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregation" name="aggregation">
<mat-label>Border Radius (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="20">
<mat-hint>Corner roundness</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Shadow</mat-label>
<mat-select [(ngModel)]="currentConfig.shadow" name="shadow">
<mat-option value="none">None</mat-option>
<mat-option value="count">Count</mat-option>
<mat-option value="small">Small</mat-option>
<mat-option value="medium">Medium</mat-option>
<mat-option value="large">Large</mat-option>
</mat-select>
<mat-hint>Drop shadow effect</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Filter Tab -->
<div class="config-section">
<h3 class="text-blue-600">Filter Configuration</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableFilter" name="enableFilter">
Enable Filtering
</mat-checkbox>
</div>
<div *ngIf="currentConfig.enableFilter" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Filter Field</mat-label>
<mat-select [(ngModel)]="currentConfig.filterField" name="filterField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field to filter by</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Filter Operator</mat-label>
<mat-select [(ngModel)]="currentConfig.filterOperator" name="filterOperator">
<mat-option value="equals">Equals</mat-option>
<mat-option value="not_equals">Not Equals</mat-option>
<mat-option value="greater_than">Greater Than</mat-option>
<mat-option value="less_than">Less Than</mat-option>
<mat-option value="contains">Contains</mat-option>
<mat-option value="starts_with">Starts With</mat-option>
<mat-option value="ends_with">Ends With</mat-option>
</mat-select>
<mat-hint>Filter comparison operator</mat-hint>
</mat-form-field>
</div>
<div *ngIf="currentConfig.enableFilter" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Filter Value</mat-label>
<input matInput [(ngModel)]="currentConfig.filterValue" name="filterValue" placeholder="Enter filter value">
<mat-hint>Value to filter by</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Filter Label</mat-label>
<input matInput [(ngModel)]="currentConfig.filterLabel" name="filterLabel" placeholder="Filter label">
<mat-hint>Label for the filter</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Aggregate Tab -->
<div class="config-section">
<h3 class="text-blue-600">Aggregate Configuration</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.showAggregate" name="showAggregate">
Show Aggregate Information
</mat-checkbox>
</div>
<div *ngIf="currentConfig.showAggregate" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Aggregate Type</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregateType" name="aggregateType">
<mat-option value="sum">Sum</mat-option>
<mat-option value="count">Count</mat-option>
<mat-option value="average">Average</mat-option>
<mat-option value="max">Maximum</mat-option>
<mat-option value="min">Minimum</mat-option>
<mat-option value="min">Min</mat-option>
<mat-option value="max">Max</mat-option>
</mat-select>
<mat-hint>How to aggregate data</mat-hint>
<mat-hint>Type of aggregation</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Size Field (for Bubble Chart)</mat-label>
<mat-select [(ngModel)]="currentConfig.sizeField" name="sizeField">
<mat-option value="">None</mat-option>
<mat-label>Aggregate Field</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregateField" name="aggregateField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field for bubble size (Bubble Chart only)</mat-hint>
<mat-hint>Field to aggregate</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Series Configuration (for Combo Chart) -->
<div *ngIf="currentConfig.chartType === 'combo'" class="config-section border p-3 rounded-lg mb-4">
<h3 class="text-lg font-semibold mb-2">Series Configuration</h3>
<div *ngFor="let series of currentConfig.series; let i = index" class="series-item p-3 mb-3 border rounded-md">
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4">
<mat-form-field appearance="fill">
<mat-label>Series Name</mat-label>
<input matInput [(ngModel)]="series.name" [name]="'series_name_' + i">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Chart Type</mat-label>
<mat-select [(ngModel)]="series.type" [name]="'series_type_' + i">
<mat-option value="Line">Line</mat-option>
<mat-option value="Column">Column</mat-option>
<mat-option value="Area">Area</mat-option>
<mat-option value="Spline">Spline</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>X-Axis Name</mat-label>
<mat-select [(ngModel)]="series.xName" [name]="'series_xName_' + i">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Y-Axis Name</mat-label>
<mat-select [(ngModel)]="series.yName" [name]="'series_yName_' + i">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="flex justify-end">
<button mat-icon-button color="warn" (click)="removeComboSeries(i)" type="button"><mat-icon>delete</mat-icon></button>
</div>
<div *ngIf="currentConfig.showAggregate" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Aggregate Format</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregateFormat" name="aggregateFormat">
<mat-option value="none">None</mat-option>
<mat-option value="number">Number</mat-option>
<mat-option value="currency">Currency</mat-option>
<mat-option value="percentage">Percentage</mat-option>
</mat-select>
<mat-hint>Format for aggregate value</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Aggregate Position</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregatePosition" name="aggregatePosition">
<mat-option value="top">Top</mat-option>
<mat-option value="bottom">Bottom</mat-option>
<mat-option value="left">Left</mat-option>
<mat-option value="right">Right</mat-option>
</mat-select>
<mat-hint>Position of aggregate info</mat-hint>
</mat-form-field>
</div>
<button mat-stroked-button color="primary" (click)="addComboSeries()" type="button"><mat-icon>add</mat-icon> Add Series</button>
</div>
</app-base-config>
`
`,
styles: [`
.config-section {
margin-bottom: 24px;
}
.column-config-item {
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
`]
})
export class ChartConfigComponent extends BaseConfigComponent implements OnInit {
ngOnInit(): void {
// Initialize default values for chart widget
if (!this.currentConfig.yFields) this.currentConfig.yFields = [];
if (!this.currentConfig.chartType) this.currentConfig.chartType = 'bar';
if (!this.currentConfig.aggregation) this.currentConfig.aggregation = 'none';
if (this.currentConfig.showLegend === undefined) this.currentConfig.showLegend = true;
if (this.currentConfig.showDataLabels === undefined) this.currentConfig.showDataLabels = false;
if (this.currentConfig.showTooltip === undefined) this.currentConfig.showTooltip = true;
if (this.currentConfig.enableAnimation === undefined) this.currentConfig.enableAnimation = true;
// Initialize series for combo chart
if (!this.currentConfig.series) this.currentConfig.series = [];
override ngOnInit() {
this.initializeDefaultConfig();
this.initializeYFields();
this.initializeColorDefaults();
}
addChartYField() {
this.currentConfig.yFields.push('');
this.configChange.emit(this.currentConfig);
private initializeColorDefaults() {
if (!this.currentConfig.backgroundColor) this.currentConfig.backgroundColor = '#FFFFFF';
if (!this.currentConfig.textColor) this.currentConfig.textColor = '#374151';
if (!this.currentConfig.gridColor) this.currentConfig.gridColor = '#E5E7EB';
if (!this.currentConfig.borderColor) this.currentConfig.borderColor = '#E5E7EB';
}
removeChartYField(index: number) {
this.currentConfig.yFields.splice(index, 1);
this.configChange.emit(this.currentConfig);
private initializeYFields() {
if (!this.currentConfig.yFields) {
this.currentConfig.yFields = [];
}
}
addComboSeries() {
this.currentConfig.series.push({
addYField() {
this.addArrayItem('yFields', {
field: '',
name: '',
type: 'Column',
xName: '',
yName: ''
type: 'bar',
color: '#2563eb',
yAxis: 'primary',
visible: true,
showDataLabels: false
});
this.configChange.emit(this.currentConfig);
}
removeComboSeries(index: number) {
this.currentConfig.series.splice(index, 1);
this.configChange.emit(this.currentConfig);
removeYField(index: number) {
this.removeArrayItem('yFields', index);
}
}
......@@ -209,7 +209,7 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
`
})
export class CompanyInfoConfigComponent extends BaseConfigComponent implements OnInit {
ngOnInit(): void {
override ngOnInit(): void {
// Initialize default values for company info widget
if (!this.currentConfig.layout) this.currentConfig.layout = 'horizontal';
if (this.currentConfig.showContactInfo === undefined) this.currentConfig.showContactInfo = true;
......
......@@ -254,7 +254,7 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
`
})
export class PayrollConfigComponent extends BaseConfigComponent implements OnInit {
ngOnInit(): void {
override ngOnInit(): void {
// Initialize default values for payroll widget
if (!this.currentConfig.layout) this.currentConfig.layout = 'summary';
if (!this.currentConfig.chartType) this.currentConfig.chartType = 'bar';
......
......@@ -5,6 +5,9 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTabsModule } from '@angular/material/tabs';
import { BaseConfigComponent } from '../../../widget-config/base-config/base-config.component';
@Component({
......@@ -17,11 +20,14 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
MatInputModule,
MatSelectModule,
MatCheckboxModule,
MatButtonModule,
MatIconModule,
MatTabsModule,
BaseConfigComponent
],
template: `
<app-base-config>
<!-- Basic Configuration -->
<!-- Basic Configuration Tab -->
<div class="config-section">
<h3 class="text-blue-600">Basic Configuration</h3>
......@@ -51,462 +57,487 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Value Format</mat-label>
<mat-select [(ngModel)]="currentConfig.valueFormat" name="valueFormat">
<mat-option value="number">Number</mat-option>
<mat-option value="currency">Currency</mat-option>
<mat-option value="percentage">Percentage</mat-option>
<mat-option value="decimal">Decimal</mat-option>
</mat-select>
<mat-hint>Format for displaying the value</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Decimal Places</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.decimalPlaces" name="decimalPlaces" min="0" max="10">
<mat-hint>Number of decimal places to show</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Aggregation</mat-label>
<mat-select [(ngModel)]="currentConfig.aggregation" name="aggregation">
<mat-option value="count">Count</mat-option>
<mat-option value="sum">Sum</mat-option>
<mat-option value="average">Average</mat-option>
<mat-option value="max">Maximum</mat-option>
<mat-option value="min">Minimum</mat-option>
<mat-option value="count">Count</mat-option>
<mat-option value="min">Min</mat-option>
<mat-option value="max">Max</mat-option>
<mat-option value="first">First</mat-option>
<mat-option value="last">Last</mat-option>
</mat-select>
<mat-hint>How to aggregate the data</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Unit</mat-label>
<input matInput [(ngModel)]="currentConfig.unit" name="unit" placeholder="e.g., $, %, items" aria-label="Unit">
<mat-hint>Unit to display after the value</mat-hint>
<input matInput [(ngModel)]="currentConfig.unit" name="unit" placeholder="e.g., %, $, items">
<mat-hint>Unit to display with the value</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Icon (Bootstrap Icons)</mat-label>
<input matInput [(ngModel)]="currentConfig.icon" name="icon" placeholder="e.g., person-fill, building" aria-label="Icon">
<mat-hint>Find icons at icons.getbootstrap.com</mat-hint>
<mat-label>Data Source</mat-label>
<mat-select [(ngModel)]="currentConfig.dataSource" name="dataSource">
<mat-option value="static">Static Data</mat-option>
<mat-option value="api">API Endpoint</mat-option>
<mat-option value="dataset">Dataset</mat-option>
</mat-select>
<mat-hint>Source of the data</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Decimal Places</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.decimalPlaces" name="decimalPlaces" min="0" max="10" aria-label="Decimal places">
<mat-hint>Number of decimal places to show</mat-hint>
<mat-form-field appearance="fill" *ngIf="currentConfig.dataSource === 'api'">
<mat-label>API Endpoint</mat-label>
<input matInput [(ngModel)]="currentConfig.apiEndpoint" name="apiEndpoint" placeholder="https://api.example.com/data">
<mat-hint>API endpoint URL</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Trend Configuration -->
<div class="config-section">
<h3 class="text-green-600">Trend Settings</h3>
<div class="flex items-center mb-3">
<mat-checkbox [(ngModel)]="currentConfig.showTrend" name="showTrend" class="mr-2">
Show Trend Indicator
</mat-checkbox>
</div>
<div *ngIf="currentConfig.showTrend" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Trend Field</mat-label>
<mat-select [(ngModel)]="currentConfig.trendField" name="trendField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field containing trend data</mat-hint>
</mat-form-field>
<!-- Size Configuration -->
<div class="config-section">
<h3 class="text-blue-600">Size Configuration</h3>
<div class="size-config">
<div
*ngFor="let option of sizeOptions"
class="size-option"
[class.selected]="currentConfig.sizeOption === option.id"
(click)="setSizeOption(option.id)">
<h4>{{ option.label }}</h4>
<p>{{ option.description }}</p>
</div>
</div>
<mat-form-field appearance="fill">
<mat-label>Trend Type</mat-label>
<mat-select [(ngModel)]="currentConfig.trendType" name="trendType">
<mat-option value="percentage">Percentage Change</mat-option>
<mat-option value="absolute">Absolute Change</mat-option>
<mat-option value="ratio">Ratio</mat-option>
</mat-select>
<mat-hint>How to calculate trend</mat-hint>
</mat-form-field>
<div *ngIf="currentConfig.sizeOption === 'custom'" class="grid grid-cols-2 gap-4 mt-4">
<mat-form-field appearance="fill">
<mat-label>Width</mat-label>
<input matInput [(ngModel)]="currentConfig.width" name="width" placeholder="e.g., 300px, 50%, auto">
<mat-hint>Widget width</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height</mat-label>
<input matInput [(ngModel)]="currentConfig.height" name="height" placeholder="e.g., 200px, 50%, auto">
<mat-hint>Widget height</mat-hint>
</mat-form-field>
</div>
</div>
</div>
<!-- Style Configuration -->
<!-- Style Tab -->
<div class="config-section">
<h3 class="text-purple-600">Style & Colors</h3>
<h3 class="text-blue-600">Style Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Background Color</mat-label>
<input matInput [(ngModel)]="currentConfig.backgroundColor" name="backgroundColor" placeholder="#FF0000 or linear-gradient(...)" aria-label="Background color">
<mat-hint>Header background color</mat-hint>
<input matInput type="color" [(ngModel)]="currentConfig.backgroundColor" name="backgroundColor">
<mat-hint>Widget background color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Text Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.textColor" name="textColor" class="h-[40px]" aria-label="Text color">
<mat-hint>Text color for labels</mat-hint>
<input matInput type="color" [(ngModel)]="currentConfig.textColor" name="textColor">
<mat-hint>Text color</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Accent Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.accentColor" name="accentColor" class="h-[40px]" aria-label="Accent color">
<mat-hint>Color for the main KPI value</mat-hint>
<mat-label>Value Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.valueColor" name="valueColor">
<mat-hint>KPI value color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Border Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor" class="h-[40px]" aria-label="Border color">
<mat-hint>Card border color</mat-hint>
<mat-label>Label Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.labelColor" name="labelColor">
<mat-hint>KPI label color</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Icon Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.iconColor" name="iconColor" class="h-[40px]" aria-label="Icon color">
<mat-hint>Icon color</mat-hint>
<mat-label>Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.fontSize" name="fontSize" min="10" max="48">
<mat-hint>Base font size</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Border Radius (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="50" aria-label="Border radius">
<mat-hint>Roundness of widget corners</mat-hint>
<mat-label>Value Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.valueFontSize" name="valueFontSize" min="10" max="72">
<mat-hint>KPI value font size</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Padding (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.padding" name="padding" min="0" max="100" aria-label="Padding">
<mat-hint>Internal spacing</mat-hint>
<mat-label>Border Radius (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="20">
<mat-hint>Corner roundness</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.fontSize" name="fontSize" min="10" max="48" aria-label="Font size">
<mat-hint>Font size for values</mat-hint>
<mat-label>Border Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderWidth" name="borderWidth" min="0" max="10">
<mat-hint>Border thickness</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Border Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor">
<mat-hint>Border color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Font Weight</mat-label>
<mat-select [(ngModel)]="currentConfig.fontWeight" name="fontWeight">
<mat-option value="normal">Normal</mat-option>
<mat-option value="bold">Bold</mat-option>
<mat-option value="lighter">Lighter</mat-option>
<mat-option value="bolder">Bolder</mat-option>
<mat-label>Shadow</mat-label>
<mat-select [(ngModel)]="currentConfig.shadow" name="shadow">
<mat-option value="none">None</mat-option>
<mat-option value="small">Small</mat-option>
<mat-option value="medium">Medium</mat-option>
<mat-option value="large">Large</mat-option>
</mat-select>
<mat-hint>Font weight</mat-hint>
<mat-hint>Drop shadow effect</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Font Family</mat-label>
<mat-select [(ngModel)]="currentConfig.fontFamily" name="fontFamily">
<mat-option value="system-ui, -apple-system, sans-serif">System Font</mat-option>
<mat-option value="Arial, sans-serif">Arial</mat-option>
<mat-option value="Helvetica, sans-serif">Helvetica</mat-option>
<mat-option value="Georgia, serif">Georgia</mat-option>
<mat-option value="Times New Roman, serif">Times New Roman</mat-option>
<mat-option value="Courier New, monospace">Courier New</mat-option>
</mat-select>
<mat-hint>Font family for the widget</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Custom CSS</mat-label>
<textarea matInput [(ngModel)]="currentConfig.customCSS" name="customCSS" rows="4"
placeholder="/* Add custom CSS rules here */&#10;.simple-kpi-widget:hover {&#10; transform: translateY(-4px);&#10;}"></textarea>
<mat-hint>Add custom CSS for advanced styling</mat-hint>
</mat-form-field>
</div>
<!-- Animation Configuration -->
<!-- Icon Tab -->
<div class="config-section">
<h3 class="text-orange-600">Animation Settings</h3>
<h3 class="text-blue-600">Icon Configuration</h3>
<div class="flex items-center mb-3">
<mat-checkbox [(ngModel)]="currentConfig.enableAnimations" name="enableAnimations" class="mr-2">
Enable Animations
</mat-checkbox>
</div>
<div *ngIf="currentConfig.enableAnimations" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Animation Type</mat-label>
<mat-select [(ngModel)]="currentConfig.animationType" name="animationType">
<mat-option value="fade">Fade</mat-option>
<mat-option value="slide">Slide</mat-option>
<mat-option value="bounce">Bounce</mat-option>
<mat-option value="pulse">Pulse</mat-option>
<mat-label>Icon</mat-label>
<mat-select [(ngModel)]="currentConfig.icon" name="icon">
<mat-option value="none">None</mat-option>
<mat-option value="trending_up">Trending Up</mat-option>
<mat-option value="trending_down">Trending Down</mat-option>
<mat-option value="show_chart">Chart</mat-option>
<mat-option value="assessment">Assessment</mat-option>
<mat-option value="analytics">Analytics</mat-option>
<mat-option value="bar_chart">Bar Chart</mat-option>
<mat-option value="pie_chart">Pie Chart</mat-option>
<mat-option value="donut_large">Donut</mat-option>
<mat-option value="account_balance">Account Balance</mat-option>
<mat-option value="attach_money">Money</mat-option>
<mat-option value="people">People</mat-option>
<mat-option value="business">Business</mat-option>
<mat-option value="home">Home</mat-option>
<mat-option value="star">Star</mat-option>
<mat-option value="favorite">Favorite</mat-option>
</mat-select>
<mat-hint>Animation effect type</mat-hint>
<mat-hint>Icon to display with KPI</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Animation Duration (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.animationDuration" name="animationDuration" min="100" max="2000" step="100" aria-label="Animation duration">
<mat-hint>Duration of animation</mat-hint>
<mat-label>Icon Position</mat-label>
<mat-select [(ngModel)]="currentConfig.iconPosition" name="iconPosition">
<mat-option value="left">Left</mat-option>
<mat-option value="right">Right</mat-option>
<mat-option value="top">Top</mat-option>
<mat-option value="bottom">Bottom</mat-option>
</mat-select>
<mat-hint>Position of the icon</mat-hint>
</mat-form-field>
</div>
<div *ngIf="currentConfig.enableAnimations" class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.hoverEffects" name="hoverEffects" class="mr-2">
Enable Hover Effects
</mat-checkbox>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Icon Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.iconSize" name="iconSize" min="16" max="64">
<mat-hint>Icon size in pixels</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Icon Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.iconColor" name="iconColor">
<mat-hint>Icon color</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Interaction Configuration -->
<!-- Filter Tab -->
<div class="config-section">
<h3 class="text-indigo-600">Interaction Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableTooltip" name="enableTooltip" class="mr-2">
Enable Tooltips
</mat-checkbox>
</div>
<h3 class="text-blue-600">Filter Configuration</h3>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableClick" name="enableClick" class="mr-2">
Enable Click Events
</mat-checkbox>
</div>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableFilter" name="enableFilter">
Enable Filtering
</mat-checkbox>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableHover" name="enableHover" class="mr-2">
Enable Hover Effects
</mat-checkbox>
</div>
<div *ngIf="currentConfig.enableFilter" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Filter Field</mat-label>
<mat-select [(ngModel)]="currentConfig.filterField" name="filterField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field to filter by</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableExport" name="enableExport" class="mr-2">
Enable Export
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Filter Operator</mat-label>
<mat-select [(ngModel)]="currentConfig.filterOperator" name="filterOperator">
<mat-option value="equals">Equals</mat-option>
<mat-option value="not_equals">Not Equals</mat-option>
<mat-option value="greater_than">Greater Than</mat-option>
<mat-option value="less_than">Less Than</mat-option>
<mat-option value="contains">Contains</mat-option>
<mat-option value="starts_with">Starts With</mat-option>
<mat-option value="ends_with">Ends With</mat-option>
</mat-select>
<mat-hint>Filter comparison operator</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Click Action</mat-label>
<mat-select [(ngModel)]="currentConfig.clickAction" name="clickAction">
<mat-option value="none">None</mat-option>
<mat-option value="drill_down">Drill Down</mat-option>
<mat-option value="open_modal">Open Modal</mat-option>
<mat-option value="navigate">Navigate</mat-option>
<mat-option value="custom">Custom</mat-option>
</mat-select>
<mat-hint>Action when widget is clicked</mat-hint>
</mat-form-field>
<mat-form-field *ngIf="currentConfig.clickAction === 'custom'" appearance="fill" class="w-full">
<mat-label>Custom Click Handler</mat-label>
<textarea matInput [(ngModel)]="currentConfig.customClickHandler" name="customClickHandler" rows="3"
placeholder="function(event) {&#10; console.log('Widget clicked:', event);&#10;}"></textarea>
<mat-hint>Custom JavaScript function for click events</mat-hint>
</mat-form-field>
</div>
<!-- Layout Configuration -->
<div class="config-section">
<h3 class="text-teal-600">Layout Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div *ngIf="currentConfig.enableFilter" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.width" name="width" min="100" max="800" aria-label="Widget width">
<mat-hint>Widget width</mat-hint>
<mat-label>Filter Value</mat-label>
<input matInput [(ngModel)]="currentConfig.filterValue" name="filterValue" placeholder="Enter filter value">
<mat-hint>Value to filter by</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.height" name="height" min="100" max="600" aria-label="Widget height">
<mat-hint>Widget height</mat-hint>
<mat-label>Filter Label</mat-label>
<input matInput [(ngModel)]="currentConfig.filterLabel" name="filterLabel" placeholder="Filter label">
<mat-hint>Label for the filter</mat-hint>
</mat-form-field>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Trend Tab -->
<div class="config-section">
<h3 class="text-blue-600">Trend Configuration</h3>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.showTrend" name="showTrend">
Show Trend Indicator
</mat-checkbox>
</div>
<div *ngIf="currentConfig.showTrend" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Minimum Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.minWidth" name="minWidth" min="50" max="400" aria-label="Minimum width">
<mat-hint>Minimum widget width</mat-hint>
<mat-label>Trend Field</mat-label>
<mat-select [(ngModel)]="currentConfig.trendField" name="trendField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field containing trend data</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Minimum Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.minHeight" name="minHeight" min="50" max="300" aria-label="Minimum height">
<mat-hint>Minimum widget height</mat-hint>
<mat-label>Trend Type</mat-label>
<mat-select [(ngModel)]="currentConfig.trendType" name="trendType">
<mat-option value="percentage">Percentage</mat-option>
<mat-option value="absolute">Absolute Value</mat-option>
<mat-option value="ratio">Ratio</mat-option>
</mat-select>
<mat-hint>How to calculate trend</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div *ngIf="currentConfig.showTrend" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Aspect Ratio</mat-label>
<mat-select [(ngModel)]="currentConfig.aspectRatio" name="aspectRatio">
<mat-option value="auto">Auto</mat-option>
<mat-option value="16:9">16:9</mat-option>
<mat-option value="4:3">4:3</mat-option>
<mat-option value="1:1">1:1 (Square)</mat-option>
<mat-option value="3:2">3:2</mat-option>
<mat-label>Trend Period</mat-label>
<mat-select [(ngModel)]="currentConfig.trendPeriod" name="trendPeriod">
<mat-option value="1">1 Day</mat-option>
<mat-option value="7">1 Week</mat-option>
<mat-option value="30">1 Month</mat-option>
<mat-option value="90">3 Months</mat-option>
<mat-option value="365">1 Year</mat-option>
</mat-select>
<mat-hint>Widget aspect ratio</mat-hint>
<mat-hint>Time period for trend calculation</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.responsive" name="responsive" class="mr-2">
Responsive Layout
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Trend Threshold (%)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.trendThreshold" name="trendThreshold" min="0" max="100">
<mat-hint>Threshold for significant trend</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Data Configuration -->
<!-- Animation Tab -->
<div class="config-section">
<h3 class="text-red-600">Data Settings</h3>
<h3 class="text-blue-600">Animation Configuration</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Source</mat-label>
<mat-select [(ngModel)]="currentConfig.dataSource" name="dataSource">
<mat-option value="static">Static Data</mat-option>
<mat-option value="api">API Endpoint</mat-option>
<mat-option value="websocket">WebSocket</mat-option>
<mat-option value="file">File Upload</mat-option>
</mat-select>
<mat-hint>Data source type</mat-hint>
</mat-form-field>
<mat-form-field *ngIf="currentConfig.dataSource === 'api'" appearance="fill" class="w-full">
<mat-label>API Endpoint</mat-label>
<input matInput [(ngModel)]="currentConfig.apiEndpoint" name="apiEndpoint" placeholder="/api/kpi-data" aria-label="API endpoint">
<mat-hint>API endpoint URL</mat-hint>
</mat-form-field>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableAnimations" name="enableAnimations">
Enable Animations
</mat-checkbox>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div *ngIf="currentConfig.enableAnimations" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Refresh Interval (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.refreshInterval" name="refreshInterval" min="0" step="1000" aria-label="Refresh interval">
<mat-hint>0 = No auto-refresh</mat-hint>
<mat-label>Animation Type</mat-label>
<mat-select [(ngModel)]="currentConfig.animationType" name="animationType">
<mat-option value="fade">Fade In</mat-option>
<mat-option value="slide">Slide In</mat-option>
<mat-option value="scale">Scale In</mat-option>
<mat-option value="bounce">Bounce</mat-option>
<mat-option value="pulse">Pulse</mat-option>
</mat-select>
<mat-hint>Type of animation effect</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.cacheEnabled" name="cacheEnabled" class="mr-2">
Enable Data Caching
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Animation Duration (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.animationDuration" name="animationDuration" min="100" max="2000">
<mat-hint>Duration of animation in milliseconds</mat-hint>
</mat-form-field>
</div>
<mat-form-field *ngIf="currentConfig.cacheEnabled" appearance="fill" class="w-full">
<mat-label>Cache Duration (seconds)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.cacheDuration" name="cacheDuration" min="1" max="3600" aria-label="Cache duration">
<mat-hint>How long to cache data</mat-hint>
</mat-form-field>
<div *ngIf="currentConfig.enableAnimations" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Animation Delay (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.animationDelay" name="animationDelay" min="0" max="1000">
<mat-hint>Delay before animation starts</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Transform Function</mat-label>
<textarea matInput [(ngModel)]="currentConfig.dataTransform" name="dataTransform" rows="3"
placeholder="data => data.map(item => ({ ...item, formattedValue: formatCurrency(item.value) }))"></textarea>
<mat-hint>JavaScript function to transform data</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Animation Easing</mat-label>
<mat-select [(ngModel)]="currentConfig.animationEasing" name="animationEasing">
<mat-option value="ease">Ease</mat-option>
<mat-option value="ease-in">Ease In</mat-option>
<mat-option value="ease-out">Ease Out</mat-option>
<mat-option value="ease-in-out">Ease In Out</mat-option>
<mat-option value="linear">Linear</mat-option>
</mat-select>
<mat-hint>Animation easing function</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.hoverEffects" name="hoverEffects">
Enable Hover Effects
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.autoRefresh" name="autoRefresh">
Auto Refresh Animation
</mat-checkbox>
</div>
</div>
<!-- Security Configuration -->
<!-- Condition Tab -->
<div class="config-section">
<h3 class="text-gray-600">Security Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.requireAuth" name="requireAuth" class="mr-2">
Require Authentication
</mat-checkbox>
</div>
<h3 class="text-blue-600">Conditional Formatting</h3>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.dataEncryption" name="dataEncryption" class="mr-2">
Enable Data Encryption
</mat-checkbox>
</div>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableConditionalFormatting" name="enableConditionalFormatting">
Enable Conditional Formatting
</mat-checkbox>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.auditLog" name="auditLog" class="mr-2">
Enable Audit Logging
</mat-checkbox>
<div *ngIf="currentConfig.enableConditionalFormatting">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
<mat-form-field appearance="fill">
<mat-label>Condition Field</mat-label>
<mat-select [(ngModel)]="currentConfig.conditionField" name="conditionField">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
<mat-hint>Field to evaluate condition</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Condition Operator</mat-label>
<mat-select [(ngModel)]="currentConfig.conditionOperator" name="conditionOperator">
<mat-option value="greater_than">Greater Than</mat-option>
<mat-option value="less_than">Less Than</mat-option>
<mat-option value="equals">Equals</mat-option>
<mat-option value="not_equals">Not Equals</mat-option>
<mat-option value="greater_equal">Greater or Equal</mat-option>
<mat-option value="less_equal">Less or Equal</mat-option>
</mat-select>
<mat-hint>Comparison operator</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Condition Value</mat-label>
<input matInput [(ngModel)]="currentConfig.conditionValue" name="conditionValue" placeholder="Threshold value">
<mat-hint>Threshold value</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill">
<mat-label>Rate Limit (requests/min)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.rateLimit" name="rateLimit" min="0" aria-label="Rate limit">
<mat-hint>0 = No limit</mat-hint>
</mat-form-field>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>True Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.trueColor" name="trueColor">
<mat-hint>Color when condition is true</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>False Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.falseColor" name="falseColor">
<mat-hint>Color when condition is false</mat-hint>
</mat-form-field>
</div>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Allowed Roles (comma-separated)</mat-label>
<input matInput [(ngModel)]="currentConfig.allowedRoles" name="allowedRoles" placeholder="admin, analyst, manager" aria-label="Allowed roles">
<mat-hint>Roles that can access this widget</mat-hint>
</mat-form-field>
</div>
</app-base-config>
`,
styles: []
styles: [`
.config-section {
margin-bottom: 24px;
}
`]
})
export class SimpleKpiConfigComponent extends BaseConfigComponent implements OnInit {
ngOnInit(): void {
// Initialize default values for Simple KPI widget
this.initializeDefaults();
override ngOnInit() {
this.initializeDefaultConfig();
this.initializeColorDefaults();
}
private initializeDefaults(): void {
if (!this.currentConfig.title) {
this.currentConfig.title = 'KPI Widget';
}
if (!this.currentConfig.aggregation) {
this.currentConfig.aggregation = 'count';
}
if (this.currentConfig.enableAnimations === undefined) {
this.currentConfig.enableAnimations = true;
}
if (!this.currentConfig.animationType) {
this.currentConfig.animationType = 'fade';
}
if (!this.currentConfig.animationDuration) {
this.currentConfig.animationDuration = 300;
}
if (this.currentConfig.hoverEffects === undefined) {
this.currentConfig.hoverEffects = true;
}
if (this.currentConfig.enableTooltip === undefined) {
this.currentConfig.enableTooltip = true;
}
if (this.currentConfig.enableClick === undefined) {
this.currentConfig.enableClick = true;
}
if (this.currentConfig.enableHover === undefined) {
this.currentConfig.enableHover = true;
}
if (this.currentConfig.enableExport === undefined) {
this.currentConfig.enableExport = true;
}
if (!this.currentConfig.clickAction) {
this.currentConfig.clickAction = 'none';
}
if (!this.currentConfig.aspectRatio) {
this.currentConfig.aspectRatio = 'auto';
}
if (this.currentConfig.responsive === undefined) {
this.currentConfig.responsive = true;
}
if (!this.currentConfig.dataSource) {
this.currentConfig.dataSource = 'static';
}
if (this.currentConfig.cacheEnabled === undefined) {
this.currentConfig.cacheEnabled = false;
}
if (!this.currentConfig.cacheDuration) {
this.currentConfig.cacheDuration = 300;
}
if (this.currentConfig.requireAuth === undefined) {
this.currentConfig.requireAuth = false;
}
if (this.currentConfig.dataEncryption === undefined) {
this.currentConfig.dataEncryption = false;
}
if (this.currentConfig.auditLog === undefined) {
this.currentConfig.auditLog = false;
}
if (!this.currentConfig.rateLimit) {
this.currentConfig.rateLimit = 0;
}
private initializeColorDefaults() {
if (!this.currentConfig.backgroundColor) this.currentConfig.backgroundColor = '#3366FF';
if (!this.currentConfig.textColor) this.currentConfig.textColor = '#FFFFFF';
if (!this.currentConfig.valueColor) this.currentConfig.valueColor = '#FFFFFF';
if (!this.currentConfig.labelColor) this.currentConfig.labelColor = '#FFFFFF';
if (!this.currentConfig.iconColor) this.currentConfig.iconColor = '#FFFFFF';
if (!this.currentConfig.borderColor) this.currentConfig.borderColor = '#FFFFFF';
if (!this.currentConfig.trueColor) this.currentConfig.trueColor = '#10B981';
if (!this.currentConfig.falseColor) this.currentConfig.falseColor = '#EF4444';
// Initialize new fields
if (!this.currentConfig.valueFormat) this.currentConfig.valueFormat = 'number';
if (!this.currentConfig.aggregation) this.currentConfig.aggregation = 'sum';
if (!this.currentConfig.unit) this.currentConfig.unit = '';
if (!this.currentConfig.dataSource) this.currentConfig.dataSource = 'static';
if (!this.currentConfig.apiEndpoint) this.currentConfig.apiEndpoint = '';
if (!this.currentConfig.showTrend) this.currentConfig.showTrend = false;
if (!this.currentConfig.trendField) this.currentConfig.trendField = '';
if (!this.currentConfig.trendType) this.currentConfig.trendType = 'percentage';
if (!this.currentConfig.trendPeriod) this.currentConfig.trendPeriod = '30';
if (!this.currentConfig.trendThreshold) this.currentConfig.trendThreshold = 5;
if (!this.currentConfig.enableAnimations) this.currentConfig.enableAnimations = true;
if (!this.currentConfig.animationType) this.currentConfig.animationType = 'fade';
if (!this.currentConfig.animationDuration) this.currentConfig.animationDuration = 300;
if (!this.currentConfig.animationDelay) this.currentConfig.animationDelay = 0;
if (!this.currentConfig.animationEasing) this.currentConfig.animationEasing = 'ease';
if (!this.currentConfig.hoverEffects) this.currentConfig.hoverEffects = true;
if (!this.currentConfig.autoRefresh) this.currentConfig.autoRefresh = false;
if (!this.currentConfig.enableConditionalFormatting) this.currentConfig.enableConditionalFormatting = false;
if (!this.currentConfig.conditionField) this.currentConfig.conditionField = '';
if (!this.currentConfig.conditionOperator) this.currentConfig.conditionOperator = 'greater_than';
if (!this.currentConfig.conditionValue) this.currentConfig.conditionValue = '';
}
}
......@@ -5,6 +5,9 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTabsModule } from '@angular/material/tabs';
import { BaseConfigComponent } from '../../../widget-config/base-config/base-config.component';
@Component({
......@@ -17,11 +20,14 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
MatInputModule,
MatSelectModule,
MatCheckboxModule,
MatButtonModule,
MatIconModule,
MatTabsModule,
BaseConfigComponent
],
template: `
<app-base-config>
<!-- Basic Configuration -->
<!-- Basic Configuration Tab -->
<div class="config-section">
<h3 class="text-blue-600">Basic Configuration</h3>
......@@ -34,456 +40,463 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Page Size</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.pageSize" name="pageSize" min="5" max="100" aria-label="Page size">
<input matInput type="number" [(ngModel)]="currentConfig.pageSize" name="pageSize" min="5" max="100">
<mat-hint>Number of rows per page</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Font Size (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.fontSize" name="fontSize" min="10" max="20" aria-label="Font size">
<input matInput type="number" [(ngModel)]="currentConfig.fontSize" name="fontSize" min="10" max="20">
<mat-hint>Font size for grid content</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Grid Functionality -->
<div class="config-section">
<h3 class="text-green-600">Grid Functionality</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enablePaging" name="enablePaging" class="mr-2">
Enable Paging
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableSorting" name="enableSorting" class="mr-2">
Enable Sorting
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableFiltering" name="enableFiltering" class="mr-2">
Enable Filtering
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableGrouping" name="enableGrouping" class="mr-2">
Enable Grouping
</mat-checkbox>
<!-- Size Configuration -->
<div class="config-section">
<h3 class="text-blue-600">Size Configuration</h3>
<div class="size-config">
<div
*ngFor="let option of sizeOptions"
class="size-option"
[class.selected]="currentConfig.sizeOption === option.id"
(click)="setSizeOption(option.id)">
<h4>{{ option.label }}</h4>
<p>{{ option.description }}</p>
</div>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableEditing" name="enableEditing" class="mr-2">
Enable Editing
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableSearch" name="enableSearch" class="mr-2">
Enable Search
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableColumnChooser" name="enableColumnChooser" class="mr-2">
Enable Column Chooser
</mat-checkbox>
<div *ngIf="currentConfig.sizeOption === 'custom'" class="grid grid-cols-2 gap-4 mt-4">
<mat-form-field appearance="fill">
<mat-label>Width</mat-label>
<input matInput [(ngModel)]="currentConfig.width" name="width" placeholder="e.g., 100%, 800px">
<mat-hint>Grid width</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height</mat-label>
<input matInput [(ngModel)]="currentConfig.height" name="height" placeholder="e.g., 100%, 400px">
<mat-hint>Grid height</mat-hint>
</mat-form-field>
</div>
</div>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableColumnResizing" name="enableColumnResizing" class="mr-2">
Enable Column Resizing
</mat-checkbox>
</div>
<!-- Columns Tab -->
<div class="config-section">
<h3 class="text-blue-600">Column Configuration</h3>
<div class="flex items-center justify-between mb-4">
<h4>Available Columns</h4>
<button mat-raised-button color="primary" (click)="addColumn()">
<mat-icon>add</mat-icon>
Add Column
</button>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableColumnReordering" name="enableColumnReordering" class="mr-2">
Enable Column Reordering
</mat-checkbox>
<div *ngFor="let column of currentConfig.columns; let i = index" class="column-config-item p-4 mb-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<mat-form-field appearance="fill">
<mat-label>Field Name</mat-label>
<mat-select [(ngModel)]="column.field" name="columnField{{i}}">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Header Text</mat-label>
<input matInput [(ngModel)]="column.headerText" name="columnHeader{{i}}" placeholder="Column header">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Width</mat-label>
<input matInput [(ngModel)]="column.width" name="columnWidth{{i}}" placeholder="e.g., 100px, 20%">
</mat-form-field>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableRowSelection" name="enableRowSelection" class="mr-2">
Enable Row Selection
</mat-checkbox>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Text Align</mat-label>
<mat-select [(ngModel)]="column.textAlign" name="columnAlign{{i}}">
<mat-option value="left">Left</mat-option>
<mat-option value="center">Center</mat-option>
<mat-option value="right">Right</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Format</mat-label>
<mat-select [(ngModel)]="column.format" name="columnFormat{{i}}">
<mat-option value="none">None</mat-option>
<mat-option value="number">Number</mat-option>
<mat-option value="currency">Currency</mat-option>
<mat-option value="percentage">Percentage</mat-option>
<mat-option value="date">Date</mat-option>
<mat-option value="datetime">Date Time</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableMultiSelection" name="enableMultiSelection" class="mr-2">
Enable Multi Selection
</mat-checkbox>
<div class="flex items-center justify-between mt-4">
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="column.visible" name="columnVisible{{i}}">
Visible
</mat-checkbox>
<mat-checkbox [(ngModel)]="column.sortable" name="columnSortable{{i}}">
Sortable
</mat-checkbox>
<mat-checkbox [(ngModel)]="column.filterable" name="columnFilterable{{i}}">
Filterable
</mat-checkbox>
</div>
<button mat-icon-button color="warn" (click)="removeColumn(i)">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</div>
<!-- Display Options -->
<!-- Rows Tab -->
<div class="config-section">
<h3 class="text-purple-600">Display Options</h3>
<h3 class="text-blue-600">Row Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showHeader" name="showHeader" class="mr-2">
Show Header
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Row Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.rowHeight" name="rowHeight" min="20" max="100">
<mat-hint>Height of each row</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showToolbar" name="showToolbar" class="mr-2">
Show Toolbar
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Alternate Row Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.alternateRowColor" name="alternateRowColor">
<mat-hint>Color for alternate rows</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showPager" name="showPager" class="mr-2">
Show Pager
</mat-checkbox>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Header Row Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.headerRowHeight" name="headerRowHeight" min="20" max="100">
<mat-hint>Height of header row</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.showFooter" name="showFooter" class="mr-2">
Show Footer
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Header Background Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.headerBackgroundColor" name="headerBackgroundColor">
<mat-hint>Background color for header</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.showAlternateRows" name="showAlternateRows">
Show Alternate Row Colors
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.allowRowSelection" name="allowRowSelection">
Allow Row Selection
</mat-checkbox>
</div>
</div>
<!-- Style Configuration -->
<!-- Style Tab -->
<div class="config-section">
<h3 class="text-orange-600">Style & Colors</h3>
<h3 class="text-blue-600">Style Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Background Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.backgroundColor" name="backgroundColor" class="h-[40px]" aria-label="Background color">
<input matInput type="color" [(ngModel)]="currentConfig.backgroundColor" name="backgroundColor">
<mat-hint>Grid background color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Header Background Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.headerBackgroundColor" name="headerBackgroundColor" class="h-[40px]" aria-label="Header background color">
<mat-hint>Header background color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Text Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.textColor" name="textColor" class="h-[40px]" aria-label="Text color">
<mat-hint>Grid text color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Header Text Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.headerTextColor" name="headerTextColor" class="h-[40px]" aria-label="Header text color">
<mat-hint>Header text color</mat-hint>
<input matInput type="color" [(ngModel)]="currentConfig.textColor" name="textColor">
<mat-hint>Text color</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Border Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor" class="h-[40px]" aria-label="Border color">
<input matInput type="color" [(ngModel)]="currentConfig.borderColor" name="borderColor">
<mat-hint>Grid border color</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Accent Color</mat-label>
<input matInput type="color" [(ngModel)]="currentConfig.accentColor" name="accentColor" class="h-[40px]" aria-label="Accent color">
<mat-hint>Selection and hover color</mat-hint>
<mat-label>Border Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderWidth" name="borderWidth" min="0" max="5">
<mat-hint>Border thickness</mat-hint>
</mat-form-field>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Border Radius (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="20" aria-label="Border radius">
<mat-hint>Widget border radius</mat-hint>
<input matInput type="number" [(ngModel)]="currentConfig.borderRadius" name="borderRadius" min="0" max="20">
<mat-hint>Corner roundness</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Padding (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.padding" name="padding" min="0" max="50" aria-label="Padding">
<mat-hint>Internal spacing</mat-hint>
<mat-label>Shadow</mat-label>
<mat-select [(ngModel)]="currentConfig.shadow" name="shadow">
<mat-option value="none">None</mat-option>
<mat-option value="small">Small</mat-option>
<mat-option value="medium">Medium</mat-option>
<mat-option value="large">Large</mat-option>
</mat-select>
<mat-hint>Drop shadow effect</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Filter Tab -->
<div class="config-section">
<h3 class="text-blue-600">Filter Configuration</h3>
<div class="flex items-center space-x-4 mb-4">
<mat-checkbox [(ngModel)]="currentConfig.allowFiltering" name="allowFiltering">
Enable Filtering
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.showFilterBar" name="showFilterBar">
Show Filter Bar
</mat-checkbox>
</div>
<div *ngIf="currentConfig.allowFiltering" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Font Weight</mat-label>
<mat-select [(ngModel)]="currentConfig.fontWeight" name="fontWeight">
<mat-option value="normal">Normal</mat-option>
<mat-option value="bold">Bold</mat-option>
<mat-option value="lighter">Lighter</mat-option>
<mat-option value="bolder">Bolder</mat-option>
<mat-label>Filter Type</mat-label>
<mat-select [(ngModel)]="currentConfig.filterType" name="filterType">
<mat-option value="menu">Menu</mat-option>
<mat-option value="checkbox">Checkbox</mat-option>
<mat-option value="excel">Excel</mat-option>
</mat-select>
<mat-hint>Font weight</mat-hint>
<mat-hint>Type of filter interface</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Filter Bar Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.filterBarHeight" name="filterBarHeight" min="30" max="100">
<mat-hint>Height of filter bar</mat-hint>
</mat-form-field>
</div>
</div>
<!-- Interaction Settings -->
<!-- Aggregate Tab -->
<div class="config-section">
<h3 class="text-indigo-600">Interaction Settings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableTooltip" name="enableTooltip" class="mr-2">
Enable Tooltips
</mat-checkbox>
</div>
<h3 class="text-blue-600">Aggregate Configuration</h3>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableClick" name="enableClick" class="mr-2">
Enable Click Events
</mat-checkbox>
</div>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.showAggregate" name="showAggregate">
Show Aggregate Row
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableHover" name="enableHover" class="mr-2">
Enable Hover Effects
</mat-checkbox>
<div *ngIf="currentConfig.showAggregate">
<div class="flex items-center justify-between mb-4">
<h4>Aggregate Columns</h4>
<button mat-raised-button color="primary" (click)="addAggregateColumn()">
<mat-icon>add</mat-icon>
Add Aggregate
</button>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.enableExport" name="enableExport" class="mr-2">
Enable Export
</mat-checkbox>
<div *ngFor="let agg of currentConfig.aggregateColumns; let i = index" class="column-config-item p-4 mb-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<mat-form-field appearance="fill">
<mat-label>Field Name</mat-label>
<mat-select [(ngModel)]="agg.field" name="aggField{{i}}">
<mat-option *ngFor="let col of availableColumns" [value]="col">{{ col }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Aggregate Type</mat-label>
<mat-select [(ngModel)]="agg.type" name="aggType{{i}}">
<mat-option value="sum">Sum</mat-option>
<mat-option value="count">Count</mat-option>
<mat-option value="average">Average</mat-option>
<mat-option value="min">Min</mat-option>
<mat-option value="max">Max</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Format</mat-label>
<mat-select [(ngModel)]="agg.format" name="aggFormat{{i}}">
<mat-option value="none">None</mat-option>
<mat-option value="number">Number</mat-option>
<mat-option value="currency">Currency</mat-option>
<mat-option value="percentage">Percentage</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="flex items-center justify-between mt-4">
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="agg.visible" name="aggVisible{{i}}">
Visible
</mat-checkbox>
</div>
<button mat-icon-button color="warn" (click)="removeAggregateColumn(i)">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Click Action</mat-label>
<mat-select [(ngModel)]="currentConfig.clickAction" name="clickAction">
<mat-option value="none">None</mat-option>
<mat-option value="drill_down">Drill Down</mat-option>
<mat-option value="open_modal">Open Modal</mat-option>
<mat-option value="navigate">Navigate</mat-option>
<mat-option value="custom">Custom</mat-option>
</mat-select>
<mat-hint>Action when widget is clicked</mat-hint>
</mat-form-field>
</div>
<!-- Layout Configuration -->
<!-- Export Tab -->
<div class="config-section">
<h3 class="text-teal-600">Layout Settings</h3>
<h3 class="text-blue-600">Export Configuration</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.enableExport" name="enableExport">
Enable Export
</mat-checkbox>
</div>
<div *ngIf="currentConfig.enableExport" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Width (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.width" name="width" min="300" max="1200" aria-label="Widget width">
<mat-hint>Widget width</mat-hint>
<mat-label>Export Formats</mat-label>
<mat-select [(ngModel)]="currentConfig.exportFormats" name="exportFormats" multiple>
<mat-option value="excel">Excel (.xlsx)</mat-option>
<mat-option value="csv">CSV (.csv)</mat-option>
<mat-option value="pdf">PDF (.pdf)</mat-option>
<mat-option value="json">JSON (.json)</mat-option>
</mat-select>
<mat-hint>Available export formats</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Height (px)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.height" name="height" min="200" max="800" aria-label="Widget height">
<mat-hint>Widget height</mat-hint>
<mat-label>Export Filename</mat-label>
<input matInput [(ngModel)]="currentConfig.exportFilename" name="exportFilename" placeholder="data-export">
<mat-hint>Default filename for exports</mat-hint>
</mat-form-field>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.responsive" name="responsive" class="mr-2">
Responsive Layout
<div *ngIf="currentConfig.enableExport" class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.includeHeaders" name="includeHeaders">
Include Headers in Export
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.includeFilters" name="includeFilters">
Include Applied Filters
</mat-checkbox>
</div>
</div>
<!-- Data Configuration -->
<!-- Security Tab -->
<div class="config-section">
<h3 class="text-red-600">Data Settings</h3>
<h3 class="text-blue-600">Security Configuration</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Source</mat-label>
<mat-select [(ngModel)]="currentConfig.dataSource" name="dataSource">
<mat-option value="static">Static Data</mat-option>
<mat-option value="api">API Endpoint</mat-option>
<mat-option value="websocket">WebSocket</mat-option>
<mat-option value="file">File Upload</mat-option>
</mat-select>
<mat-hint>Data source type</mat-hint>
</mat-form-field>
<mat-form-field *ngIf="currentConfig.dataSource === 'api'" appearance="fill" class="w-full">
<mat-label>API Endpoint</mat-label>
<input matInput [(ngModel)]="currentConfig.apiEndpoint" name="apiEndpoint" placeholder="/api/grid-data" aria-label="API endpoint">
<mat-hint>API endpoint URL</mat-hint>
</mat-form-field>
<div class="flex items-center mb-4">
<mat-checkbox [(ngModel)]="currentConfig.requireAuth" name="requireAuth">
Require Authentication
</mat-checkbox>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div *ngIf="currentConfig.requireAuth" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<mat-form-field appearance="fill">
<mat-label>Refresh Interval (ms)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.refreshInterval" name="refreshInterval" min="0" step="1000" aria-label="Refresh interval">
<mat-hint>0 = No auto-refresh</mat-hint>
<mat-label>Allowed Roles</mat-label>
<input matInput [(ngModel)]="currentConfig.allowedRoles" name="allowedRoles" placeholder="admin,user,manager">
<mat-hint>Comma-separated list of allowed roles</mat-hint>
</mat-form-field>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.cacheEnabled" name="cacheEnabled" class="mr-2">
Enable Data Caching
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Permission Level</mat-label>
<mat-select [(ngModel)]="currentConfig.permissionLevel" name="permissionLevel">
<mat-option value="read">Read Only</mat-option>
<mat-option value="write">Read/Write</mat-option>
<mat-option value="admin">Full Access</mat-option>
</mat-select>
<mat-hint>Required permission level</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Data Transform Function</mat-label>
<textarea matInput [(ngModel)]="currentConfig.dataTransform" name="dataTransform" rows="3"
placeholder="data => data.map(item => ({ ...item, formattedValue: formatCurrency(item.value) }))"></textarea>
<mat-hint>JavaScript function to transform data</mat-hint>
</mat-form-field>
</div>
<!-- Security Configuration -->
<div class="config-section">
<h3 class="text-gray-600">Security Settings</h3>
<div class="flex items-center space-x-4">
<mat-checkbox [(ngModel)]="currentConfig.dataEncryption" name="dataEncryption">
Enable Data Encryption
</mat-checkbox>
<mat-checkbox [(ngModel)]="currentConfig.auditLog" name="auditLog">
Enable Audit Logging
</mat-checkbox>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.requireAuth" name="requireAuth" class="mr-2">
Require Authentication
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.dataEncryption" name="dataEncryption" class="mr-2">
Enable Data Encryption
</mat-checkbox>
</div>
<div class="flex items-center">
<mat-checkbox [(ngModel)]="currentConfig.auditLog" name="auditLog" class="mr-2">
Enable Audit Logging
</mat-checkbox>
</div>
<mat-form-field appearance="fill">
<mat-label>Rate Limit (requests/hour)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.rateLimit" name="rateLimit" min="0" max="10000">
<mat-hint>Maximum requests per hour per user</mat-hint>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Rate Limit (requests/min)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.rateLimit" name="rateLimit" min="0" aria-label="Rate limit">
<mat-hint>0 = No limit</mat-hint>
<mat-label>Session Timeout (minutes)</mat-label>
<input matInput type="number" [(ngModel)]="currentConfig.sessionTimeout" name="sessionTimeout" min="5" max="480">
<mat-hint>Session timeout in minutes</mat-hint>
</mat-form-field>
</div>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Allowed Roles (comma-separated)</mat-label>
<input matInput [(ngModel)]="currentConfig.allowedRoles" name="allowedRoles" placeholder="admin, analyst, manager" aria-label="Allowed roles">
<mat-hint>Roles that can access this widget</mat-hint>
</mat-form-field>
</div>
<!-- Custom CSS -->
<div class="config-section">
<h3 class="text-purple-600">Custom Styling</h3>
<mat-form-field appearance="fill" class="w-full">
<mat-label>Custom CSS</mat-label>
<textarea matInput [(ngModel)]="currentConfig.customCSS" name="customCSS" rows="4"
placeholder="/* Add custom CSS rules here */&#10;.syncfusion-datagrid-widget:hover {&#10; transform: translateY(-4px);&#10;}"></textarea>
<mat-hint>Add custom CSS for advanced styling</mat-hint>
</mat-form-field>
</div>
</app-base-config>
`,
styles: []
styles: [`
.config-section {
margin-bottom: 24px;
}
.column-config-item {
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
`]
})
export class SyncfusionDatagridConfigComponent extends BaseConfigComponent implements OnInit {
override ngOnInit() {
this.initializeDefaultConfig();
this.initializeColumns();
this.initializeColorDefaults();
}
ngOnInit(): void {
// Initialize default values for Syncfusion Datagrid widget
this.initializeDefaults();
private initializeColorDefaults() {
if (!this.currentConfig.backgroundColor) this.currentConfig.backgroundColor = '#FFFFFF';
if (!this.currentConfig.textColor) this.currentConfig.textColor = '#374151';
if (!this.currentConfig.headerBackgroundColor) this.currentConfig.headerBackgroundColor = '#F9FAFB';
if (!this.currentConfig.headerTextColor) this.currentConfig.headerTextColor = '#374151';
if (!this.currentConfig.borderColor) this.currentConfig.borderColor = '#E5E7EB';
if (!this.currentConfig.alternateRowColor) this.currentConfig.alternateRowColor = '#F9FAFB';
}
private initializeDefaults(): void {
if (!this.currentConfig.title) {
this.currentConfig.title = 'Data Grid';
}
if (!this.currentConfig.pageSize) {
this.currentConfig.pageSize = 10;
}
if (!this.currentConfig.fontSize) {
this.currentConfig.fontSize = 14;
}
if (this.currentConfig.enablePaging === undefined) {
this.currentConfig.enablePaging = true;
}
if (this.currentConfig.enableSorting === undefined) {
this.currentConfig.enableSorting = true;
}
if (this.currentConfig.enableFiltering === undefined) {
this.currentConfig.enableFiltering = true;
}
if (this.currentConfig.enableGrouping === undefined) {
this.currentConfig.enableGrouping = false;
}
if (this.currentConfig.enableEditing === undefined) {
this.currentConfig.enableEditing = false;
}
if (this.currentConfig.enableSearch === undefined) {
this.currentConfig.enableSearch = true;
}
if (this.currentConfig.enableColumnChooser === undefined) {
this.currentConfig.enableColumnChooser = true;
}
if (this.currentConfig.enableColumnResizing === undefined) {
this.currentConfig.enableColumnResizing = true;
}
if (this.currentConfig.enableColumnReordering === undefined) {
this.currentConfig.enableColumnReordering = true;
}
if (this.currentConfig.enableRowSelection === undefined) {
this.currentConfig.enableRowSelection = true;
}
if (this.currentConfig.enableMultiSelection === undefined) {
this.currentConfig.enableMultiSelection = false;
}
if (this.currentConfig.showHeader === undefined) {
this.currentConfig.showHeader = true;
}
if (this.currentConfig.showToolbar === undefined) {
this.currentConfig.showToolbar = true;
}
if (this.currentConfig.showPager === undefined) {
this.currentConfig.showPager = true;
}
if (this.currentConfig.showFooter === undefined) {
this.currentConfig.showFooter = false;
}
if (this.currentConfig.enableTooltip === undefined) {
this.currentConfig.enableTooltip = true;
}
if (this.currentConfig.enableClick === undefined) {
this.currentConfig.enableClick = true;
}
if (this.currentConfig.enableHover === undefined) {
this.currentConfig.enableHover = true;
}
if (this.currentConfig.enableExport === undefined) {
this.currentConfig.enableExport = true;
}
if (!this.currentConfig.clickAction) {
this.currentConfig.clickAction = 'none';
}
if (!this.currentConfig.width) {
this.currentConfig.width = 800;
}
if (!this.currentConfig.height) {
this.currentConfig.height = 600;
}
if (this.currentConfig.responsive === undefined) {
this.currentConfig.responsive = true;
}
if (!this.currentConfig.dataSource) {
this.currentConfig.dataSource = 'static';
}
if (this.currentConfig.cacheEnabled === undefined) {
this.currentConfig.cacheEnabled = false;
}
if (this.currentConfig.requireAuth === undefined) {
this.currentConfig.requireAuth = false;
}
if (this.currentConfig.dataEncryption === undefined) {
this.currentConfig.dataEncryption = false;
}
if (this.currentConfig.auditLog === undefined) {
this.currentConfig.auditLog = false;
}
if (!this.currentConfig.rateLimit) {
this.currentConfig.rateLimit = 0;
private initializeColumns() {
if (!this.currentConfig.columns) {
this.currentConfig.columns = [];
}
}
addColumn() {
this.addArrayItem('columns', {
field: '',
headerText: '',
width: '',
textAlign: 'left',
format: 'none',
visible: true,
sortable: true,
filterable: true
});
}
removeColumn(index: number) {
this.removeArrayItem('columns', index);
}
addAggregateColumn() {
this.addArrayItem('aggregateColumns', {
field: '',
type: 'sum',
format: 'none',
visible: true
});
}
removeAggregateColumn(index: number) {
this.removeArrayItem('aggregateColumns', index);
}
}
......@@ -195,7 +195,7 @@ import { BaseConfigComponent } from '../../../widget-config/base-config/base-con
`
})
export class SyncfusionPivotConfigComponent extends BaseConfigComponent implements OnInit {
ngOnInit(): void {
override ngOnInit(): void {
// Initialize default values for pivot widget
if (!this.currentConfig.rows) this.currentConfig.rows = [];
if (!this.currentConfig.columns) this.currentConfig.columns = [];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment