Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e70f85644 | ||
|
|
482805b5cf | ||
|
|
ffda17e6b1 | ||
|
|
7396843bc6 | ||
|
|
4f75ecb3e0 | ||
|
|
7c1a487114 | ||
|
|
47e9fb92e3 | ||
|
|
7f735dcada | ||
|
|
bd315f42a3 | ||
|
|
e8c1f46430 | ||
|
|
fa96ca81bd | ||
|
|
c384f44c0c | ||
|
|
50df914ca9 | ||
|
|
e0bd888c45 | ||
|
|
02b82fcaf8 | ||
|
|
557afc348f | ||
|
|
1dec787062 | ||
|
|
8853cf75cf | ||
|
|
ad57f11f8a | ||
|
|
9b775a8c63 | ||
|
|
cf4fc1be93 | ||
|
|
4c135c4901 | ||
|
|
0b738ca7ca | ||
|
|
1b17bb706d | ||
|
|
f740076d60 | ||
|
|
bedcc0822d | ||
|
|
87810acc9e | ||
|
|
96b90e5dbd | ||
|
|
ced99e0940 | ||
|
|
4f82ae8698 | ||
|
|
e6779e8f34 | ||
|
|
82425d5377 | ||
|
|
2995328ec1 | ||
|
|
afc2c1f8a1 | ||
|
|
418b02acd7 | ||
|
|
cdd752469c | ||
|
|
cdcf1e07c7 | ||
|
|
c6ad8b5c2f | ||
|
|
98e0908920 | ||
|
|
8266bfdc01 | ||
|
|
f2b6a4d145 | ||
|
|
02f37a1bc5 | ||
|
|
f24138cfbd | ||
|
|
aade12d6ff | ||
|
|
6c01e71d04 | ||
|
|
f60657ca64 |
@@ -1,8 +1,9 @@
|
|||||||
export class ReportBuilder {
|
export class ReportBuilder {
|
||||||
public report_id: number;
|
public report_id: number;
|
||||||
public report_name:string;
|
public report_name: string;
|
||||||
public description: string;
|
public description: string;
|
||||||
public report_tags: string;
|
public report_tags: string;
|
||||||
public servicename:string;
|
public servicename: string;
|
||||||
|
// Add SureConnect reference
|
||||||
|
public sureConnectId: number | null;
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,39 @@ export interface DashboardContentModel {
|
|||||||
component?: any;
|
component?: any;
|
||||||
name: string;
|
name: string;
|
||||||
type?:string;
|
type?:string;
|
||||||
|
// Chart properties
|
||||||
|
xAxis?: string;
|
||||||
|
yAxis?: string | string[];
|
||||||
|
chartType?: string;
|
||||||
|
charttitle?: string;
|
||||||
|
chartlegend?: boolean;
|
||||||
|
showlabel?: boolean;
|
||||||
|
chartcolor?: boolean;
|
||||||
|
slices?: boolean;
|
||||||
|
donut?: boolean;
|
||||||
|
charturl?: string;
|
||||||
|
chartparameter?: string;
|
||||||
|
datastore?: string;
|
||||||
|
table?: string;
|
||||||
|
datasource?: string;
|
||||||
|
fieldName?: string;
|
||||||
|
connection?: string;
|
||||||
|
baseFilters?: any[];
|
||||||
|
// Common filter properties
|
||||||
|
commonFilterEnabled?: boolean;
|
||||||
|
// Drilldown properties
|
||||||
|
drilldownEnabled?: boolean;
|
||||||
|
drilldownApiUrl?: string;
|
||||||
|
drilldownXAxis?: string;
|
||||||
|
drilldownYAxis?: string;
|
||||||
|
drilldownParameter?: string;
|
||||||
|
drilldownFilters?: any[];
|
||||||
|
drilldownLayers?: any[];
|
||||||
|
// Compact filter properties
|
||||||
|
filterKey?: string;
|
||||||
|
filterType?: string;
|
||||||
|
filterLabel?: string;
|
||||||
|
filterOptions?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DashboardModel {
|
export interface DashboardModel {
|
||||||
@@ -67,6 +100,10 @@ export class value1{
|
|||||||
|
|
||||||
|
|
||||||
export const WidgetsMock: WidgetModel[] = [
|
export const WidgetsMock: WidgetModel[] = [
|
||||||
|
{
|
||||||
|
name: 'Common Filter',
|
||||||
|
identifier: 'common_filter'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Radar Chart',
|
name: 'Radar Chart',
|
||||||
identifier: 'radar_chart'
|
identifier: 'radar_chart'
|
||||||
|
|||||||
@@ -1,39 +1,155 @@
|
|||||||
<!-- <router-outlet></router-outlet> -->
|
<!-- Modern AI-Inspired Login Page -->
|
||||||
|
<div class="modern-login-container" *ngIf="showLogin2">
|
||||||
|
<!-- Background Elements -->
|
||||||
|
<div class="login-background">
|
||||||
|
<div class="gradient-overlay"></div>
|
||||||
|
<div class="floating-shapes">
|
||||||
|
<div class="shape shape-1"></div>
|
||||||
|
<div class="shape shape-2"></div>
|
||||||
|
<div class="shape shape-3"></div>
|
||||||
|
<div class="shape shape-4"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <div class="login-wrapper" *ngIf="showLogin2 && loginEnvironment.templateNo == 'Template 1'" [style.backgroundImage]="'url(' + loginEnvironment.imagePath + ')'">
|
<!-- Main Login Content -->
|
||||||
<form class="login">
|
<div class="login-content">
|
||||||
<section class="title">
|
<!-- Left Side - Branding -->
|
||||||
<h3 class="welcome">Welcome to</h3>
|
<div class="login-branding">
|
||||||
{{ loginEnvironment.companyName }}
|
<div class="brand-content">
|
||||||
<h5 class="hint">Use your ID to sign in OR <a [routerLink]="['/email-verification']" class="signup">create one now </a> </h5>
|
<div class="logo-section">
|
||||||
</section>
|
<div class="logo-container">
|
||||||
<div class="login-group">
|
<img class="logo-img" src="assets/images/icon/micrologo.png" alt="CloudnSure Logo">
|
||||||
<clr-input-container>
|
<div class="logo-glow"></div>
|
||||||
<label class="clr-sr-only">Username</label>
|
</div>
|
||||||
<input type="text" name="username" clrInput autocomplete="off" [(ngModel)]="model.email"
|
<h1 class="brand-title">
|
||||||
placeholder="Username (sysadmin)"/>
|
<ng-container *ngIf="loginEnvironment?.companyName; else defaultTitle">
|
||||||
</clr-input-container>
|
{{ loginEnvironment.companyName }}
|
||||||
<clr-password-container>
|
</ng-container>
|
||||||
<label class="clr-sr-only">Password</label>
|
<ng-template #defaultTitle>
|
||||||
<input type="password" name="password" clrPassword id="login_password" autocomplete="off" [(ngModel)]="model.password"
|
CloudnSure
|
||||||
placeholder="Password (test3)"/>
|
</ng-template>
|
||||||
</clr-password-container>
|
</h1>
|
||||||
<clr-checkbox-wrapper>
|
<p class="brand-subtitle">Enterprise Platform</p>
|
||||||
<input clrCheckbox type="checkbox" id="rememberme">
|
|
||||||
<label for="rememberme"> Remember me </label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
|
|
||||||
<div class="error active" *ngIf="isError">
|
|
||||||
Invalid user name or password
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button [disabled]="!model.email || !model.password" type="submit" class="btn btn-primary"
|
<div class="brand-features">
|
||||||
(click)="onLogin()">Login</button>
|
<div class="feature-item">
|
||||||
<a (click)="goforgotpass()" class="signup">{{ loginEnvironment.forgotpassword }}</a>
|
<clr-icon shape="shield" class="feature-icon"></clr-icon>
|
||||||
|
<span>Secure & Reliable</span>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<clr-icon shape="analytics" class="feature-icon"></clr-icon>
|
||||||
|
<span>Advanced Analytics</span>
|
||||||
|
</div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<clr-icon shape="cog" class="feature-icon"></clr-icon>
|
||||||
|
<span>Customizable</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div> -->
|
|
||||||
|
|
||||||
|
<!-- Right Side - Login Form -->
|
||||||
|
<div class="login-form-section">
|
||||||
|
<div class="form-container">
|
||||||
|
<div class="form-header">
|
||||||
|
<h2 class="form-title">Welcome Back</h2>
|
||||||
|
<p class="form-subtitle">Sign in to your account to continue</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="modern-login-form" (ngSubmit)="onLogin()">
|
||||||
|
<!-- Username Field -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Username</label>
|
||||||
|
<div class="input-container">
|
||||||
|
<clr-icon shape="user" class="input-icon"></clr-icon>
|
||||||
|
<input type="text" name="username" class="modern-input" autocomplete="username" [(ngModel)]="model.email"
|
||||||
|
(input)="clearError()" placeholder="Enter your username" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Password Field -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Password</label>
|
||||||
|
<div class="input-container">
|
||||||
|
<clr-icon shape="lock" class="input-icon"></clr-icon>
|
||||||
|
<input [type]="showPassword ? 'text' : 'password'" name="password" class="modern-input"
|
||||||
|
autocomplete="current-password" [(ngModel)]="model.password" (input)="clearError()"
|
||||||
|
placeholder="Enter your password" required>
|
||||||
|
<button type="button" class="password-toggle" (click)="togglePasswordVisibility()"
|
||||||
|
[attr.aria-label]="showPassword ? 'Hide password' : 'Show password'">
|
||||||
|
<span class="toggle-text">{{ showPassword ? 'Hide' : 'Show' }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Remember Me & Forgot Password -->
|
||||||
|
<div class="form-options">
|
||||||
|
<label class="checkbox-container">
|
||||||
|
<input type="checkbox" id="rememberme" class="modern-checkbox">
|
||||||
|
<span class="checkmark"></span>
|
||||||
|
<span class="checkbox-label">Remember me</span>
|
||||||
|
</label>
|
||||||
|
<a href="javascript://" class="forgot-link" (click)="goforgotpass()">
|
||||||
|
Forgot password?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Error Message -->
|
||||||
|
<div class="error-message" *ngIf="isError">
|
||||||
|
<clr-icon shape="exclamation-triangle"></clr-icon>
|
||||||
|
<span>{{ errMsg || 'Invalid username or password' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Login Button -->
|
||||||
|
<button type="submit" class="modern-login-btn" [disabled]="isLoading" [class.loading]="isLoading">
|
||||||
|
<span class="btn-content" *ngIf="!isLoading">
|
||||||
|
<clr-icon shape="login"></clr-icon>
|
||||||
|
Sign In
|
||||||
|
</span>
|
||||||
|
<span class="btn-loading" *ngIf="isLoading">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
Signing In...
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Sign Up Link -->
|
||||||
|
<div class="signup-section" *ngIf="loginEnvironment?.isSignup === 'true'">
|
||||||
|
<p class="signup-text">
|
||||||
|
Don't have an account?
|
||||||
|
<a [routerLink]="['/email-verification']" class="signup-link">
|
||||||
|
Create one now
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Social Login (Optional) -->
|
||||||
|
<div class="social-login">
|
||||||
|
<div class="divider">
|
||||||
|
<span>Or continue with</span>
|
||||||
|
</div>
|
||||||
|
<div class="social-buttons">
|
||||||
|
<button class="social-btn google-btn" type="button">
|
||||||
|
<clr-icon shape="world"></clr-icon>
|
||||||
|
<span>Google</span>
|
||||||
|
</button>
|
||||||
|
<button class="social-btn microsoft-btn" type="button">
|
||||||
|
<clr-icon shape="world"></clr-icon>
|
||||||
|
<span>Microsoft</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
<div [class]="getWrapperClass()" *ngIf="showLogin2 && loginEnvironment.templateNo == '<templateNo>'" [style.backgroundImage]="'url(' + ('../../../../assets/images/new.png') + ')'">
|
<div [class]="getWrapperClass()" *ngIf="showLogin2 && loginEnvironment.templateNo == '<templateNo>'" [style.backgroundImage]="'url(' + ('../../../../assets/images/new.png') + ')'">
|
||||||
<form class="login">
|
<form class="login">
|
||||||
<section class="title">
|
<section class="title">
|
||||||
@@ -108,7 +224,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <div class="login-wrapper"> -->
|
|
||||||
<div [class]="getWrapperClass()" *ngIf="showLogin2 && loginEnvironment.templateNo == 'Template 2'" [style.backgroundImage]="'url(' + (loginEnvironment.loginImageURL !== 'null' || null ? loginEnvironment.loginImageURL : '../../../../assets/images/new.png') + ')'">
|
<div [class]="getWrapperClass()" *ngIf="showLogin2 && loginEnvironment.templateNo == 'Template 2'" [style.backgroundImage]="'url(' + (loginEnvironment.loginImageURL !== 'null' || null ? loginEnvironment.loginImageURL : '../../../../assets/images/new.png') + ')'">
|
||||||
<form class="login">
|
<form class="login">
|
||||||
<section class="title">
|
<section class="title">
|
||||||
@@ -143,4 +259,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
@@ -1,23 +1,653 @@
|
|||||||
.tamplate1 {
|
// ========================================
|
||||||
background-image: url(../../../../assets/images/new.png);
|
// MODERN AI-INSPIRED LOGIN PAGE STYLES
|
||||||
background-repeat: no-repeat;
|
// ========================================
|
||||||
background-size: 60%;
|
|
||||||
background-position: right center;
|
@import '../../../../styles/design-tokens';
|
||||||
border: 1px solid #000;
|
|
||||||
|
// Modern Login Container
|
||||||
|
.modern-login-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(135deg, $primary-50 0%, $primary-100 50%, $secondary-50 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background Elements
|
||||||
|
.login-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.gradient-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgba($primary-600, 0.1) 0%,
|
||||||
|
rgba($primary-400, 0.05) 50%,
|
||||||
|
rgba($accent-purple, 0.1) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tamplate2 {
|
.floating-shapes {
|
||||||
background-image: url(../../../../assets/images/new.png);
|
position: absolute;
|
||||||
background-repeat: no-repeat;
|
top: 0;
|
||||||
background-size: 60%;
|
left: 0;
|
||||||
background-position: left center;
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
.shape {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(45deg, rgba($primary-400, 0.1), rgba($accent-purple, 0.1));
|
||||||
|
animation: float 6s ease-in-out infinite;
|
||||||
|
|
||||||
|
&.shape-1 {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
top: 10%;
|
||||||
|
left: 10%;
|
||||||
|
animation-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shape-2 {
|
||||||
|
width: 150px;
|
||||||
|
height: 150px;
|
||||||
|
top: 60%;
|
||||||
|
right: 15%;
|
||||||
|
animation-delay: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shape-3 {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
bottom: 20%;
|
||||||
|
left: 20%;
|
||||||
|
animation-delay: 4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shape-4 {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
top: 30%;
|
||||||
|
right: 30%;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main Content
|
||||||
|
.login-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
max-width: 1200px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: $space-8;
|
||||||
|
gap: $space-12;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left Side - Branding
|
||||||
|
.login-branding {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: $space-8;
|
||||||
|
|
||||||
|
.brand-content {
|
||||||
|
text-align: center;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-section {
|
||||||
|
margin-bottom: $space-8;
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
|
||||||
|
.logo-img {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 8px 16px rgba($primary-600, 0.3));
|
||||||
|
transition: transform $duration-300 $ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-glow {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
left: -10px;
|
||||||
|
right: -10px;
|
||||||
|
bottom: -10px;
|
||||||
|
background: linear-gradient(45deg, $primary-400, $accent-purple);
|
||||||
|
border-radius: 50%;
|
||||||
|
opacity: 0;
|
||||||
|
filter: blur(20px);
|
||||||
|
transition: opacity $duration-300 $ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.logo-img {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-glow {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-title {
|
||||||
|
font-size: $text-4xl;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
color: $gray-900;
|
||||||
|
margin-bottom: $space-2;
|
||||||
|
background: linear-gradient(135deg, $primary-600, $accent-purple);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-subtitle {
|
||||||
|
font-size: $text-lg;
|
||||||
|
color: $gray-600;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-features {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
flex-direction: column;
|
||||||
align-items: center;
|
gap: $space-4;
|
||||||
border: 1px solid #000;
|
|
||||||
|
.feature-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
padding: $space-3;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
backdrop-filter: $backdrop-blur-sm;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateX(8px);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
box-shadow: $shadow-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-icon {
|
||||||
|
color: $primary-600;
|
||||||
|
font-size: $text-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
color: $gray-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.signup {
|
}
|
||||||
cursor: pointer;
|
|
||||||
// color:#80b0ff;
|
// Right Side - Login Form
|
||||||
|
.login-form-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: $space-8;
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: $backdrop-blur-md;
|
||||||
|
border-radius: $radius-2xl;
|
||||||
|
padding: $space-8;
|
||||||
|
box-shadow: $shadow-xl;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: $space-8;
|
||||||
|
|
||||||
|
.form-title {
|
||||||
|
font-size: $text-2xl;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
color: $gray-900;
|
||||||
|
margin-bottom: $space-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-subtitle {
|
||||||
|
font-size: $text-sm;
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-login-form {
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
color: $gray-700;
|
||||||
|
margin-bottom: $space-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.input-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: $space-3;
|
||||||
|
color: $gray-400;
|
||||||
|
font-size: $text-base;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: $space-3 $space-3 $space-3 $space-10;
|
||||||
|
border: 2px solid $gray-200;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
color: $gray-900;
|
||||||
|
background: $white;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: $gray-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: $primary-500;
|
||||||
|
box-shadow: 0 0 0 3px rgba($primary-500, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $gray-300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password field with toggle button
|
||||||
|
&:has(.password-toggle) .modern-input {
|
||||||
|
padding-right: $space-10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-toggle {
|
||||||
|
position: absolute;
|
||||||
|
right: $space-3;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: $gray-400;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: $space-1;
|
||||||
|
border-radius: $radius-sm;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $gray-600;
|
||||||
|
background: rgba($gray-100, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
color: $primary-600;
|
||||||
|
background: rgba($primary-50, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
font-size: $text-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-text {
|
||||||
|
font-size: $text-xs;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
color: inherit;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-options {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
|
||||||
|
.checkbox-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-2;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.modern-checkbox {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
accent-color: $primary-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
font-size: $text-sm;
|
||||||
|
color: $gray-600;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-link {
|
||||||
|
font-size: $text-sm;
|
||||||
|
color: $primary-600;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
transition: color $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $primary-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-2;
|
||||||
|
padding: $space-3;
|
||||||
|
background: rgba($accent-red, 0.1);
|
||||||
|
border: 1px solid rgba($accent-red, 0.2);
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
color: $accent-red;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
font-size: $text-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-login-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: $space-4;
|
||||||
|
background: linear-gradient(135deg, $primary-600, $primary-700);
|
||||||
|
color: $white;
|
||||||
|
border: none;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
font-size: $text-base;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: $shadow-lg;
|
||||||
|
background: linear-gradient(135deg, $primary-700, $primary-800);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.loading {
|
||||||
|
.btn-content {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-loading {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: $space-2;
|
||||||
|
transition: opacity $duration-200 $ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-2;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity $duration-200 $ease-out;
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-top: 2px solid $white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-section {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: $space-6;
|
||||||
|
|
||||||
|
.signup-text {
|
||||||
|
font-size: $text-sm;
|
||||||
|
color: $gray-600;
|
||||||
|
|
||||||
|
.signup-link {
|
||||||
|
color: $primary-600;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
transition: color $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $primary-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-login {
|
||||||
|
margin-top: $space-8;
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: $gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
padding: 0 $space-4;
|
||||||
|
font-size: $text-xs;
|
||||||
|
color: $gray-500;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-buttons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: $space-3;
|
||||||
|
|
||||||
|
.social-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: $space-2;
|
||||||
|
padding: $space-3;
|
||||||
|
border: 2px solid $gray-200;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
background: $white;
|
||||||
|
color: $gray-700;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $gray-300;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: $shadow-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.google-btn:hover {
|
||||||
|
border-color: #db4437;
|
||||||
|
color: #db4437;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.microsoft-btn:hover {
|
||||||
|
border-color: #0078d4;
|
||||||
|
color: #0078d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
font-size: $text-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animations
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0px) rotate(0deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-20px) rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive Design
|
||||||
|
@media (max-width: $breakpoint-lg) {
|
||||||
|
.login-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: $space-8;
|
||||||
|
padding: $space-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-branding {
|
||||||
|
order: 2;
|
||||||
|
padding: $space-4;
|
||||||
|
|
||||||
|
.brand-content {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-section .brand-title {
|
||||||
|
font-size: $text-3xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-features {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-section {
|
||||||
|
order: 1;
|
||||||
|
padding: $space-4;
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
padding: $space-6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
.modern-login-container {
|
||||||
|
padding: $space-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-content {
|
||||||
|
padding: $space-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-branding {
|
||||||
|
.brand-features {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form-section .form-container {
|
||||||
|
padding: $space-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy styles (preserved for compatibility)
|
||||||
|
.tamplate1 {
|
||||||
|
background-image: url(../../../../assets/images/new.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 60%;
|
||||||
|
background-position: right center;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tamplate2 {
|
||||||
|
background-image: url(../../../../assets/images/new.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 60%;
|
||||||
|
background-position: left center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export class LoginPageComponent implements OnInit {
|
|||||||
email = '';
|
email = '';
|
||||||
password = '';
|
password = '';
|
||||||
isError = false;
|
isError = false;
|
||||||
|
isLoading = false;
|
||||||
|
showPassword = false;
|
||||||
|
|
||||||
model: any = {};
|
model: any = {};
|
||||||
errMsg: string = '';
|
errMsg: string = '';
|
||||||
@@ -71,32 +73,82 @@ export class LoginPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onLogin() {
|
onLogin() {
|
||||||
|
console.log('onLogin called');
|
||||||
|
console.log('Form data:', { email: this.model.email, password: this.model.password });
|
||||||
|
|
||||||
|
// Reset error state
|
||||||
|
this.isError = false;
|
||||||
|
this.errMsg = '';
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
if (!this.model.email || !this.model.password) {
|
||||||
|
this.isError = true;
|
||||||
|
this.errMsg = 'Please enter both email and password';
|
||||||
|
console.log('Validation failed - missing email or password');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set loading state
|
||||||
|
this.isLoading = true;
|
||||||
|
console.log('Loading state set to true, making API call...');
|
||||||
|
|
||||||
// tslint:disable-next-line:max-line-length
|
// tslint:disable-next-line:max-line-length
|
||||||
this.loginService.getToken(this.model.email, this.model.password)
|
this.loginService.getToken(this.model.email, this.model.password)
|
||||||
.subscribe(resp => {
|
.subscribe(
|
||||||
if (resp.user === undefined || resp.user.token === undefined || resp.user.token === "INVALID") {
|
resp => {
|
||||||
this.errMsg = 'Checking Email or password';
|
console.log('API Response received:', resp);
|
||||||
return;
|
// Always reset loading state when response is received
|
||||||
}
|
this.isLoading = false;
|
||||||
this.router.navigate([resp.landingPage]);// add , {skipLocationChange: true}
|
|
||||||
},
|
// Handle different response formats
|
||||||
|
if (resp.operationStatus === 'ERROR') {
|
||||||
|
this.isError = true;
|
||||||
|
this.errMsg = resp.operationMessage || 'Login failed';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Handle different response formats
|
||||||
|
if (resp.success === 'false' || resp.success === false) {
|
||||||
|
this.isError = true;
|
||||||
|
this.errMsg = resp.message || 'Login failed';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (resp.user === undefined || resp.user.token === undefined || resp.user.token === "INVALID") {
|
||||||
|
this.isError = true;
|
||||||
|
this.errMsg = 'Invalid email or password';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success - navigate to landing page
|
||||||
|
console.log('Login successful, navigating to:', resp.landingPage);
|
||||||
|
this.router.navigate([resp.landingPage]);
|
||||||
|
},
|
||||||
(errResponse: HttpErrorResponse) => {
|
(errResponse: HttpErrorResponse) => {
|
||||||
|
console.log('API Error received:', errResponse);
|
||||||
|
// Always reset loading state when error occurs
|
||||||
|
this.isLoading = false;
|
||||||
|
this.isError = true;
|
||||||
|
|
||||||
switch (errResponse.status) {
|
switch (errResponse.status) {
|
||||||
case 401:
|
case 401:
|
||||||
this.errMsg = 'Email or password is incorrect';
|
this.errMsg = 'Email or password is incorrect';
|
||||||
break;
|
break;
|
||||||
case 404:
|
case 404:
|
||||||
this.errMsg = 'Service not found';
|
this.errMsg = 'Service not found';
|
||||||
|
break;
|
||||||
case 408:
|
case 408:
|
||||||
this.errMsg = 'Request Timedout';
|
this.errMsg = 'Request timeout';
|
||||||
|
break;
|
||||||
case 500:
|
case 500:
|
||||||
this.errMsg = 'Internal Server Error';
|
this.errMsg = 'Internal server error';
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
this.errMsg = 'Network error - please check your connection';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.errMsg = 'Server Error';
|
this.errMsg = 'An error occurred. Please try again.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
goaccount(){
|
goaccount(){
|
||||||
}
|
}
|
||||||
@@ -104,5 +156,17 @@ export class LoginPageComponent implements OnInit {
|
|||||||
this.router.navigate(["../forgotpass"], { relativeTo: this.route });
|
this.router.navigate(["../forgotpass"], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear error when user starts typing
|
||||||
|
clearError() {
|
||||||
|
if (this.isError) {
|
||||||
|
this.isError = false;
|
||||||
|
this.errMsg = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle password visibility
|
||||||
|
togglePasswordVisibility() {
|
||||||
|
this.showPassword = !this.showPassword;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,12 +12,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -240,7 +234,8 @@ export class Stepper_workflowComponent implements OnInit {
|
|||||||
|
|
||||||
rsModaldescription = false;
|
rsModaldescription = false;
|
||||||
goToReplaceStringdescription(row) {
|
goToReplaceStringdescription(row) {
|
||||||
this.rowSelected = row; this.rsModaldescription = true;
|
this.rowSelected = row;
|
||||||
|
this.rsModaldescription = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -253,6 +248,39 @@ export class Stepper_workflowComponent implements OnInit {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.router.navigate(["../demostepper/", id], { relativeTo: this.route });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateaction
|
// updateaction
|
||||||
|
|||||||
@@ -1,279 +1,465 @@
|
|||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>{{'SECURITY' | translate}}</a></li>
|
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon> {{'SECURITY' | translate}}</a></li>
|
||||||
<li><a href="javascript://">{{ 'MENU_ACCESS_CONTROL' | translate }}</a></li>
|
<li><a href="javascript://">{{'MENU_ACCESS_CONTROL' | translate}}</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="clr-row">
|
<section class="mac-hero">
|
||||||
<div class="clr-col-6" style="display:flex ;">
|
<div class="mac-hero__content">
|
||||||
<div style="margin-right: 25px;">
|
<div class="mac-hero__icon">
|
||||||
<h4 style="font-weight:300;">{{ 'MENU_ACCESS_CONTROL' | translate }} </h4>
|
<clr-icon shape="shield-check"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:27px ; position: relative;">
|
<div>
|
||||||
<span class="label label-light-blue" style="display: inline;">{{ 'EDIT_MODE' | translate }}</span>
|
<h2 class="mac-hero__title">{{'MENU_ACCESS_CONTROL' | translate}}</h2>
|
||||||
|
<p class="mac-hero__subtitle">{{'SECURITY' | translate}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-6" style="text-align: right;">
|
<div class="mac-hero__actions">
|
||||||
{{ 'FOR' | translate }} <select id="" style="height: 30px;" (change)="idofselected($event.target.value)">
|
<span class="mac-chip">{{'EDIT_MODE' | translate}}</span>
|
||||||
|
<div class="mac-sep"></div>
|
||||||
|
<label class="mac-label">{{'FOR' | translate}}</label>
|
||||||
|
<select class="mac-select" (change)="idofselected($event.target.value)">
|
||||||
<option *ngFor="let sub of givendata" [value]="sub.usrGrp" [selected]="sub.usrGrp== 40">{{sub.groupName}}</option>
|
<option *ngFor="let sub of givendata" [value]="sub.usrGrp" [selected]="sub.usrGrp== 40">{{sub.groupName}}</option>
|
||||||
</select>
|
</select>
|
||||||
<button id="add" class="btn btn-primary" style="margin-left: 20px;" (click)="getbyuseriddata()"> {{ 'RELOAD' |
|
<button id="add" class="mac-btn mac-btn-primary" (click)="getbyuseriddata()">
|
||||||
translate }} </button>
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
<span *ngIf="!toggle">{{ 'SHOW_ALL' | translate }} </span><span *ngIf="toggle">{{ 'ONLY_MAIN_MENU' | translate
|
<span class="mac-btn-text">{{'RELOAD' | translate}}</span>
|
||||||
}}</span>
|
</button>
|
||||||
<label class="switch">
|
<div class="mac-toggle">
|
||||||
<input type="checkbox" ([ngModel])="toggle" checked (click)="toggleCheckbox()">
|
<span class="mac-toggle__label" *ngIf="!toggle">{{'SHOW_ALL' | translate}}</span>
|
||||||
<span class="slider round"></span>
|
<span class="mac-toggle__label" *ngIf="toggle">{{'ONLY_MAIN_MENU' | translate}}</span>
|
||||||
</label>
|
<label class="switch">
|
||||||
|
<input type="checkbox" ([ngModel])="toggle" checked (click)="toggleCheckbox()">
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="dg-wrapper">
|
<div class="mac-container">
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-12 mac-actions">
|
||||||
<div class="clr-col-12" style="text-align: right;">
|
<button id="add" class="mac-btn mac-btn-primary" (click)="modaladd()" [disabled]="!toggle">
|
||||||
|
<clr-icon shape="plus"></clr-icon>
|
||||||
<button id="add" class="btn btn-primary" style="margin-left: 20px;" (click)="modaladd()" [disabled]="!toggle">
|
<span>{{'ADD' | translate}}</span>
|
||||||
<clr-icon shape="plus"></clr-icon>{{ 'ADD' | translate }} </button>
|
</button>
|
||||||
<div *ngIf="!toggle" class="error-message" style="color: red; font-size: 14px; margin-top: 5px;">
|
<div *ngIf="!toggle" class="error-message mac-hint">
|
||||||
{{ ' Close toggle to activate Add button. ' | translate }}
|
{{'CLOSE_TOGGLE_TO_ACTIVATE_ADD_BUTTON' | translate}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mac-toolbar">
|
||||||
|
<div class="mac-toolbar__left">
|
||||||
|
<div class="mac-search">
|
||||||
|
<clr-icon shape="search"></clr-icon>
|
||||||
|
<input class="mac-search__input" type="text" placeholder="{{'SEARCH' | translate}}" [(ngModel)]="filterText" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mac-toolbar__right mac-stats">
|
||||||
|
<div class="mac-stat">
|
||||||
|
<div class="mac-stat__value">{{ totalItems }}</div>
|
||||||
|
<div class="mac-stat__label">{{'TOTAL_ITEMS' | translate}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mac-stat">
|
||||||
|
<div class="mac-stat__value">{{ mainMenuCount }}</div>
|
||||||
|
<div class="mac-stat__label">{{'MAIN_MENUS' | translate}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mac-stat">
|
||||||
|
<div class="mac-stat__value">{{ subMenuCount }}</div>
|
||||||
|
<div class="mac-stat__label">{{'SUB_MENUS' | translate}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading" *ngIf="!toggle">
|
<clr-datagrid class="mac-grid" [clrDgLoading]="loading" *ngIf="!toggle">
|
||||||
<clr-dg-placeholder>
|
<clr-dg-placeholder>
|
||||||
<ng-template #loadingSpinner><clr-spinner>Loading ... </clr-spinner></ng-template>
|
<ng-template #loadingSpinner>
|
||||||
<div *ngIf="alldata?.length === 0;else loadingSpinner">{{ 'NO_DATA_AVAILABLE' | translate }}</div>
|
<clr-spinner>{{'LOADING' | translate}} ... </clr-spinner>
|
||||||
|
</ng-template>
|
||||||
|
<div *ngIf="alldata?.length === 0;else loadingSpinner">{{'NO_DATA_AVAILABLE' | translate}}</div>
|
||||||
</clr-dg-placeholder>
|
</clr-dg-placeholder>
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'no'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-column [clrDgField]="'no'" class="mac-col mac-col--no">
|
||||||
{{ 'NO' | translate }}
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
</ng-container></clr-dg-column>
|
<span class="mac-col-title">
|
||||||
<clr-dg-column [clrDgField]="'menuname'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-icon shape="hashtag"></clr-icon> {{'NO' | translate}}
|
||||||
{{ 'MENU_ITEM_NAME' | translate }}
|
</span>
|
||||||
</ng-container></clr-dg-column>
|
</ng-container>
|
||||||
<clr-dg-column [clrDgField]="'mvisible'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
</clr-dg-column>
|
||||||
{{ 'VIEW' | translate }}
|
<clr-dg-column [clrDgField]="'menuname'" class="mac-col mac-col--name">
|
||||||
</ng-container></clr-dg-column>
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
<clr-dg-column [clrDgField]="'mcreate'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<span class="mac-col-title">
|
||||||
{{ 'CREATE' | translate }}
|
<clr-icon shape="tag"></clr-icon> {{'MENU_ITEM_NAME' | translate}}
|
||||||
</ng-container></clr-dg-column>
|
</span>
|
||||||
<clr-dg-column [clrDgField]="'medit'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</ng-container>
|
||||||
{{ 'EDIT' | translate }}
|
</clr-dg-column>
|
||||||
</ng-container></clr-dg-column>
|
<clr-dg-column [clrDgField]="'mvisible'" class="mac-col mac-col--perm">
|
||||||
<clr-dg-column [clrDgField]="'mdelete'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'DELETE' | translate }}
|
<span class="mac-col-title">
|
||||||
<!-- <input type="checkbox" clrCheckbox name="mdelete" [checked]="colvalue" (change)="changedelete($event.target.checked)"> -->
|
<clr-icon shape="eye"></clr-icon> {{'VIEW' | translate}}
|
||||||
</ng-container></clr-dg-column>
|
</span>
|
||||||
<clr-dg-column [clrDgField]="'mquery'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</ng-container>
|
||||||
{{ 'QUERY' | translate }}
|
</clr-dg-column>
|
||||||
</ng-container></clr-dg-column>
|
<clr-dg-column [clrDgField]="'mcreate'" class="mac-col mac-col--perm">
|
||||||
<clr-dg-column [clrDgField]="'mexport'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'EXPORT' | translate }}
|
<span class="mac-col-title">
|
||||||
</ng-container></clr-dg-column>
|
<clr-icon shape="plus"></clr-icon> {{'CREATE' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'medit'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="edit"></clr-icon> {{'EDIT' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'mdelete'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="trash"></clr-icon> {{'DELETE' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'mquery'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="search"></clr-icon> {{'QUERY' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'mexport'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="export"></clr-icon> {{'EXPORT' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-row *ngFor="let all of alldata;let i=index">
|
<clr-dg-row *ngFor="let all of filteredAllData;let i=index">
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
<clr-dg-cell *ngIf="all.menuId==0"><b>{{all.menuItemDesc}}</b></clr-dg-cell>
|
<clr-dg-cell *ngIf="all.menuId==0">
|
||||||
|
<b>{{all.menuItemDesc}}</b>
|
||||||
|
</clr-dg-cell>
|
||||||
<clr-dg-cell *ngIf="all.menuId!==0"> {{all.menuItemDesc}}</clr-dg-cell>
|
<clr-dg-cell *ngIf="all.menuId!==0"> {{all.menuItemDesc}}</clr-dg-cell>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mvisible"></clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mcreate"></clr-dg-cell>
|
<input type="checkbox" [checked]="selected ===all.mvisible">
|
||||||
<clr-dg-cell> <input type="checkbox" [checked]="selected ===all.medit"></clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mdelete"></clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mquery"></clr-dg-cell>
|
<input type="checkbox" [checked]="selected ===all.mcreate">
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mexport"></clr-dg-cell>
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.medit">
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.mdelete">
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.mquery">
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.mexport">
|
||||||
|
</clr-dg-cell>
|
||||||
|
|
||||||
<clr-dg-action-overflow>
|
<clr-dg-action-overflow>
|
||||||
<button class="action-item" (click)="modalEdit(all)">{{ 'EDIT' | translate }} <clr-icon shape="edit"
|
<button class="mac-action-item" (click)="modalEdit(all)">
|
||||||
class="is-error"></clr-icon></button>
|
{{'EDIT' | translate}}
|
||||||
<button class="action-item" *ngIf="all.menuId!==0" (click)="modalDelete(all)">{{ 'DELETE' | translate }}
|
<clr-icon shape="edit" class="is-error"></clr-icon>
|
||||||
<clr-icon shape="trash" class="is-error"></clr-icon></button>
|
</button>
|
||||||
<button class="action-item" *ngIf="all.menuId==0" (click)="modaldeletemainmenu(all)">{{ 'DELETE_MENU_SUBMENU' |
|
<button class="mac-action-item" *ngIf="all.menuId!==0" (click)="modalDelete(all)">
|
||||||
translate }}
|
{{'DELETE' | translate}}
|
||||||
<clr-icon shape="edit" class="is-error"></clr-icon></button>
|
<clr-icon shape="trash" class="is-error"></clr-icon>
|
||||||
|
</button>
|
||||||
|
<button class="mac-action-item" *ngIf="all.menuId==0" (click)="modaldeletemainmenu(all)">
|
||||||
|
{{'DELETE_MENU_SUBMENU' | translate}}
|
||||||
|
<clr-icon shape="edit" class="is-error"></clr-icon>
|
||||||
|
</button>
|
||||||
</clr-dg-action-overflow>
|
</clr-dg-action-overflow>
|
||||||
|
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="td-title">Menuname</td>
|
<td class="td-title">{{'MENUNAME' | translate}}</td>
|
||||||
<td class="td-content"></td>
|
<td class="td-content"></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</clr-dg-row-detail>
|
</clr-dg-row-detail>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
|
|
||||||
<clr-dg-footer>
|
<clr-dg-footer class="mac-grid-footer">
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Record per page</clr-dg-page-size>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{'RECORDS_PER_PAGE' | translate}}</clr-dg-page-size>
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
of {{pagination.totalItems}} Records
|
{{'OF' | translate}} {{pagination.totalItems}} {{'RECORDS' | translate}}
|
||||||
</clr-dg-pagination>
|
</clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading" *ngIf="toggle">
|
<clr-datagrid class="mac-grid" [clrDgLoading]="loading" *ngIf="toggle">
|
||||||
<clr-dg-placeholder>
|
<clr-dg-placeholder>
|
||||||
<ng-template #loadingSpinner><clr-spinner>Loading ... </clr-spinner></ng-template>
|
<ng-template #loadingSpinner>
|
||||||
<div *ngIf="alldata?.length === 0;else loadingSpinner">No data available</div>
|
<clr-spinner>{{'LOADING' | translate}} ... </clr-spinner>
|
||||||
|
</ng-template>
|
||||||
|
<div *ngIf="alldata?.length === 0;else loadingSpinner">{{'NO_DATA_AVAILABLE' | translate}}</div>
|
||||||
</clr-dg-placeholder>
|
</clr-dg-placeholder>
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'no'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-column [clrDgField]="'no'" class="mac-col mac-col--no">
|
||||||
NO
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
</ng-container></clr-dg-column>
|
<span class="mac-col-title">
|
||||||
<clr-dg-column [clrDgField]="'menuname'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-icon shape="hashtag"></clr-icon> {{'NO' | translate}}
|
||||||
Menu Item Name
|
</span>
|
||||||
</ng-container></clr-dg-column>
|
</ng-container>
|
||||||
<clr-dg-column [clrDgField]="'mvisible'"><ng-container *clrDgHideableColumn="{hidden: false}">
|
</clr-dg-column>
|
||||||
View
|
<clr-dg-column [clrDgField]="'menuname'" class="mac-col mac-col--name">
|
||||||
</ng-container></clr-dg-column>
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
<clr-dg-column [clrDgField]="'mcreate'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<span class="mac-col-title">
|
||||||
Create
|
<clr-icon shape="tag"></clr-icon> {{'MENU_ITEM_NAME' | translate}}
|
||||||
</ng-container></clr-dg-column>
|
</span>
|
||||||
<clr-dg-column [clrDgField]="'medit'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</ng-container>
|
||||||
Edit
|
</clr-dg-column>
|
||||||
</ng-container></clr-dg-column>
|
<clr-dg-column [clrDgField]="'mvisible'" class="mac-col mac-col--perm">
|
||||||
<clr-dg-column [clrDgField]="'mdelete'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
Delete
|
<span class="mac-col-title">
|
||||||
<!-- <input type="checkbox" clrCheckbox name="mdelete" [checked]="colvalue" (change)="changedelete($event.target.checked)"> -->
|
<clr-icon shape="eye"></clr-icon> {{'VIEW' | translate}}
|
||||||
</ng-container></clr-dg-column>
|
</span>
|
||||||
<clr-dg-column [clrDgField]="'mquery'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</ng-container>
|
||||||
Query
|
</clr-dg-column>
|
||||||
</ng-container></clr-dg-column>
|
<clr-dg-column [clrDgField]="'mcreate'" class="mac-col mac-col--perm">
|
||||||
<clr-dg-column [clrDgField]="'mexport'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
Export
|
<span class="mac-col-title">
|
||||||
</ng-container></clr-dg-column>
|
<clr-icon shape="plus"></clr-icon> {{'CREATE' | translate}}
|
||||||
<ng-container *ngFor="let all of alldata;let i=index">
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'medit'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="edit"></clr-icon> {{'EDIT' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'mdelete'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="trash"></clr-icon> {{'DELETE' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'mquery'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="search"></clr-icon> {{'QUERY' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'mexport'" class="mac-col mac-col--perm">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="mac-col-title">
|
||||||
|
<clr-icon shape="export"></clr-icon> {{'EXPORT' | translate}}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<ng-container *ngFor="let all of filteredAllData;let i=index">
|
||||||
<clr-dg-row *ngIf="all.menuId==0">
|
<clr-dg-row *ngIf="all.menuId==0">
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
<clr-dg-cell><b>{{all.menuItemDesc}}</b></clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mvisible"></clr-dg-cell>
|
<b>{{all.menuItemDesc}}</b>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mcreate"></clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell> <input type="checkbox" [checked]="selected ===all.medit"></clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mdelete"></clr-dg-cell>
|
<input type="checkbox" [checked]="selected ===all.mvisible">
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mquery"></clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell><input type="checkbox" [checked]="selected ===all.mexport"></clr-dg-cell>
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.mcreate">
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.medit">
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.mdelete">
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.mquery">
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<input type="checkbox" [checked]="selected ===all.mexport">
|
||||||
|
</clr-dg-cell>
|
||||||
<clr-dg-action-overflow>
|
<clr-dg-action-overflow>
|
||||||
<button class="action-item" (click)="Sync(all.menuItemId?.menuItemId,all)">Sync <clr-icon shape="shrink"
|
<button class="mac-action-item" (click)="Sync(all.menuItemId?.menuItemId,all)">
|
||||||
class="is-error"></clr-icon> </button>
|
{{'SYNC' | translate}}
|
||||||
|
<clr-icon shape="shrink" class="is-error"></clr-icon>
|
||||||
|
</button>
|
||||||
</clr-dg-action-overflow>
|
</clr-dg-action-overflow>
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="td-title">Menuname</td>
|
<td class="td-title">{{'MENUNAME' | translate}}</td>
|
||||||
<td class="td-content"></td>
|
<td class="td-content"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</clr-dg-row-detail>
|
</clr-dg-row-detail>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer class="mac-grid-footer">
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Record per page</clr-dg-page-size>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{'RECORDS_PER_PAGE' | translate}}</clr-dg-page-size>
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
of {{pagination.totalItems}} Records
|
{{'OF' | translate}} {{pagination.totalItems}} {{'RECORDS' | translate}}
|
||||||
</clr-dg-pagination>
|
</clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<clr-modal class="mac-modal" [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Add Menu Access Control</h3>
|
<h3 class="modal-title mac-modal-title">
|
||||||
|
<clr-icon shape="plus"></clr-icon> {{'ADD_MENU_ACCESS_CONTROL' | translate}}
|
||||||
|
</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12 mac-form-group">
|
||||||
<label for="name">Menu Name<span class="required-field">*</span></label>
|
<label class="mac-form-label" for="name">{{'MENU_NAME' | translate}}
|
||||||
<select name="" id="" style="width: 100%;padding: 5px 5px;border: 1px solid #ccc;border-radius: 4px;"
|
<span class="required-field">*</span>
|
||||||
(change)="idselected($event.target.value)">
|
</label>
|
||||||
|
<select name="" id="" class="mac-form-select w-100" (change)="idselected($event.target.value)">
|
||||||
<option *ngFor="let sub of menus" [value]="sub.menuItemId">{{sub.menuItemDesc}}</option>
|
<option *ngFor="let sub of menus" [value]="sub.menuItemId">{{sub.menuItemDesc}}</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div class="mac-form-help">{{'CHOOSE_MAIN_MENU_FOR_ACCESS' | translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modalAdd = false">Cancel</button>
|
<button type="button" class="mac-btn mac-btn-outline" (click)="modalAdd = false">
|
||||||
<button type="submit" class="btn btn-primary">ADD</button>
|
<clr-icon shape="times"></clr-icon>
|
||||||
|
<span>{{'CANCEL' | translate}}</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="mac-btn mac-btn-primary">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
<span>{{'ADD' | translate}}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<clr-modal class="mac-modal" [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Update Menu Access Control</h3>
|
<h3 class="modal-title mac-modal-title">
|
||||||
|
<clr-icon shape="edit"></clr-icon> {{'UPDATE_MENU_ACCESS_CONTROL' | translate}}
|
||||||
|
</h3>
|
||||||
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
||||||
<h2 class="heading">{{rowSelected.menuItemId.menuItemId}}</h2>
|
<div class="mac-form-help">{{'MENU_ID' | translate}}:
|
||||||
|
<code class="clr-code">{{rowSelected.menuItemId.menuItemId}}</code>
|
||||||
|
</div>
|
||||||
<form (ngSubmit)="onUpdate(rowSelected.menuItemId.menuItemId,rowSelected.usrGrp.usrGrp)">
|
<form (ngSubmit)="onUpdate(rowSelected.menuItemId.menuItemId,rowSelected.usrGrp.usrGrp)">
|
||||||
<div class="clr-row">
|
<div class="mac-perms">
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<label class="mac-perm">
|
||||||
<label for="tags">View</label>
|
<input type="checkbox" clrCheckbox name="mvisible" [(ngModel)]="selected ===rowSelected.mvisible" (change)="rowSelected.mvisible === $event.target.checked ? 'y' : 'n'">
|
||||||
<input type="checkbox" clrCheckbox name="mvisible" [(ngModel)]="selected ===rowSelected.mvisible"
|
<span class="mac-perm__icon">
|
||||||
(change)="rowSelected.mvisible === $event.target.checked ? 'y' : 'n'">
|
<clr-icon shape="eye"></clr-icon>
|
||||||
</div>
|
</span>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<span class="mac-perm__label">{{'VIEW' | translate}}</span>
|
||||||
<label for="name">Create</label>
|
</label>
|
||||||
<input type="checkbox" clrCheckbox name="mcreate" name="mcreate"
|
<label class="mac-perm">
|
||||||
[(ngModel)]="selected ===rowSelected.mcreate"
|
<input type="checkbox" clrCheckbox name="mcreate" [(ngModel)]="selected ===rowSelected.mcreate" (change)="rowSelected.mcreate === $event.target.checked ? 'y' : 'n'">
|
||||||
(change)="rowSelected.mcreate === $event.target.checked ? 'y' : 'n'">
|
<span class="mac-perm__icon">
|
||||||
</div>
|
<clr-icon shape="plus"></clr-icon>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
</span>
|
||||||
<label for="tags">Edit</label>
|
<span class="mac-perm__label">{{'CREATE' | translate}}</span>
|
||||||
<input type="checkbox" clrCheckbox name="medit" [(ngModel)]="selected ===rowSelected.medit"
|
</label>
|
||||||
(change)="rowSelected.medit === $event.target.checked ? 'y' : 'n'">
|
<label class="mac-perm">
|
||||||
</div>
|
<input type="checkbox" clrCheckbox name="medit" [(ngModel)]="selected ===rowSelected.medit" (change)="rowSelected.medit === $event.target.checked ? 'y' : 'n'">
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<span class="mac-perm__icon">
|
||||||
<label for="tags">Delete</label>
|
<clr-icon shape="edit"></clr-icon>
|
||||||
<input type="checkbox" clrCheckbox name="mdelete" [(ngModel)]="selected ===rowSelected.mdelete"
|
</span>
|
||||||
(change)="rowSelected.mdelete === $event.target.checked ? 'y' : 'n'">
|
<span class="mac-perm__label">{{'EDIT' | translate}}</span>
|
||||||
</div>
|
</label>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<label class="mac-perm">
|
||||||
<label for="tags">Query</label>
|
<input type="checkbox" clrCheckbox name="mdelete" [(ngModel)]="selected ===rowSelected.mdelete" (change)="rowSelected.mdelete === $event.target.checked ? 'y' : 'n'">
|
||||||
<input type="checkbox" clrCheckbox name="mquery" [(ngModel)]="selected ===rowSelected.mquery"
|
<span class="mac-perm__icon">
|
||||||
(change)="rowSelected.mquery === $event.target.checked ? 'y' : 'n'">
|
<clr-icon shape="trash"></clr-icon>
|
||||||
</div>
|
</span>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<span class="mac-perm__label">{{'DELETE' | translate}}</span>
|
||||||
<label for="tags">Export</label>
|
</label>
|
||||||
<input type="checkbox" clrCheckbox name="mexport" [(ngModel)]="selected ===rowSelected.mexport"
|
<label class="mac-perm">
|
||||||
(change)="rowSelected.mexport === $event.target.checked ? 'y' : 'n'">
|
<input type="checkbox" clrCheckbox name="mquery" [(ngModel)]="selected ===rowSelected.mquery" (change)="rowSelected.mquery === $event.target.checked ? 'y' : 'n'">
|
||||||
</div>
|
<span class="mac-perm__icon">
|
||||||
|
<clr-icon shape="search"></clr-icon>
|
||||||
|
</span>
|
||||||
|
<span class="mac-perm__label">{{'QUERY' | translate}}</span>
|
||||||
|
</label>
|
||||||
|
<label class="mac-perm">
|
||||||
|
<input type="checkbox" clrCheckbox name="mexport" [(ngModel)]="selected ===rowSelected.mexport" (change)="rowSelected.mexport === $event.target.checked ? 'y' : 'n'">
|
||||||
|
<span class="mac-perm__icon">
|
||||||
|
<clr-icon shape="export"></clr-icon>
|
||||||
|
</span>
|
||||||
|
<span class="mac-perm__label">{{'EXPORT' | translate}}</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaledit = false">Cancel</button>
|
<button type="button" class="mac-btn mac-btn-outline" (click)="modaledit = false">
|
||||||
<button type="submit" class="btn btn-primary">UPDATE</button>
|
<clr-icon shape="times"></clr-icon>
|
||||||
|
<span>{{'CANCEL' | translate}}</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="mac-btn mac-btn-primary">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
<span>{{'UPDATE' | translate}}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
<clr-modal class="mac-modal" [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
<div class="modal-body">
|
||||||
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
<div class="mac-delete-header">
|
||||||
<h1 class="delete">Are You Sure Want to delete?</h1>
|
<div class="mac-delete-icon">
|
||||||
<h2 class="heading">{{rowSelected.menuItemId.menuItemId}}</h2>
|
<clr-icon shape="exclamation-triangle" size="48"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<h1 class="mac-delete-title">{{'ARE_YOU_SURE_WANT_TO_DELETE' | translate}}?</h1>
|
||||||
|
<p class="mac-delete-subtitle">{{'THIS_ACTION_CANNOT_BE_UNDONE' | translate}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="mac-delete-details" *ngIf="rowSelected.menuItemId">
|
||||||
|
<div class="mac-delete-detail-item">
|
||||||
|
<span class="mac-delete-detail-label">{{'MENU_ITEM_ID' | translate}}:</span>
|
||||||
|
<span class="mac-delete-detail-value">{{rowSelected.menuItemId.menuItemId}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaldelete = false">Cancel</button>
|
<button type="button" class="mac-btn mac-btn-outline" (click)="modaldelete = false">
|
||||||
<button type="submit" (click)="delete(rowSelected.menuItemId.menuItemId,rowSelected.usrGrp.usrGrp)"
|
<clr-icon shape="times"></clr-icon>
|
||||||
class="btn btn-primary">Delete</button>
|
<span>{{'CANCEL' | translate}}</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" (click)="delete(rowSelected.menuItemId.menuItemId,rowSelected.usrGrp.usrGrp)" class="mac-btn mac-btn-error">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
<span>{{'DELETE' | translate}}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
<clr-modal [(clrModalOpen)]="modaldelete1" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
|
||||||
|
|
||||||
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
<clr-modal class="mac-modal" [(clrModalOpen)]="modaldelete1" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
<h1 class="delete">Are You Sure Want to delete Main menu with submenu?</h1>
|
<div class="modal-body">
|
||||||
<h2 class="heading">{{rowSelected.menuItemId.menuItemId}}</h2>
|
<div class="mac-delete-header">
|
||||||
|
<div class="mac-delete-icon">
|
||||||
|
<clr-icon shape="exclamation-triangle" size="48"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<h1 class="mac-delete-title">{{'ARE_YOU_SURE_WANT_TO_DELETE_MAIN_MENU_WITH_SUBMENU' | translate}}?</h1>
|
||||||
|
<p class="mac-delete-subtitle">{{'THIS_ACTION_CANNOT_BE_UNDONE' | translate}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="mac-delete-details" *ngIf="rowSelected.menuItemId">
|
||||||
|
<div class="mac-delete-detail-item">
|
||||||
|
<span class="mac-delete-detail-label">{{'MENU_ITEM_ID' | translate}}:</span>
|
||||||
|
<span class="mac-delete-detail-value">{{rowSelected.menuItemId.menuItemId}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaldelete1 = false">Cancel</button>
|
<button type="button" class="mac-btn mac-btn-outline" (click)="modaldelete1 = false">
|
||||||
<button type="submit" (click)="delete1(rowSelected.menuItemId.menuItemId,rowSelected.usrGrp.usrGrp)"
|
<clr-icon shape="times"></clr-icon>
|
||||||
class="btn btn-primary">Delete</button>
|
<span>{{'CANCEL' | translate}}</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" (click)="delete1(rowSelected.menuItemId.menuItemId,rowSelected.usrGrp.usrGrp)" class="mac-btn mac-btn-error">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
<span>{{'DELETE' | translate}}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
</div>
|
||||||
@@ -1,43 +1,160 @@
|
|||||||
input[type=text],[type=date],[type=password],[type=checkbox] {
|
@import '../../../../../styles/_design-tokens.scss';
|
||||||
|
|
||||||
|
// Import base styles
|
||||||
|
input[type=text],
|
||||||
|
[type=date],
|
||||||
|
[type=password],
|
||||||
|
[type=checkbox] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
// margin: 3px 0;
|
background-color: var(--theme-surface);
|
||||||
background-color:rgb(255, 255, 255);
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
color: var(--theme-text);
|
||||||
}
|
}
|
||||||
.required-field{
|
|
||||||
color: red;
|
|
||||||
font-size: 18px;
|
|
||||||
|
|
||||||
|
input[type=text]:focus,
|
||||||
|
[type=date]:focus,
|
||||||
|
[type=password]:focus,
|
||||||
|
[type=checkbox]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
|
||||||
}
|
}
|
||||||
// select{
|
|
||||||
// width: 100%;
|
.required-field {
|
||||||
// padding: 5px 5px;
|
color: var(--theme-error, #ef4444);
|
||||||
// border: 1px solid #ccc;
|
font-size: 18px;
|
||||||
// border-radius: 4px;
|
}
|
||||||
// }
|
|
||||||
.td-title {
|
.td-title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background-color: rgba(63, 122, 231, 0.863);
|
background-color: var(--theme-primary);
|
||||||
//color: rgb(24, 13, 13);
|
|
||||||
}
|
}
|
||||||
th{
|
|
||||||
//background-color:rgb(170, 169, 169);
|
th {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.td-content{
|
|
||||||
|
.td-content {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
.delete,.heading{
|
|
||||||
text-align: center;
|
// Modern Button Styles using ThemeService
|
||||||
color: red;
|
.mac-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
text-decoration: none;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--theme-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizes
|
||||||
|
&.mac-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mac-btn-md {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mac-btn-lg {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variants
|
||||||
|
&.mac-btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary, #0284c7) 0%, var(--theme-accent, #7c3aed) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mac-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.1);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mac-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
border-color: var(--theme-error-dark, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(239, 68, 68, 0.3), 0 4px 6px -2px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mac-btn-ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//toggle button
|
//toggle button
|
||||||
.switch {
|
.switch {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -76,24 +193,585 @@ th{
|
|||||||
transition: .4s;
|
transition: .4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked + .slider {
|
input:checked+.slider {
|
||||||
background-color: #0072a3;
|
background-color: var(--theme-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus + .slider {
|
input:focus+.slider {
|
||||||
box-shadow: 0 0 1px #0072a3;
|
box-shadow: 0 0 1px var(--theme-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked + .slider:before {
|
input:checked+.slider:before {
|
||||||
-webkit-transform: translateX(26px);
|
-webkit-transform: translateX(26px);
|
||||||
-ms-transform: translateX(26px);
|
-ms-transform: translateX(26px);
|
||||||
transform: translateX(26px);
|
transform: translateX(26px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider.round {
|
.slider.round {
|
||||||
border-radius: 34px;
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.round:before {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern themed enhancements (ThemeService variables)
|
||||||
|
.mm-breadcrumb {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-header-left {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-title h4 {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-edit-chip {
|
||||||
|
margin-top: 27px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: var(--theme-background);
|
||||||
|
color: var(--theme-text);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-header-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-select {
|
||||||
|
height: 40px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-100 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn-primary {
|
||||||
|
margin-left: 20px;
|
||||||
|
box-shadow: var(--theme-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-actions {
|
||||||
|
text-align: right;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-hint {
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-grid {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
::ng-deep .datagrid {
|
||||||
|
.datagrid-head {
|
||||||
|
background: var(--theme-background);
|
||||||
|
|
||||||
|
.datagrid-column {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-row {
|
||||||
|
transition: background-color 150ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider.round:before {
|
&:not(:last-child) {
|
||||||
border-radius: 50%;
|
border-bottom: 1px solid #e5e7eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.datagrid-cell {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-grid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-modal .modal-title {
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hero and actions
|
||||||
|
.mac-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px 32px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
font-family: var(--theme-font-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-sep {
|
||||||
|
width: 1px;
|
||||||
|
height: 22px;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-label {
|
||||||
|
color: white;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-toggle__label {
|
||||||
|
color: white;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid columns
|
||||||
|
.mac-col-title {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-col--perm .mac-col-title clr-icon {
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toolbar, search and stats
|
||||||
|
.mac-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-search {
|
||||||
|
position: relative;
|
||||||
|
max-width: 400px;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 12px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: #9ca3af;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 12px 12px 40px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-stat {
|
||||||
|
min-width: 110px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: var(--theme-shadow);
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhanced modals
|
||||||
|
.mac-modal-title clr-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--theme-text);
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 12px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 12px;
|
||||||
|
padding-right: 40px;
|
||||||
|
appearance: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-form-help {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-perms {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin: 24px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-perm {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
box-shadow: var(--theme-shadow);
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
width: auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-perm__icon clr-icon {
|
||||||
|
color: var(--theme-primary);
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-perm__label {
|
||||||
|
color: var(--theme-text);
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datagrid action overflow buttons
|
||||||
|
.mac-action-item {
|
||||||
|
@extend .mac-btn;
|
||||||
|
@extend .mac-btn-ghost;
|
||||||
|
@extend .mac-btn-sm;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mac-btn-error {
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Modal Styles
|
||||||
|
.mac-delete-header {
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-delete-icon {
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-delete-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-delete-subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-delete-details {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-delete-detail-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-delete-detail-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-delete-detail-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete,
|
||||||
|
.heading {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Header
|
||||||
|
::ng-deep .modal-header {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px 16px 0 0 !important;
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Body
|
||||||
|
::ng-deep .modal-body {
|
||||||
|
padding: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Footer
|
||||||
|
::ng-deep .modal-footer {
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-radius: 0 0 16px 16px !important;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mac-hero {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-search {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-stats {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-perms {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-actions {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mac-btn {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ import { MenuGroupService } from 'src/app/services/admin/menu-group.service';
|
|||||||
import { MenumaintanceService } from 'src/app/services/admin/menumaintance.service';
|
import { MenumaintanceService } from 'src/app/services/admin/menumaintance.service';
|
||||||
import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service';
|
import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service';
|
||||||
import { UsermaintanceService } from '../../../../services/admin/usermaintance.service';
|
import { UsermaintanceService } from '../../../../services/admin/usermaintance.service';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-menuaccesscontrol',
|
selector: 'app-menuaccesscontrol',
|
||||||
templateUrl: './menuaccesscontrol.component.html',
|
templateUrl: './menuaccesscontrol.component.html',
|
||||||
@@ -36,15 +38,26 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
toggle: boolean = false;
|
toggle: boolean = false;
|
||||||
maindata;
|
maindata;
|
||||||
showdata;
|
showdata;
|
||||||
constructor(private mainservice: UsermaintanceService,
|
// UI enhancements (no API changes)
|
||||||
|
filterText: string = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private mainservice: UsermaintanceService,
|
||||||
private _fb: FormBuilder,
|
private _fb: FormBuilder,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private usergrpservice: UsergrpmaintainceService,
|
private usergrpservice: UsergrpmaintainceService,
|
||||||
private menuGroupService: MenuGroupService,
|
private menuGroupService: MenuGroupService,
|
||||||
private menuservice: MenumaintanceService,) { }
|
private menuservice: MenumaintanceService,
|
||||||
|
private themeService: ThemeService
|
||||||
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Ensure theme variables are applied to the view
|
||||||
|
this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// CSS variables are updated globally; no extra handling needed here
|
||||||
|
});
|
||||||
|
|
||||||
this.showdata = this.menuGroupService.getdata();
|
this.showdata = this.menuGroupService.getdata();
|
||||||
console.log(this.showdata);
|
console.log(this.showdata);
|
||||||
this.mcreate = this.showdata.mcreate;
|
this.mcreate = this.showdata.mcreate;
|
||||||
@@ -61,35 +74,64 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
grpid: this.usergrpid,
|
grpid: this.usergrpid,
|
||||||
gmenuid: this.menuselectid,
|
gmenuid: this.menuselectid,
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats & filtering helpers for UI only
|
||||||
|
get totalItems(): number {
|
||||||
|
const list: any[] = (this.alldata as unknown as any[]) || [];
|
||||||
|
return list.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get mainMenuCount(): number {
|
||||||
|
const list: any[] = (this.alldata as unknown as any[]) || [];
|
||||||
|
return list.filter(it => it && it.menuId === 0).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get subMenuCount(): number {
|
||||||
|
const list: any[] = (this.alldata as unknown as any[]) || [];
|
||||||
|
return list.filter(it => it && it.menuId !== 0).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get filteredAllData(): any[] {
|
||||||
|
const items: any[] = (this.alldata as unknown as any[]) || [];
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
if (!text) { return items; }
|
||||||
|
return items.filter(m => (
|
||||||
|
(m?.menuItemDesc || '').toLowerCase().includes(text)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
dropddowngetdata() {
|
dropddowngetdata() {
|
||||||
this.usergrpservice.getAll().subscribe((data) => {
|
this.usergrpservice.getAll().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.givendata = data;
|
this.givendata = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getdata() {
|
getdata() {
|
||||||
this.menuservice.getByCurrentUserMenuGroupId1().subscribe(resp => {
|
this.menuservice.getByCurrentUserMenuGroupId1().subscribe(resp => {
|
||||||
this.menus = resp;
|
this.menus = resp;
|
||||||
console.log('menus: ', this.menus);
|
console.log('menus: ', this.menus);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getall() {
|
getall() {
|
||||||
this.usergrpservice.getall().subscribe((data) => {
|
this.usergrpservice.getall().subscribe((data) => {
|
||||||
this.secmenuaccessdata = data
|
this.secmenuaccessdata = data
|
||||||
console.log(data);
|
console.log(data);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
idofselected(val) {
|
idofselected(val) {
|
||||||
console.log(val);
|
console.log(val);
|
||||||
this.usergrpid = val;
|
this.usergrpid = val;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
idselected(val) {
|
idselected(val) {
|
||||||
console.log(val)
|
console.log(val)
|
||||||
this.menuselectid = val;
|
this.menuselectid = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
getbyuseriddata() {
|
getbyuseriddata() {
|
||||||
this.usergrpservice.getbyusergrpid(this.usergrpid).subscribe((data) => {
|
this.usergrpservice.getbyusergrpid(this.usergrpid).subscribe((data) => {
|
||||||
this.alldata = data;
|
this.alldata = data;
|
||||||
@@ -104,17 +146,19 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
|
|
||||||
// console.log(this.menudata)
|
// console.log(this.menudata)
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.log('Error in adding data...', error);
|
console.log('Error in adding data...', error);
|
||||||
if (error) {
|
if (error) {
|
||||||
this.error = "No data Available"
|
this.error = "No data Available"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
modaladd() {
|
modaladd() {
|
||||||
this.modalAdd = true;
|
this.modalAdd = true;
|
||||||
this.getdata();
|
this.getdata();
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.modalAdd = false;
|
this.modalAdd = false;
|
||||||
this.entryForm.value.grpid = this.usergrpid;
|
this.entryForm.value.grpid = this.usergrpid;
|
||||||
@@ -128,28 +172,30 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
|
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.log('Error in adding data...', error);
|
console.log('Error in adding data...', error);
|
||||||
if (error.status == 400) {
|
if (error.status == 400) {
|
||||||
this.toastr.error(error.error);
|
this.toastr.error(error.error);
|
||||||
|
|
||||||
} else if (error) {
|
|
||||||
this.toastr.error('Not added Data Getting Some Error');
|
|
||||||
}
|
|
||||||
this.ngOnInit();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
} else if (error) {
|
||||||
|
this.toastr.error('Not added Data Getting Some Error');
|
||||||
|
}
|
||||||
|
this.ngOnInit();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
modalEdit(row) {
|
modalEdit(row) {
|
||||||
this.rowSelected = row;
|
this.rowSelected = row;
|
||||||
console.log(this.rowSelected);
|
console.log(this.rowSelected);
|
||||||
this.modaledit = true;
|
this.modaledit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
modalDelete(row) {
|
modalDelete(row) {
|
||||||
this.rowSelected = row;
|
this.rowSelected = row;
|
||||||
console.log(this.rowSelected)
|
console.log(this.rowSelected)
|
||||||
this.modaldelete = true;
|
this.modaldelete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(id, usrgrp) {
|
delete(id, usrgrp) {
|
||||||
this.modaldelete = false;
|
this.modaldelete = false;
|
||||||
this.usergrpservice.delete(id, usrgrp).subscribe((data) => {
|
this.usergrpservice.delete(id, usrgrp).subscribe((data) => {
|
||||||
@@ -166,11 +212,13 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
modaldeletemainmenu(row) {
|
modaldeletemainmenu(row) {
|
||||||
this.rowSelected = row;
|
this.rowSelected = row;
|
||||||
console.log(this.rowSelected)
|
console.log(this.rowSelected)
|
||||||
this.modaldelete1 = true;
|
this.modaldelete1 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete1(id, usrgrp) {
|
delete1(id, usrgrp) {
|
||||||
this.modaldelete1 = false;
|
this.modaldelete1 = false;
|
||||||
this.usergrpservice.deletemain(id, usrgrp).subscribe((data) => {
|
this.usergrpservice.deletemain(id, usrgrp).subscribe((data) => {
|
||||||
@@ -187,6 +235,7 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(id: any, usrgrp: any) {
|
onUpdate(id: any, usrgrp: any) {
|
||||||
this.modaledit = false;
|
this.modaledit = false;
|
||||||
console.log(id, usrgrp);
|
console.log(id, usrgrp);
|
||||||
@@ -204,6 +253,7 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onChecked(value) {
|
onChecked(value) {
|
||||||
if (value == "y") {
|
if (value == "y") {
|
||||||
this.selected = "y"
|
this.selected = "y"
|
||||||
@@ -213,11 +263,13 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
this.selected = "n"// make a call for unchecked
|
this.selected = "n"// make a call for unchecked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changedelete(val) {
|
changedelete(val) {
|
||||||
console.log(val);
|
console.log(val);
|
||||||
val = this.colvalue = val;
|
val = this.colvalue = val;
|
||||||
console.log(val);
|
console.log(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
data: {};
|
data: {};
|
||||||
Sync(id: any, row) {
|
Sync(id: any, row) {
|
||||||
this.rowSelected = row;
|
this.rowSelected = row;
|
||||||
@@ -239,6 +291,7 @@ export class MenuaccesscontrolComponent implements OnInit {
|
|||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCheckbox() {
|
toggleCheckbox() {
|
||||||
this.toggle = !this.toggle;
|
this.toggle = !this.toggle;
|
||||||
//this.dataService.setDivToggler(this.toggler);
|
//this.dataService.setDivToggler(this.toggler);
|
||||||
|
|||||||
@@ -1,11 +1,39 @@
|
|||||||
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
|
||||||
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>{{ 'SECURITY' | translate }}</a></li>
|
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>{{ 'SECURITY' | translate }}</a></li>
|
||||||
<li><a href="javascript://">{{ 'MENU_MAINTENANCE' | translate }}</a></li>
|
<li><a href="javascript://">{{ 'MENU_MAINTENANCE' | translate }}</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<div class="mm-container">
|
||||||
|
<section class="mm-hero">
|
||||||
|
<div class="mm-hero__content">
|
||||||
|
<div class="mm-hero__icon">
|
||||||
|
<clr-icon shape="organization"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 class="mm-hero__title">{{ 'MENU_MAINTENANCE' | translate }}</h2>
|
||||||
|
<p class="mm-hero__subtitle">{{ 'MAIN_MENU' | translate }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mm-hero__actions">
|
||||||
|
<button class="btn btn-outline mm-btn-outline" (click)="onExport()">
|
||||||
|
<clr-icon shape="export"></clr-icon>
|
||||||
|
<span class="mm-btn-text">{{ 'EXPORT_XLSX' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary mm-btn-primary" (click)="csvImport()">
|
||||||
|
<clr-icon shape="import"></clr-icon>
|
||||||
|
<span class="mm-btn-text">{{ 'IMPORT' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button id="add" class="btn btn-primary mm-btn-primary" (click)="goToAdd()">
|
||||||
|
<clr-icon shape="plus"></clr-icon>
|
||||||
|
<span class="mm-btn-text">{{ 'ADD' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn mm-btn-secondary" (click)="downloadFiles()">
|
||||||
|
<clr-icon shape="download"></clr-icon>
|
||||||
|
<span class="mm-btn-text">{{ 'DOWNLOAD_TEMPLATE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
<!-- <div class="clr-row">
|
<!-- <div class="clr-row">
|
||||||
<div class="clr-col-12" style="text-align: right;">
|
<div class="clr-col-12" style="text-align: right;">
|
||||||
<button id="add" *ngIf="mcreate == 'true'" class="btn btn-primary" (click)="goToAdd()">
|
<button id="add" *ngIf="mcreate == 'true'" class="btn btn-primary" (click)="goToAdd()">
|
||||||
@@ -13,29 +41,44 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="clr-row" style="margin-top: 15px;">
|
<div class="mm-stats">
|
||||||
<div class="clr-col">
|
<div class="mm-stat">
|
||||||
<h3 style="font-weight: 300;display: inline;">{{ 'MENU_MAINTENANCE' | translate }} </h3>
|
<div class="mm-stat__value">{{ totalMenus }}</div>
|
||||||
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">{{ 'MAIN_MENU' | translate }}</span><br>
|
<div class="mm-stat__label">{{ 'TOTAL_MENUS' | translate }}</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mm-stat">
|
||||||
<div class="clr-col" style="text-align: right;">
|
<div class="mm-stat__value">{{ enabledMenusCount }}</div>
|
||||||
<div class="btn-group">
|
<div class="mm-stat__label">{{ 'ENABLED' | translate }}</div>
|
||||||
<button class="btn text-dark" (click)="downloadFiles()"><b>{{ 'DOWNLOAD_TEMPLATE' | translate }}</b></button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mm-stat">
|
||||||
|
<div class="mm-stat__value">{{ disabledMenusCount }}</div>
|
||||||
|
<div class="mm-stat__label">{{ 'DISABLED' | translate }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-primary" (click)="csvImport()">{{ 'IMPORT' | translate }}</button>
|
<div class="mm-toolbar">
|
||||||
|
<div class="mm-toolbar__left">
|
||||||
<button class="btn btn-outline" (click)="onExport()">
|
<div class="mm-search">
|
||||||
<clr-icon shape="export"></clr-icon> {{ 'EXPORT_XLSX' | translate }}
|
<clr-icon shape="search"></clr-icon>
|
||||||
</button>
|
<input class="mm-search__input" type="text" [placeholder]="'SEARCH' | translate" [(ngModel)]="filterText" />
|
||||||
<button id="add" class="btn btn-primary" (click)="goToAdd()">
|
</div>
|
||||||
<clr-icon shape="plus"></clr-icon> {{ 'ADD' | translate }}
|
</div>
|
||||||
|
<div class="mm-toolbar__right">
|
||||||
|
<select class="mm-select" [(ngModel)]="statusFilter">
|
||||||
|
<option value="All">{{ 'ALL' | translate }}</option>
|
||||||
|
<option value="Enable">{{ 'ENABLED' | translate }}</option>
|
||||||
|
<option value="Disable">{{ 'DISABLED' | translate }}</option>
|
||||||
|
</select>
|
||||||
|
<div class="mm-view-toggle">
|
||||||
|
<button class="btn btn-sm" [class.btn-primary]="viewMode==='cards'" (click)="setViewMode('cards')">
|
||||||
|
<clr-icon shape="view-cards"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-sm" [class.btn-primary]="viewMode==='table'" (click)="setViewMode('table')">
|
||||||
|
<clr-icon shape="table"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <table class="table">
|
<!-- <table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -144,62 +187,81 @@
|
|||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table> -->
|
</table> -->
|
||||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSelected)]="selected">
|
|
||||||
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>{{'LOADING'|translate}} ... </clr-spinner></ng-template>
|
<div class="mm-skeleton-list" *ngIf="loading">
|
||||||
<div *ngIf="error;else loadingSpinner">{{error}}</div></clr-dg-placeholder>
|
<div class="mm-skeleton-item" *ngFor="let i of [1,2,3,4,5,6]">
|
||||||
<clr-dg-column [clrDgField]="''" style="max-width: 40px;">
|
<div class="mm-skel mm-skel--icon"></div>
|
||||||
|
<div class="mm-skel mm-skel--title"></div>
|
||||||
|
<div class="mm-skel mm-skel--meta"></div>
|
||||||
|
<div class="mm-skel mm-skel--chip"></div>
|
||||||
|
<div class="mm-skel mm-skel--link"></div>
|
||||||
|
<div class="mm-skel mm-skel--badge"></div>
|
||||||
|
<div class="mm-skel mm-skel--actions"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mm-card" *ngIf="viewMode==='table'">
|
||||||
|
<clr-datagrid class="mm-grid mm-grid--modern" [clrDgLoading]="loading" [(clrDgSelected)]="selected">
|
||||||
|
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>{{'LOADING'|translate}} ...
|
||||||
|
</clr-spinner></ng-template>
|
||||||
|
<div class="mm-empty" *ngIf="error;else loadingSpinner">
|
||||||
|
<clr-icon shape="warning-standard"></clr-icon>
|
||||||
|
<div class="mm-empty__title">{{ 'NO_DATA' | translate }}</div>
|
||||||
|
<div class="mm-empty__subtitle">{{ error }}</div>
|
||||||
|
</div>
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
<clr-dg-column [clrDgField]="''" class="mm-col mm-col--icon">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}"></ng-container>
|
<ng-container *clrDgHideableColumn="{hidden: false}"></ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--no">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'NO' | translate}}
|
<span class="mm-col-title"><clr-icon shape="hashtag"></clr-icon> {{'NO' | translate}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--name">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'MENU_ITEM_NAME' | translate}}
|
<span class="mm-col-title"><clr-icon shape="tag"></clr-icon> {{'MENU_ITEM_NAME' | translate}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--id">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'ID' | translate}}
|
<span class="mm-col-title"><clr-icon shape="id-badge"></clr-icon> {{'ID' | translate}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--seq">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'SEQUENCE' | translate}}
|
<span class="mm-col-title"><clr-icon shape="number-list"></clr-icon> {{'SEQUENCE' | translate}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--module">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'MODULE_NAME' | translate}}
|
<span class="mm-col-title"><clr-icon shape="application"></clr-icon> {{'MODULE_NAME' | translate}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--link">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'MENU_ACTION_LINK' | translate}}
|
<span class="mm-col-title"><clr-icon shape="link"></clr-icon> {{'MENU_ACTION_LINK' | translate}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--status">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'STATUS' | translate}}
|
<span class="mm-col-title"><clr-icon shape="shield-check"></clr-icon> {{'STATUS' | translate}}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--submenu">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{'SUB_MENU' | translate}}
|
{{'SUB_MENU' | translate}}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-column>
|
<clr-dg-column class="mm-col mm-col--action">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
<clr-icon shape="bars"></clr-icon>
|
<clr-icon shape="bars"></clr-icon>
|
||||||
{{'ACTION' | translate}}
|
{{'ACTION' | translate}}
|
||||||
@@ -207,110 +269,171 @@
|
|||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of menus;let i = index" [clrDgItem]="user">
|
<clr-dg-row class="mm-row" *clrDgItems="let user of filteredMenus;let i = index" [clrDgItem]="user">
|
||||||
<clr-dg-cell style="max-width: 40px;">
|
<clr-dg-cell class="mm-cell mm-cell--icon">
|
||||||
<span style="cursor: pointer;"><clr-icon shape="edit" (click)="goToEdit(user)" class="red is-error" style="color:red;"></clr-icon></span>
|
<span class="mm-icon-action"><clr-icon shape="edit" (click)="goToEdit(user)"
|
||||||
</clr-dg-cell>
|
class="is-error mm-icon-edit"></clr-icon></span>
|
||||||
<clr-dg-cell >{{i+1}}</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.menuItemDesc}}</clr-dg-cell>
|
<clr-dg-cell class="mm-cell mm-cell--no">{{i+1}}</clr-dg-cell>
|
||||||
<clr-dg-cell >{{user.menuId}}</clr-dg-cell>
|
<clr-dg-cell class="mm-cell mm-cell--name">
|
||||||
<clr-dg-cell >{{user.itemSeq}}</clr-dg-cell>
|
<div class="mm-item">
|
||||||
<clr-dg-cell > {{user.moduleName}} </clr-dg-cell>
|
<div class="mm-item__avatar"><clr-icon shape="bookmark"></clr-icon></div>
|
||||||
<clr-dg-cell > {{user.main_menu_action_name}}</clr-dg-cell>
|
<div class="mm-item__text">
|
||||||
<clr-dg-cell >{{user.status}} </clr-dg-cell>
|
<div class="mm-item__title">{{user.menuItemDesc}}</div>
|
||||||
<clr-dg-cell ><clr-icon shape="bullet-list" (click)="submenu(user.menuItemId)" style="color: rgb(108, 108, 194);"></clr-icon> </clr-dg-cell>
|
<div class="mm-item__meta">ID: {{user.menuId}} ⢠Seq: {{user.itemSeq}}</div>
|
||||||
<clr-dg-cell>
|
</div>
|
||||||
<span style="cursor: pointer;padding: 10px; "><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error" style="color: red;"></clr-icon></span>
|
</div>
|
||||||
<clr-signpost>
|
</clr-dg-cell>
|
||||||
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span>
|
<clr-dg-cell class="mm-cell mm-cell--id">{{user.menuId}}</clr-dg-cell>
|
||||||
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
<clr-dg-cell class="mm-cell mm-cell--seq">{{user.itemSeq}}</clr-dg-cell>
|
||||||
<h5 style="margin-top: 0">{{'WHO_COLUMN'|translate}} </h5>
|
<clr-dg-cell class="mm-cell mm-cell--module">
|
||||||
<div>{{'ACCOUNT_ID'|translate}} <code class="clr-code">{{user.accountId}}</code></div>
|
<div class="mm-chip mm-chip--module">
|
||||||
<div>{{'CREATED_AT'|translate}} <code class="clr-code">{{user.createdAt| date}}</code></div>
|
<clr-icon shape="application"></clr-icon>
|
||||||
<div>{{'CREATED_BY'|translate}} <code class="clr-code">{{user.createdBy}}</code></div>
|
<span>{{user.moduleName}}</span>
|
||||||
<div>{{'UPDATED_AT'|translate}} <code class="clr-code">{{user.updatedAt | date}}</code></div>
|
</div>
|
||||||
<div>{{'UPDATED_BY'|translate}} <code class="clr-code">{{user.updatedBy}}</code></div>
|
</clr-dg-cell>
|
||||||
</clr-signpost-content>
|
<clr-dg-cell class="mm-cell mm-cell--link">
|
||||||
</clr-signpost>
|
<div class="mm-link">
|
||||||
</clr-dg-cell>
|
<clr-icon shape="link"></clr-icon>
|
||||||
<!-- <clr-dg-action-overflow>
|
<span>{{user.main_menu_action_name}}</span>
|
||||||
|
</div>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell class="mm-cell mm-cell--status">
|
||||||
|
<span class="mm-badge" [class.mm-badge--success]="user.status==='Enable'"
|
||||||
|
[class.mm-badge--muted]="user.status!=='Enable'">{{user.status}}</span>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell class="mm-cell mm-cell--submenu"><clr-icon shape="bullet-list" (click)="submenu(user.menuItemId)"
|
||||||
|
class="mm-icon-link"></clr-icon> </clr-dg-cell>
|
||||||
|
<clr-dg-cell class="mm-cell mm-cell--action">
|
||||||
|
<div class="mm-actions-inline">
|
||||||
|
<button class="btn btn-sm btn-outline" (click)="goToEdit(user)"><clr-icon shape="edit"></clr-icon></button>
|
||||||
|
<button class="btn btn-sm btn-outline" (click)="onDelete(user)"><clr-icon shape="trash"></clr-icon></button>
|
||||||
|
</div>
|
||||||
|
<clr-signpost>
|
||||||
|
<span class="mm-icon-action" clrSignpostTrigger><clr-icon shape="help"
|
||||||
|
class="mm-icon-help"></clr-icon></span>
|
||||||
|
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
||||||
|
<h5 style="margin-top: 0">{{'WHO_COLUMN'|translate}} </h5>
|
||||||
|
<div>{{'ACCOUNT_ID'|translate}} <code class="clr-code">{{user.accountId}}</code></div>
|
||||||
|
<div>{{'CREATED_AT'|translate}} <code class="clr-code">{{user.createdAt| date}}</code></div>
|
||||||
|
<div>{{'CREATED_BY'|translate}} <code class="clr-code">{{user.createdBy}}</code></div>
|
||||||
|
<div>{{'UPDATED_AT'|translate}} <code class="clr-code">{{user.updatedAt | date}}</code></div>
|
||||||
|
<div>{{'UPDATED_BY'|translate}} <code class="clr-code">{{user.updatedBy}}</code></div>
|
||||||
|
</clr-signpost-content>
|
||||||
|
</clr-signpost>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<!-- <clr-dg-action-overflow>
|
||||||
<button class="action-item" *ngIf="medit == 'true'" (click)="goToEdit(user)">Edit <clr-icon shape="edit" class="is-error"></clr-icon></button>
|
<button class="action-item" *ngIf="medit == 'true'" (click)="goToEdit(user)">Edit <clr-icon shape="edit" class="is-error"></clr-icon></button>
|
||||||
<button class="action-item" *ngIf="mdelete == 'true'" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button>
|
<button class="action-item" *ngIf="mdelete == 'true'" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button>
|
||||||
</clr-dg-action-overflow> -->
|
</clr-dg-action-overflow> -->
|
||||||
|
|
||||||
<clr-dg-row-detail *clrIfExpanded >
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
<table class="table">
|
<div class="mm-detail">
|
||||||
<tr>
|
<div class="mm-detail__row"><span>Menu Name</span><strong>{{user.menuItemDesc}}</strong></div>
|
||||||
<td class="td-title">Menu Name: </td>
|
<div class="mm-detail__row"><span>Action Link</span><strong>{{user.main_menu_action_name}}</strong></div>
|
||||||
<td class="td-content">{{user.menuItemDesc}}</td>
|
<div class="mm-detail__row"><span>Module</span><strong>{{user.moduleName}}</strong></div>
|
||||||
</tr>
|
</div>
|
||||||
</table>
|
</clr-dg-row-detail>
|
||||||
</clr-dg-row-detail>
|
</clr-dg-row>
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
<clr-dg-footer class="mm-grid-footer">
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Users per page</clr-dg-page-size>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Users per page</clr-dg-page-size>
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
of {{pagination.totalItems}} users
|
of {{pagination.totalItems}} users
|
||||||
</clr-dg-pagination>
|
</clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<div *ngIf="viewMode==='cards'">
|
||||||
|
<div class="mm-cards">
|
||||||
|
<div class="mm-card-item" *ngFor="let user of filteredMenus; let i = index">
|
||||||
|
<div class="mm-card-item__header">
|
||||||
|
<!-- <div class="mm-card-item__icon"><clr-icon [shape]="getIconShape(user)"></clr-icon></div> -->
|
||||||
|
<div class="mm-card-item__title">{{user.menuItemDesc}}</div>
|
||||||
|
<div class="mm-card-item__badge" [class.mm-badge--success]="user.status==='Enable'"
|
||||||
|
[class.mm-badge--muted]="user.status!=='Enable'">{{user.status}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mm-card-item__body">
|
||||||
|
<div class="mm-kv"><span>ID</span><strong>{{user.menuId}}</strong></div>
|
||||||
|
<div class="mm-kv"><span>Sequence</span><strong>{{user.itemSeq}}</strong></div>
|
||||||
|
<div class="mm-kv"><span>Module</span><strong>{{user.moduleName}}</strong></div>
|
||||||
|
<div class="mm-kv mm-link"><clr-icon shape="link"></clr-icon><strong>{{user.main_menu_action_name}}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mm-card-item__footer">
|
||||||
|
<button class="btn btn-sm btn-outline" (click)="goToEdit(user)"><clr-icon shape="edit"></clr-icon>
|
||||||
|
Edit</button>
|
||||||
|
<button class="btn btn-sm btn-outline" (click)="onDelete(user)"><clr-icon shape="trash"></clr-icon>
|
||||||
|
Delete</button>
|
||||||
|
<button class="btn btn-sm" (click)="submenu(user.menuItemId)"><clr-icon shape="bullet-list"></clr-icon>
|
||||||
|
Submenu</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<clr-modal class="mm-modal" [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Add Menu Maintenance</h3>
|
<h3 class="modal-title">Add Menu Maintenance</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<!-- <div class="clr-col-sm-12">
|
<!-- <div class="clr-col-sm-12">
|
||||||
<label for="name">Menu Item ID<span class="required-field">*</span></label>
|
<label for="name">Menu Item ID<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" formControlName="menuItemId">
|
<input type="text" class="clr-input" formControlName="menuItemId">
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="name">Menu ID<span class="required-field">*</span></label>
|
<label for="name">Menu ID<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" formControlName="menuId" name="menuId" [(ngModel)]="mainid" readonly>
|
<input type="text" class="clr-input" formControlName="menuId" name="menuId" [(ngModel)]="mainid" readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="name">Menu Item Name<span class="required-field">*</span></label>
|
<label for="name">Menu Item Name<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" formControlName="menuItemDesc">
|
<input type="text" class="clr-input" formControlName="menuItemDesc">
|
||||||
<div *ngIf="submitted && entryForm.controls.menuItemDesc.errors" class="error_mess">
|
<div *ngIf="submitted && entryForm.controls.menuItemDesc.errors" class="error_mess">
|
||||||
<div *ngIf="submitted && entryForm.controls.menuItemDesc.errors.required" class="error_mess">*This field is Required</div>
|
<div *ngIf="submitted && entryForm.controls.menuItemDesc.errors.required" class="error_mess">*This field
|
||||||
</div>
|
is Required</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="tags">Sequence</label>
|
<label for="tags">Sequence</label>
|
||||||
<input type="text" class="clr-input" formControlName="itemSeq">
|
<input type="text" class="clr-input" formControlName="itemSeq">
|
||||||
<div *ngIf="submitted && entryForm.controls.itemSeq.errors" class="error_mess">
|
<div *ngIf="submitted && entryForm.controls.itemSeq.errors" class="error_mess">
|
||||||
<div *ngIf="submitted && entryForm.controls.itemSeq.errors.required" class="error_mess">*This field is Required</div>
|
<div *ngIf="submitted && entryForm.controls.itemSeq.errors.required" class="error_mess">*This field is
|
||||||
|
Required</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="submitted && entryForm.controls.itemSeq.errors" class="error_mess">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.itemSeq.errors.pattern" class="error_mess">*Required Only
|
||||||
|
Number</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="submitted && entryForm.controls.itemSeq.errors" class="error_mess">
|
<div class="clr-col-sm-12">
|
||||||
<div *ngIf="submitted && entryForm.controls.itemSeq.errors.pattern" class="error_mess">*Required Only Number</div>
|
<label for="tags">Module Name</label>
|
||||||
</div>
|
<input type="text" class="clr-input" formControlName="moduleName">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.moduleName.errors" class="error_mess">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.moduleName.errors.required" class="error_mess">*This field is
|
||||||
|
Required</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
</div>
|
||||||
<label for="tags">Module Name</label>
|
<div class="clr-col-sm-12">
|
||||||
<input type="text" class="clr-input" formControlName="moduleName">
|
<label for="tags">Status</label>
|
||||||
<div *ngIf="submitted && entryForm.controls.moduleName.errors" class="error_mess">
|
<select id="" formControlName="status">
|
||||||
<div *ngIf="submitted && entryForm.controls.moduleName.errors.required" class="error_mess">*This field is Required</div>
|
<option value="Enable">Enable</option>
|
||||||
|
<option value="Disable">Disable</option>
|
||||||
|
</select>
|
||||||
|
<div *ngIf="submitted && entryForm.controls.status.errors" class="error_mess">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.status.errors.required" class="error_mess">*This field is
|
||||||
|
Required</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="tags">Status</label>
|
<label for="tags">Menu Action Link</label>
|
||||||
<select id="" formControlName="status">
|
<input type="text" class="clr-input" formControlName="main_menu_action_name">
|
||||||
<option value="Enable">Enable</option>
|
<div *ngIf="submitted && entryForm.controls.main_menu_action_name.errors" class="error_mess">
|
||||||
<option value="Disable">Disable</option>
|
<div *ngIf="submitted && entryForm.controls.main_menu_action_name.errors.required" class="error_mess">
|
||||||
</select>
|
*This field is Required</div>
|
||||||
<div *ngIf="submitted && entryForm.controls.status.errors" class="error_mess">
|
</div>
|
||||||
<div *ngIf="submitted && entryForm.controls.status.errors.required" class="error_mess">*This field is Required</div>
|
<!-- <select NAME="menu_id" >
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Menu Action Link</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="main_menu_action_name">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.main_menu_action_name.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.main_menu_action_name.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
|
||||||
<!-- <select NAME="menu_id" >
|
|
||||||
<option SELECTED >Home Menu
|
<option SELECTED >Home Menu
|
||||||
<OPTION >Home
|
<OPTION >Home
|
||||||
<OPTION >Job
|
<OPTION >Job
|
||||||
@@ -348,94 +471,99 @@
|
|||||||
|
|
||||||
|
|
||||||
</select> -->
|
</select> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="tags">Menu Icon Name</label>
|
<label for="tags">Menu Icon Name</label>
|
||||||
<input type="text" class="clr-input" formControlName="main_menu_icon_name">
|
<input type="text" class="clr-input" formControlName="main_menu_icon_name">
|
||||||
<div *ngIf="submitted && entryForm.controls.main_menu_icon_name.errors" class="error_mess">
|
<div *ngIf="submitted && entryForm.controls.main_menu_icon_name.errors" class="error_mess">
|
||||||
<div *ngIf="submitted && entryForm.controls.main_menu_icon_name.errors.required" class="error_mess">*This field is Required</div>
|
<div *ngIf="submitted && entryForm.controls.main_menu_icon_name.errors.required" class="error_mess">*This
|
||||||
</div>
|
field is Required</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modalAdd = false">Cancel</button>
|
<button type="button" class="btn btn-outline" (click)="modalAdd = false">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary" >ADD</button>
|
<button type="submit" class="btn btn-primary">ADD</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
<clr-modal class="mm-modal" [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
|
||||||
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
||||||
<h1 class="delete">Are You Sure Want to delete?</h1>
|
<h1 class="delete">Are You Sure Want to delete?</h1>
|
||||||
<h2 class="heading">{{rowSelected.menuItemId}}</h2>
|
<h2 class="heading">{{rowSelected.menuItemId}}</h2>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaldelete = false">Cancel</button>
|
<button type="button" class="btn btn-outline" (click)="modaldelete = false">Cancel</button>
|
||||||
<button type="submit" (click)="delete(rowSelected.menuItemId)" class="btn btn-primary" >Delete</button>
|
<button type="submit" (click)="delete(rowSelected.menuItemId)" class="btn btn-primary">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<clr-modal class="mm-modal" [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Update Menu Maintenance</h3>
|
<h3 class="modal-title">Update Menu Maintenance</h3>
|
||||||
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
||||||
<h2 class="heading">{{rowSelected.menuItemId}}</h2>
|
<h2 class="heading">{{rowSelected.menuItemId}}</h2>
|
||||||
<form (ngSubmit)="onUpdate(rowSelected.menuItemId)">
|
<form (ngSubmit)="onUpdate(rowSelected.menuItemId)">
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
|
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="name">Menu ID<span class="required-field">*</span></label>
|
<label for="name">Menu ID<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" name="menuId" [(ngModel)]="rowSelected.menuId" readonly>
|
<input type="text" class="clr-input" name="menuId" [(ngModel)]="rowSelected.menuId" readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="name">Menu Item Name<span class="required-field">*</span></label>
|
<label for="name">Menu Item Name<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" name="menuItemDesc" [(ngModel)]="rowSelected.menuItemDesc">
|
<input type="text" class="clr-input" name="menuItemDesc" [(ngModel)]="rowSelected.menuItemDesc">
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="tags">Sequence</label>
|
<label for="tags">Sequence</label>
|
||||||
<input type="text" class="clr-input" name="itemSeq" [(ngModel)]="rowSelected.itemSeq">
|
<input type="text" class="clr-input" name="itemSeq" [(ngModel)]="rowSelected.itemSeq">
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="tags">Module Name</label>
|
<label for="tags">Module Name</label>
|
||||||
<input type="text" class="clr-input" name="moduleName" [(ngModel)]="rowSelected.moduleName">
|
<input type="text" class="clr-input" name="moduleName" [(ngModel)]="rowSelected.moduleName">
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="tags">Status</label>
|
<label for="tags">Status</label>
|
||||||
<select id="" name="status" [(ngModel)]="rowSelected.status">
|
<select id="" name="status" [(ngModel)]="rowSelected.status">
|
||||||
<option value="Enable">Enable</option>
|
<option value="Enable">Enable</option>
|
||||||
<option value="Disable">Disable</option>
|
<option value="Disable">Disable</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label for="tags">Menu Action Link</label>
|
<label for="tags">Menu Action Link</label>
|
||||||
<input type="text" class="clr-input" name="main_menu_action_name" [(ngModel)]="rowSelected.main_menu_action_name">
|
<input type="text" class="clr-input" name="main_menu_action_name"
|
||||||
</div>
|
[(ngModel)]="rowSelected.main_menu_action_name">
|
||||||
<div class="clr-col-sm-12">
|
</div>
|
||||||
<label for="tags">Menu Icon Name </label>
|
<div class="clr-col-sm-12">
|
||||||
<input type="text" class="clr-input" name="main_menu_icon_name" [(ngModel)]="rowSelected.main_menu_icon_name">
|
<label for="tags">Menu Icon Name </label>
|
||||||
</div>
|
<input type="text" class="clr-input" name="main_menu_icon_name"
|
||||||
|
[(ngModel)]="rowSelected.main_menu_icon_name">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaledit = false">Cancel</button>
|
<button type="button" class="btn btn-outline" (click)="modaledit = false">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary" >Update</button>
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalCsv" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="false">
|
<clr-modal class="mm-modal" [(clrModalOpen)]="modalCsv" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="false">
|
||||||
<h3 class="modal-title">Import File</h3>
|
<h3 class="modal-title">Import File</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="file" name="file" class="file" (change)="selectFile($event)" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel">
|
<input type="file" name="file" class="file" (change)="selectFile($event)"
|
||||||
|
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel">
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary" type="button" [disabled]="!selectedFiles" (click)="saveCsv()">Import</button>
|
<button class="btn btn-primary" type="button" [disabled]="!selectedFiles" (click)="saveCsv()">Import</button>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
</div>
|
||||||
@@ -60,3 +60,591 @@ select{
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Modern theme-aware enhancements */
|
||||||
|
.mm-container {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
box-shadow: var(--theme-shadow);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--theme-glass-bg);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
border: 1px solid var(--theme-glass-border);
|
||||||
|
border-radius: calc(var(--theme-border-radius) + 4px);
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
box-shadow: var(--theme-glass-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-hero__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-hero__icon clr-icon {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
filter: drop-shadow(0 4px 10px rgba(0,0,0,0.15));
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-hero__title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-hero__subtitle {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-hero__actions .btn + .btn {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-breadcrumb {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-header-row {
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-title-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-title {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-subtitle {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-actions {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-btn-group {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-btn-primary {
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
background: var(--theme-primary);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
|
||||||
|
transition: transform 120ms var(--ease-out, cubic-bezier(0, 0, 0.2, 1)), box-shadow 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-btn-primary:hover { box-shadow: 0 4px 10px rgba(0,0,0,0.12); transform: translateY(-1px); }
|
||||||
|
.mm-btn-primary:active { transform: translateY(0); }
|
||||||
|
|
||||||
|
.mm-btn-outline {
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
border: 1px solid var(--theme-primary);
|
||||||
|
color: var(--theme-primary);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-btn-outline:hover { background: var(--theme-primary-100); }
|
||||||
|
|
||||||
|
.mm-btn-secondary {
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
background: var(--theme-secondary);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-btn-text {
|
||||||
|
margin-left: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-grid {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
box-shadow: var(--theme-shadow);
|
||||||
|
}
|
||||||
|
.mm-grid--modern .datagrid-header {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary-50), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-col-title {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-col--icon { width: 44px; }
|
||||||
|
.mm-col--no { width: 70px; }
|
||||||
|
.mm-col--name { min-width: 220px; }
|
||||||
|
.mm-col--id { width: 120px; }
|
||||||
|
.mm-col--seq { width: 120px; }
|
||||||
|
.mm-col--module { min-width: 160px; }
|
||||||
|
.mm-col--link { min-width: 220px; }
|
||||||
|
.mm-col--status { width: 140px; }
|
||||||
|
.mm-col--submenu { width: 100px; }
|
||||||
|
.mm-col--action { width: 140px; }
|
||||||
|
|
||||||
|
.mm-card {
|
||||||
|
background: var(--theme-glass-bg);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--theme-glass-border);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-grid-footer {
|
||||||
|
border-top: 1px solid rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-col--icon,
|
||||||
|
.mm-cell--icon {
|
||||||
|
max-width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-row:hover {
|
||||||
|
background: var(--theme-primary-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-icon-action {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 150ms ease-out, transform 150ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-icon-action:hover {
|
||||||
|
background: var(--theme-primary-100);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-icon-edit {
|
||||||
|
color: var(--theme-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-icon-link {
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-icon-help {
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-icon-delete {
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-danger:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal .modal-body {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal .modal-title {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modernize Add/Edit modals */
|
||||||
|
.mm-modal .modal-body form {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal .modal-body .clr-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mm-modal .modal-body .clr-row { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal input.clr-input,
|
||||||
|
.mm-modal select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.12);
|
||||||
|
background: var(--theme-background);
|
||||||
|
transition: border-color 150ms ease, box-shadow 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal input.clr-input:focus,
|
||||||
|
.mm-modal select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px var(--theme-primary-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal .modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal .modal-footer .btn.btn-primary {
|
||||||
|
background: var(--theme-primary);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal .modal-footer .btn.btn-outline {
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-modal .error_mess { color: #ef4444; font-size: 12px; margin-top: 4px; }
|
||||||
|
|
||||||
|
.mm-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-item__avatar clr-icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
color: var(--theme-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-item__title {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-item__meta {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.06);
|
||||||
|
background: var(--theme-primary-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--theme-secondary);
|
||||||
|
color: white;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-badge--success {
|
||||||
|
background: var(--theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-badge--muted {
|
||||||
|
background: var(--theme-primary-200);
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-actions-inline .btn {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-detail {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-detail__row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skeleton loader */
|
||||||
|
.mm-skeleton-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 8px 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-skeleton-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 44px 1.2fr 0.8fr 0.6fr 1fr 120px 140px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 8px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
border: 1px solid rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-skel {
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(0,0,0,0.05) 25%,
|
||||||
|
rgba(0,0,0,0.08) 37%,
|
||||||
|
rgba(0,0,0,0.05) 63%
|
||||||
|
);
|
||||||
|
background-size: 400% 100%;
|
||||||
|
animation: mm-shimmer 1.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-skel--icon { width: 24px; height: 24px; border-radius: 50%; }
|
||||||
|
.mm-skel--title { width: 60%; height: 14px; }
|
||||||
|
.mm-skel--meta { width: 40%; }
|
||||||
|
.mm-skel--chip { width: 80px; }
|
||||||
|
.mm-skel--link { width: 60%; }
|
||||||
|
.mm-skel--badge { width: 70px; }
|
||||||
|
.mm-skel--actions { width: 60px; }
|
||||||
|
|
||||||
|
@keyframes mm-shimmer {
|
||||||
|
0% { background-position: 100% 0; }
|
||||||
|
100% { background-position: 0 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.mm-skel { animation: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Row mount animation */
|
||||||
|
.mm-row { transition: transform 220ms var(--ease-out, cubic-bezier(0, 0, 0.2, 1)), background 200ms ease; }
|
||||||
|
.mm-row.ng-enter { transform: translateY(8px); opacity: 0.001; }
|
||||||
|
.mm-row.ng-enter-active { transform: translateY(0); opacity: 1; }
|
||||||
|
|
||||||
|
/* Responsive layouts */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.mm-col--id, .mm-col--seq, .mm-col--submenu { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mm-col--link { display: none; }
|
||||||
|
.mm-item__meta { display: none; }
|
||||||
|
.mm-actions-inline .btn { padding: 0 6px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mm-col--action { width: 100px; }
|
||||||
|
.mm-col--status { width: 110px; }
|
||||||
|
.mm-hero { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
|
||||||
|
.mm-hero__actions { width: 100%; display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 6px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sticky toolbar */
|
||||||
|
.mm-toolbar {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0,0,0,0.05);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
box-shadow: 0 1px 0 rgba(0,0,0,0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-search {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.08);
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-search__input {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-select {
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid rgba(0,0,0,0.08);
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mm-search__input { min-width: 140px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-view-toggle .btn { margin-left: 6px; border-radius: 999px; }
|
||||||
|
|
||||||
|
/* Card view */
|
||||||
|
.mm-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) { .mm-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||||
|
@media (max-width: 640px) { .mm-cards { grid-template-columns: 1fr; } }
|
||||||
|
|
||||||
|
.mm-card-item {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0,0,0,0.06);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
box-shadow: var(--theme-shadow);
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-card-item__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary-50), transparent);
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-card-item__icon clr-icon { color: var(--theme-primary); width: 18px; height: 18px; }
|
||||||
|
.mm-card-item__title { font-weight: 700; flex: 1 1 auto; }
|
||||||
|
.mm-card-item__badge { margin-left: auto; }
|
||||||
|
|
||||||
|
/* Prevent overlaps and long text issues */
|
||||||
|
.mm-ellipsis {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-card-item__title {
|
||||||
|
max-width: 60%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.mm-card-item__title { max-width: 54%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-card-item__body {
|
||||||
|
padding: 10px 12px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0,1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) { .mm-card-item__body { grid-template-columns: 1fr; } }
|
||||||
|
|
||||||
|
.mm-kv { display: flex; justify-content: space-between; }
|
||||||
|
.mm-kv span { color: var(--theme-text-secondary); }
|
||||||
|
|
||||||
|
.mm-card-item__footer {
|
||||||
|
padding: 10px 12px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
border-top: 1px solid rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Theme the card footer buttons */
|
||||||
|
.mm-card-item__footer .btn {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-card-item__footer .btn.btn-outline {
|
||||||
|
border: 1px solid var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-card-item__footer .btn.btn-outline:hover { background: rgba(0,0,0,0.04); }
|
||||||
|
|
||||||
|
.mm-card-item__footer .btn:not(.btn-outline) {
|
||||||
|
background: var(--theme-primary);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-card-item__footer .btn:not(.btn-outline):hover {
|
||||||
|
filter: brightness(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* View toggle buttons */
|
||||||
|
.mm-view-toggle .btn.btn-primary { background: var(--theme-primary); color: #fff; }
|
||||||
|
.mm-view-toggle .btn:not(.btn-primary) { background: var(--theme-primary-100); color: var(--theme-text); }
|
||||||
|
|
||||||
|
.mm-stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin: 0.75rem 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-stat {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-stat__value {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-stat__label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem 0.5rem;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-empty clr-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mm-empty__title {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { CsvService } from 'src/app/services/csv.service';
|
|||||||
import { ExcelService } from 'src/app/services/excel.service';
|
import { ExcelService } from 'src/app/services/excel.service';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-menumaintance',
|
selector: 'app-menumaintance',
|
||||||
templateUrl: './menumaintance.component.html',
|
templateUrl: './menumaintance.component.html',
|
||||||
@@ -31,6 +32,10 @@ export class MenumaintanceComponent implements OnInit {
|
|||||||
showdata;
|
showdata;
|
||||||
error;
|
error;
|
||||||
submitted = false;
|
submitted = false;
|
||||||
|
// Quick filter UI state (no API/logic changes)
|
||||||
|
filterText = '';
|
||||||
|
statusFilter: 'All' | 'Enable' | 'Disable' | '' = 'All';
|
||||||
|
viewMode: 'cards' | 'table' = 'cards';
|
||||||
constructor(private menuservice: MenumaintanceService,
|
constructor(private menuservice: MenumaintanceService,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
private excel: ExcelService,
|
private excel: ExcelService,
|
||||||
@@ -39,9 +44,14 @@ export class MenumaintanceComponent implements OnInit {
|
|||||||
private menuGroupService: MenuGroupService,
|
private menuGroupService: MenuGroupService,
|
||||||
private csvService: CsvService,
|
private csvService: CsvService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private router: Router,) { }
|
private router: Router,
|
||||||
|
private themeService: ThemeService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Ensure theme variables are applied; subscription keeps this view reactive to theme changes
|
||||||
|
this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// Theme applied globally via CSS variables; no additional handling needed here
|
||||||
|
});
|
||||||
this.showdata = this.menuGroupService.getdata();
|
this.showdata = this.menuGroupService.getdata();
|
||||||
console.log(this.showdata);
|
console.log(this.showdata);
|
||||||
this.mcreate = this.showdata.mcreate;
|
this.mcreate = this.showdata.mcreate;
|
||||||
@@ -69,6 +79,71 @@ export class MenumaintanceComponent implements OnInit {
|
|||||||
this.getdata();
|
this.getdata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats for UI (template-safe, no inline arrow functions)
|
||||||
|
get totalMenus(): number {
|
||||||
|
return this.menus ? this.menus.length : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get enabledMenusCount(): number {
|
||||||
|
const list: any[] = (this.menus as unknown as any[]) || [];
|
||||||
|
return list.filter(menu => menu && menu.status === 'Enable').length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabledMenusCount(): number {
|
||||||
|
const list: any[] = (this.menus as unknown as any[]) || [];
|
||||||
|
return list.filter(menu => menu && menu.status === 'Disable').length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtered list for view
|
||||||
|
get filteredMenus(): Rn_Main_Menu[] {
|
||||||
|
const items: any[] = (this.menus as unknown as any[]) || [];
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
const status = this.statusFilter;
|
||||||
|
return items.filter(m => {
|
||||||
|
const matchText = !text || (
|
||||||
|
(m.menuItemDesc || '').toLowerCase().includes(text) ||
|
||||||
|
(m.moduleName || '').toLowerCase().includes(text) ||
|
||||||
|
(m.main_menu_action_name || '').toLowerCase().includes(text)
|
||||||
|
);
|
||||||
|
const matchStatus = !status || status === 'All' || m.status === status;
|
||||||
|
return matchText && matchStatus;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewMode(mode: 'cards' | 'table') {
|
||||||
|
this.viewMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve a safe Clarity icon shape for a menu item
|
||||||
|
getIconShape(menu: any): string {
|
||||||
|
const raw = (menu?.main_menu_icon_name ?? menu?.mainMenuIconName ?? '').toString().trim();
|
||||||
|
const name = raw.toLowerCase();
|
||||||
|
const invalid = !name || name === 'undefined' || name === 'null' || name === '-' || name === 'na' || name === 'n/a';
|
||||||
|
if (invalid) {
|
||||||
|
return 'file'; // universal fallback icon
|
||||||
|
}
|
||||||
|
// Optional alias normalization
|
||||||
|
const aliasMap: Record<string, string> = {
|
||||||
|
'home': 'home',
|
||||||
|
'dashboard': 'dashboard',
|
||||||
|
'menu': 'list',
|
||||||
|
'list': 'list',
|
||||||
|
'link': 'link',
|
||||||
|
'application': 'application',
|
||||||
|
'applications': 'applications',
|
||||||
|
'module': 'application',
|
||||||
|
'settings': 'cog',
|
||||||
|
'config': 'cog',
|
||||||
|
'user': 'user',
|
||||||
|
'users': 'users',
|
||||||
|
'folder': 'folder',
|
||||||
|
'file': 'file',
|
||||||
|
'tag': 'tag',
|
||||||
|
'bookmark': 'bookmark'
|
||||||
|
};
|
||||||
|
return aliasMap[name] ?? name;
|
||||||
|
}
|
||||||
|
|
||||||
switchLanguage(language: string) {
|
switchLanguage(language: string) {
|
||||||
this.translate.use(language);
|
this.translate.use(language);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>Security</a></li>
|
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>Security</a></li>
|
||||||
<li><a href="javascript://">Menu Maintenance</a></li>
|
<li><a href="javascript://">Menu Maintenance</a></li>
|
||||||
@@ -9,22 +9,50 @@
|
|||||||
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">Sub Menu</span><br> -->
|
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">Sub Menu</span><br> -->
|
||||||
|
|
||||||
|
|
||||||
<div class="clr-row" style="margin-top: 15px;">
|
<section class="mm-hero">
|
||||||
<div class="clr-col-8">
|
<div class="mm-hero__content">
|
||||||
<h3 style="font-weight: 300;display: inline;">Sub-Menu Maintenance </h3>
|
<div class="mm-hero__icon"><clr-icon shape="organization"></clr-icon></div>
|
||||||
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">Sub Menu</span><br>
|
<div>
|
||||||
|
<h2 class="mm-hero__title">Sub-Menu Maintenance</h2>
|
||||||
</div>
|
<p class="mm-hero__subtitle">Sub Menu</p>
|
||||||
<div class="clr-col-4" style="text-align: right;">
|
</div>
|
||||||
<button id="add" class="btn btn-primary" (click)="goToAdd()">
|
</div>
|
||||||
<clr-icon shape="plus"></clr-icon>ADD New
|
<div class="mm-hero__actions">
|
||||||
|
<button id="add" class="btn btn-primary mm-btn-primary" (click)="goToAdd()">
|
||||||
|
<clr-icon shape="plus"></clr-icon>
|
||||||
|
<span class="mm-btn-text">Add</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="mm-toolbar">
|
||||||
|
<div class="mm-toolbar__left">
|
||||||
|
<div class="mm-search">
|
||||||
|
<clr-icon shape="search"></clr-icon>
|
||||||
|
<input class="mm-search__input" type="text" placeholder="Search" [(ngModel)]="filterText" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mm-toolbar__right">
|
||||||
|
<select class="mm-select" [(ngModel)]="statusFilter">
|
||||||
|
<option value="All">All</option>
|
||||||
|
<option value="Enable">Enabled</option>
|
||||||
|
<option value="Disable">Disabled</option>
|
||||||
|
</select>
|
||||||
|
<div class="mm-view-toggle">
|
||||||
|
<button class="btn btn-sm" [class.btn-primary]="viewMode==='cards'" (click)="setViewMode('cards')">
|
||||||
|
<clr-icon shape="view-cards"></clr-icon>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm" [class.btn-primary]="viewMode==='table'" (click)="setViewMode('table')">
|
||||||
|
<clr-icon shape="table"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading" >
|
<div class="mm-card" *ngIf="viewMode==='table'">
|
||||||
|
<clr-datagrid class="mm-grid mm-grid--modern" [clrDgLoading]="loading">
|
||||||
<clr-dg-placeholder><clr-spinner [clrMedium]="true">Loading ...</clr-spinner></clr-dg-placeholder>
|
<clr-dg-placeholder><clr-spinner [clrMedium]="true">Loading ...</clr-spinner></clr-dg-placeholder>
|
||||||
|
|
||||||
<clr-dg-column > <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-column > <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
@@ -49,19 +77,29 @@
|
|||||||
Status
|
Status
|
||||||
</ng-container></clr-dg-column>
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of sub;let i = index" [clrDgItem]="user">
|
<clr-dg-row class="mm-row" *clrDgItems="let user of filteredSubMenus;let i = index" [clrDgItem]="user">
|
||||||
<clr-dg-cell >{{i+1}}</clr-dg-cell>
|
<clr-dg-cell >{{i+1}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.menuItemDesc}}</clr-dg-cell>
|
<clr-dg-cell>
|
||||||
|
<div class="mm-item">
|
||||||
|
<!-- <div class="mm-item__avatar"><clr-icon [shape]="getIconShape(user)"></clr-icon></div> -->
|
||||||
|
<div class="mm-item__text">
|
||||||
|
<div class="mm-item__title">{{user.menuItemDesc}}</div>
|
||||||
|
<div class="mm-item__meta">ID: {{ user.menuId || 'â' }} | Seq: {{ (user.itemSeq === 0 || user.itemSeq) ? user.itemSeq : 'â' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-dg-cell>
|
||||||
<clr-dg-cell >{{user.menuId}}</clr-dg-cell>
|
<clr-dg-cell >{{user.menuId}}</clr-dg-cell>
|
||||||
<clr-dg-cell >{{user.itemSeq}}</clr-dg-cell>
|
<clr-dg-cell >{{user.itemSeq}}</clr-dg-cell>
|
||||||
<clr-dg-cell > {{user.moduleName}} </clr-dg-cell>
|
<clr-dg-cell > {{user.moduleName}} </clr-dg-cell>
|
||||||
<clr-dg-cell > {{user.main_menu_action_name}}</clr-dg-cell>
|
<clr-dg-cell > {{user.main_menu_action_name}}</clr-dg-cell>
|
||||||
<clr-dg-cell >{{user.status}} </clr-dg-cell>
|
<clr-dg-cell >{{user.status}} </clr-dg-cell>
|
||||||
|
|
||||||
<clr-dg-action-overflow>
|
<clr-dg-cell class="mm-cell mm-cell--action">
|
||||||
<button class="action-item" (click)="goToEdit(user)">Edit <clr-icon shape="edit" class="is-error"></clr-icon></button>
|
<div class="mm-actions-inline">
|
||||||
<button class="action-item" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button>
|
<button class="btn btn-sm btn-outline mm-btn-edit" (click)="goToEdit(user)"><clr-icon shape="edit"></clr-icon></button>
|
||||||
</clr-dg-action-overflow>
|
<button class="btn btn-sm btn-outline mm-btn-delete" (click)="onDelete(user)"><clr-icon shape="trash"></clr-icon></button>
|
||||||
|
</div>
|
||||||
|
</clr-dg-cell>
|
||||||
|
|
||||||
<clr-dg-row-detail *clrIfExpanded >
|
<clr-dg-row-detail *clrIfExpanded >
|
||||||
<table class="table">
|
<table class="table">
|
||||||
@@ -73,7 +111,7 @@
|
|||||||
</clr-dg-row-detail>
|
</clr-dg-row-detail>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
|
|
||||||
<clr-dg-footer>
|
<clr-dg-footer class="mm-grid-footer">
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Users per page</clr-dg-page-size>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Users per page</clr-dg-page-size>
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
@@ -81,8 +119,31 @@
|
|||||||
</clr-dg-pagination>
|
</clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<div *ngIf="viewMode==='cards'">
|
||||||
|
<div class="mm-cards">
|
||||||
|
<div class="mm-card-item" *ngFor="let user of filteredSubMenus; let i = index">
|
||||||
|
<div class="mm-card-item__header">
|
||||||
|
<!-- <div class="mm-card-item__icon"><clr-icon [shape]="getIconShape(user)"></clr-icon></div> -->
|
||||||
|
<div class="mm-card-item__title">{{user.menuItemDesc}}</div>
|
||||||
|
<div class="mm-card-item__badge" [class.mm-badge--success]="user.status==='Enable'" [class.mm-badge--muted]="user.status!=='Enable'">{{user.status}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mm-card-item__body">
|
||||||
|
<div class="mm-kv"><span>ID</span><strong>{{ user.menuId || 'â' }}</strong></div>
|
||||||
|
<div class="mm-kv"><span>Sequence</span><strong>{{ (user.itemSeq === 0 || user.itemSeq) ? user.itemSeq : 'â' }}</strong></div>
|
||||||
|
<div class="mm-kv"><span>Module</span><strong class="mm-ellipsis">{{ user.moduleName || 'â' }}</strong></div>
|
||||||
|
<div class="mm-kv mm-link"><clr-icon shape="link"></clr-icon><strong class="mm-ellipsis">{{ user.main_menu_action_name || 'â' }}</strong></div>
|
||||||
|
</div>
|
||||||
|
<div class="mm-card-item__footer">
|
||||||
|
<button class="btn btn-sm btn-outline mm-btn-edit" (click)="goToEdit(user)"><clr-icon shape="edit"></clr-icon> Edit</button>
|
||||||
|
<button class="btn btn-sm btn-outline mm-btn-delete" (click)="onDelete(user)"><clr-icon shape="trash"></clr-icon> Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<clr-modal class="mm-modal" [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Add Sub Menu Maintenance</h3>
|
<h3 class="modal-title">Add Sub Menu Maintenance</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
||||||
@@ -181,7 +242,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modalAdd = false">Cancel</button>
|
<button type="button" class="btn btn-outline" (click)="modalAdd = false">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary" >ADD</button>
|
<button type="submit" class="btn btn-primary">ADD</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -201,7 +262,7 @@
|
|||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<clr-modal class="mm-modal" [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Update Sub Menu Maintenance</h3>
|
<h3 class="modal-title">Update Sub Menu Maintenance</h3>
|
||||||
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
<div class="modal-body" *ngIf="rowSelected.menuItemId">
|
||||||
<h2 class="heading">{{rowSelected.menuItemId}}</h2>
|
<h2 class="heading">{{rowSelected.menuItemId}}</h2>
|
||||||
@@ -240,7 +301,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaledit = false">Cancel</button>
|
<button type="button" class="btn btn-outline" (click)="modaledit = false">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary" >Update</button>
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,88 @@
|
|||||||
|
@import '../../../../../styles/design-tokens';
|
||||||
|
|
||||||
|
.mm-breadcrumb { margin-bottom: 0.75rem; }
|
||||||
|
.mm-modal .modal-body { background: var(--theme-surface); }
|
||||||
|
.mm-modal .modal-title { font-weight: 600; }
|
||||||
|
.mm-modal .modal-body .clr-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 16px; }
|
||||||
|
@media (max-width: 640px) { .mm-modal .modal-body .clr-row { grid-template-columns: 1fr; } }
|
||||||
|
.mm-modal label { display: inline-block; margin-bottom: 6px; font-weight: 600; color: var(--theme-text); }
|
||||||
|
.mm-modal input.clr-input, .mm-modal select { width: 100%; padding: 10px 12px; border-radius: 10px; border: 1px solid rgba(0,0,0,0.12); background: var(--theme-background); transition: border-color 150ms ease, box-shadow 150ms ease; }
|
||||||
|
.mm-modal input.clr-input:focus, .mm-modal select:focus { outline: none; border-color: var(--theme-primary); box-shadow: 0 0 0 3px var(--theme-primary-100); }
|
||||||
|
.mm-modal .modal-footer { display: flex; justify-content: flex-end; gap: 10px; }
|
||||||
|
.mm-modal .modal-footer .btn.btn-primary { background: var(--theme-primary); color: #fff; border-radius: 10px; }
|
||||||
|
.mm-modal .modal-footer .btn.btn-outline { border-radius: 10px; border: 1px solid var(--theme-secondary); color: var(--theme-secondary); }
|
||||||
|
.mm-modal .error_mess { color: #ef4444; font-size: 12px; margin-top: 4px; }
|
||||||
|
|
||||||
|
.mm-hero {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
background: var(--theme-glass-bg); backdrop-filter: blur(12px);
|
||||||
|
border: 1px solid var(--theme-glass-border);
|
||||||
|
border-radius: calc(var(--theme-border-radius) + 4px);
|
||||||
|
padding: 1rem 1.25rem; margin-bottom: 0.75rem; box-shadow: var(--theme-glass-shadow);
|
||||||
|
}
|
||||||
|
.mm-hero__content { display: flex; align-items: center; gap: 0.75rem; }
|
||||||
|
.mm-hero__icon clr-icon { width: 28px; height: 28px; color: var(--theme-primary); }
|
||||||
|
.mm-hero__title { font-size: 1.25rem; margin: 0; }
|
||||||
|
.mm-hero__subtitle { margin: 0; font-size: 0.875rem; color: var(--theme-text-secondary); }
|
||||||
|
.mm-hero__actions .btn + .btn { margin-left: 0.5rem; }
|
||||||
|
|
||||||
|
.mm-btn-primary { border-radius: var(--theme-border-radius); background: var(--theme-primary); color: #fff; }
|
||||||
|
.mm-btn-text { margin-left: 0.35rem; }
|
||||||
|
|
||||||
|
.mm-toolbar { position: sticky; top: 0; z-index: 10; display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 8px 10px; margin-bottom: 8px; background: var(--theme-surface); border: 1px solid rgba(0,0,0,0.05); border-radius: var(--theme-border-radius); box-shadow: 0 1px 0 rgba(0,0,0,0.03); }
|
||||||
|
.mm-search { display: inline-flex; align-items: center; gap: 6px; border: 1px solid rgba(0,0,0,0.08); padding: 6px 8px; border-radius: 999px; background: var(--theme-background); }
|
||||||
|
.mm-search__input { border: none; outline: none; background: transparent; min-width: 220px; }
|
||||||
|
.mm-select { border-radius: 999px; padding: 6px 10px; border: 1px solid rgba(0,0,0,0.08); background: var(--theme-background); }
|
||||||
|
.mm-view-toggle .btn { margin-left: 6px; border-radius: 999px; }
|
||||||
|
|
||||||
|
.mm-card { background: var(--theme-glass-bg); backdrop-filter: blur(10px); border: 1px solid var(--theme-glass-border); border-radius: var(--theme-border-radius); padding: 0.25rem; }
|
||||||
|
.mm-grid { background: var(--theme-surface); border-radius: var(--theme-border-radius); box-shadow: var(--theme-shadow); }
|
||||||
|
.mm-grid--modern .datagrid-header { background: linear-gradient(135deg, var(--theme-primary-50), transparent); }
|
||||||
|
.mm-grid-footer { border-top: 1px solid rgba(0,0,0,0.06); }
|
||||||
|
.mm-row:hover { background: var(--theme-primary-50); }
|
||||||
|
.mm-item { display: flex; align-items: center; gap: 0.75rem; }
|
||||||
|
.mm-item__avatar clr-icon { width: 18px; height: 18px; color: var(--theme-accent); }
|
||||||
|
.mm-item__title { font-weight: 600; }
|
||||||
|
.mm-item__meta { font-size: 0.75rem; color: var(--theme-text-secondary); }
|
||||||
|
.mm-actions-inline .btn { margin-right: 6px; }
|
||||||
|
.mm-actions-inline .mm-btn-edit { border: 1px solid var(--theme-accent); color: var(--theme-accent); }
|
||||||
|
.mm-actions-inline .mm-btn-edit:hover { background: rgba(139, 92, 246, 0.12); }
|
||||||
|
.mm-actions-inline .mm-btn-delete { border: 1px solid #ef4444; color: #ef4444; }
|
||||||
|
.mm-actions-inline .mm-btn-delete:hover { background: rgba(239, 68, 68, 0.12); }
|
||||||
|
|
||||||
|
.mm-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
|
||||||
|
@media (max-width: 1024px) { .mm-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||||
|
@media (max-width: 640px) { .mm-cards { grid-template-columns: 1fr; } .mm-search__input { min-width: 140px; } }
|
||||||
|
.mm-card-item { background: var(--theme-surface); border: 1px solid rgba(0,0,0,0.06); border-radius: var(--theme-border-radius); box-shadow: var(--theme-shadow); overflow: hidden; display: flex; flex-direction: column; }
|
||||||
|
.mm-card-item__header { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: linear-gradient(135deg, var(--theme-primary-50), transparent); }
|
||||||
|
.mm-card-item__icon clr-icon { color: var(--theme-primary); width: 18px; height: 18px; }
|
||||||
|
.mm-card-item__title { font-weight: 700; flex: 1 1 auto; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.mm-card-item__badge { margin-left: auto; }
|
||||||
|
.mm-card-item__body { padding: 10px 12px; display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; }
|
||||||
|
@media (max-width: 640px) { .mm-card-item__body { grid-template-columns: 1fr; } }
|
||||||
|
.mm-kv { display: flex; justify-content: space-between; }
|
||||||
|
.mm-kv span { color: var(--theme-text-secondary); }
|
||||||
|
.mm-card-item__footer { padding: 10px 12px; display: flex; gap: 8px; border-top: 1px solid rgba(0,0,0,0.06); }
|
||||||
|
.mm-card-item__footer .mm-btn-edit { border: 1px solid var(--theme-accent); color: var(--theme-accent); }
|
||||||
|
.mm-card-item__footer .mm-btn-edit:hover { background: rgba(139, 92, 246, 0.12); }
|
||||||
|
.mm-card-item__footer .mm-btn-delete { border: 1px solid #ef4444; color: #ef4444; }
|
||||||
|
.mm-card-item__footer .mm-btn-delete:hover { background: rgba(239, 68, 68, 0.12); }
|
||||||
|
.mm-badge { display: inline-block; padding: 2px 10px; border-radius: 999px; background: var(--theme-secondary); color: #fff; font-size: 0.75rem; }
|
||||||
|
.mm-badge--success { background: var(--theme-primary); }
|
||||||
|
.mm-badge--muted { background: var(--theme-primary-200); color: var(--theme-text); }
|
||||||
|
|
||||||
|
/* Modals */
|
||||||
|
.mm-modal .modal-body { background: var(--theme-surface); }
|
||||||
|
.mm-modal .modal-title { font-weight: 600; }
|
||||||
|
.mm-modal .modal-body .clr-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 16px; }
|
||||||
|
@media (max-width: 640px) { .mm-modal .modal-body .clr-row { grid-template-columns: 1fr; } }
|
||||||
|
.mm-modal label { display: inline-block; margin-bottom: 6px; font-weight: 600; color: var(--theme-text); }
|
||||||
|
.mm-modal input.clr-input, .mm-modal select { width: 100%; padding: 10px 12px; border-radius: 10px; border: 1px solid rgba(0,0,0,0.12); background: var(--theme-background); transition: border-color 150ms ease, box-shadow 150ms ease; }
|
||||||
|
.mm-modal input.clr-input:focus, .mm-modal select:focus { outline: none; border-color: var(--theme-primary); box-shadow: 0 0 0 3px var(--theme-primary-100); }
|
||||||
|
.mm-modal .modal-footer { display: flex; justify-content: flex-end; gap: 10px; }
|
||||||
|
.mm-modal .modal-footer .btn.btn-primary { background: var(--theme-primary); color: #fff; border-radius: 10px; }
|
||||||
|
.mm-modal .modal-footer .btn.btn-outline { border-radius: 10px; border: 1px solid var(--theme-secondary); color: var(--theme-secondary); }
|
||||||
|
.mm-modal .error_mess { color: #ef4444; font-size: 12px; margin-top: 4px; }
|
||||||
input[type=text],[type=date],[type=password],[type=number] {
|
input[type=text],[type=date],[type=password],[type=number] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
import { MenumaintanceService } from '../../../../services/admin/menumaintance.service';
|
import { MenumaintanceService } from '../../../../services/admin/menumaintance.service';
|
||||||
import { Rn_Main_Menu } from '../../../../models/builder/Rn_Main_Menu';
|
import { Rn_Main_Menu } from '../../../../models/builder/Rn_Main_Menu';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
@@ -30,13 +31,22 @@ export class SubmenuComponent implements OnInit {
|
|||||||
mainid;
|
mainid;
|
||||||
submitted = false;
|
submitted = false;
|
||||||
public entryForm: FormGroup;
|
public entryForm: FormGroup;
|
||||||
|
// UI filter/view state
|
||||||
|
filterText = '';
|
||||||
|
statusFilter: 'All' | 'Enable' | 'Disable' | '' = 'All';
|
||||||
|
viewMode: 'cards' | 'table' = 'cards';
|
||||||
|
|
||||||
constructor(private menuservice: MenumaintanceService,
|
constructor(private menuservice: MenumaintanceService,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
private _fb: FormBuilder,
|
private _fb: FormBuilder,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,) { }
|
private router: Router,
|
||||||
|
private themeService: ThemeService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// Theme CSS variables applied globally
|
||||||
|
});
|
||||||
this.id = this.route.snapshot.params["id"];
|
this.id = this.route.snapshot.params["id"];
|
||||||
console.log("project mainmenu with id = ", this.id);
|
console.log("project mainmenu with id = ", this.id);
|
||||||
this.getById(this.id);
|
this.getById(this.id);
|
||||||
@@ -55,6 +65,34 @@ export class SubmenuComponent implements OnInit {
|
|||||||
|
|
||||||
// this.getdata();
|
// this.getdata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Derived/filtered list for view
|
||||||
|
get filteredSubMenus(): any[] {
|
||||||
|
const items: any[] = (this.sub as unknown as any[]) || [];
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
const status = this.statusFilter;
|
||||||
|
return items.filter(m => {
|
||||||
|
const matchText = !text || (
|
||||||
|
(m.menuItemDesc || '').toLowerCase().includes(text) ||
|
||||||
|
(m.moduleName || '').toLowerCase().includes(text) ||
|
||||||
|
(m.main_menu_action_name || '').toLowerCase().includes(text)
|
||||||
|
);
|
||||||
|
const matchStatus = !status || status === 'All' || m.status === status;
|
||||||
|
return matchText && matchStatus;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewMode(mode: 'cards' | 'table') { this.viewMode = mode; }
|
||||||
|
|
||||||
|
// Resolve a safe icon
|
||||||
|
getIconShape(menu: any): string {
|
||||||
|
const raw = (menu?.main_menu_icon_name ?? menu?.mainMenuIconName ?? '').toString().trim();
|
||||||
|
const name = raw.toLowerCase();
|
||||||
|
const invalid = !name || name === 'undefined' || name === 'null' || name === '-' || name === 'na' || name === 'n/a';
|
||||||
|
if (invalid) return 'file';
|
||||||
|
const alias: Record<string, string> = { application:'application', applications:'applications', settings:'cog', config:'cog', user:'user', users:'users', folder:'folder', file:'file', tag:'tag', bookmark:'bookmark', home:'home', dashboard:'dashboard', menu:'list', list:'list', link:'link', module:'application' };
|
||||||
|
return alias[name] ?? name;
|
||||||
|
}
|
||||||
getById(id: any) {
|
getById(id: any) {
|
||||||
this.menuservice.getbyid(id).subscribe((data) => {
|
this.menuservice.getbyid(id).subscribe((data) => {
|
||||||
this.sub = data;
|
this.sub = data;
|
||||||
|
|||||||
@@ -1,224 +1,320 @@
|
|||||||
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
|
||||||
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>{{ 'SECURITY' | translate }}</a></li>
|
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>{{ 'SECURITY' | translate }}</a></li>
|
||||||
<li><a href="javascript://">{{'USER_GROUP_MAINTENANCE' | translate}}</a></li>
|
<li><a href="javascript://">{{'USER_GROUP_MAINTENANCE' | translate}}</a></li>
|
||||||
|
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<section class="ug-hero">
|
||||||
<div class="dg-wrapper">
|
<div class="ug-hero__content">
|
||||||
<div class="clr-row">
|
<div class="ug-hero__icon"><clr-icon shape="users"></clr-icon></div>
|
||||||
<div class="clr-col">
|
<div>
|
||||||
<h3>{{'USER_GROUP_MAINTENANCE' | translate}}</h3>
|
<h2 class="ug-hero__title">{{'USER_GROUP_MAINTENANCE' | translate}}</h2>
|
||||||
|
<p class="ug-hero__subtitle">{{ 'SECURITY' | translate }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col" style="text-align: right;">
|
<div class="ug-hero__actions">
|
||||||
<div class="btn-group">
|
<button class="ug-btn ug-btn-secondary" (click)="downloadFiles()">
|
||||||
<button class="btn text-dark" (click)="downloadFiles()"><b>{{ 'DOWNLOAD_TEMPLATE' | translate }}</b></button>
|
<clr-icon shape="download"></clr-icon>
|
||||||
|
<span class="ug-btn-text">{{ 'DOWNLOAD_TEMPLATE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="ug-btn ug-btn-secondary" (click)="csvImport()">
|
||||||
|
<clr-icon shape="import"></clr-icon>
|
||||||
|
<span class="ug-btn-text">{{ 'IMPORT' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="ug-btn ug-btn-secondary" (click)="onExport()">
|
||||||
|
<clr-icon shape="export"></clr-icon>
|
||||||
|
<span class="ug-btn-text">{{ 'EXPORT_XLSX' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button id="add" class="ug-btn ug-btn-primary" (click)="goToAdd()">
|
||||||
|
<clr-icon shape="plus"></clr-icon>
|
||||||
|
<span class="ug-btn-text">{{ 'ADD' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary" (click)="csvImport()">{{ 'IMPORT' | translate }}</button>
|
</section>
|
||||||
<button class="btn btn-outline" (click)="onExport()">
|
|
||||||
<clr-icon shape="export"></clr-icon> {{ 'EXPORT_XLSX' | translate }}
|
<div class="ug-toolbar">
|
||||||
</button>
|
<div class="ug-toolbar__left">
|
||||||
<button id="add" class="btn btn-primary" *ngIf="mcreate == 'true'" (click)="goToAdd()">
|
<div class="ug-search">
|
||||||
<clr-icon shape="plus"></clr-icon> {{ 'ADD' | translate }}
|
<clr-icon shape="search"></clr-icon>
|
||||||
|
<input class="ug-search__input" type="text" [placeholder]="'SEARCH' | translate" [(ngModel)]="filterText" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ug-toolbar__right">
|
||||||
|
<div class="ug-view-toggle">
|
||||||
|
<button class="ug-btn ug-btn-sm" [class.ug-btn-primary]="viewMode==='cards'" (click)="setViewMode('cards')">
|
||||||
|
<clr-icon shape="view-cards"></clr-icon>
|
||||||
|
</button>
|
||||||
|
<button class="ug-btn ug-btn-sm" [class.ug-btn-primary]="viewMode==='table'" (click)="setViewMode('table')">
|
||||||
|
<clr-icon shape="table"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<clr-datagrid class="ug-grid" [clrDgLoading]="loading" *ngIf="viewMode==='table'">
|
||||||
|
<clr-dg-placeholder>
|
||||||
|
<ng-template #loadingSpinner>
|
||||||
|
<clr-spinner>{{'LOADING' | translate}} ... </clr-spinner>
|
||||||
|
</ng-template>
|
||||||
|
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="'usrGrp'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'USERGROUP_NO' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="'groupName'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'GROUP_NAME' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'groupDesc'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'GROUP_DESC' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'groupLevel'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'GROUP_LEVEL' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'status'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'STATUS' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'updateDateFormated'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'UPDATED_DATE' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let user of filteredGroups;let i = index" [clrDgItem]="user">
|
||||||
|
<clr-dg-cell>{{user.usrGrp}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.groupName}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.groupDesc}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.groupLevel}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>
|
||||||
|
<span class="ug-status-badge" [class.ug-status-enabled]="user.status === 'E'"
|
||||||
|
[class.ug-status-disabled]="user.status === 'D'">
|
||||||
|
{{user.status === 'E' ? 'Enabled' : 'Disabled'}}
|
||||||
|
</span>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.updateDateFormated}}</clr-dg-cell>
|
||||||
|
|
||||||
|
<clr-dg-action-overflow>
|
||||||
|
<!-- <button class="ug-action-item" *ngIf="medit == 'true' || medit == true" (click)="goToEdit(user)">
|
||||||
|
<clr-icon shape="edit"></clr-icon>
|
||||||
|
{{'EDIT' | translate}}
|
||||||
|
</button> -->
|
||||||
|
<button class="ug-action-item" (click)="goToEdit(user)">
|
||||||
|
<clr-icon shape="edit"></clr-icon>
|
||||||
|
{{'EDIT' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="ug-action-item ug-btn-error" (click)="onDelete(user)">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
{{'DELETE'|translate}}
|
||||||
|
</button>
|
||||||
|
</clr-dg-action-overflow>
|
||||||
|
|
||||||
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">username</td>
|
||||||
|
<td class="td-content">{{user.groupName}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-dg-footer class="ug-grid-footer">
|
||||||
<clr-dg-placeholder> <ng-template #loadingSpinner><clr-spinner>{{'LOADING' | translate}} ... </clr-spinner></ng-template>
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<div *ngIf="error;else loadingSpinner">{{error}}</div></clr-dg-placeholder>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Record per page</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
of {{pagination.totalItems}} Records
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'usrGrp'">
|
<div class="ug-cards" *ngIf="viewMode==='cards'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<div class="ug-card-item" *ngFor="let user of filteredGroups; let i = index">
|
||||||
{{'USERGROUP_NO' | translate}}
|
<div class="ug-card-item__header">
|
||||||
</ng-container>
|
<div class="ug-card-item__icon"><clr-icon shape="users"></clr-icon></div>
|
||||||
</clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'groupName'">
|
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
{{'GROUP_NAME' | translate}}
|
|
||||||
</ng-container>
|
|
||||||
</clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'groupDesc'">
|
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
{{'GROUP_DESC' | translate}}
|
|
||||||
</ng-container>
|
|
||||||
</clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'groupLevel'">
|
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
{{'GROUP_LEVEL' | translate}}
|
|
||||||
</ng-container>
|
|
||||||
</clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'status'">
|
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
{{'STATUS' | translate}}
|
|
||||||
</ng-container>
|
|
||||||
</clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'updateDateFormated'">
|
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
{{'UPDATED_DATE' | translate}}
|
|
||||||
</ng-container>
|
|
||||||
</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of givendata;let i = index" [clrDgItem]="user">
|
<div class="ug-card-item__title">{{user.groupName}}</div>
|
||||||
<clr-dg-cell>{{user.usrGrp}}</clr-dg-cell>
|
<div class="ug-card-item__badge" [class.ug-status-enabled]="user.status === 'E'"
|
||||||
<clr-dg-cell>{{user.groupName}}</clr-dg-cell>
|
[class.ug-status-disabled]="user.status === 'D'">
|
||||||
<clr-dg-cell >{{user.groupDesc}}</clr-dg-cell>
|
{{user.status === 'E' ? 'Enabled' : 'Disabled'}}
|
||||||
<clr-dg-cell>{{user.groupLevel}}</clr-dg-cell>
|
</div>
|
||||||
<clr-dg-cell>{{user.status}}</clr-dg-cell>
|
</div>
|
||||||
<!-- <clr-dg-cell>{{user.usrGrp}}</clr-dg-cell> -->
|
<div class="ug-card-item__body">
|
||||||
<clr-dg-cell>{{user.updateDateFormated}}</clr-dg-cell>
|
<div class="ug-kv"><span>ID</span><strong>{{user.usrGrp}}</strong></div>
|
||||||
|
<div class="ug-kv"><span>Desc</span><strong>{{user.groupDesc}}</strong></div>
|
||||||
<clr-dg-action-overflow>
|
<div class="ug-kv"><span>Level</span><strong>{{user.groupLevel}}</strong></div>
|
||||||
|
<div class="ug-kv"><span>Updated</span><strong>{{user.updateDateFormated}}</strong></div>
|
||||||
<button class="action-item" *ngIf="medit == 'true'" (click)="goToEdit(user)"> {{'EDIT' | translate}}</button>
|
</div>
|
||||||
<button class="action-item" (click)="onDelete(user)">{{'DELETE'|translate}}<clr-icon shape="trash" class="is-error"></clr-icon></button>
|
<div class="ug-card-item__footer">
|
||||||
</clr-dg-action-overflow>
|
<!-- <button class="ug-btn ug-btn-sm ug-btn-outline" *ngIf="medit == 'true' || medit == true" (click)="goToEdit(user)">
|
||||||
|
<clr-icon shape="edit"></clr-icon> Edit
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
</button> -->
|
||||||
<table class="table">
|
<button class="ug-btn ug-btn-sm ug-btn-outline" (click)="goToEdit(user)">
|
||||||
<tr>
|
<clr-icon shape="edit"></clr-icon> Edit
|
||||||
<td class="td-title">username</td>
|
</button>
|
||||||
<td class="td-content">{{user.groupName}}</td>
|
<button class="ug-btn ug-btn-sm ug-btn-error" (click)="onDelete(user)">
|
||||||
</tr>
|
<clr-icon shape="trash"></clr-icon> Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</table>
|
</div>
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Record per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<clr-modal [(clrModalOpen)]="modalAdd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Add User Group Maintenance</h3>
|
<h3 class="modal-title">Add User Group Maintenance</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">User Group<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" formControlName="groupName">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.groupName.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.groupName.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">Description<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" formControlName="groupDesc">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.groupDesc.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.groupDesc.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Status<span class="required-field">*</span></label>
|
|
||||||
<select id="" formControlName="status">
|
|
||||||
<option value="E">Enable</option>
|
|
||||||
<option value="D">Disable</option></select>
|
|
||||||
<div *ngIf="submitted && entryForm.controls.status.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.status.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Group Level<span class="required-field">*</span></label>
|
|
||||||
<select id="" formControlName="groupLevel">
|
|
||||||
<option >10</option>
|
|
||||||
<option >20</option>
|
|
||||||
<option >30</option>
|
|
||||||
<option >40</option>
|
|
||||||
<option>50</option>
|
|
||||||
</select>
|
|
||||||
<div *ngIf="submitted && entryForm.controls.groupLevel.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.groupLevel.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline" (click)="modalAdd = false">Cancel</button>
|
|
||||||
<button type="submit" class="btn btn-primary" >ADD</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="groupName" class="ug-form-label">User Group<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="groupName" class="ug-form-input" formControlName="groupName">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.groupName.errors" class="ug-form-error">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.groupName.errors.required">*This field is Required</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="groupDesc" class="ug-form-label">Description<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="groupDesc" class="ug-form-input" formControlName="groupDesc">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.groupDesc.errors" class="ug-form-error">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.groupDesc.errors.required">*This field is Required</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="status" class="ug-form-label">Status<span class="required-field">*</span></label>
|
||||||
|
<select id="status" class="ug-form-select" formControlName="status">
|
||||||
|
<option value="">Select Status</option>
|
||||||
|
<option value="E">Enable</option>
|
||||||
|
<option value="D">Disable</option>
|
||||||
|
</select>
|
||||||
|
<div *ngIf="submitted && entryForm.controls.status.errors" class="ug-form-error">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.status.errors.required">*This field is Required</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="groupLevel" class="ug-form-label">Group Level<span class="required-field">*</span></label>
|
||||||
|
<select id="groupLevel" class="ug-form-select" formControlName="groupLevel">
|
||||||
|
<option value="">Select Level</option>
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="20">20</option>
|
||||||
|
<option value="30">30</option>
|
||||||
|
<option value="40">40</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
</select>
|
||||||
|
<div *ngIf="submitted && entryForm.controls.groupLevel.errors" class="ug-form-error">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.groupLevel.errors.required">*This field is Required</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="ug-btn ug-btn-outline" (click)="modalAdd = false">Cancel</button>
|
||||||
|
<button type="submit" class="ug-btn ug-btn-primary">ADD</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<clr-modal [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
||||||
<h3 class="modal-title">Update User Group Maintenance</h3>
|
<h3 class="modal-title">Update User Group Maintenance</h3>
|
||||||
|
|
||||||
|
|
||||||
<div class="modal-body" *ngIf="rowSelected.usrGrp">
|
<div class="modal-body" *ngIf="rowSelected.usrGrp">
|
||||||
<h2 class="heading">{{rowSelected.usrGrp}}</h2>
|
|
||||||
<form (ngSubmit)="onUpdate(rowSelected.usrGrp)">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">User Group<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="rowSelected.groupName" name="groupName">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">Description<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="rowSelected.groupDesc" name="groupDesc">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Status<span class="required-field">*</span></label>
|
|
||||||
<select id="" [(ngModel)]="rowSelected.status" name="status">
|
|
||||||
<option value="E">Enable</option>
|
|
||||||
<option value="D">Disable</option></select>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Group Level<span class="required-field">*</span></label>
|
|
||||||
<select id="" [(ngModel)]="rowSelected.groupLevel" name="groupLevel">
|
|
||||||
<option >10</option>
|
|
||||||
<option >20</option>
|
|
||||||
<option >30</option>
|
|
||||||
<option >40</option>
|
|
||||||
<option>50</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline" (click)="modaledit = false">Cancel</button>
|
|
||||||
<button type="submit" class="btn btn-primary" >Update</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</clr-modal>
|
<div class="ug-form-group">
|
||||||
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
<label for="editUsrGrp" class="ug-form-label">User Group ID</label>
|
||||||
|
<input type="text" id="editUsrGrp" class="ug-form-input" [(ngModel)]="rowSelected.usrGrp" name="usrGrp" disabled>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="editGroupName" class="ug-form-label">User Group<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="editGroupName" class="ug-form-input" [(ngModel)]="rowSelected.groupName" name="groupName">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="editGroupDesc" class="ug-form-label">Description<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="editGroupDesc" class="ug-form-input" [(ngModel)]="rowSelected.groupDesc" name="groupDesc">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="editStatus" class="ug-form-label">Status<span class="required-field">*</span></label>
|
||||||
|
<select id="editStatus" class="ug-form-select" [(ngModel)]="rowSelected.status" name="status">
|
||||||
|
<option value="E">Enable</option>
|
||||||
|
<option value="D">Disable</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-form-group">
|
||||||
|
<label for="editGroupLevel" class="ug-form-label">Group Level<span class="required-field">*</span></label>
|
||||||
|
<select id="editGroupLevel" class="ug-form-select" [(ngModel)]="rowSelected.groupLevel" name="groupLevel">
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="20">20</option>
|
||||||
|
<option value="30">30</option>
|
||||||
|
<option value="40">40</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal-body" *ngIf="rowSelected.usrGrp">
|
|
||||||
<h1 class="delete">Are You Sure Want to delete?</h1>
|
|
||||||
<h2 class="heading">{{rowSelected.usrGrp}}</h2>
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaldelete = false">Cancel</button>
|
<button type="button" class="ug-btn ug-btn-outline" (click)="modaledit = false">Cancel</button>
|
||||||
<button type="submit" (click)="delete(rowSelected.usrGrp)" class="btn btn-primary" >Delete</button>
|
<button type="submit" (click)="onUpdate(rowSelected.usrGrp)" class="ug-btn ug-btn-primary">Update</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
|
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
<div class="modal-body" *ngIf="rowSelected.usrGrp">
|
||||||
|
<div class="ug-delete-header">
|
||||||
|
<div class="ug-delete-icon">
|
||||||
|
<clr-icon shape="exclamation-triangle" size="48"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<h1 class="ug-delete-title">Are You Sure Want to Delete?</h1>
|
||||||
|
<p class="ug-delete-subtitle">This action cannot be undone</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ug-delete-details">
|
||||||
|
<div class="ug-delete-detail-item">
|
||||||
|
<span class="ug-delete-detail-label">User Group ID:</span>
|
||||||
|
<span class="ug-delete-detail-value">{{rowSelected.usrGrp}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="ug-delete-detail-item">
|
||||||
|
<span class="ug-delete-detail-label">Group Name:</span>
|
||||||
|
<span class="ug-delete-detail-value">{{rowSelected.groupName}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="ug-delete-detail-item">
|
||||||
|
<span class="ug-delete-detail-label">Description:</span>
|
||||||
|
<span class="ug-delete-detail-value">{{rowSelected.groupDesc}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="ug-btn ug-btn-outline" (click)="modaldelete = false">Cancel</button>
|
||||||
|
<button type="submit" (click)="delete(rowSelected.usrGrp)" class="ug-btn ug-btn-error">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalCsv" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="false">
|
<clr-modal [(clrModalOpen)]="modalCsv" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="false">
|
||||||
<h3 class="modal-title">Import File</h3>
|
<h3 class="modal-title">Import File</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="file" name="file" class="file" (change)="selectFile($event)" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel">
|
<div class="ug-form-group">
|
||||||
|
<label class="ug-form-label">Select CSV File</label>
|
||||||
|
<input type="file" name="file" class="ug-file-input" (change)="selectFile($event)"
|
||||||
|
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary" type="button" [disabled]="!selectedFiles" (click)="saveCsv()">Import</button>
|
<button class="ug-btn ug-btn-outline" type="button" (click)="modalCsv = false">Cancel</button>
|
||||||
|
<button class="ug-btn ug-btn-primary" type="button" [disabled]="!selectedFiles" (click)="saveCsv()">Import</button>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
@@ -1,8 +1,748 @@
|
|||||||
@import '../../../../../styles1.scss';
|
@import '../../../../../styles.scss';
|
||||||
input.ng-invalid.ng-touched {
|
|
||||||
border-color: red;
|
// Import design tokens
|
||||||
|
@import '../../../../../styles/design-tokens';
|
||||||
|
|
||||||
|
// User Group Maintenance Styles
|
||||||
|
.ug-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px 32px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
font-family: var(--theme-font-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.error_mess {
|
.ug-btn-text {
|
||||||
color: red;
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Button Styles using ThemeService
|
||||||
|
.ug-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
text-decoration: none;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--theme-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizes
|
||||||
|
&.ug-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-md {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-lg {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variants
|
||||||
|
&.ug-btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary, #0284c7) 0%, var(--theme-accent, #7c3aed) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-secondary {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
border-color: rgba(0, 0, 0, 0.1);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-color: rgba(0, 0, 0, 0.2);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.1); // var(--theme-secondary) with 10% opacity
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
border-color: var(--theme-error-dark, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(239, 68, 68, 0.3), 0 4px 6px -2px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-search {
|
||||||
|
position: relative;
|
||||||
|
max-width: 400px;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 12px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: #9ca3af;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 12px 12px 40px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1); // var(--theme-primary) with 10% opacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-view-toggle {
|
||||||
|
display: flex;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.ug-btn {
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-right: 1px solid #d1d5db;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-primary {
|
||||||
|
background: var(--theme-primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Table Styles
|
||||||
|
.ug-grid {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
::ng-deep .datagrid {
|
||||||
|
.datagrid-head {
|
||||||
|
background: var(--theme-background);
|
||||||
|
|
||||||
|
.datagrid-column {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-row {
|
||||||
|
transition: background-color 150ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-cell {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-grid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Card Styles - More beautiful design
|
||||||
|
.ug-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-card-item {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
border-radius: var(--theme-border-radius); // Using theme variable
|
||||||
|
box-shadow: var(--theme-shadow); // Using theme variable
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||||
|
border-color: rgba(14, 165, 233, 0.3); // var(--theme-primary) with 30% opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 16px;
|
||||||
|
background: linear-gradient(135deg, rgba(14, 165, 233, 0.1), transparent); // var(--theme-primary) with 10% opacity
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
clr-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-weight: 700;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
max-width: 60%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__badge {
|
||||||
|
margin-left: auto;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 9999px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 16px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-kv {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--theme-text);
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: right;
|
||||||
|
max-width: 60%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme the card footer buttons
|
||||||
|
.ug-card-item__footer .ug-btn {
|
||||||
|
border-radius: 10px;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-card-item__footer .ug-btn.ug-btn-outline {
|
||||||
|
border: 1px solid var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.1); // var(--theme-secondary) with 10% opacity
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-card-item__footer .ug-btn.ug-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: #fff;
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
border-color: var(--theme-error-dark, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table view action buttons
|
||||||
|
.ug-action-item {
|
||||||
|
@extend .ug-btn;
|
||||||
|
@extend .ug-btn-ghost;
|
||||||
|
@extend .ug-btn-sm;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ug-btn-error {
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure action overflow is visible
|
||||||
|
::ng-deep .datagrid-action-overflow {
|
||||||
|
button.action-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px 12px;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status Badges
|
||||||
|
.ug-status-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 9999px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-status-enabled {
|
||||||
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0.05) 100%);
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-status-disabled {
|
||||||
|
background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(239, 68, 68, 0.05) 100%);
|
||||||
|
color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form Styles
|
||||||
|
.ug-form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-form-input, .ug-form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--theme-text);
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1); // var(--theme-primary) with 10% opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
border-color: #ef4444;
|
||||||
|
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-form-select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 12px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 12px;
|
||||||
|
padding-right: 40px;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-form-error {
|
||||||
|
color: #ef4444;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required-field {
|
||||||
|
color: #ef4444;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-file-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
|
||||||
|
&::file-selector-button {
|
||||||
|
@extend .ug-btn;
|
||||||
|
@extend .ug-btn-secondary;
|
||||||
|
margin-right: 12px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Modal Styles
|
||||||
|
.ug-delete-header {
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-delete-icon {
|
||||||
|
color: #ef4444;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-delete-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-delete-subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-delete-details {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-delete-detail-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-delete-detail-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-delete-detail-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
color: #ef4444;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Header
|
||||||
|
::ng-deep .modal-header {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px 16px 0 0 !important;
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Body
|
||||||
|
::ng-deep .modal-body {
|
||||||
|
padding: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Footer
|
||||||
|
::ng-deep .modal-footer {
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-radius: 0 0 16px 16px !important;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ug-hero {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-search {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-cards {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ug-card-item {
|
||||||
|
&__header {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ExcelService } from '../../../../services/excel.service';
|
import { ExcelService } from '../../../../services/excel.service';
|
||||||
@@ -8,12 +8,15 @@ import { ToastrService } from 'ngx-toastr';
|
|||||||
import { MenuGroupService } from 'src/app/services/admin/menu-group.service';
|
import { MenuGroupService } from 'src/app/services/admin/menu-group.service';
|
||||||
import { CsvService } from 'src/app/services/csv.service';
|
import { CsvService } from 'src/app/services/csv.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-usergrpmaintenance',
|
selector: 'app-usergrpmaintenance',
|
||||||
templateUrl: './usergrpmaintenance.component.html',
|
templateUrl: './usergrpmaintenance.component.html',
|
||||||
styleUrls: ['./usergrpmaintenance.component.scss']
|
styleUrls: ['./usergrpmaintenance.component.scss']
|
||||||
})
|
})
|
||||||
export class UsergrpmaintenanceComponent implements OnInit {
|
export class UsergrpmaintenanceComponent implements OnInit, OnDestroy {
|
||||||
loading = false;
|
loading = false;
|
||||||
public entryForm: FormGroup;
|
public entryForm: FormGroup;
|
||||||
givendata;
|
givendata;
|
||||||
@@ -23,10 +26,13 @@ export class UsergrpmaintenanceComponent implements OnInit {
|
|||||||
modaledit=false;
|
modaledit=false;
|
||||||
modaldelete=false;
|
modaldelete=false;
|
||||||
rowSelected :any= {};
|
rowSelected :any= {};
|
||||||
mcreate;
|
mcreate: string | boolean = false;
|
||||||
medit;
|
medit: string | boolean = false;
|
||||||
showdata;
|
showdata: any;
|
||||||
submitted=false;
|
submitted=false;
|
||||||
|
filterText: string = '';
|
||||||
|
viewMode: 'table' | 'cards' = 'cards';
|
||||||
|
private themeSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private excel: ExcelService,
|
private excel: ExcelService,
|
||||||
@@ -38,21 +44,32 @@ export class UsergrpmaintenanceComponent implements OnInit {
|
|||||||
private mainservice:UsergrpmaintainceService,
|
private mainservice:UsergrpmaintainceService,
|
||||||
private csvService: CsvService,
|
private csvService: CsvService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
|
private themeService: ThemeService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
switchLanguage(language: string) {
|
switchLanguage(language: string) {
|
||||||
this.translate.use(language);
|
this.translate.use(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.themeSubscription = this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// Theme changes will automatically update CSS variables
|
||||||
|
// This triggers a re-render of themed elements
|
||||||
|
});
|
||||||
|
|
||||||
this.showdata = this.menuGroupService.getdata();
|
this.showdata = this.menuGroupService.getdata();
|
||||||
console.log(this.showdata);
|
console.log('Showdata:', this.showdata);
|
||||||
this.mcreate=this.showdata.mcreate;
|
if (this.showdata) {
|
||||||
console.log(this.mcreate);
|
// Handle both string and boolean values
|
||||||
this.medit=this.showdata.medit
|
this.mcreate = this.showdata.mcreate === 'true' || this.showdata.mcreate === true ? true : false;
|
||||||
console.log(this.medit);
|
this.medit = this.showdata.medit === 'true' || this.showdata.medit === true ? true : false;
|
||||||
|
console.log('MCREATE:', this.mcreate);
|
||||||
|
console.log('MEDIT:', this.medit);
|
||||||
|
}
|
||||||
|
|
||||||
this.mainservice.getAll().subscribe((data) => {
|
this.mainservice.getAll().subscribe((data) => {
|
||||||
console.log(data);
|
console.log('Data received:', data);
|
||||||
this.givendata = data;
|
this.givendata = data || [];
|
||||||
if(this.givendata.length==0){
|
if(this.givendata.length==0){
|
||||||
this.error="No data Available";
|
this.error="No data Available";
|
||||||
console.log(this.error)
|
console.log(this.error)
|
||||||
@@ -72,6 +89,30 @@ export class UsergrpmaintenanceComponent implements OnInit {
|
|||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.themeSubscription) {
|
||||||
|
this.themeSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalGroups(): number {
|
||||||
|
const list: any[] = (this.givendata as unknown as any[]) || [];
|
||||||
|
return list.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get filteredGroups(): any[] {
|
||||||
|
const items: any[] = (this.givendata as unknown as any[]) || [];
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
if (!text) { return items; }
|
||||||
|
return items.filter(g => (
|
||||||
|
(g?.groupName || '').toLowerCase().includes(text) ||
|
||||||
|
(g?.groupDesc || '').toLowerCase().includes(text)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewMode(mode: 'table' | 'cards') { this.viewMode = mode; }
|
||||||
onExport() {
|
onExport() {
|
||||||
this.excel.exportAsExcelFile(this.givendata, 'user_',
|
this.excel.exportAsExcelFile(this.givendata, 'user_',
|
||||||
moment().format('YYYYMMDD_HHmmss'))
|
moment().format('YYYYMMDD_HHmmss'))
|
||||||
@@ -81,7 +122,8 @@ export class UsergrpmaintenanceComponent implements OnInit {
|
|||||||
//this.router.navigate(["../usermaintanceadd"],{relativeTo:this.route});
|
//this.router.navigate(["../usermaintanceadd"],{relativeTo:this.route});
|
||||||
}
|
}
|
||||||
goToEdit(row){
|
goToEdit(row){
|
||||||
this.rowSelected = row;
|
console.log('Edit clicked for row:', row);
|
||||||
|
this.rowSelected = {...row}; // Create a copy to avoid reference issues
|
||||||
this.modaledit=true;
|
this.modaledit=true;
|
||||||
//this.router.navigate(["../usermaintanceedit/"+ id], { relativeTo: this.route });
|
//this.router.navigate(["../usermaintanceedit/"+ id], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
@@ -135,7 +177,8 @@ export class UsergrpmaintenanceComponent implements OnInit {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
onDelete(row) {
|
onDelete(row) {
|
||||||
this.rowSelected = row;
|
console.log('Delete clicked for row:', row);
|
||||||
|
this.rowSelected = {...row}; // Create a copy to avoid reference issues
|
||||||
this.modaldelete=true;
|
this.modaldelete=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +1,202 @@
|
|||||||
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
|
||||||
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>{{ 'SECURITY' | translate }}</a></li>
|
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon>{{ 'SECURITY' | translate }}</a></li>
|
||||||
<li><a href="javascript://">{{'USER_MAINTENANCE'| translate}}</a></li>
|
<li><a href="javascript://">{{'USER_MAINTENANCE'| translate}}</a></li>
|
||||||
|
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
<section class="um-hero">
|
||||||
<div class="dg-wrapper">
|
<div class="um-hero__content">
|
||||||
<div class="clr-row">
|
<div class="um-hero__icon"><clr-icon shape="user"></clr-icon></div>
|
||||||
<div class="clr-col">
|
<div>
|
||||||
<h3>{{'USER_MAINTENANCE'| translate}} </h3>
|
<h2 class="um-hero__title">{{'USER_MAINTENANCE'| translate}}</h2>
|
||||||
</div>
|
<p class="um-hero__subtitle">{{ 'SECURITY' | translate }}</p>
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn text-dark" (click)="downloadFiles()"><b>{{ 'DOWNLOAD_TEMPLATE' | translate }}</b></button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary" (click)="csvImport()">{{ 'IMPORT' | translate }}</button>
|
</div>
|
||||||
<button class="btn btn-outline" (click)="onExport()">
|
<div class="um-hero__actions">
|
||||||
<clr-icon shape="export"></clr-icon> {{ 'EXPORT_XLSX' | translate }}
|
<button class="um-btn um-btn-secondary" (click)="downloadFiles()">
|
||||||
|
<clr-icon shape="download"></clr-icon>
|
||||||
|
<span class="um-btn-text">{{ 'DOWNLOAD_TEMPLATE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="um-btn um-btn-secondary" (click)="csvImport()">
|
||||||
|
<clr-icon shape="import"></clr-icon>
|
||||||
|
<span class="um-btn-text">{{ 'IMPORT' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="um-btn um-btn-secondary" (click)="onExport()">
|
||||||
|
<clr-icon shape="export"></clr-icon>
|
||||||
|
<span class="um-btn-text">{{ 'EXPORT_XLSX' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button id="add" class="um-btn um-btn-primary" (click)="goToAdd()">
|
||||||
|
<clr-icon shape="plus"></clr-icon>
|
||||||
|
<span class="um-btn-text">{{ 'ADD' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<!-- <button id="add" class="um-btn um-btn-primary" *ngIf="mcreate == 'true' || mcreate == true" (click)="goToAdd()">
|
||||||
|
<clr-icon shape="plus"></clr-icon>
|
||||||
|
<span class="um-btn-text">{{ 'ADD' | translate }}</span>
|
||||||
|
</button> -->
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="um-toolbar">
|
||||||
|
<div class="um-toolbar__left">
|
||||||
|
<div class="um-search">
|
||||||
|
<clr-icon shape="search"></clr-icon>
|
||||||
|
<input class="um-search__input" type="text" [placeholder]="'SEARCH' | translate" [(ngModel)]="filterText" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="um-toolbar__right">
|
||||||
|
<div class="um-view-toggle">
|
||||||
|
<button class="um-btn um-btn-sm" [class.um-btn-primary]="viewMode==='cards'" (click)="setViewMode('cards')">
|
||||||
|
<clr-icon shape="view-cards"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
<button id="add" class="btn btn-primary" *ngIf="mcreate == 'true'" (click)="goToAdd()">
|
<button class="um-btn um-btn-sm" [class.um-btn-primary]="viewMode==='table'" (click)="setViewMode('table')">
|
||||||
<clr-icon shape="plus"></clr-icon> {{ 'ADD' | translate }}
|
<clr-icon shape="table"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
|
||||||
<clr-dg-placeholder >
|
|
||||||
<ng-template #loadingSpinner><clr-spinner>Loading ... </clr-spinner></ng-template>
|
|
||||||
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
|
||||||
</clr-dg-placeholder>
|
|
||||||
<!-- <clr-dg-placeholder *ngIf="!loading1;" > <clr-spinner>not</clr-spinner></clr-dg-placeholder>
|
|
||||||
<ng-template #other_content style="text-align: center;">Data Not found...</ng-template> -->
|
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'userId'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
User Id
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
<!-- <clr-dg-column [clrDgField]="'username'" style="width: 300px;"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
User Name
|
|
||||||
</ng-container></clr-dg-column> -->
|
|
||||||
<clr-dg-column [clrDgField]="'fullName'" style="width: 200px;"><ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Full Name
|
|
||||||
</ng-container></clr-dg-column >
|
|
||||||
<clr-dg-column [clrDgField]="'email'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Email
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'usrGrpName'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
User Group Name
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
<!-- <clr-dg-column [clrDgField]="'customerNumer'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Customer Number
|
|
||||||
</ng-container></clr-dg-column> -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of givendata;let i = index" [clrDgItem]="user">
|
|
||||||
<clr-dg-cell>{{user.userId}}</clr-dg-cell>
|
|
||||||
<!-- <clr-dg-cell>{{user.username}}</clr-dg-cell> -->
|
|
||||||
<clr-dg-cell >{{user.fullName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{user.email}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{user.usrGrpName}}</clr-dg-cell>
|
|
||||||
<!-- <clr-dg-cell>{{user.customerNumer}}</clr-dg-cell> -->
|
|
||||||
|
|
||||||
|
|
||||||
<clr-dg-action-overflow>
|
|
||||||
|
|
||||||
<button class="action-item" *ngIf="medit == 'true'" (click)="goToEdit(user.userId)">Edit</button>
|
|
||||||
<button class="action-item" (click)="onDelete(user)">Delete<cds-icon shape="trash" class="is-error"></cds-icon></button>
|
|
||||||
</clr-dg-action-overflow>
|
|
||||||
|
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
|
||||||
<table class="table">
|
|
||||||
<tr>
|
|
||||||
<td class="td-title">username</td>
|
|
||||||
<td class="td-content">{{user.username}}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Record per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<clr-datagrid class="um-grid" [clrDgLoading]="loading" *ngIf="viewMode==='table'">
|
||||||
|
<clr-dg-placeholder>
|
||||||
|
<ng-template #loadingSpinner>
|
||||||
|
<clr-spinner>{{'LOADING' | translate}} ... </clr-spinner>
|
||||||
|
</ng-template>
|
||||||
|
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="'userId'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'USER_ID' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'fullName'" style="width: 200px;">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'FULL_NAME' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'email'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'EMAIL' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'usrGrpName'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
{{'USER_GROUP_NAME' | translate}}
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let user of filteredUsers;let i = index" [clrDgItem]="user">
|
||||||
|
<clr-dg-cell>{{user.userId}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.fullName}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.email}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.usrGrpName}}</clr-dg-cell>
|
||||||
|
|
||||||
|
<clr-dg-action-overflow>
|
||||||
|
<!-- <button class="um-action-item" *ngIf="medit == 'true' || medit == true" (click)="goToEdit(user.userId)">
|
||||||
|
<clr-icon shape="edit"></clr-icon>
|
||||||
|
{{'EDIT' | translate}}
|
||||||
|
</button> -->
|
||||||
|
<button class="um-action-item" (click)="goToEdit(user.userId)">
|
||||||
|
<clr-icon shape="edit"></clr-icon>
|
||||||
|
{{'EDIT' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="um-action-item um-btn-error" (click)="onDelete(user)">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
{{'DELETE'|translate}}
|
||||||
|
</button>
|
||||||
|
</clr-dg-action-overflow>
|
||||||
|
|
||||||
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">username</td>
|
||||||
|
<td class="td-content">{{user.username}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
|
<clr-dg-footer class="um-grid-footer">
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Record per page</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
of {{pagination.totalItems}} Records
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
|
||||||
|
<div class="um-cards" *ngIf="viewMode==='cards'">
|
||||||
|
<div class="um-card-item" *ngFor="let user of filteredUsers; let i = index">
|
||||||
|
<div class="um-card-item__header">
|
||||||
|
<div class="um-card-item__icon"><clr-icon shape="user"></clr-icon></div>
|
||||||
|
<div class="um-card-item__title">{{user.fullName}}</div>
|
||||||
|
<div class="um-card-item__badge">ID: {{user.userId}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="um-card-item__body">
|
||||||
|
<div class="um-kv"><span>{{'EMAIL' | translate}}</span><strong>{{user.email}}</strong></div>
|
||||||
|
<div class="um-kv"><span>{{'GROUP' | translate}}</span><strong>{{user.usrGrpName}}</strong></div>
|
||||||
|
</div>
|
||||||
|
<div class="um-card-item__footer">
|
||||||
|
<!-- <button class="um-btn um-btn-sm um-btn-outline" *ngIf="medit == 'true' || medit == true"
|
||||||
|
(click)="goToEdit(user.userId)">
|
||||||
|
<clr-icon shape="edit"></clr-icon> {{'EDIT' | translate}}
|
||||||
|
</button> -->
|
||||||
|
|
||||||
|
<button class="um-btn um-btn-sm um-btn-outline" (click)="goToEdit(user.userId)">
|
||||||
|
<clr-icon shape="edit"></clr-icon> {{'EDIT' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="um-btn um-btn-sm um-btn-error" (click)="onDelete(user)">
|
||||||
|
<clr-icon shape="trash"></clr-icon> {{'DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
|
||||||
<div class="modal-body" *ngIf="rowSelected.userId">
|
<div class="modal-body" *ngIf="rowSelected.userId">
|
||||||
<h1 class="delete">Are You Sure Want to delete?</h1>
|
<div class="um-delete-header">
|
||||||
<h2 class="heading">{{rowSelected.userId}}</h2>
|
<div class="um-delete-icon">
|
||||||
|
<clr-icon shape="exclamation-triangle" size="48"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<h1 class="um-delete-title">{{'ARE_YOU_SURE_WANT_TO_DELETE' | translate}}?</h1>
|
||||||
|
<p class="um-delete-subtitle">{{'THIS_ACTION_CANNOT_BE_UNDONE' | translate}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="um-delete-details">
|
||||||
|
<div class="um-delete-detail-item">
|
||||||
|
<span class="um-delete-detail-label">{{'USER_ID' | translate}}:</span>
|
||||||
|
<span class="um-delete-detail-value">{{rowSelected.userId}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="um-delete-detail-item">
|
||||||
|
<span class="um-delete-detail-label">{{'FULL_NAME' | translate}}:</span>
|
||||||
|
<span class="um-delete-detail-value">{{rowSelected.fullName}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="um-delete-detail-item">
|
||||||
|
<span class="um-delete-detail-label">{{'EMAIL' | translate}}:</span>
|
||||||
|
<span class="um-delete-detail-value">{{rowSelected.email}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modaldelete = false">Cancel</button>
|
<button type="button" class="um-btn um-btn-outline" (click)="modaldelete = false">{{'CANCEL' |
|
||||||
<button type="submit" (click)="delete(rowSelected.userId)" class="btn btn-primary" >Delete</button>
|
translate}}</button>
|
||||||
|
<button type="submit" (click)="delete(rowSelected.userId)" class="um-btn um-btn-error">{{'DELETE' |
|
||||||
|
translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalCsv" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="false">
|
<clr-modal [(clrModalOpen)]="modalCsv" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="false">
|
||||||
<h3 class="modal-title">Import File</h3>
|
<h3 class="modal-title">{{'IMPORT_FILE' | translate}}</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<input type="file" name="file" class="file" (change)="selectFile($event)" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel">
|
<div class="um-form-group">
|
||||||
|
<label class="um-form-label">{{'SELECT_CSV_FILE' | translate}}</label>
|
||||||
|
<input type="file" name="file" class="um-file-input" (change)="selectFile($event)"
|
||||||
|
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary" type="button" [disabled]="!selectedFiles" (click)="saveCsv()">Import</button>
|
<button class="um-btn um-btn-outline" type="button" (click)="modalCsv = false">{{'CANCEL' | translate}}</button>
|
||||||
|
<button class="um-btn um-btn-primary" type="button" [disabled]="!selectedFiles" (click)="saveCsv()">{{'IMPORT' |
|
||||||
|
translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
@@ -1,4 +1,656 @@
|
|||||||
.delete,.heading{
|
@import '../../../../../styles.scss';
|
||||||
|
|
||||||
|
// Import design tokens
|
||||||
|
@import '../../../../../styles/design-tokens';
|
||||||
|
|
||||||
|
.delete, .heading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modern User Maintenance Styles
|
||||||
|
.um-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px 32px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
font-family: var(--theme-font-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-btn-text {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Button Styles using ThemeService
|
||||||
|
.um-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
text-decoration: none;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--theme-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizes
|
||||||
|
&.um-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-md {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-lg {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variants
|
||||||
|
&.um-btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary, #0284c7) 0%, var(--theme-accent, #7c3aed) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-secondary {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
border-color: rgba(0, 0, 0, 0.1);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-color: rgba(0, 0, 0, 0.2);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.1); // var(--theme-secondary) with 10% opacity
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
border-color: var(--theme-error-dark, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(239, 68, 68, 0.3), 0 4px 6px -2px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-search {
|
||||||
|
position: relative;
|
||||||
|
max-width: 400px;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 12px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: #9ca3af;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 12px 12px 40px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1); // var(--theme-primary) with 10% opacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-view-toggle {
|
||||||
|
display: flex;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.um-btn {
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-right: 1px solid #d1d5db;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-primary {
|
||||||
|
background: var(--theme-primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Table Styles
|
||||||
|
.um-grid {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
::ng-deep .datagrid {
|
||||||
|
.datagrid-head {
|
||||||
|
background: var(--theme-background);
|
||||||
|
|
||||||
|
.datagrid-column {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-row {
|
||||||
|
transition: background-color 150ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-cell {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-grid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Card Styles - More beautiful design
|
||||||
|
.um-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-card-item {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
border-radius: var(--theme-border-radius); // Using theme variable
|
||||||
|
box-shadow: var(--theme-shadow); // Using theme variable
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||||
|
border-color: rgba(14, 165, 233, 0.3); // var(--theme-primary) with 30% opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 16px;
|
||||||
|
background: linear-gradient(135deg, rgba(14, 165, 233, 0.1), transparent); // var(--theme-primary) with 10% opacity
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
clr-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-weight: 700;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
max-width: 60%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__badge {
|
||||||
|
margin-left: auto;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 9999px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
background: linear-gradient(135deg, rgba(100, 116, 139, 0.1) 0%, rgba(100, 116, 139, 0.05) 100%);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 16px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-kv {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--theme-text);
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: right;
|
||||||
|
max-width: 60%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme the card footer buttons
|
||||||
|
.um-card-item__footer .um-btn {
|
||||||
|
border-radius: 10px;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-card-item__footer .um-btn.um-btn-outline {
|
||||||
|
border: 1px solid var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-card-item__footer .um-btn.um-btn-outline:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-card-item__footer .um-btn.um-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-card-item__footer .um-btn.um-btn-error:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action items in table
|
||||||
|
.um-action-item {
|
||||||
|
@extend .um-btn;
|
||||||
|
@extend .um-btn-ghost;
|
||||||
|
@extend .um-btn-sm;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-error {
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure action overflow is visible
|
||||||
|
::ng-deep .datagrid-action-overflow {
|
||||||
|
button.action-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px 12px;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Modal Styles
|
||||||
|
.um-delete-header {
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-delete-icon {
|
||||||
|
color: #ef4444;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-delete-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-delete-subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-delete-details {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-delete-detail-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-delete-detail-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-delete-detail-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form Styles
|
||||||
|
.um-form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-file-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
color: var(--theme-text);
|
||||||
|
|
||||||
|
&::file-selector-button {
|
||||||
|
@extend .um-btn;
|
||||||
|
@extend .um-btn-secondary;
|
||||||
|
margin-right: 12px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Header
|
||||||
|
::ng-deep .modal-header {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px 16px 0 0 !important;
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Body
|
||||||
|
::ng-deep .modal-body {
|
||||||
|
padding: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Footer
|
||||||
|
::ng-deep .modal-footer {
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-radius: 0 0 16px 16px !important;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.um-hero {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-search {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-cards {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-card-item {
|
||||||
|
&__header {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ExcelService } from '../../../../services/excel.service';
|
import { ExcelService } from '../../../../services/excel.service';
|
||||||
@@ -8,12 +8,15 @@ import { MenuGroupService } from 'src/app/services/admin/menu-group.service';
|
|||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { CsvService } from 'src/app/services/csv.service';
|
import { CsvService } from 'src/app/services/csv.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-usermaintance',
|
selector: 'app-usermaintance',
|
||||||
templateUrl: './usermaintance.component.html',
|
templateUrl: './usermaintance.component.html',
|
||||||
styleUrls: ['./usermaintance.component.scss']
|
styleUrls: ['./usermaintance.component.scss']
|
||||||
})
|
})
|
||||||
export class UsermaintanceComponent implements OnInit {
|
export class UsermaintanceComponent implements OnInit, OnDestroy {
|
||||||
loading = false;
|
loading = false;
|
||||||
loading1=false;
|
loading1=false;
|
||||||
public entryForm: FormGroup;
|
public entryForm: FormGroup;
|
||||||
@@ -21,13 +24,18 @@ export class UsermaintanceComponent implements OnInit {
|
|||||||
orders;
|
orders;
|
||||||
modalAdd= false;
|
modalAdd= false;
|
||||||
modaledit=false;
|
modaledit=false;
|
||||||
mcreate;
|
mcreate: string | boolean = false;
|
||||||
medit;
|
medit: string | boolean = false;
|
||||||
mdelete;
|
mdelete;
|
||||||
showdata;
|
showdata;
|
||||||
error;
|
error;
|
||||||
modaldelete=false;
|
modaldelete=false;
|
||||||
rowSelected :any= {};
|
rowSelected :any= {};
|
||||||
|
// UI-only state
|
||||||
|
filterText: string = '';
|
||||||
|
viewMode: 'table' | 'cards' = 'cards';
|
||||||
|
private themeSubscription: Subscription;
|
||||||
|
|
||||||
constructor(private excel: ExcelService,
|
constructor(private excel: ExcelService,
|
||||||
private _fb: FormBuilder,
|
private _fb: FormBuilder,
|
||||||
private router: Router, private toastr:ToastrService,
|
private router: Router, private toastr:ToastrService,
|
||||||
@@ -36,25 +44,41 @@ rowSelected :any= {};
|
|||||||
private mainservice:UsermaintanceService,
|
private mainservice:UsermaintanceService,
|
||||||
private csvService: CsvService,
|
private csvService: CsvService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
|
private themeService: ThemeService,
|
||||||
) {this.loading1 = true;
|
) {this.loading1 = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.loading1 = false;
|
this.loading1 = false;
|
||||||
}, 1000); }
|
}, 1000); }
|
||||||
switchLanguage(language: string) {
|
|
||||||
this.translate.use(language);
|
switchLanguage(language: string) {
|
||||||
}
|
this.translate.use(language);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.themeSubscription = this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// Theme changes will automatically update CSS variables
|
||||||
|
// This triggers a re-render of themed elements
|
||||||
|
});
|
||||||
|
|
||||||
this.showdata = this.menuGroupService.getdata();
|
this.showdata = this.menuGroupService.getdata();
|
||||||
console.log(this.showdata);
|
console.log(this.showdata);
|
||||||
this.mcreate=this.showdata.mcreate;
|
if (this.showdata) {
|
||||||
|
this.mcreate = this.showdata.mcreate === 'true' || this.showdata.mcreate === true ? true : false;
|
||||||
console.log(this.mcreate);
|
console.log(this.mcreate);
|
||||||
this.mdelete=this.showdata.mdelete
|
this.mdelete=this.showdata.mdelete
|
||||||
console.log(this.mdelete);
|
console.log(this.mdelete);
|
||||||
this.medit=this.showdata.medit
|
this.medit = this.showdata.medit === 'true' || this.showdata.medit === true ? true : false;
|
||||||
console.log(this.medit);
|
console.log(this.medit);
|
||||||
|
}
|
||||||
this.getData();
|
this.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.themeSubscription) {
|
||||||
|
this.themeSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
getData(){
|
getData(){
|
||||||
this.mainservice.getAll().subscribe((data) => {
|
this.mainservice.getAll().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@@ -79,6 +103,23 @@ rowSelected :any= {};
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UI helpers
|
||||||
|
get totalUsers(): number {
|
||||||
|
const list: any[] = (this.givendata as unknown as any[]) || [];
|
||||||
|
return list.length;
|
||||||
|
}
|
||||||
|
get filteredUsers(): any[] {
|
||||||
|
const items: any[] = (this.givendata as unknown as any[]) || [];
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
if (!text) { return items; }
|
||||||
|
return items.filter(u => (
|
||||||
|
(u?.fullName || '').toLowerCase().includes(text) ||
|
||||||
|
(u?.email || '').toLowerCase().includes(text) ||
|
||||||
|
(u?.usrGrpName || '').toLowerCase().includes(text)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
setViewMode(mode: 'table' | 'cards') { this.viewMode = mode; }
|
||||||
|
|
||||||
|
|
||||||
// csv
|
// csv
|
||||||
|
|
||||||
@@ -129,7 +170,7 @@ rowSelected :any= {};
|
|||||||
this.router.navigate(["../usermaintancedit/"+ id], { relativeTo: this.route });
|
this.router.navigate(["../usermaintancedit/"+ id], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
onDelete(row) {
|
onDelete(row) {
|
||||||
this.rowSelected = row;
|
this.rowSelected = {...row}; // Create a copy to avoid reference issues
|
||||||
this.modaldelete=true;
|
this.modaldelete=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,319 +1,282 @@
|
|||||||
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
|
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon> Security</a></li>
|
||||||
|
<li><a href="javascript://">User Maintenance</a></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<h4 style="font-weight: 300;display: inline;"> User Maintenance</h4>
|
<section class="um-hero">
|
||||||
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">Add Mode</span><br>
|
<div class="um-hero__content">
|
||||||
<hr>
|
<div class="um-hero__icon"><clr-icon shape="user"></clr-icon></div>
|
||||||
<div class="main" >
|
<div>
|
||||||
<form [formGroup]="entryForm">
|
<h2 class="um-hero__title">User Maintenance</h2>
|
||||||
|
<p class="um-hero__subtitle">Add New User</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="clr-row">
|
<div class="um-add-container">
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
<div class="um-form-card">
|
||||||
<label for="name"> First Name</label>
|
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()" class="um-form">
|
||||||
<input type="text" class="clr-input" formControlName="first_name">
|
<div class="clr-row">
|
||||||
<div *ngIf="submitted && entryForm.controls.first_name.errors" class="error_mess">
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<div *ngIf="submitted && entryForm.controls.first_name.errors.required" class="error_mess">*This field is Required</div>
|
<label for="first_name" class="um-form-label">First Name<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="first_name" class="um-form-input" formControlName="first_name">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.first_name.errors" class="um-error-message">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.first_name.errors.required">*This field is Required</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="last_name" class="um-form-label">Last Name<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="last_name" class="um-form-input" formControlName="last_name">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.last_name.errors" class="um-error-message">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.last_name.errors.required">*This field is Required</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Last Name</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="last_name">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.last_name.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.last_name.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Mobile Number</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="mob_no" required pattern="((\+)?([1-9]{2}))?[1-9]{1}[0-9]{9}$">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.mob_no.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.mob_no.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
<div *ngIf="submitted && entryForm.controls.mob_no.errors.pattern" class="error_mess">* Please Follow your pattern,+91 Enter 10 digit Mobile Number.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="mob_no" class="um-form-label">Mobile Number<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="mob_no" class="um-form-input" formControlName="mob_no" required pattern="((\+)?([1-9]{2}))?[1-9]{1}[0-9]{9}$">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.mob_no.errors" class="um-error-message">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.mob_no.errors.required">*This field is Required</div>
|
||||||
|
<div *ngIf="submitted && entryForm.controls.mob_no.errors.pattern">* Please Follow your pattern,+91 Enter 10 digit Mobile Number.</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">New Password<span class="required-field">*</span></label>
|
|
||||||
<input type="password" class="clr-input" formControlName="new_password" minlenght="6">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.new_password.errors" class="error_mess">
|
|
||||||
<div *ngIf="entryForm.controls.new_password.errors.required" class="error_mess">*Password is required</div>
|
|
||||||
<div *ngIf="entryForm.controls.new_password.errors.minlength" class="error_mess">* Password must be at least 6 characters</div>
|
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="email" class="um-form-label">Email<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="email" class="um-form-input" formControlName="email" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.email.errors" class="um-error-message">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.email.errors.required">*This field is Required</div>
|
||||||
|
<div *ngIf="submitted && entryForm.controls.email.errors.pattern">*Email must be a valid email address</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Confirm New Password<span class="required-field">*</span></label>
|
|
||||||
<input type="password" class="clr-input" formControlName="confirm_password">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.confirm_password.errors" class="error_mess">
|
|
||||||
<div *ngIf="entryForm.controls.confirm_password.errors.required" class="error_mess">* Confirm Password is required</div>
|
|
||||||
<div *ngIf="entryForm.controls.confirm_password.errors.confirmedValidator" class="error_mess">* Password and Confirm Password must be match.</div>
|
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="new_password" class="um-form-label">New Password<span class="required-field">*</span></label>
|
||||||
|
<input type="password" id="new_password" class="um-form-input" formControlName="new_password" minlength="6">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.new_password.errors" class="um-error-message">
|
||||||
|
<div *ngIf="entryForm.controls.new_password.errors.required">*Password is required</div>
|
||||||
|
<div *ngIf="entryForm.controls.new_password.errors.minlength">* Password must be at least 6 characters</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Business Title</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="title">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name"> Short Name</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="shortName">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Full Name</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="fullName">
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">Status</label>
|
|
||||||
<select id="" formControlName="status">
|
|
||||||
<option value="E">Enable</option>
|
|
||||||
<option value="A">Active</option></select>
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Position Code</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="positionCodeString" [(ngModel)]="userobjpos.positionCode" name="positionCode" >
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" (click)="gotoposition()">V</button>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Department Code</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="departmentCodeString" [(ngModel)]="userobjdept.departmentCode" name="departmentCode">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" (click)="gotodepartmet()">V</button>
|
|
||||||
</div> -->
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">User Group</label>
|
|
||||||
<select id="tags" formControlName="usrGrpId" >
|
|
||||||
<option *ngFor="let sub of usergrpdata" [value]="sub.usrGrp">{{sub.groupName}}</option>
|
|
||||||
</select>
|
|
||||||
<div *ngIf="submitted && entryForm.controls.usrGrpId.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.usrGrpId.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Customer Id</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="customerId" name="customerCode">
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" >V</button>
|
|
||||||
</div> -->
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Email</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="email" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.email.errors" class="error_mess">
|
|
||||||
<div *ngIf="submitted && entryForm.controls.email.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
<!-- <div *ngIf="submitted && entryForm.controls.email.errors.email" class="error_mess">*Email must be a valid email address</div> -->
|
|
||||||
<div *ngIf="submitted && entryForm.controls.email.errors.pattern" class="error_mess">*Email must be a valid email address</div>
|
|
||||||
|
|
||||||
</div>
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
</div>
|
<label for="confirm_password" class="um-form-label">Confirm New Password<span class="required-field">*</span></label>
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
<input type="password" id="confirm_password" class="um-form-input" formControlName="confirm_password">
|
||||||
<label for="tags">Access Type</label>
|
<div *ngIf="submitted && entryForm.controls.confirm_password.errors" class="um-error-message">
|
||||||
<select name="usrGrpId" formControlName="accesstype">
|
<div *ngIf="entryForm.controls.confirm_password.errors.required">* Confirm Password is required</div>
|
||||||
<option *ngFor="let sub of accessdata" [value]="sub.name" [selected]="true">{{sub.name}}</option>
|
<div *ngIf="entryForm.controls.confirm_password.errors.confirmedValidator">* Password and Confirm Password must be match.</div>
|
||||||
</select>
|
</div>
|
||||||
<div *ngIf="submitted && entryForm.controls.accesstype.errors" class="error_mess">
|
</div>
|
||||||
<div *ngIf="submitted && entryForm.controls.accesstype.errors.required" class="error_mess">*This field is Required</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">Notification</label>
|
|
||||||
<select id="" formControlName="notification">
|
|
||||||
<option value="N" >NO</option>
|
|
||||||
<option value="Y">YES</option></select>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
</div>
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<!-- <div class="clr-row">
|
<label for="usrGrpId" class="um-form-label">User Group<span class="required-field">*</span></label>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<select id="usrGrpId" class="um-form-select" formControlName="usrGrpId">
|
||||||
<label for="name">Created By</label>
|
<option value="">Select User Group</option>
|
||||||
<input type="text" class="clr-input">
|
<option *ngFor="let group of usergrpdata" [value]="group.usrGrp">{{group.groupName}}</option>
|
||||||
</div>
|
</select>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<div *ngIf="submitted && entryForm.controls.usrGrpId.errors" class="um-error-message">
|
||||||
<label for="name">Created On</label>
|
<div *ngIf="submitted && entryForm.controls.usrGrpId.errors.required">*This field is Required</div>
|
||||||
<input type="text" class="clr-input">
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Updated By</label>
|
|
||||||
<input type="text" class="clr-input">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Updated On</label>
|
|
||||||
<input type="text" class="clr-input">
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<button type="button" class="btn btn-outline" (click)="goback()">Cancel</button>
|
<label for="accesstype" class="um-form-label">Access Type<span class="required-field">*</span></label>
|
||||||
<button type="button" class="btn btn-primary" (click)="onSubmit()">Add</button>
|
<select id="accesstype" class="um-form-select" formControlName="accesstype">
|
||||||
|
<option value="">Select Access Type</option>
|
||||||
|
<option *ngFor="let access of accessdata" [value]="access.name">{{access.name}}</option>
|
||||||
|
</select>
|
||||||
|
<div *ngIf="submitted && entryForm.controls.accesstype.errors" class="um-error-message">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.accesstype.errors.required">*This field is Required</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="um-form-actions">
|
||||||
|
<button type="button" class="um-btn um-btn-outline" (click)="goback()">
|
||||||
|
<clr-icon shape="times"></clr-icon>
|
||||||
|
<span>Cancel</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="um-btn um-btn-primary">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
<span>Add User</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Customer Modal -->
|
||||||
|
<clr-modal [(clrModalOpen)]="customer" [clrModalSize]="'lg'">
|
||||||
|
<h3 class="modal-title"><b>Select From List:</b></h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-12 clr-col-sm-12">
|
||||||
|
<input id="data" type="text" placeholder="Enter search Criteria" class="um-form-input">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
|
<clr-dg-placeholder>
|
||||||
|
<clr-spinner>Loading ...</clr-spinner>
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
<clr-dg-column [clrDgField]="'id'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
No
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerId'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Customer Code
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerCode'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Customer Id
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerName'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Customer Name
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="customer" [clrModalSize]="'lg'">
|
<clr-dg-row *clrDgItems="let user of custdata;let i = index" [clrDgItem]="user">
|
||||||
<h3 class="modal-title"><b>Select From List:</b></h3>
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
<div class="modal-body">
|
<clr-dg-cell (click)="getcustID(user.customerId)" class="um-clickable-cell">{{user.customerCode}}</clr-dg-cell>
|
||||||
<div class="clr-row">
|
<clr-dg-cell>{{user.customerId}}</clr-dg-cell>
|
||||||
<div class="clr-col-md-12 clr-col-sm-12">
|
<clr-dg-cell>{{user.customerName}}</clr-dg-cell>
|
||||||
<input id="data" type="text" placeholder="Enter search Criteria" class="clr-input">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
<clr-dg-placeholder><clr-spinner>Loading ...</clr-spinner></clr-dg-placeholder>
|
<table class="table">
|
||||||
<clr-dg-column [clrDgField]="'id'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<tr>
|
||||||
No
|
<td class="td-title">No</td>
|
||||||
</ng-container></clr-dg-column>
|
<td class="td-content">{{user.id}}</td>
|
||||||
<clr-dg-column [clrDgField]="'customerId'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</tr>
|
||||||
Customer Code
|
<tr>
|
||||||
</ng-container></clr-dg-column>
|
<td class="td-title">Customer Code</td>
|
||||||
<clr-dg-column [clrDgField]="'customerCode'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<td class="td-content">{{user.customerCode}}</td>
|
||||||
Customer Id
|
</tr>
|
||||||
</ng-container></clr-dg-column>
|
<tr>
|
||||||
|
<td class="td-title">Customer Id</td>
|
||||||
|
<td class="td-content">{{user.customerId}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Customer Name</td>
|
||||||
|
<td class="td-content">{{user.customerName}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'customerName'">
|
<clr-dg-footer>
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
Customer Name
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
||||||
</ng-container>
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
</clr-dg-column >
|
of {{pagination.totalItems}} Records
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of custdata;let i = index" [clrDgItem]="user">
|
<!-- Department Modal -->
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
<clr-modal [(clrModalOpen)]="department" [clrModalSize]="'lg'">
|
||||||
<clr-dg-cell (click)="getcustID(user.customerId)" style="color: rgb(108, 108, 194);" >{{user.customerCode}}</clr-dg-cell>
|
<h3 class="modal-title"><b>Select From List:</b></h3>
|
||||||
<clr-dg-cell >{{user.customerId}}</clr-dg-cell>
|
<div class="modal-body">
|
||||||
<clr-dg-cell>{{user.customerName}}</clr-dg-cell>
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-12 clr-col-sm-12">
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
<input id="data" type="text" placeholder="Enter search Criteria" class="um-form-input">
|
||||||
<table class="table">
|
|
||||||
<tr>
|
|
||||||
<td class="td-title">NO</td>
|
|
||||||
<td class="td-content">{{user.id}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="td-title"> Customer Code</td>
|
|
||||||
<td class="td-content">{{user.customerCode}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="td-title">Customer Id</td>
|
|
||||||
<td class="td-content">{{user.customerId}} </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="td-title"> Customer Name<td>
|
|
||||||
<td class="td-content">{{user.customerName}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="department" [clrModalSize]="'lg'">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<h3 class="modal-title"><b>Select From List:</b></h3>
|
<clr-dg-placeholder>
|
||||||
<div class="modal-body">
|
<clr-spinner>Loading ...</clr-spinner>
|
||||||
<div class="clr-row">
|
</clr-dg-placeholder>
|
||||||
<div class="clr-col-md-12 clr-col-sm-12">
|
<clr-dg-column [clrDgField]="'id'">
|
||||||
<input id="data" type="text" placeholder="Enter search Criteria" class="clr-input">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
</div>
|
No
|
||||||
</div>
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerId'">
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
<clr-dg-placeholder><clr-spinner>Loading ...</clr-spinner></clr-dg-placeholder>
|
Department Code
|
||||||
<clr-dg-column [clrDgField]="'id'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</ng-container>
|
||||||
No
|
</clr-dg-column>
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'customerId'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Department Code
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of departmentdata;let i = index" [clrDgItem]="user">
|
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell (click)="getdepid(user.departmentCode)" style="color: rgb(108, 108, 194);" >{{user.departmentCode}}</clr-dg-cell>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
|
||||||
<table class="table">
|
|
||||||
<tr>
|
|
||||||
<td class="td-title">NO</td>
|
|
||||||
<td class="td-content">{{user.id}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let user of departmentdata;let i = index" [clrDgItem]="user">
|
||||||
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell (click)="getdepid(user.departmentCode)" class="um-clickable-cell">{{user.departmentCode}}</clr-dg-cell>
|
||||||
|
|
||||||
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">No</td>
|
||||||
|
<td class="td-content">{{user.id}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
|
<clr-dg-footer>
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
of {{pagination.totalItems}} Records
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
|
<!-- Position Modal -->
|
||||||
|
<clr-modal [(clrModalOpen)]="position" [clrModalSize]="'lg'">
|
||||||
|
<h3 class="modal-title"><b>Select From List:</b></h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-12 clr-col-sm-12">
|
||||||
|
<input id="data" type="text" placeholder="Enter search Criteria" class="um-form-input">
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="position" [clrModalSize]="'lg'">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<h3 class="modal-title"><b>Select From List:</b></h3>
|
<clr-dg-placeholder>
|
||||||
<div class="modal-body">
|
<clr-spinner>Loading ...</clr-spinner>
|
||||||
<div class="clr-row">
|
</clr-dg-placeholder>
|
||||||
<div class="clr-col-md-12 clr-col-sm-12">
|
<clr-dg-column [clrDgField]="'id'">
|
||||||
<input id="data" type="text" placeholder="Enter search Criteria" class="clr-input">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
</div>
|
No
|
||||||
</div>
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerId'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Position Code
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let user of positiondata;let i = index" [clrDgItem]="user">
|
||||||
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell (click)="getposid(user.positionCode)" class="um-clickable-cell">{{user.positionCode}}</clr-dg-cell>
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
<clr-dg-placeholder><clr-spinner>Loading ...</clr-spinner></clr-dg-placeholder>
|
<table class="table">
|
||||||
<clr-dg-column [clrDgField]="'id'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<tr>
|
||||||
No
|
<td class="td-title">No</td>
|
||||||
</ng-container></clr-dg-column>
|
<td class="td-content">{{user.id}}</td>
|
||||||
<clr-dg-column [clrDgField]="'customerId'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</tr>
|
||||||
Position Code
|
</table>
|
||||||
</ng-container></clr-dg-column>
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of positiondata;let i = index" [clrDgItem]="user">
|
<clr-dg-footer>
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-cell (click)="getposid(user.positionCode)" style="color: rgb(108, 108, 194);" >{{user.positionCode}}</clr-dg-cell>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
of {{pagination.totalItems}} Records
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
</clr-datagrid>
|
||||||
<table class="table">
|
</div>
|
||||||
<tr>
|
</clr-modal>
|
||||||
<td class="td-title">NO</td>
|
|
||||||
<td class="td-content">{{user.id}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
@import '../../../../../styles1.scss';
|
@import '../../../../../styles1.scss';
|
||||||
|
@import '../../../../../styles/_design-tokens.scss';
|
||||||
|
|
||||||
input.ng-invalid.ng-touched {
|
input.ng-invalid.ng-touched {
|
||||||
border-color: red;
|
border-color: red;
|
||||||
}
|
}
|
||||||
@@ -6,3 +8,383 @@ input.ng-invalid.ng-touched {
|
|||||||
.error_mess {
|
.error_mess {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User Maintenance Add Styles
|
||||||
|
.um-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px 32px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
font-family: var(--theme-font-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-add-container {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-card {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form {
|
||||||
|
.clr-row {
|
||||||
|
margin: 0 -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clr-col-md-6,
|
||||||
|
.clr-col-sm-12 {
|
||||||
|
padding: 0 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-input,
|
||||||
|
.um-form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--theme-text);
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1); // var(--theme-primary) with 10% opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
border-color: #ef4444;
|
||||||
|
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 12px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 12px;
|
||||||
|
padding-right: 40px;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-error-message {
|
||||||
|
color: #ef4444;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required-field {
|
||||||
|
color: #ef4444;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 24px;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Button Styles using ThemeService
|
||||||
|
.um-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
text-decoration: none;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--theme-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizes
|
||||||
|
&.um-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-md {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-lg {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variants
|
||||||
|
&.um-btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary, #0284c7) 0%, var(--theme-accent, #7c3aed) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.1); // var(--theme-secondary) with 10% opacity
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
border-color: var(--theme-error-dark, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(239, 68, 68, 0.3), 0 4px 6px -2px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-clickable-cell {
|
||||||
|
color: var(--theme-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Styles
|
||||||
|
::ng-deep .modal-header {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px 16px 0 0 !important;
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .modal-body {
|
||||||
|
padding: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .modal-footer {
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-radius: 0 0 16px 16px !important;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data Grid Styles
|
||||||
|
.um-datagrid {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
::ng-deep .datagrid {
|
||||||
|
.datagrid-head {
|
||||||
|
background: var(--theme-background);
|
||||||
|
|
||||||
|
.datagrid-column {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-row {
|
||||||
|
transition: background-color 150ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-cell {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.um-hero {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,9 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { AccesstypeService } from 'src/app/services/admin/accesstype.service';
|
import { AccesstypeService } from 'src/app/services/admin/accesstype.service';
|
||||||
import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service';
|
import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service';
|
||||||
import { UsermaintanceService } from '../../../../services/admin/usermaintance.service';
|
import { UsermaintanceService } from '../../../../services/admin/usermaintance.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-usermaintanceadd',
|
selector: 'app-usermaintanceadd',
|
||||||
@@ -12,51 +15,63 @@ import { UsermaintanceService } from '../../../../services/admin/usermaintance.s
|
|||||||
})
|
})
|
||||||
export class UsermaintanceaddComponent implements OnInit {
|
export class UsermaintanceaddComponent implements OnInit {
|
||||||
public entryForm: FormGroup;
|
public entryForm: FormGroup;
|
||||||
customer:boolean=false;
|
customer: boolean = false;
|
||||||
department:boolean=false;
|
department: boolean = false;
|
||||||
position:boolean=false;
|
position: boolean = false;
|
||||||
custdata: any;
|
custdata: any;
|
||||||
loading = false;
|
loading = false;
|
||||||
clickedID:number;
|
clickedID: number;
|
||||||
custiddata: any;
|
custiddata: any;
|
||||||
userobjcust={
|
userobjcust = {
|
||||||
customerName:'',
|
customerName: '',
|
||||||
customerCode:'',
|
customerCode: '',
|
||||||
}
|
}
|
||||||
departmentdata: any;
|
departmentdata: any;
|
||||||
positiondata: any;
|
positiondata: any;
|
||||||
deptiddata: any;
|
deptiddata: any;
|
||||||
userobjdept={
|
userobjdept = {
|
||||||
departmentCode:'',
|
departmentCode: '',
|
||||||
}
|
}
|
||||||
userobjpos={
|
userobjpos = {
|
||||||
positionCode:'',
|
positionCode: '',
|
||||||
}
|
}
|
||||||
posiddata: any;
|
posiddata: any;
|
||||||
usergrpdata;
|
usergrpdata;
|
||||||
accessdata;
|
accessdata;
|
||||||
error;
|
error;
|
||||||
submitted=false;
|
submitted = false;
|
||||||
|
private themeSubscription: Subscription;
|
||||||
|
|
||||||
constructor( private _fb: FormBuilder,
|
constructor(private _fb: FormBuilder,
|
||||||
private mainservice:UsermaintanceService,
|
private mainservice: UsermaintanceService,
|
||||||
private router: Router,private accesstype:AccesstypeService,
|
private router: Router, private accesstype: AccesstypeService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private usergrpservice: UsergrpmaintainceService
|
private usergrpservice: UsergrpmaintainceService,
|
||||||
) { }
|
private translate: TranslateService,
|
||||||
|
private themeService: ThemeService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
switchLanguage(language: string) {
|
||||||
|
this.translate.use(language);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.themeSubscription = this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// Theme changes will automatically update CSS variables
|
||||||
|
// This triggers a re-render of themed elements
|
||||||
|
});
|
||||||
|
|
||||||
this.entryForm = this._fb.group({
|
this.entryForm = this._fb.group({
|
||||||
first_name :['',[Validators.required]],
|
first_name: ['', [Validators.required]],
|
||||||
last_name:['',[Validators.required]],
|
last_name: ['', [Validators.required]],
|
||||||
email:['',[Validators.required,Validators.email]],
|
email: ['', [Validators.required, Validators.email]],
|
||||||
mob_no:['',[Validators.required]],
|
mob_no: ['', [Validators.required]],
|
||||||
new_password:['',[Validators.required,Validators.minLength(6)]],
|
new_password: ['', [Validators.required, Validators.minLength(6)]],
|
||||||
confirm_password:['',[Validators.required]],
|
confirm_password: ['', [Validators.required]],
|
||||||
usrGrpId:['',[Validators.required]],
|
usrGrpId: ['', [Validators.required]],
|
||||||
account_id:1,
|
account_id: 1,
|
||||||
accesstype:['',[Validators.required]],
|
accesstype: ['', [Validators.required]],
|
||||||
// status:[null],
|
// status:[null],
|
||||||
// username:[null] ,
|
// username:[null] ,
|
||||||
// userPassw:[null] ,
|
// userPassw:[null] ,
|
||||||
// confirmPassword:[null],
|
// confirmPassword:[null],
|
||||||
@@ -72,31 +87,38 @@ export class UsermaintanceaddComponent implements OnInit {
|
|||||||
// notification:[null],
|
// notification:[null],
|
||||||
|
|
||||||
//departmentCode: this._fb.array([this.department()]),
|
//departmentCode: this._fb.array([this.department()]),
|
||||||
// positionCode: this._fb.array([this.position()]),
|
// positionCode: this._fb.array([this.position()]),
|
||||||
//usrGrp: this._fb.array([this.user()]),
|
//usrGrp: this._fb.array([this.user()]),
|
||||||
|
|
||||||
}, {
|
}, {
|
||||||
validator: ConfirmedValidator('new_password', 'confirm_password')
|
validator: ConfirmedValidator('new_password', 'confirm_password')
|
||||||
});
|
});
|
||||||
this.usergrp();
|
this.usergrp();
|
||||||
this.getdata();
|
this.getdata();
|
||||||
}
|
}
|
||||||
getdata(){
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.themeSubscription) {
|
||||||
|
this.themeSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getdata() {
|
||||||
this.accesstype.getAll().subscribe(resp => {
|
this.accesstype.getAll().subscribe(resp => {
|
||||||
this.accessdata = resp;
|
this.accessdata = resp;
|
||||||
console.log('accessdata: ', this.accessdata);
|
console.log('accessdata: ', this.accessdata);
|
||||||
if(this.accessdata.length==0){
|
if (this.accessdata.length == 0) {
|
||||||
this.error="No data Available";
|
this.error = "No data Available";
|
||||||
console.log(this.error)
|
console.log(this.error)
|
||||||
}
|
}
|
||||||
},(error) => {
|
}, (error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
if(error){
|
if (error) {
|
||||||
this.error="Server Error";
|
this.error = "Server Error";
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
usergrp(){
|
usergrp() {
|
||||||
this.usergrpservice.getAll().subscribe((data) => {
|
this.usergrpservice.getAll().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.usergrpdata = data;
|
this.usergrpdata = data;
|
||||||
@@ -120,11 +142,11 @@ export class UsermaintanceaddComponent implements OnInit {
|
|||||||
|
|
||||||
// });
|
// });
|
||||||
//}
|
//}
|
||||||
onSubmit(){
|
onSubmit() {
|
||||||
this.submitted=true
|
this.submitted = true
|
||||||
if (this.entryForm.invalid) {
|
if (this.entryForm.invalid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(this.entryForm.value);
|
console.log(this.entryForm.value);
|
||||||
this.mainservice.create(this.entryForm.value).subscribe(data => {
|
this.mainservice.create(this.entryForm.value).subscribe(data => {
|
||||||
console.log(data)
|
console.log(data)
|
||||||
@@ -136,63 +158,63 @@ export class UsermaintanceaddComponent implements OnInit {
|
|||||||
this.router.navigate(["../usermaintance"], { relativeTo: this.route });
|
this.router.navigate(["../usermaintance"], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
|
|
||||||
goback(){
|
goback() {
|
||||||
this.router.navigate(["../usermaintance"], { relativeTo: this.route });
|
this.router.navigate(["../usermaintance"], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
gotodepartmet(){
|
gotodepartmet() {
|
||||||
this.department=!this.department;
|
this.department = !this.department;
|
||||||
this.mainservice.getalldepartment().subscribe((data)=>{
|
this.mainservice.getalldepartment().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.departmentdata=data;
|
this.departmentdata = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getdepid(id:number){
|
getdepid(id: number) {
|
||||||
this.clickedID=id;
|
this.clickedID = id;
|
||||||
console.log("clicked by id"+ id);
|
console.log("clicked by id" + id);
|
||||||
this.mainservice.getbydepartmentid(id).subscribe((data) => {
|
this.mainservice.getbydepartmentid(id).subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.deptiddata= data;
|
this.deptiddata = data;
|
||||||
// this.userObj= this.custiddata;
|
// this.userObj= this.custiddata;
|
||||||
this.userobjdept =this.deptiddata;
|
this.userobjdept = this.deptiddata;
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
this.department=false;
|
this.department = false;
|
||||||
}
|
}
|
||||||
gotoposition(){
|
gotoposition() {
|
||||||
this.position=!this.position;
|
this.position = !this.position;
|
||||||
this.mainservice.getallposition().subscribe((data)=>{
|
this.mainservice.getallposition().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.positiondata=data;
|
this.positiondata = data;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
getposid(id:number){
|
getposid(id: number) {
|
||||||
this.clickedID=id;
|
this.clickedID = id;
|
||||||
console.log("clicked by id"+ id);
|
console.log("clicked by id" + id);
|
||||||
this.mainservice.getbypositionid(id).subscribe((data) => {
|
this.mainservice.getbypositionid(id).subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.posiddata= data;
|
this.posiddata = data;
|
||||||
// this.userObj= this.custiddata;
|
// this.userObj= this.custiddata;
|
||||||
this.userobjpos =this.posiddata;
|
this.userobjpos = this.posiddata;
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
this.position=false;
|
this.position = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
export function ConfirmedValidator(controlName: string, matchingControlName: string){
|
export function ConfirmedValidator(controlName: string, matchingControlName: string) {
|
||||||
return (formGroup: FormGroup) => {
|
return (formGroup: FormGroup) => {
|
||||||
const control = formGroup.controls[controlName];
|
const control = formGroup.controls[controlName];
|
||||||
const matchingControl = formGroup.controls[matchingControlName];
|
const matchingControl = formGroup.controls[matchingControlName];
|
||||||
if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
|
if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (control.value !== matchingControl.value) {
|
if (control.value !== matchingControl.value) {
|
||||||
matchingControl.setErrors({ confirmedValidator: true });
|
matchingControl.setErrors({ confirmedValidator: true });
|
||||||
} else {
|
} else {
|
||||||
matchingControl.setErrors(null);
|
matchingControl.setErrors(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,348 +1,249 @@
|
|||||||
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
|
<li><a href="javascript://"><clr-icon shape="lock"></clr-icon> Security</a></li>
|
||||||
|
<li><a href="javascript://">User Maintenance</a></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<h4 style="font-weight: 300;display: inline;"> User Maintenance</h4>
|
<section class="um-hero">
|
||||||
<span class="label label-light-blue" style="display: inline;margin-left: 30px;">Edit Mode</span><br>
|
<div class="um-hero__content">
|
||||||
<hr>
|
<div class="um-hero__icon"><clr-icon shape="user"></clr-icon></div>
|
||||||
<div class="main" >
|
<div>
|
||||||
<form *ngIf="data1">
|
<h2 class="um-hero__title">User Maintenance</h2>
|
||||||
<div class="clr-row">
|
<p class="um-hero__subtitle">Edit User</p>
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
</div>
|
||||||
<label for="name"> First Name</label>
|
</div>
|
||||||
<input type="text" class="clr-input" name="first_name" [(ngModel)]="data1.fullName" >
|
</section>
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Last Name</label>
|
|
||||||
<input type="text" class="clr-input" name="last_name" [(ngModel)]="data1.last_name">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Mobile Number</label>
|
|
||||||
<input type="text" class="clr-input" name="mob_no" [(ngModel)]="data1.mob_no">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">New Password<span class="required-field">*</span></label>
|
|
||||||
<input type="password" class="clr-input" name="new_password" [(ngModel)]="data1.userPassw">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
<div class="um-edit-container">
|
||||||
<label for="name">Business Title</label>
|
<div class="um-form-card">
|
||||||
<input type="text" class="clr-input" formControlName="title">
|
<form *ngIf="data1" (ngSubmit)="update()" class="um-form">
|
||||||
</div>
|
<div class="clr-row">
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<label for="name"> Short Name</label>
|
<label for="first_name" class="um-form-label">First Name<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" formControlName="shortName">
|
<input type="text" id="first_name" class="um-form-input" name="first_name" [(ngModel)]="data1.fullName">
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Full Name</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="fullName">
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">Status</label>
|
|
||||||
<select id="" formControlName="status">
|
|
||||||
<option value="E">Enable</option>
|
|
||||||
<option value="A">Active</option></select>
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Position Code</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="positionCodeString" [(ngModel)]="userobjpos.positionCode" name="positionCode" >
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" (click)="gotoposition()">V</button>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Department Code</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="departmentCodeString" [(ngModel)]="userobjdept.departmentCode" name="departmentCode">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" (click)="gotodepartmet()">V</button>
|
|
||||||
</div> -->
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">User Group</label>
|
|
||||||
<select [(ngModel)]="data1.usrGrpId" name="usrGrpId">
|
|
||||||
<option *ngFor="let sub of usergrpdata" [value]="sub.usrGrp">{{sub.groupName}}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Customer Id</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="customerId" name="customerCode">
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" >V</button>
|
|
||||||
</div> -->
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Email</label>
|
|
||||||
<input type="text" class="clr-input" name="email" [(ngModel)]="data1.email">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">Access Type</label>
|
|
||||||
<select [(ngModel)]="data1.accesstype" name="usrGrpId">
|
|
||||||
<option *ngFor="let sub of accessdata" [value]="sub.name">{{sub.name}}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">Notification</label>
|
|
||||||
<select id="" formControlName="notification">
|
|
||||||
<option value="N" >NO</option>
|
|
||||||
<option value="Y">YES</option></select>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
</div>
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<!-- <div class="clr-row">
|
<label for="last_name" class="um-form-label">Last Name<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="last_name" class="um-form-input" name="last_name" [(ngModel)]="data1.last_name">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="mob_no" class="um-form-label">Mobile Number<span class="required-field">*</span></label>
|
||||||
|
<input type="text" id="mob_no" class="um-form-input" name="mob_no" [(ngModel)]="data1.mob_no">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<label for="name"> User Name</label>
|
<label for="email" class="um-form-label">Email<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.username" name="username">
|
<input type="text" id="email" class="um-form-input" name="email" [(ngModel)]="data1.email">
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">New Password<span class="required-field">*</span></label>
|
|
||||||
<input type="password" class="clr-input" [(ngModel)]="data1.userPassw" name="userPassw">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<label for="name">Business Title</label>
|
<label for="new_password" class="um-form-label">New Password<span class="required-field">*</span></label>
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.title" name="title">
|
<input type="password" id="new_password" class="um-form-input" name="new_password" [(ngModel)]="data1.userPassw">
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name"> Short Name</label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.shortName" name="shortName">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Full Name</label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.fullName" name="fullName">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">Status</label>
|
|
||||||
<select [(ngModel)]="data1.status" name="status">
|
|
||||||
<option value="E">Enable</option>
|
|
||||||
<option value="A">Active</option></select>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Position Code</label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.positionCodeString" [value]="userobjpos.positionCode" name="positionCodeString">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" (click)="gotoposition()">V</button>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Department Code</label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.departmentCodeString" [value]="userobjdept.departmentCode" name="departmentCodeString">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" (click)="gotodepartmet()">V</button>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">User Group</label>
|
|
||||||
<select [(ngModel)]="data1.usrGrpId" name="usrGrpId">
|
|
||||||
<option *ngFor="let sub of usergrpdata" [value]="sub.usrGrp">{{sub.groupName}}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Customer Id</label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.customerId" [value]="userobjcust.customerCode" name="customerId">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-1 clr-col-sm-12" style="margin-top: 25px;">
|
|
||||||
<button style="margin-right: 5px;" (click)="gotocustomerid()">V</button>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="name">Email</label>
|
|
||||||
<input type="text" class="clr-input"[(ngModel)]="data1.email" name="email">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-4 clr-col-sm-12">
|
|
||||||
<label for="tags">Notification</label>
|
|
||||||
<select [(ngModel)]="data1.notification" name="notification">
|
|
||||||
<option value="N" >NO</option>
|
|
||||||
<option value="Y">YES</option></select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<div class="clr-row">
|
<label for="usrGrpId" class="um-form-label">User Group<span class="required-field">*</span></label>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
<select id="usrGrpId" class="um-form-select" [(ngModel)]="data1.usrGrpId" name="usrGrpId">
|
||||||
<label for="name">Created By</label>
|
<option value="">Select User Group</option>
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.createby" name="createby">
|
<option *ngFor="let group of usergrpdata" [value]="group.usrGrp">{{group.groupName}}</option>
|
||||||
</div>
|
</select>
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
</div>
|
||||||
<label for="name">Created On</label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.createdate" name="createdate">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Updated By</label>
|
|
||||||
<input type="text" class="clr-input" [(ngModel)]="data1.updateby" name="updateby">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-md-3 clr-col-sm-12">
|
|
||||||
<label for="name">Updated On</label>
|
|
||||||
<input type="text" class="clr-input"[(ngModel)]="data1.updatedate" name="updatedate">
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<button type="button" class="btn btn-outline" (click)="goback()">Close</button>
|
<label for="accesstype" class="um-form-label">Access Type<span class="required-field">*</span></label>
|
||||||
<button type="button" class="btn btn-primary" (click)="update()">Update</button>
|
<select id="accesstype" class="um-form-select" [(ngModel)]="data1.accesstype" name="accesstype">
|
||||||
|
<option value="">Select Access Type</option>
|
||||||
|
<option *ngFor="let access of accessdata" [value]="access.name">{{access.name}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="um-form-actions">
|
||||||
|
<button type="button" class="um-btn um-btn-outline" (click)="goback()">
|
||||||
|
<clr-icon shape="times"></clr-icon>
|
||||||
|
<span>Close</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="um-btn um-btn-primary">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
<span>Update User</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Customer Modal -->
|
||||||
|
<clr-modal [(clrModalOpen)]="customer" [clrModalSize]="'lg'">
|
||||||
|
<h3 class="modal-title"><b>Select From List:</b></h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-12 clr-col-sm-12">
|
||||||
|
<input id="data" type="text" placeholder="Enter search Criteria" class="um-form-input">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="customer" [clrModalSize]="'lg'">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<h3 class="modal-title"><b>Select From List:</b></h3>
|
<clr-dg-placeholder>
|
||||||
<div class="modal-body">
|
<clr-spinner>Loading ...</clr-spinner>
|
||||||
<div class="clr-row">
|
</clr-dg-placeholder>
|
||||||
<div class="clr-col-md-12 clr-col-sm-12">
|
<clr-dg-column [clrDgField]="'id'">
|
||||||
<input id="data" type="text" placeholder="Enter search Criteria" class="clr-input">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
</div>
|
No
|
||||||
</div>
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerId'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Customer Code
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerCode'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Customer Id
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerName'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Customer Name
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-dg-row *clrDgItems="let user of custdata;let i = index" [clrDgItem]="user">
|
||||||
<clr-dg-placeholder><clr-spinner>Loading ...</clr-spinner></clr-dg-placeholder>
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
<clr-dg-column [clrDgField]="'id'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-cell (click)="getcustID(user.customerId)" class="um-clickable-cell">{{user.customerCode}}</clr-dg-cell>
|
||||||
No
|
<clr-dg-cell>{{user.customerId}}</clr-dg-cell>
|
||||||
</ng-container></clr-dg-column>
|
<clr-dg-cell>{{user.customerName}}</clr-dg-cell>
|
||||||
<clr-dg-column [clrDgField]="'customerId'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Customer Code
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'customerCode'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Customer Id
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'customerName'">
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<table class="table">
|
||||||
Customer Name
|
<tr>
|
||||||
</ng-container>
|
<td class="td-title">No</td>
|
||||||
</clr-dg-column >
|
<td class="td-content">{{user.id}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Customer Code</td>
|
||||||
|
<td class="td-content">{{user.customerCode}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Customer Id</td>
|
||||||
|
<td class="td-content">{{user.customerId}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">Customer Name</td>
|
||||||
|
<td class="td-content">{{user.customerName}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of custdata;let i = index" [clrDgItem]="user">
|
<clr-dg-footer>
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-cell (click)="getcustID(user.customerId)" style="color: rgb(108, 108, 194);" >{{user.customerCode}}</clr-dg-cell>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
||||||
<clr-dg-cell >{{user.customerId}}</clr-dg-cell>
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
<clr-dg-cell>{{user.customerName}}</clr-dg-cell>
|
of {{pagination.totalItems}} Records
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
<!-- Department Modal -->
|
||||||
<table class="table">
|
<clr-modal [(clrModalOpen)]="department" [clrModalSize]="'lg'">
|
||||||
<tr>
|
<h3 class="modal-title"><b>Select From List:</b></h3>
|
||||||
<td class="td-title">NO</td>
|
<div class="modal-body">
|
||||||
<td class="td-content">{{user.id}}</td>
|
<div class="clr-row">
|
||||||
</tr>
|
<div class="clr-col-md-12 clr-col-sm-12">
|
||||||
<tr>
|
<input id="data" type="text" placeholder="Enter search Criteria" class="um-form-input">
|
||||||
<td class="td-title"> Customer Code</td>
|
|
||||||
<td class="td-content">{{user.customerCode}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="td-title">Customer Id</td>
|
|
||||||
<td class="td-content">{{user.customerId}} </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="td-title"> Customer Name<td>
|
|
||||||
<td class="td-content">{{user.customerName}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="department" [clrModalSize]="'lg'">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<h3 class="modal-title"><b>Select From List:</b></h3>
|
<clr-dg-placeholder>
|
||||||
<div class="modal-body">
|
<clr-spinner>Loading ...</clr-spinner>
|
||||||
<div class="clr-row">
|
</clr-dg-placeholder>
|
||||||
<div class="clr-col-md-12 clr-col-sm-12">
|
<clr-dg-column [clrDgField]="'id'">
|
||||||
<input id="data" type="text" placeholder="Enter search Criteria" class="clr-input">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
</div>
|
No
|
||||||
</div>
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerId'">
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
<clr-dg-placeholder><clr-spinner>Loading ...</clr-spinner></clr-dg-placeholder>
|
Department Code
|
||||||
<clr-dg-column [clrDgField]="'id'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
</ng-container>
|
||||||
No
|
</clr-dg-column>
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'customerId'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Department Code
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of departmentdata;let i = index" [clrDgItem]="user">
|
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell (click)="getdepid(user.departmentCode)" style="color: rgb(108, 108, 194);" >{{user.departmentCode}}</clr-dg-cell>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
|
||||||
<table class="table">
|
|
||||||
<tr>
|
|
||||||
<td class="td-title">NO</td>
|
|
||||||
<td class="td-content">{{user.id}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let user of departmentdata;let i = index" [clrDgItem]="user">
|
||||||
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell (click)="getdepid(user.departmentCode)" class="um-clickable-cell">{{user.departmentCode}}</clr-dg-cell>
|
||||||
|
|
||||||
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td class="td-title">No</td>
|
||||||
|
<td class="td-content">{{user.id}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
|
<clr-dg-footer>
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
of {{pagination.totalItems}} Records
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
|
<!-- Position Modal -->
|
||||||
|
<clr-modal [(clrModalOpen)]="position" [clrModalSize]="'lg'">
|
||||||
|
<h3 class="modal-title"><b>Select From List:</b></h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-12 clr-col-sm-12">
|
||||||
|
<input id="data" type="text" placeholder="Enter search Criteria" class="um-form-input">
|
||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</div>
|
||||||
<clr-modal [(clrModalOpen)]="position" [clrModalSize]="'lg'">
|
|
||||||
<h3 class="modal-title"><b>Select From List:</b></h3>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col-md-12 clr-col-sm-12">
|
|
||||||
<input id="data" type="text" placeholder="Enter search Criteria" class="clr-input">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
|
<clr-dg-placeholder>
|
||||||
|
<clr-spinner>Loading ...</clr-spinner>
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
<clr-dg-column [clrDgField]="'id'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
No
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
<clr-dg-column [clrDgField]="'customerId'">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
Position Code
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-dg-row *clrDgItems="let user of positiondata;let i = index" [clrDgItem]="user">
|
||||||
<clr-dg-placeholder><clr-spinner>Loading ...</clr-spinner></clr-dg-placeholder>
|
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
||||||
<clr-dg-column [clrDgField]="'id'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-cell (click)="getposid(user.positionCode)" class="um-clickable-cell">{{user.positionCode}}</clr-dg-cell>
|
||||||
No
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
<clr-dg-column [clrDgField]="'customerId'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Position Code
|
|
||||||
</ng-container></clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of positiondata;let i = index" [clrDgItem]="user">
|
<clr-dg-row-detail *clrIfExpanded>
|
||||||
<clr-dg-cell>{{i+1}}</clr-dg-cell>
|
<table class="table">
|
||||||
<clr-dg-cell (click)="getposid(user.positionCode)" style="color: rgb(108, 108, 194);" >{{user.positionCode}}</clr-dg-cell>
|
<tr>
|
||||||
|
<td class="td-title">No</td>
|
||||||
|
<td class="td-content">{{user.id}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</clr-dg-row-detail>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
|
<clr-dg-footer>
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
||||||
<clr-dg-row-detail *clrIfExpanded>
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
<table class="table">
|
of {{pagination.totalItems}} Records
|
||||||
<tr>
|
</clr-dg-pagination>
|
||||||
<td class="td-title">NO</td>
|
</clr-dg-footer>
|
||||||
<td class="td-content">{{user.id}}</td>
|
</clr-datagrid>
|
||||||
</tr>
|
</div>
|
||||||
</table>
|
</clr-modal>
|
||||||
</clr-dg-row-detail>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">Records per page</clr-dg-page-size>
|
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
|
||||||
of {{pagination.totalItems}} Records
|
|
||||||
</clr-dg-pagination>
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
@@ -1 +1,375 @@
|
|||||||
@import '../../../../../styles1.scss';
|
@import '../../../../../styles1.scss';
|
||||||
|
@import '../../../../../styles/_design-tokens.scss';
|
||||||
|
|
||||||
|
// User Maintenance Edit Styles
|
||||||
|
.um-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px 32px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
font-family: var(--theme-font-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-edit-container {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-card {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form {
|
||||||
|
.clr-row {
|
||||||
|
margin: 0 -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clr-col-md-6,
|
||||||
|
.clr-col-sm-12 {
|
||||||
|
padding: 0 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-input,
|
||||||
|
.um-form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--theme-text);
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1); // var(--theme-primary) with 10% opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
border-color: #ef4444;
|
||||||
|
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 12px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 12px;
|
||||||
|
padding-right: 40px;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required-field {
|
||||||
|
color: #ef4444;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 24px;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern Button Styles using ThemeService
|
||||||
|
.um-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
text-decoration: none;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--theme-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizes
|
||||||
|
&.um-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-md {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-lg {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variants
|
||||||
|
&.um-btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary, #0284c7) 0%, var(--theme-accent, #7c3aed) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.1); // var(--theme-secondary) with 10% opacity
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
border-color: var(--theme-error-dark, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(239, 68, 68, 0.3), 0 4px 6px -2px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.um-btn-ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-clickable-cell {
|
||||||
|
color: var(--theme-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Styles
|
||||||
|
::ng-deep .modal-header {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px 16px 0 0 !important;
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .modal-body {
|
||||||
|
padding: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .modal-footer {
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-radius: 0 0 16px 16px !important;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data Grid Styles
|
||||||
|
.um-datagrid {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
::ng-deep .datagrid {
|
||||||
|
.datagrid-head {
|
||||||
|
background: var(--theme-background);
|
||||||
|
|
||||||
|
.datagrid-column {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-row {
|
||||||
|
transition: background-color 150ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-cell {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.um-hero {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-form-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.um-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { AccesstypeService } from 'src/app/services/admin/accesstype.service';
|
import { AccesstypeService } from 'src/app/services/admin/accesstype.service';
|
||||||
import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service';
|
import { UsergrpmaintainceService } from 'src/app/services/admin/usergrpmaintaince.service';
|
||||||
import { UsermaintanceService } from '../../../../services/admin/usermaintance.service';
|
import { UsermaintanceService } from '../../../../services/admin/usermaintance.service';
|
||||||
//import { Usermain } from 'src/app/models/usermaintaince';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-usermaintanceedit',
|
selector: 'app-usermaintanceedit',
|
||||||
@@ -11,40 +13,52 @@ import { UsermaintanceService } from '../../../../services/admin/usermaintance.s
|
|||||||
styleUrls: ['./usermaintanceedit.component.scss']
|
styleUrls: ['./usermaintanceedit.component.scss']
|
||||||
})
|
})
|
||||||
export class UsermaintanceeditComponent implements OnInit {
|
export class UsermaintanceeditComponent implements OnInit {
|
||||||
id:number;
|
id: number;
|
||||||
data1:any={};
|
data1: any = {};
|
||||||
customer:boolean=false;
|
customer: boolean = false;
|
||||||
custdata: any;
|
custdata: any;
|
||||||
clickedID:number;
|
clickedID: number;
|
||||||
custiddata: any;
|
custiddata: any;
|
||||||
loading = false;
|
loading = false;
|
||||||
userobjcust={
|
userobjcust = {
|
||||||
customerName:'',
|
customerName: '',
|
||||||
customerCode:'',
|
customerCode: '',
|
||||||
}
|
}
|
||||||
accessdata;
|
accessdata;
|
||||||
department:boolean=false;
|
department: boolean = false;
|
||||||
departmentdata: any;
|
departmentdata: any;
|
||||||
positiondata: any;
|
positiondata: any;
|
||||||
deptiddata: any;
|
deptiddata: any;
|
||||||
userobjdept={
|
userobjdept = {
|
||||||
departmentCode:'',
|
departmentCode: '',
|
||||||
}
|
}
|
||||||
userobjpos={
|
userobjpos = {
|
||||||
positionCode:'',
|
positionCode: '',
|
||||||
}
|
}
|
||||||
posiddata: any;
|
posiddata: any;
|
||||||
position:boolean=false;
|
position: boolean = false;
|
||||||
usergrpdata;
|
usergrpdata;
|
||||||
error;
|
error;
|
||||||
constructor(private route:ActivatedRoute,
|
private themeSubscription: Subscription;
|
||||||
private mainservice:UsermaintanceService,
|
|
||||||
private router: Router,private accesstype:AccesstypeService,
|
|
||||||
private usergrpservice: UsergrpmaintainceService
|
|
||||||
|
|
||||||
) { }
|
constructor(private route: ActivatedRoute,
|
||||||
|
private mainservice: UsermaintanceService,
|
||||||
|
private router: Router, private accesstype: AccesstypeService,
|
||||||
|
private usergrpservice: UsergrpmaintainceService,
|
||||||
|
private translate: TranslateService,
|
||||||
|
private themeService: ThemeService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
switchLanguage(language: string) {
|
||||||
|
this.translate.use(language);
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.themeSubscription = this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// Theme changes will automatically update CSS variables
|
||||||
|
// This triggers a re-render of themed elements
|
||||||
|
});
|
||||||
|
|
||||||
this.id = this.route.snapshot.params["id"];
|
this.id = this.route.snapshot.params["id"];
|
||||||
console.log("update with id = ", this.id);
|
console.log("update with id = ", this.id);
|
||||||
this.getById(this.id);
|
this.getById(this.id);
|
||||||
@@ -52,82 +66,89 @@ export class UsermaintanceeditComponent implements OnInit {
|
|||||||
this.usergrp();
|
this.usergrp();
|
||||||
this.getdata();
|
this.getdata();
|
||||||
}
|
}
|
||||||
getdata(){
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.themeSubscription) {
|
||||||
|
this.themeSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getdata() {
|
||||||
this.accesstype.getAll().subscribe(resp => {
|
this.accesstype.getAll().subscribe(resp => {
|
||||||
this.accessdata = resp;
|
this.accessdata = resp;
|
||||||
console.log('accessdata: ', this.accessdata);
|
console.log('accessdata: ', this.accessdata);
|
||||||
if(this.accessdata.length==0){
|
if (this.accessdata.length == 0) {
|
||||||
this.error="No data Available";
|
this.error = "No data Available";
|
||||||
console.log(this.error)
|
console.log(this.error)
|
||||||
}
|
}
|
||||||
},(error) => {
|
}, (error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
if(error){
|
if (error) {
|
||||||
this.error="Server Error";
|
this.error = "Server Error";
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
usergrp(){
|
usergrp() {
|
||||||
this.usergrpservice.getAll().subscribe((data) => {
|
this.usergrpservice.getAll().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.usergrpdata = data;
|
this.usergrpdata = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getById(id:number){
|
getById(id: number) {
|
||||||
this.mainservice.getbyid(id).subscribe((data)=>{
|
this.mainservice.getbyid(id).subscribe((data) => {
|
||||||
this.data1=data;
|
this.data1 = data;
|
||||||
// this.data1=this.data3;
|
// this.data1=this.data3;
|
||||||
console.log(this.data1);
|
console.log(this.data1);
|
||||||
});
|
|
||||||
}
|
|
||||||
update(){
|
|
||||||
console.log(this.data1);
|
|
||||||
this.mainservice.updatenew(this.id,this.data1).subscribe((data)=>{
|
|
||||||
console.log(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
goback(){
|
|
||||||
this.router.navigate(["../../usermaintance"], { relativeTo: this.route });
|
|
||||||
}
|
|
||||||
gotodepartmet(){
|
|
||||||
this.department=!this.department;
|
|
||||||
this.mainservice.getalldepartment().subscribe((data)=>{
|
|
||||||
console.log(data);
|
|
||||||
this.departmentdata=data;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getdepid(id:number){
|
update() {
|
||||||
this.clickedID=id;
|
console.log(this.data1);
|
||||||
console.log("clicked by id"+ id);
|
this.mainservice.updatenew(this.id, this.data1).subscribe((data) => {
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
goback() {
|
||||||
|
this.router.navigate(["../../usermaintance"], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
gotodepartmet() {
|
||||||
|
this.department = !this.department;
|
||||||
|
this.mainservice.getalldepartment().subscribe((data) => {
|
||||||
|
console.log(data);
|
||||||
|
this.departmentdata = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getdepid(id: number) {
|
||||||
|
this.clickedID = id;
|
||||||
|
console.log("clicked by id" + id);
|
||||||
this.mainservice.getbydepartmentid(id).subscribe((data) => {
|
this.mainservice.getbydepartmentid(id).subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.deptiddata= data;
|
this.deptiddata = data;
|
||||||
// this.userObj= this.custiddata;
|
// this.userObj= this.custiddata;
|
||||||
this.userobjdept =this.deptiddata;
|
this.userobjdept = this.deptiddata;
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
this.department=false;
|
this.department = false;
|
||||||
}
|
}
|
||||||
gotoposition(){
|
gotoposition() {
|
||||||
this.position=!this.position;
|
this.position = !this.position;
|
||||||
this.mainservice.getallposition().subscribe((data)=>{
|
this.mainservice.getallposition().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.positiondata=data;
|
this.positiondata = data;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
getposid(id:number){
|
getposid(id: number) {
|
||||||
this.clickedID=id;
|
this.clickedID = id;
|
||||||
console.log("clicked by id"+ id);
|
console.log("clicked by id" + id);
|
||||||
this.mainservice.getbypositionid(id).subscribe((data) => {
|
this.mainservice.getbypositionid(id).subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
this.posiddata= data;
|
this.posiddata = data;
|
||||||
// this.userObj= this.custiddata;
|
// this.userObj= this.custiddata;
|
||||||
this.userobjpos =this.posiddata;
|
this.userobjpos = this.posiddata;
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
this.position=false;
|
this.position = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,19 +71,13 @@
|
|||||||
Calculated Fields
|
Calculated Fields
|
||||||
</ng-container></clr-dg-column>
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
<!-- Webhook Status Column -->
|
|
||||||
<clr-dg-column>
|
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
|
||||||
Webhook Status
|
|
||||||
</ng-container>
|
|
||||||
</clr-dg-column>
|
|
||||||
|
|
||||||
<!-- who column -->
|
<!-- who column -->
|
||||||
<clr-dg-column>
|
<clr-dg-column>
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
<clr-icon shape="bars"></clr-icon> Action
|
<clr-icon shape="bars"></clr-icon> Action
|
||||||
</ng-container>
|
</ng-container></clr-dg-column>
|
||||||
</clr-dg-column>
|
|
||||||
<!-- end -->
|
<!-- end -->
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of product" [clrDgItem]="user">
|
<clr-dg-row *clrDgItems="let user of product" [clrDgItem]="user">
|
||||||
@@ -110,7 +104,7 @@
|
|||||||
<span
|
<span
|
||||||
style="margin-right: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-grow: 1;"
|
style="margin-right: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-grow: 1;"
|
||||||
title="{{user.url_endpoint}}" (click)="showFullUrl(user.url_endpoint)">
|
title="{{user.url_endpoint}}" (click)="showFullUrl(user.url_endpoint)">
|
||||||
{{user.url | slice:0:30}}{{user?.url_endpoint?.length > 30 ? '...' : ''}}
|
{{user.url | slice:0:30}}{{user.url_endpoint.length > 30 ? '...' : ''}}
|
||||||
</span>
|
</span>
|
||||||
<button class="btn btn-icon btn-sm" (click)="copyToClipboard(user.url_endpoint)" title="Copy URL">
|
<button class="btn btn-icon btn-sm" (click)="copyToClipboard(user.url_endpoint)" title="Copy URL">
|
||||||
<clr-icon shape="copy-to-clipboard"></clr-icon>
|
<clr-icon shape="copy-to-clipboard"></clr-icon>
|
||||||
@@ -144,79 +138,39 @@
|
|||||||
style="cursor: pointer; align-items: center;"><clr-icon shape="details"
|
style="cursor: pointer; align-items: center;"><clr-icon shape="details"
|
||||||
*ngIf="user.calculated_field_json"></clr-icon></clr-dg-cell>
|
*ngIf="user.calculated_field_json"></clr-icon></clr-dg-cell>
|
||||||
|
|
||||||
<!-- Webhook Status Cell -->
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span *ngIf="user.webhook_url && user.webhook_url.trim() !== ''" class="webhook-enabled">Enabled</span>
|
|
||||||
<span *ngIf="!user.webhook_url || user.webhook_url.trim() === ''" class="webhook-disabled">Disabled</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
|
|
||||||
<!-- who column -->
|
<!-- who column -->
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<clr-signpost>
|
<div style="display: flex; align-items: center; gap: 1px; flex-wrap: nowrap;">
|
||||||
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success"
|
<clr-signpost>
|
||||||
style="color: rgb(0, 130, 236);"></clr-icon></span>
|
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success"
|
||||||
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
style="color: rgb(0, 130, 236);"></clr-icon></span>
|
||||||
<h5 style="margin-top: 0">Who Column</h5>
|
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
||||||
<div>Account ID: <code class="clr-code">{{user.accountId}}</code></div>
|
<h5 style="margin-top: 0">Who Column</h5>
|
||||||
<div>Created At: <code class="clr-code">{{user.createdAt| date}}</code></div>
|
<div>Account ID: <code class="clr-code">{{user.accountId}}</code></div>
|
||||||
<div>Created By: <code class="clr-code">{{user.createdBy}}</code></div>
|
<div>Created At: <code class="clr-code">{{user.createdAt| date}}</code></div>
|
||||||
<div>Updated At: <code class="clr-code">{{user.updatedAt | date}}</code></div>
|
<div>Created By: <code class="clr-code">{{user.createdBy}}</code></div>
|
||||||
<div>Updated By: <code class="clr-code">{{user.updatedBy}}</code></div>
|
<div>Updated At: <code class="clr-code">{{user.updatedAt | date}}</code></div>
|
||||||
</clr-signpost-content>
|
<div>Updated By: <code class="clr-code">{{user.updatedBy}}</code></div>
|
||||||
</clr-signpost>
|
</clr-signpost-content>
|
||||||
|
</clr-signpost>
|
||||||
|
|
||||||
<!-- New JSON Update button -->
|
<!-- New JSON Update button -->
|
||||||
<button class="btn btn-icon" (click)="updateJson(user.id)" title="Update JSON">
|
<button class="btn btn-icon btn-sm" (click)="updateJson(user.id)" title="Update JSON">
|
||||||
<clr-icon shape="refresh"></clr-icon>
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Calculated Field button -->
|
<!-- Calculated Field button -->
|
||||||
<button class="btn btn-icon" (click)="fetchAvailableKeys(user)" title="Create Calculated Field">
|
<button class="btn btn-icon btn-sm" (click)="fetchAvailableKeys(user, false)" title="Create Calculated Field">
|
||||||
<clr-icon shape="calculator"></clr-icon>
|
<clr-icon shape="calculator"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Group By button -->
|
<!-- Group By button -->
|
||||||
<button class="btn btn-icon" (click)="openGroupByModal(user)" title="Group By Configuration">
|
<button class="btn btn-icon btn-sm" (click)="openGroupByModal(user)" title="Group By Configuration">
|
||||||
<clr-icon shape="group"></clr-icon>
|
<clr-icon shape="group"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
<!-- Blending Keys button (only shown for blending type with non-empty blending_lakeids) -->
|
|
||||||
<button class="btn btn-icon"
|
|
||||||
(click)="fetchBlendingKeys(user)"
|
|
||||||
title="Configure Blending Query"
|
|
||||||
*ngIf="canShowBlendingAction(user)">
|
|
||||||
<clr-icon shape="code"></clr-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Field Mapping button -->
|
|
||||||
<button class="btn btn-icon"
|
|
||||||
(click)="openFieldMappingModal(user)"
|
|
||||||
title="Field Mapping"
|
|
||||||
*ngIf="user.url && user.sure_connect_id">
|
|
||||||
<clr-icon shape="map"></clr-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Scheduler button -->
|
|
||||||
<button class="btn btn-icon"
|
|
||||||
(click)="openSchedulerModal(user)"
|
|
||||||
title="Scheduler">
|
|
||||||
<clr-icon shape="clock"></clr-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Webhook toggle button -->
|
|
||||||
<button class="btn btn-icon"
|
|
||||||
(click)="toggleWebhook(user)"
|
|
||||||
title="Toggle Webhook">
|
|
||||||
<clr-icon shape="network-globe"></clr-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Copy Webhook URL button -->
|
|
||||||
<button class="btn btn-icon"
|
|
||||||
(click)="copyWebhookUrl(user.webhook_url)"
|
|
||||||
title="Copy Webhook URL"
|
|
||||||
*ngIf="user.webhook_url && user.webhook_url.trim() !== ''">
|
|
||||||
<clr-icon shape="copy-to-clipboard"></clr-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
|
|
||||||
<!-- who colmn -->
|
<!-- who colmn -->
|
||||||
@@ -480,32 +434,8 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Data Lake Type -->
|
<!-- Add ref_datalake_id dropdown -->
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label>Data Lake Type</label>
|
|
||||||
<select class="clr-input" [(ngModel)]="rowSelected.datalake_type" name="datalake_type">
|
|
||||||
<option *ngFor="let type of dataLakeTypes" [value]="type.value">{{ type.label }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Blending Data Lakes (only show when blending type is selected) -->
|
|
||||||
<div class="clr-col-sm-12" *ngIf="rowSelected.datalake_type === 'blending'">
|
|
||||||
<label>Blending Data Lakes</label>
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<div *ngFor="let dataLake of dataLakeList" class="checkbox-item">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
[value]="dataLake.id"
|
|
||||||
(change)="onEditBlendingLakeCheckboxChange($event, dataLake.id)"
|
|
||||||
[checked]="isEditBlendingLakeSelected(dataLake.id)">
|
|
||||||
<label>{{ dataLake.name }}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<small class="clr-subtext">Select data lakes to blend</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reference Data Lake (only show when normal type is selected) -->
|
|
||||||
<div class="clr-col-sm-12" *ngIf="rowSelected.datalake_type === 'normal'">
|
|
||||||
<label>Reference Data Lake</label>
|
<label>Reference Data Lake</label>
|
||||||
<select class="clr-input" [(ngModel)]="rowSelected.ref_datalake_id" name="ref_datalake_id">
|
<select class="clr-input" [(ngModel)]="rowSelected.ref_datalake_id" name="ref_datalake_id">
|
||||||
<option value="">Select Data Lake</option>
|
<option value="">Select Data Lake</option>
|
||||||
@@ -633,32 +563,8 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Data Lake Type -->
|
<!-- Add ref_datalake_id dropdown -->
|
||||||
<div class="clr-col-sm-12">
|
<div class="clr-col-sm-12">
|
||||||
<label>Data Lake Type</label>
|
|
||||||
<select formControlName="datalake_type">
|
|
||||||
<option *ngFor="let type of dataLakeTypes" [value]="type.value">{{ type.label }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Blending Data Lakes (only show when blending type is selected) -->
|
|
||||||
<div class="clr-col-sm-12" *ngIf="entryForm.get('datalake_type')?.value === 'blending'">
|
|
||||||
<label>Blending Data Lakes</label>
|
|
||||||
<div class="checkbox-container">
|
|
||||||
<div *ngFor="let dataLake of dataLakeList" class="checkbox-item">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
[value]="dataLake.id"
|
|
||||||
(change)="onBlendingLakeCheckboxChange($event, dataLake.id)"
|
|
||||||
[checked]="isBlendingLakeSelected(dataLake.id)">
|
|
||||||
<label>{{ dataLake.name }}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<small class="clr-subtext">Select data lakes to blend</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reference Data Lake (only show when normal type is selected) -->
|
|
||||||
<div class="clr-col-sm-12" *ngIf="entryForm.get('datalake_type')?.value === 'normal'">
|
|
||||||
<label>Reference Data Lake</label>
|
<label>Reference Data Lake</label>
|
||||||
<select formControlName="ref_datalake_id">
|
<select formControlName="ref_datalake_id">
|
||||||
<option value="">Select Data Lake</option>
|
<option value="">Select Data Lake</option>
|
||||||
@@ -1010,200 +916,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
<!-- Blending Keys Panel -->
|
<!-- htmlpopup -->
|
||||||
<div class="blending-keys-panel" *ngIf="showBlendingKeys">
|
|
||||||
<div class="panel-header">
|
|
||||||
<h3>Blending Configuration</h3>
|
|
||||||
<button class="btn btn-icon" (click)="closeBlendingKeys()" title="Close">
|
|
||||||
<clr-icon shape="close"></clr-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel-content">
|
|
||||||
<!-- Display keys for each blending lake -->
|
|
||||||
<div class="keys-container" *ngFor="let keyData of blendingKeysData">
|
|
||||||
<h4>Data Lake {{ keyData.lakeId }} - Table: {{ keyData.tableName }}</h4>
|
|
||||||
<div class="headers-container">
|
|
||||||
<span class="header-tag" *ngFor="let header of keyData.headers">{{ header }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- SQL Query Editor -->
|
|
||||||
<div class="sql-editor-container">
|
|
||||||
<h4>SQL Query Builder</h4>
|
|
||||||
<textarea class="clr-textarea sql-textarea"
|
|
||||||
[(ngModel)]="sqlQueryText"
|
|
||||||
placeholder="Enter your SQL query here using the available headers..."
|
|
||||||
rows="6"></textarea>
|
|
||||||
<div class="editor-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateSqlQuery()">Update SQL Query</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Blending Configuration Modal -->
|
|
||||||
<clr-modal [(clrModalOpen)]="showBlendingKeys" [clrModalSize]="'xl'" [clrModalStaticBackdrop]="true">
|
|
||||||
<h3 class="modal-title">Blending Configuration</h3>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="blending-config-container">
|
|
||||||
<!-- Display keys for each blending lake -->
|
|
||||||
<div class="keys-section" *ngIf="blendingKeysData && blendingKeysData.length > 0">
|
|
||||||
<h4 class="section-title">
|
|
||||||
<clr-icon shape="data-field" class="section-icon"></clr-icon>
|
|
||||||
Available Data Sources
|
|
||||||
</h4>
|
|
||||||
<div class="keys-container">
|
|
||||||
<div class="key-card" *ngFor="let keyData of blendingKeysData">
|
|
||||||
<div class="key-header">
|
|
||||||
<h5>Data Lake {{ keyData.lakeId }} - Table: {{ keyData.tableName }}</h5>
|
|
||||||
</div>
|
|
||||||
<div class="headers-container">
|
|
||||||
<span class="header-tag" *ngFor="let header of keyData.headers"
|
|
||||||
(click)="insertHeaderIntoQuery(header)">
|
|
||||||
{{ header }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- SQL Query Editor -->
|
|
||||||
<div class="sql-editor-section">
|
|
||||||
<h4 class="section-title">
|
|
||||||
<clr-icon shape="code" class="section-icon"></clr-icon>
|
|
||||||
SQL Query Builder
|
|
||||||
</h4>
|
|
||||||
<div class="sql-editor-container">
|
|
||||||
<textarea class="clr-textarea sql-textarea"
|
|
||||||
[(ngModel)]="sqlQueryText"
|
|
||||||
placeholder="Enter your SQL query here using the available headers. Click on header names above to insert them into your query."
|
|
||||||
rows="8"></textarea>
|
|
||||||
<div class="editor-hint">
|
|
||||||
<small class="clr-subtext">Tip: Click on header names above to automatically insert them into your query</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline" (click)="closeBlendingKeys()">Cancel</button>
|
|
||||||
<button type="button" class="btn btn-primary" (click)="updateSqlQuery()">Update SQL Query</button>
|
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
|
|
||||||
<!-- Field Mapping Modal -->
|
|
||||||
<clr-modal [(clrModalOpen)]="showFieldMappingModal" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
|
||||||
<h3 class="modal-title">Field Mapping Configuration</h3>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="field-mapping-container">
|
|
||||||
<div class="instructions">
|
|
||||||
<p>Map original field names to new names. Leave mapping blank to exclude field from mapping.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mapping-table-container" *ngIf="fieldMappingData && fieldMappingData.length > 0">
|
|
||||||
<table class="table mapping-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="50%">Original Field Name</th>
|
|
||||||
<th width="50%">Mapped Field Name</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let field of fieldMappingData; let i = index">
|
|
||||||
<td class="original-field">
|
|
||||||
<span class="field-tag">{{ field.original }}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text"
|
|
||||||
class="clr-input mapping-input"
|
|
||||||
[(ngModel)]="field.mapped"
|
|
||||||
placeholder="Enter new field name"
|
|
||||||
(ngModelChange)="updateFieldMapping(i, $event)">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="no-data-message" *ngIf="fieldMappingData && fieldMappingData.length === 0">
|
|
||||||
<p>No fields available for mapping. Please check the data source configuration.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline" (click)="closeFieldMappingModal()">Cancel</button>
|
|
||||||
<button type="button" class="btn btn-primary" (click)="updateFieldMappings()" [disabled]="!fieldMappingData || fieldMappingData.length === 0">Update Field Mappings</button>
|
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
|
|
||||||
<!-- Scheduler Modal -->
|
|
||||||
<clr-modal [(clrModalOpen)]="showSchedulerModal" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
|
||||||
<h3 class="modal-title">Job Scheduler</h3>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="scheduler-container">
|
|
||||||
<div class="data-lake-info">
|
|
||||||
<h4>Data Lake: {{ selectedSchedulerItem?.name }}</h4>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Job Information -->
|
|
||||||
<div class="job-info-section" *ngIf="schedulerJob">
|
|
||||||
<div class="job-header">
|
|
||||||
<h5>Job Information</h5>
|
|
||||||
<span class="badge" [ngClass]="getStatusBadgeClass(schedulerJob.status)">
|
|
||||||
{{ schedulerJob.status }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="job-details">
|
|
||||||
<div class="detail-row">
|
|
||||||
<label>Job Name:</label>
|
|
||||||
<span>{{ schedulerJob.name }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-row">
|
|
||||||
<label>Job Type:</label>
|
|
||||||
<span>{{ schedulerJob.jobType }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-row">
|
|
||||||
<label>Description:</label>
|
|
||||||
<span>{{ schedulerJob.description }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Job Actions -->
|
|
||||||
<div class="job-actions">
|
|
||||||
<button class="btn btn-warning" (click)="pauseJob()" *ngIf="schedulerJob.status === 'RUNNING'">
|
|
||||||
<clr-icon shape="pause"></clr-icon> Pause Job
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-success" (click)="resumeJob()" *ngIf="schedulerJob.status === 'PAUSED'">
|
|
||||||
<clr-icon shape="play"></clr-icon> Resume Job
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-danger" (click)="stopJob()" *ngIf="schedulerJob.status !== 'STOPPED'">
|
|
||||||
<clr-icon shape="stop"></clr-icon> Stop Job
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary" (click)="restartJob()" *ngIf="schedulerJob.status === 'STOPPED'">
|
|
||||||
<clr-icon shape="play"></clr-icon> Restart Job
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- No Job Found -->
|
|
||||||
<div class="no-job-section" *ngIf="schedulerJob === null">
|
|
||||||
<div class="no-job-message">
|
|
||||||
<p>No scheduled job found for this Data Lake.</p>
|
|
||||||
<p>Would you like to create a new job?</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="create-job-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createJob()">
|
|
||||||
<clr-icon shape="plus"></clr-icon> Create Job
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline" (click)="closeSchedulerModal()">Close</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</clr-modal>
|
|
||||||
@@ -384,494 +384,3 @@
|
|||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Data Lake Type Styles */
|
|
||||||
.clr-subtext {
|
|
||||||
display: block;
|
|
||||||
margin-top: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-multiple] {
|
|
||||||
height: auto !important;
|
|
||||||
min-height: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checkbox Styles for Blending Data Lakes */
|
|
||||||
.checkbox-container {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 10px;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-item input[type="checkbox"] {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-item label {
|
|
||||||
margin-bottom: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Blending Keys Panel Styles */
|
|
||||||
.blending-keys-panel {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background-color: white;
|
|
||||||
border-top: 1px solid #ccc;
|
|
||||||
box-shadow: 0 -2px 4px rgba(0,0,0,0.1);
|
|
||||||
z-index: 1000;
|
|
||||||
max-height: 50vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 15px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-content {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.keys-container {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
padding: 10px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.keys-container h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.headers-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-tag {
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #e3f2fd;
|
|
||||||
border: 1px solid #bbdefb;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 4px 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sql-editor-container {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sql-textarea {
|
|
||||||
width: 100%;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-actions {
|
|
||||||
margin-top: 10px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Blending Configuration Modal Styles */
|
|
||||||
.blending-config-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
color: #0072a0;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-icon {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.keys-section {
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.key-card {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.key-header h5 {
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.headers-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-tag {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #e3f2fd;
|
|
||||||
border: 1px solid #bbdefb;
|
|
||||||
border-radius: 20px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
margin: 4px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-family: monospace;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-tag:hover {
|
|
||||||
background-color: #bbdefb;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sql-editor-section {
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sql-editor-container {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sql-textarea {
|
|
||||||
width: 100%;
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 14px;
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-hint {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Field Mapping Modal Styles */
|
|
||||||
.field-mapping-container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions {
|
|
||||||
background-color: #e3f2fd;
|
|
||||||
border: 1px solid #bbdefb;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.instructions p {
|
|
||||||
margin: 0;
|
|
||||||
color: #1976d2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-table-container {
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-table th {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
padding: 12px 15px;
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
border-bottom: 2px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-table td {
|
|
||||||
padding: 12px 15px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.original-field {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-tag {
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
border: 1px solid #bdbdbd;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 6px 10px;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapping-input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #0072a0;
|
|
||||||
box-shadow: 0 0 0 2px rgba(0, 114, 160, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-data-message {
|
|
||||||
text-align: center;
|
|
||||||
padding: 30px;
|
|
||||||
color: #666;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scheduler Modal Styles */
|
|
||||||
.scheduler-container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-lake-info h4 {
|
|
||||||
margin: 0 0 20px 0;
|
|
||||||
color: #333;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-info-section {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-header h5 {
|
|
||||||
margin: 0;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-success {
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-warning {
|
|
||||||
background-color: #ff9800;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-danger {
|
|
||||||
background-color: #f44336;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-light {
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-details {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-row {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-row label {
|
|
||||||
font-weight: 500;
|
|
||||||
width: 120px;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-row span {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-actions .btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-job-section {
|
|
||||||
text-align: center;
|
|
||||||
padding: 30px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-job-message p {
|
|
||||||
margin: 10px 0;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-job-message p:first-child {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-job-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-job-actions .btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 15px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Webhook Status Styles */
|
|
||||||
.webhook-enabled {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webhook-disabled {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #f44336;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Webhook Status Styles */
|
|
||||||
.webhook-status {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webhook-status.enabled {
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webhook-status.disabled {
|
|
||||||
background-color: #f44336;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Webhook Modal Styles */
|
|
||||||
.webhook-container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webhook-container .data-lake-info h4 {
|
|
||||||
margin: 0 0 20px 0;
|
|
||||||
color: #333;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webhook-form {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webhook-info {
|
|
||||||
background-color: #e3f2fd;
|
|
||||||
border: 1px solid #bbdefb;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 15px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.webhook-info p {
|
|
||||||
margin: 0;
|
|
||||||
color: #1976d2;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { AlertService } from 'src/app/services/alert.service';
|
import { AlertService } from 'src/app/services/alert.service';
|
||||||
import { Data_lakeservice } from './Data_lake.service';
|
import { Data_lakeservice } from './Data_lake.service';
|
||||||
import { SchedulerService } from 'src/app/services/scheduler.service';
|
|
||||||
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators, ValidationErrors } from '@angular/forms';
|
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators, ValidationErrors } from '@angular/forms';
|
||||||
import { ExtensionService } from 'src/app/services/fnd/extension.service';
|
import { ExtensionService } from 'src/app/services/fnd/extension.service';
|
||||||
import { DashboardContentModel2 } from 'src/app/models/builder/dashboard';
|
import { DashboardContentModel2 } from 'src/app/models/builder/dashboard';
|
||||||
@@ -11,7 +10,6 @@ import { UserInfoService } from 'src/app/services/user-info.service';
|
|||||||
import { SureconnectService } from '../sureconnect/sureconnect.service';
|
import { SureconnectService } from '../sureconnect/sureconnect.service';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
||||||
import { DataLakeSchedulerService } from './dataLakescheduler.service';
|
|
||||||
declare var JsBarcode: any;
|
declare var JsBarcode: any;
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-Data_lake',
|
selector: 'app-Data_lake',
|
||||||
@@ -68,12 +66,6 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
dataLakeList: any[] = [];
|
dataLakeList: any[] = [];
|
||||||
selectedDataLake: any = null;
|
selectedDataLake: any = null;
|
||||||
|
|
||||||
// New properties for data lake type
|
|
||||||
dataLakeTypes = [
|
|
||||||
{ value: 'normal', label: 'Normal' },
|
|
||||||
{ value: 'blending', label: 'Blending' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// Calculated field properties
|
// Calculated field properties
|
||||||
calculatedFieldModalOpen = false;
|
calculatedFieldModalOpen = false;
|
||||||
availableKeys: string[] = [];
|
availableKeys: string[] = [];
|
||||||
@@ -107,36 +99,10 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
|
|
||||||
selectedAggregationFields: { field: string; operation: string }[] = [];
|
selectedAggregationFields: { field: string; operation: string }[] = [];
|
||||||
|
|
||||||
// New property to store selected blending lake IDs as string
|
|
||||||
selectedBlendingLakeIds: string[] = [];
|
|
||||||
editSelectedBlendingLakeIds: string[] = [];
|
|
||||||
|
|
||||||
// New properties for blending functionality
|
|
||||||
showBlendingKeys = false;
|
|
||||||
blendingKeysData: any[] = [];
|
|
||||||
sqlQueryText = '';
|
|
||||||
selectedBlendingItem: any = null;
|
|
||||||
|
|
||||||
// New properties for field mapping
|
|
||||||
showFieldMappingModal = false;
|
|
||||||
fieldMappingData: any[] = [];
|
|
||||||
fieldMappings: { [key: string]: string } = {};
|
|
||||||
selectedMappingItem: any = null;
|
|
||||||
|
|
||||||
// New properties for scheduler functionality
|
|
||||||
showSchedulerModal = false;
|
|
||||||
schedulerJob: any = null;
|
|
||||||
selectedSchedulerItem: any = null;
|
|
||||||
|
|
||||||
// New properties for webhook functionality
|
|
||||||
showWebhookModal = false;
|
|
||||||
selectedWebhookItem: any = null;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private extensionService: ExtensionService,
|
private extensionService: ExtensionService,
|
||||||
private userInfoService: UserInfoService,
|
private userInfoService: UserInfoService,
|
||||||
private mainService: Data_lakeservice,
|
private mainService: Data_lakeservice,
|
||||||
private schedulerService: DataLakeSchedulerService,
|
|
||||||
private alertService: AlertService,
|
private alertService: AlertService,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
private _fb: FormBuilder,
|
private _fb: FormBuilder,
|
||||||
@@ -158,19 +124,23 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.getDataLakeList(); // Fetch Data Lake list for reference dropdown
|
this.getDataLakeList(); // Fetch Data Lake list for reference dropdown
|
||||||
this.entryForm = this._fb.group({
|
this.entryForm = this._fb.group({
|
||||||
name: [null],
|
name: [null],
|
||||||
url: [null],
|
|
||||||
schedule: [null],
|
|
||||||
cron_job: [null],
|
|
||||||
json: [null],
|
|
||||||
url_endpoint: [null],
|
|
||||||
batch_volume: [null],
|
|
||||||
sure_connect_id: [null],
|
|
||||||
ref_datalake_id: [null],
|
|
||||||
datalake_type: ['normal'], // Default to normal
|
|
||||||
blending_lakeids: [[]], // Array for multiple selections
|
|
||||||
webhook_url: [null] // Add webhook_url field
|
|
||||||
});
|
|
||||||
|
|
||||||
|
url: [null],
|
||||||
|
|
||||||
|
schedule: [null],
|
||||||
|
|
||||||
|
cron_job: [null],
|
||||||
|
|
||||||
|
json: [null],
|
||||||
|
|
||||||
|
url_endpoint: [null],
|
||||||
|
|
||||||
|
batch_volume: [null],
|
||||||
|
|
||||||
|
sure_connect_id: [null], // Add SureConnect field
|
||||||
|
ref_datalake_id: [null], // Add Data Lake reference field
|
||||||
|
|
||||||
|
}); // component_button200
|
||||||
// form code start
|
// form code start
|
||||||
this.extensionService.getJsonObjectsByFormCodeList(this.formcode).subscribe(data => {
|
this.extensionService.getJsonObjectsByFormCodeList(this.formcode).subscribe(data => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@@ -270,7 +240,8 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
// Fetch Data Lake list for reference dropdown
|
// Fetch Data Lake list for reference dropdown
|
||||||
getDataLakeList() {
|
getDataLakeList() {
|
||||||
this.mainService.getAll().subscribe((data: any[]) => {
|
this.mainService.getAll().subscribe((data: any[]) => {
|
||||||
this.dataLakeList = data;
|
// Filter out the current item to avoid self-reference
|
||||||
|
this.dataLakeList = data.filter(item => item.id !== (this.rowSelected?.id || 0));
|
||||||
console.log('Data Lake List:', this.dataLakeList);
|
console.log('Data Lake List:', this.dataLakeList);
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
console.log('Error fetching Data Lake list:', error);
|
console.log('Error fetching Data Lake list:', error);
|
||||||
@@ -715,16 +686,8 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
this.editCronExpression = row.cron_job || '';
|
this.editCronExpression = row.cron_job || '';
|
||||||
// Set the selected SureConnect for edit form
|
// Set the selected SureConnect for edit form
|
||||||
this.selectedSureConnect = row.sure_connect_id || null;
|
this.selectedSureConnect = row.sure_connect_id || null;
|
||||||
|
// Set the selected Data Lake for edit form
|
||||||
// Initialize blending lake IDs for edit form from string
|
this.selectedDataLake = row.ref_datalake_id || null;
|
||||||
if (row.blending_lakeids && typeof row.blending_lakeids === 'string') {
|
|
||||||
this.editSelectedBlendingLakeIds = row.blending_lakeids.split(',').filter(id => id.trim() !== '');
|
|
||||||
} else if (row.blending_lakeids && Array.isArray(row.blending_lakeids)) {
|
|
||||||
this.editSelectedBlendingLakeIds = row.blending_lakeids.map(id => id.toString());
|
|
||||||
} else {
|
|
||||||
this.editSelectedBlendingLakeIds = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use setTimeout to ensure the component has time to initialize
|
// Use setTimeout to ensure the component has time to initialize
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.modalEdit = true;
|
this.modalEdit = true;
|
||||||
@@ -875,22 +838,18 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
goToAdd(row) {
|
goToAdd(row) {
|
||||||
this.modalAdd = false;
|
this.modalAdd = true;
|
||||||
this.addCronExpression = '';
|
this.addCronExpression = '';
|
||||||
this.selectedSureConnect = null; // Reset SureConnect selection
|
this.selectedSureConnect = null; // Reset SureConnect selection
|
||||||
|
this.selectedDataLake = null; // Reset Data Lake selection
|
||||||
this.submitted = false;
|
this.submitted = false;
|
||||||
// Reset blending lake IDs for add form
|
|
||||||
this.selectedBlendingLakeIds = [];
|
|
||||||
|
|
||||||
// Reset the form control for cron_job and sure_connect_id
|
// Reset the form control for cron_job and sure_connect_id
|
||||||
if (this.entryForm) {
|
if (this.entryForm) {
|
||||||
if (this.entryForm.get('cron_job')) {
|
if (this.entryForm.get('cron_job')) {
|
||||||
this.entryForm.get('cron_job')?.setValue('');
|
this.entryForm.get('cron_job')?.setValue('');
|
||||||
}
|
}
|
||||||
// Reset blending_lakeids to empty string
|
|
||||||
this.entryForm.get('blending_lakeids')?.setValue('');
|
|
||||||
}
|
}
|
||||||
this.modalAdd = true;
|
|
||||||
}
|
}
|
||||||
submitted = false;
|
submitted = false;
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
@@ -1331,433 +1290,4 @@ export class Data_lakeComponent implements OnInit {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to check if blending action should be available
|
|
||||||
canShowBlendingAction(item: any): boolean {
|
|
||||||
return item.datalake_type === 'blending' &&
|
|
||||||
item.blending_lakeids &&
|
|
||||||
item.blending_lakeids.trim() !== '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to insert header into SQL query at cursor position
|
|
||||||
insertHeaderIntoQuery(header: string) {
|
|
||||||
const textarea = document.querySelector('.sql-textarea') as HTMLTextAreaElement;
|
|
||||||
if (textarea) {
|
|
||||||
const start = textarea.selectionStart;
|
|
||||||
const end = textarea.selectionEnd;
|
|
||||||
const text = textarea.value;
|
|
||||||
const before = text.substring(0, start);
|
|
||||||
const after = text.substring(end, text.length);
|
|
||||||
|
|
||||||
// Insert the header at cursor position
|
|
||||||
this.sqlQueryText = before + header + after;
|
|
||||||
|
|
||||||
// Set cursor position after the inserted text
|
|
||||||
setTimeout(() => {
|
|
||||||
textarea.selectionStart = start + header.length;
|
|
||||||
textarea.selectionEnd = start + header.length;
|
|
||||||
textarea.focus();
|
|
||||||
}, 0);
|
|
||||||
} else {
|
|
||||||
// Fallback: append to end of text
|
|
||||||
this.sqlQueryText += header;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to fetch blending keys
|
|
||||||
fetchBlendingKeys(item: any) {
|
|
||||||
this.selectedBlendingItem = item;
|
|
||||||
this.blendingKeysData = [];
|
|
||||||
this.showBlendingKeys = true;
|
|
||||||
|
|
||||||
// Initialize SQL query text from existing data
|
|
||||||
if (item.sqlquery_json) {
|
|
||||||
this.sqlQueryText = item.sqlquery_json;
|
|
||||||
} else {
|
|
||||||
this.sqlQueryText = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse blending_lakeids string to array
|
|
||||||
const lakeIds = item.blending_lakeids.split(',').map(id => id.trim()).filter(id => id !== '');
|
|
||||||
|
|
||||||
// Fetch keys for each lake ID
|
|
||||||
lakeIds.forEach(lakeId => {
|
|
||||||
this.mainService.fetchBlendingKeys(Number(lakeId)).subscribe(
|
|
||||||
(data: any) => {
|
|
||||||
this.blendingKeysData.push({
|
|
||||||
lakeId: lakeId,
|
|
||||||
tableName: data.tableName,
|
|
||||||
headers: data.headers
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error fetching keys for lake ID:', lakeId, error);
|
|
||||||
this.toastr.error(`Failed to fetch keys for Data Lake ${lakeId}`);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to update SQL query
|
|
||||||
updateSqlQuery() {
|
|
||||||
if (!this.selectedBlendingItem) {
|
|
||||||
this.toastr.error('No blending item selected');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the SQL query JSON field
|
|
||||||
this.selectedBlendingItem.sqlquery_json = this.sqlQueryText;
|
|
||||||
|
|
||||||
// Call update service
|
|
||||||
this.mainService.update(this.selectedBlendingItem.id, this.selectedBlendingItem).subscribe(
|
|
||||||
(response) => {
|
|
||||||
this.toastr.success('SQL query updated successfully');
|
|
||||||
// Update the local data
|
|
||||||
const index = this.product.findIndex(p => p.id === this.selectedBlendingItem.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.product[index] = {...this.selectedBlendingItem};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error updating SQL query:', error);
|
|
||||||
this.toastr.error('Failed to update SQL query');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to close blending keys panel
|
|
||||||
closeBlendingKeys() {
|
|
||||||
this.showBlendingKeys = false;
|
|
||||||
this.blendingKeysData = [];
|
|
||||||
this.sqlQueryText = '';
|
|
||||||
this.selectedBlendingItem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to handle checkbox change for blending lakes (ADD form)
|
|
||||||
onBlendingLakeCheckboxChange(event: any, lakeId: number) {
|
|
||||||
const lakeIdStr = lakeId.toString();
|
|
||||||
if (event.target.checked) {
|
|
||||||
// Add to selected blending lake IDs
|
|
||||||
if (!this.selectedBlendingLakeIds.includes(lakeIdStr)) {
|
|
||||||
this.selectedBlendingLakeIds.push(lakeIdStr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Remove from selected blending lake IDs
|
|
||||||
const index = this.selectedBlendingLakeIds.indexOf(lakeIdStr);
|
|
||||||
if (index > -1) {
|
|
||||||
this.selectedBlendingLakeIds.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the form control value as a comma-separated string
|
|
||||||
this.entryForm.get('blending_lakeids')?.setValue(this.selectedBlendingLakeIds.join(','));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to check if a blending lake is selected (ADD form)
|
|
||||||
isBlendingLakeSelected(lakeId: number): boolean {
|
|
||||||
return this.selectedBlendingLakeIds.includes(lakeId.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to handle checkbox change for blending lakes (EDIT form)
|
|
||||||
onEditBlendingLakeCheckboxChange(event: any, lakeId: number) {
|
|
||||||
const lakeIdStr = lakeId.toString();
|
|
||||||
if (event.target.checked) {
|
|
||||||
// Add to selected blending lake IDs
|
|
||||||
if (!this.editSelectedBlendingLakeIds.includes(lakeIdStr)) {
|
|
||||||
this.editSelectedBlendingLakeIds.push(lakeIdStr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Remove from selected blending lake IDs
|
|
||||||
const index = this.editSelectedBlendingLakeIds.indexOf(lakeIdStr);
|
|
||||||
if (index > -1) {
|
|
||||||
this.editSelectedBlendingLakeIds.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the rowSelected value as a comma-separated string
|
|
||||||
this.rowSelected.blending_lakeids = this.editSelectedBlendingLakeIds.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to check if a blending lake is selected (EDIT form)
|
|
||||||
isEditBlendingLakeSelected(lakeId: number): boolean {
|
|
||||||
return this.editSelectedBlendingLakeIds.includes(lakeId.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to open field mapping modal
|
|
||||||
openFieldMappingModal(item: any) {
|
|
||||||
this.selectedMappingItem = item;
|
|
||||||
this.fieldMappingData = [];
|
|
||||||
this.fieldMappings = {};
|
|
||||||
this.showFieldMappingModal = true;
|
|
||||||
|
|
||||||
// Fetch available keys using the same API as calculated fields
|
|
||||||
this.mainService.fetchAvailableKeys(item.url, item.sure_connect_id).subscribe(
|
|
||||||
(keys: string[]) => {
|
|
||||||
// Initialize field mappings with empty values
|
|
||||||
this.fieldMappingData = keys.map(key => ({
|
|
||||||
original: key,
|
|
||||||
mapped: ''
|
|
||||||
}));
|
|
||||||
|
|
||||||
// If there's existing mapping data, populate it
|
|
||||||
if (item.mapping_json) {
|
|
||||||
try {
|
|
||||||
const existingMappings = JSON.parse(item.mapping_json);
|
|
||||||
this.fieldMappingData.forEach(field => {
|
|
||||||
if (existingMappings[field.original]) {
|
|
||||||
field.mapped = existingMappings[field.original];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error parsing existing mapping JSON:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error fetching keys for field mapping:', error);
|
|
||||||
this.toastr.error('Failed to fetch available keys for field mapping');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to update field mappings
|
|
||||||
updateFieldMappings() {
|
|
||||||
if (!this.selectedMappingItem) {
|
|
||||||
this.toastr.error('No item selected for mapping');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create mapping object
|
|
||||||
const mappings: { [key: string]: string } = {};
|
|
||||||
this.fieldMappingData.forEach(field => {
|
|
||||||
if (field.mapped && field.mapped.trim() !== '') {
|
|
||||||
mappings[field.original] = field.mapped.trim();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Convert to JSON string
|
|
||||||
const mappingJson = JSON.stringify(mappings);
|
|
||||||
|
|
||||||
// Update the mapping JSON field
|
|
||||||
this.selectedMappingItem.mapping_json = mappingJson;
|
|
||||||
|
|
||||||
// Call update service
|
|
||||||
this.mainService.update(this.selectedMappingItem.id, this.selectedMappingItem).subscribe(
|
|
||||||
(response) => {
|
|
||||||
this.toastr.success('Field mappings updated successfully');
|
|
||||||
// Update the local data
|
|
||||||
const index = this.product.findIndex(p => p.id === this.selectedMappingItem.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.product[index] = {...this.selectedMappingItem};
|
|
||||||
}
|
|
||||||
this.closeFieldMappingModal();
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error updating field mappings:', error);
|
|
||||||
this.toastr.error('Failed to update field mappings');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to close field mapping modal
|
|
||||||
closeFieldMappingModal() {
|
|
||||||
this.showFieldMappingModal = false;
|
|
||||||
this.fieldMappingData = [];
|
|
||||||
this.fieldMappings = {};
|
|
||||||
this.selectedMappingItem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to update individual field mapping
|
|
||||||
updateFieldMapping(index: number, value: string) {
|
|
||||||
if (index >= 0 && index < this.fieldMappingData.length) {
|
|
||||||
this.fieldMappingData[index].mapped = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Method to open scheduler modal
|
|
||||||
openSchedulerModal(item: any) {
|
|
||||||
this.selectedSchedulerItem = item;
|
|
||||||
this.schedulerJob = null;
|
|
||||||
this.showSchedulerModal = true;
|
|
||||||
|
|
||||||
// Fetch job by lake ID
|
|
||||||
this.schedulerService.getJobByLakeId(item.id).subscribe(
|
|
||||||
(job: any) => {
|
|
||||||
this.schedulerJob = job;
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
// If job not found, it's expected - we'll show create option
|
|
||||||
if (error.status === 404) {
|
|
||||||
this.schedulerJob = null;
|
|
||||||
} else {
|
|
||||||
console.error('Error fetching scheduler job:', error);
|
|
||||||
this.toastr.error('Failed to fetch scheduler job information');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to create a new job
|
|
||||||
createJob() {
|
|
||||||
if (!this.selectedSchedulerItem) {
|
|
||||||
this.toastr.error('No data lake selected');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const jobData = {
|
|
||||||
name: this.selectedSchedulerItem.name,
|
|
||||||
status: 'RUNNING',
|
|
||||||
description: `Scheduled job for ${this.selectedSchedulerItem.name}`,
|
|
||||||
jobType: 'DATALAKE',
|
|
||||||
lakeid: this.selectedSchedulerItem.id
|
|
||||||
};
|
|
||||||
|
|
||||||
this.schedulerService.createJob(jobData).subscribe(
|
|
||||||
(job: any) => {
|
|
||||||
this.schedulerJob = job;
|
|
||||||
this.toastr.success('Job created successfully');
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error creating job:', error);
|
|
||||||
this.toastr.error('Failed to create job');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to pause a job
|
|
||||||
pauseJob() {
|
|
||||||
if (!this.schedulerJob || !this.schedulerJob.id) {
|
|
||||||
this.toastr.error('No job selected');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.schedulerService.pauseJob(this.schedulerJob.id).subscribe(
|
|
||||||
(response: any) => {
|
|
||||||
this.schedulerJob.status = 'PAUSED';
|
|
||||||
this.toastr.success('Job paused successfully');
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error pausing job:', error);
|
|
||||||
this.toastr.error('Failed to pause job');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to resume a job
|
|
||||||
resumeJob() {
|
|
||||||
if (!this.schedulerJob || !this.schedulerJob.id) {
|
|
||||||
this.toastr.error('No job selected');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.schedulerService.resumeJob(this.schedulerJob.id).subscribe(
|
|
||||||
(response: any) => {
|
|
||||||
this.schedulerJob.status = 'RUNNING';
|
|
||||||
this.toastr.success('Job resumed successfully');
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error resuming job:', error);
|
|
||||||
this.toastr.error('Failed to resume job');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to stop a job
|
|
||||||
stopJob() {
|
|
||||||
if (!this.schedulerJob || !this.schedulerJob.id) {
|
|
||||||
this.toastr.error('No job selected');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.schedulerService.stopJob(this.schedulerJob.id).subscribe(
|
|
||||||
(response: any) => {
|
|
||||||
this.schedulerJob.status = 'STOPPED';
|
|
||||||
this.toastr.success('Job stopped successfully');
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error stopping job:', error);
|
|
||||||
this.toastr.error('Failed to stop job');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to restart a stopped job
|
|
||||||
restartJob() {
|
|
||||||
if (!this.schedulerJob || !this.schedulerJob.id) {
|
|
||||||
this.toastr.error('No job selected');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// To restart a job, we need to create a new job with the same parameters
|
|
||||||
const jobData = {
|
|
||||||
name: this.schedulerJob.name,
|
|
||||||
status: 'RUNNING',
|
|
||||||
description: this.schedulerJob.description,
|
|
||||||
jobType: this.schedulerJob.jobType,
|
|
||||||
lakeid: this.schedulerJob.lakeid
|
|
||||||
};
|
|
||||||
|
|
||||||
this.schedulerService.createJob(jobData).subscribe(
|
|
||||||
(job: any) => {
|
|
||||||
this.schedulerJob = job;
|
|
||||||
this.toastr.success('Job restarted successfully');
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error restarting job:', error);
|
|
||||||
this.toastr.error('Failed to restart job');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to close scheduler modal
|
|
||||||
closeSchedulerModal() {
|
|
||||||
this.showSchedulerModal = false;
|
|
||||||
this.schedulerJob = null;
|
|
||||||
this.selectedSchedulerItem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to get status badge class
|
|
||||||
getStatusBadgeClass(status: string): string {
|
|
||||||
switch (status) {
|
|
||||||
case 'RUNNING':
|
|
||||||
return 'badge-success';
|
|
||||||
case 'PAUSED':
|
|
||||||
return 'badge-warning';
|
|
||||||
case 'STOPPED':
|
|
||||||
return 'badge-danger';
|
|
||||||
default:
|
|
||||||
return 'badge-light';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to toggle webhook for a data lake item
|
|
||||||
toggleWebhook(item: any) {
|
|
||||||
// Call the enableWebhook service method
|
|
||||||
this.mainService.enableWebhook(item.id).subscribe(
|
|
||||||
(updatedItem: any) => {
|
|
||||||
// Update the local data with the response from the server
|
|
||||||
const index = this.product.findIndex(p => p.id === item.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.product[index] = {...updatedItem};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message based on the new webhook status
|
|
||||||
if (updatedItem.webhook_url && updatedItem.webhook_url.trim() !== '') {
|
|
||||||
this.toastr.success('Webhook enabled successfully');
|
|
||||||
} else {
|
|
||||||
this.toastr.success('Webhook disabled successfully');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.error('Error toggling webhook:', error);
|
|
||||||
this.toastr.error('Failed to toggle webhook');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to copy webhook URL to clipboard
|
|
||||||
copyWebhookUrl(webhookUrl: string) {
|
|
||||||
this.copyToClipboard(webhookUrl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,12 +42,6 @@ export class Data_lakeservice{
|
|||||||
return this.apiRequest.get(apiUrl);
|
return this.apiRequest.get(apiUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to fetch blending keys for a specific lake ID
|
|
||||||
fetchBlendingKeys(lakeId: number): Observable<any> {
|
|
||||||
const _http = `${this.baseURL}/keys/${lakeId}`;
|
|
||||||
return this.apiRequest.get(_http);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to update calculated fields for a data lake item
|
// Method to update calculated fields for a data lake item
|
||||||
updateCalculatedFields(id: number, calculatedFieldJson: string, isCalculatedField: boolean): Observable<any> {
|
updateCalculatedFields(id: number, calculatedFieldJson: string, isCalculatedField: boolean): Observable<any> {
|
||||||
const _http = this.baseURL + "/" + id;
|
const _http = this.baseURL + "/" + id;
|
||||||
@@ -57,11 +51,5 @@ export class Data_lakeservice{
|
|||||||
};
|
};
|
||||||
return this.apiRequest.put(_http, data);
|
return this.apiRequest.put(_http, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to enable webhook for a data lake item
|
|
||||||
enableWebhook(id: number): Observable<any> {
|
|
||||||
const _http = `${this.baseURL}/webhook/${id}`;
|
|
||||||
return this.apiRequest.get(_http);
|
|
||||||
}
|
|
||||||
// updateaction
|
// updateaction
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,6 @@
|
|||||||
<div class="cron-job-builder">
|
<div class="cron-job-builder">
|
||||||
<h4>Cron Job Builder</h4>
|
<h4>Cron Job Builder</h4>
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="clr-col-lg-2 clr-col-md-4 clr-col-sm-6 clr-col-12">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Second</label>
|
|
||||||
<select class="form-control" [(ngModel)]="second" (change)="onFieldChange()">
|
|
||||||
<option *ngFor="let option of secondOptions" [value]="option.value">{{ option.label }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="clr-col-lg-2 clr-col-md-4 clr-col-sm-6 clr-col-12">
|
<div class="clr-col-lg-2 clr-col-md-4 clr-col-sm-6 clr-col-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Minute</label>
|
<label>Minute</label>
|
||||||
@@ -37,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clr-col-lg-2 clr-col-md-4 clr-col-sm-6 clr-col-12">
|
<div class="clr-col-lg-3 clr-col-md-6 clr-col-sm-6 clr-col-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Month</label>
|
<label>Month</label>
|
||||||
<select class="form-control" [(ngModel)]="month" (change)="onFieldChange()">
|
<select class="form-control" [(ngModel)]="month" (change)="onFieldChange()">
|
||||||
@@ -46,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clr-col-lg-2 clr-col-md-4 clr-col-sm-6 clr-col-12">
|
<div class="clr-col-lg-3 clr-col-md-6 clr-col-sm-12 clr-col-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Day of Week</label>
|
<label>Day of Week</label>
|
||||||
<select class="form-control" [(ngModel)]="dayOfWeek" (change)="onFieldChange()">
|
<select class="form-control" [(ngModel)]="dayOfWeek" (change)="onFieldChange()">
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ export class CronJobBuilderComponent implements OnInit, OnChanges {
|
|||||||
@Input() instanceId: string = ''; // Unique identifier for each instance
|
@Input() instanceId: string = ''; // Unique identifier for each instance
|
||||||
@Output() cronExpressionChange = new EventEmitter<string>();
|
@Output() cronExpressionChange = new EventEmitter<string>();
|
||||||
|
|
||||||
// Cron job fields (now starting with seconds)
|
// Cron job fields
|
||||||
second: string = '*';
|
|
||||||
minute: string = '*';
|
minute: string = '*';
|
||||||
hour: string = '*';
|
hour: string = '*';
|
||||||
dayOfMonth: string = '*';
|
dayOfMonth: string = '*';
|
||||||
@@ -22,7 +21,6 @@ export class CronJobBuilderComponent implements OnInit, OnChanges {
|
|||||||
cronDescription: string = '';
|
cronDescription: string = '';
|
||||||
|
|
||||||
// Options for selectors
|
// Options for selectors
|
||||||
secondOptions: any[] = [];
|
|
||||||
minuteOptions: any[] = [];
|
minuteOptions: any[] = [];
|
||||||
hourOptions: any[] = [];
|
hourOptions: any[] = [];
|
||||||
dayOfMonthOptions: any[] = [];
|
dayOfMonthOptions: any[] = [];
|
||||||
@@ -31,10 +29,9 @@ export class CronJobBuilderComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
// Initialize options for each instance
|
// Initialize options for each instance
|
||||||
this.secondOptions = this.generateOptions(0, 59, 'second');
|
this.minuteOptions = this.generateOptions(0, 59);
|
||||||
this.minuteOptions = this.generateOptions(0, 59, 'minute');
|
this.hourOptions = this.generateOptions(0, 23);
|
||||||
this.hourOptions = this.generateOptions(0, 23, 'hour');
|
this.dayOfMonthOptions = this.generateOptions(1, 31);
|
||||||
this.dayOfMonthOptions = this.generateOptions(1, 31, 'day');
|
|
||||||
this.monthOptions = [
|
this.monthOptions = [
|
||||||
{ value: '*', label: 'Every month' },
|
{ value: '*', label: 'Every month' },
|
||||||
{ value: '1', label: 'January' },
|
{ value: '1', label: 'January' },
|
||||||
@@ -76,8 +73,8 @@ export class CronJobBuilderComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateOptions(start: number, end: number, type: string): any[] {
|
generateOptions(start: number, end: number): any[] {
|
||||||
const options = [{ value: '*', label: `Every ${type} (${start === 0 ? '0' : start}-${end})` }];
|
const options = [{ value: '*', label: `Every (${start === 0 ? '0' : start}-${end})` }];
|
||||||
for (let i = start; i <= end; i++) {
|
for (let i = start; i <= end; i++) {
|
||||||
options.push({ value: i.toString(), label: i.toString() });
|
options.push({ value: i.toString(), label: i.toString() });
|
||||||
}
|
}
|
||||||
@@ -86,16 +83,7 @@ export class CronJobBuilderComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
parseCronExpression(expression: string) {
|
parseCronExpression(expression: string) {
|
||||||
const parts = expression.split(' ');
|
const parts = expression.split(' ');
|
||||||
if (parts.length >= 6) { // Now expecting 6 parts with seconds
|
if (parts.length >= 5) {
|
||||||
this.second = parts[0];
|
|
||||||
this.minute = parts[1];
|
|
||||||
this.hour = parts[2];
|
|
||||||
this.dayOfMonth = parts[3];
|
|
||||||
this.month = parts[4];
|
|
||||||
this.dayOfWeek = parts[5];
|
|
||||||
} else if (parts.length >= 5) { // For backward compatibility with 5-part cron expressions
|
|
||||||
// Default seconds to 0 if not provided
|
|
||||||
this.second = '0';
|
|
||||||
this.minute = parts[0];
|
this.minute = parts[0];
|
||||||
this.hour = parts[1];
|
this.hour = parts[1];
|
||||||
this.dayOfMonth = parts[2];
|
this.dayOfMonth = parts[2];
|
||||||
@@ -107,33 +95,19 @@ export class CronJobBuilderComponent implements OnInit, OnChanges {
|
|||||||
generateDescription() {
|
generateDescription() {
|
||||||
let description = 'Runs ';
|
let description = 'Runs ';
|
||||||
|
|
||||||
// Second description
|
|
||||||
if (this.second === '*') {
|
|
||||||
description += 'every second';
|
|
||||||
} else if (this.second.includes('/')) {
|
|
||||||
const interval = this.second.split('/')[1];
|
|
||||||
description += `every ${interval} seconds`;
|
|
||||||
} else {
|
|
||||||
description += `at second ${this.second}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minute description
|
// Minute description
|
||||||
if (this.minute === '*') {
|
if (this.minute === '*') {
|
||||||
if (this.second === '*') {
|
description += 'every minute';
|
||||||
description += '';
|
|
||||||
} else {
|
|
||||||
description += ' of every minute';
|
|
||||||
}
|
|
||||||
} else if (this.minute.includes('/')) {
|
} else if (this.minute.includes('/')) {
|
||||||
const interval = this.minute.split('/')[1];
|
const interval = this.minute.split('/')[1];
|
||||||
description += ` of every ${interval} minutes`;
|
description += `every ${interval} minutes`;
|
||||||
} else {
|
} else {
|
||||||
description += ` at minute ${this.minute}`;
|
description += `at minute ${this.minute}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hour description
|
// Hour description
|
||||||
if (this.hour === '*') {
|
if (this.hour === '*') {
|
||||||
if (this.minute === '*' && this.second === '*') {
|
if (this.minute === '*') {
|
||||||
description += '';
|
description += '';
|
||||||
} else {
|
} else {
|
||||||
description += ' past every hour';
|
description += ' past every hour';
|
||||||
@@ -172,23 +146,21 @@ export class CronJobBuilderComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Special case for simple patterns
|
// Special case for simple patterns
|
||||||
if (this.second === '0' && this.minute === '0' && this.hour === '0' && this.dayOfMonth === '1' && this.month === '*' && this.dayOfWeek === '*') {
|
if (this.minute === '0' && this.hour === '0' && this.dayOfMonth === '1' && this.month === '*' && this.dayOfWeek === '*') {
|
||||||
description = 'Runs at midnight on the first day of every month';
|
description = 'Runs at midnight on the first day of every month';
|
||||||
} else if (this.second === '0' && this.minute === '0' && this.hour === '0' && this.dayOfMonth === '*' && this.month === '*' && this.dayOfWeek === '*') {
|
} else if (this.minute === '0' && this.hour === '0' && this.dayOfMonth === '*' && this.month === '*' && this.dayOfWeek === '*') {
|
||||||
description = 'Runs at midnight every day';
|
description = 'Runs at midnight every day';
|
||||||
} else if (this.second === '0' && this.minute === '0' && this.hour === '*' && this.dayOfMonth === '*' && this.month === '*' && this.dayOfWeek === '*') {
|
} else if (this.minute === '0' && this.hour === '*' && this.dayOfMonth === '*' && this.month === '*' && this.dayOfWeek === '*') {
|
||||||
description = 'Runs at the top of every hour';
|
description = 'Runs at the top of every hour';
|
||||||
} else if (this.second === '0' && this.minute === '*' && this.hour === '*' && this.dayOfMonth === '*' && this.month === '*' && this.dayOfWeek === '*') {
|
} else if (this.minute === '*' && this.hour === '*' && this.dayOfMonth === '*' && this.month === '*' && this.dayOfWeek === '*') {
|
||||||
description = 'Runs at the top of every minute';
|
description = 'Runs every minute';
|
||||||
} else if (this.second === '*' && this.minute === '*' && this.hour === '*' && this.dayOfMonth === '*' && this.month === '*' && this.dayOfWeek === '*') {
|
|
||||||
description = 'Runs every second';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cronDescription = description;
|
this.cronDescription = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCronExpression() {
|
buildCronExpression() {
|
||||||
const expression = `${this.second} ${this.minute} ${this.hour} ${this.dayOfMonth} ${this.month} ${this.dayOfWeek}`;
|
const expression = `${this.minute} ${this.hour} ${this.dayOfMonth} ${this.month} ${this.dayOfWeek}`;
|
||||||
this.cronExpressionChange.emit(expression);
|
this.cronExpressionChange.emit(expression);
|
||||||
this.generateDescription(); // Update description when expression changes
|
this.generateDescription(); // Update description when expression changes
|
||||||
return expression;
|
return expression;
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from "rxjs";
|
|
||||||
import { HttpClient } from "@angular/common/http";
|
|
||||||
import { ApiRequestService } from "src/app/services/api/api-request.service";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class DataLakeSchedulerService {
|
|
||||||
private baseURL = "scheduler";
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private http: HttpClient,
|
|
||||||
private apiRequest: ApiRequestService,
|
|
||||||
) { }
|
|
||||||
|
|
||||||
// Get job by lake ID
|
|
||||||
getJobByLakeId(lakeId: number): Observable<any> {
|
|
||||||
const _http = `${this.baseURL}/lake/${lakeId}`;
|
|
||||||
return this.apiRequest.get(_http);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new job
|
|
||||||
createJob(jobData: any): Observable<any> {
|
|
||||||
const _http = `${this.baseURL}/create`;
|
|
||||||
return this.apiRequest.post(_http, jobData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause a job
|
|
||||||
pauseJob(jobId: number): Observable<any> {
|
|
||||||
const _http = `${this.baseURL}/pause/${jobId}`;
|
|
||||||
return this.apiRequest.post(_http, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume a job
|
|
||||||
resumeJob(jobId: number): Observable<any> {
|
|
||||||
const _http = `${this.baseURL}/resume/${jobId}`;
|
|
||||||
return this.apiRequest.post(_http, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop a job
|
|
||||||
stopJob(jobId: number): Observable<any> {
|
|
||||||
const _http = `${this.baseURL}/stop/${jobId}`;
|
|
||||||
return this.apiRequest.delete(_http);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +1,61 @@
|
|||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
||||||
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
<li><a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']"><clr-icon shape="home"></clr-icon></a></li>
|
||||||
<li><a href="javascript://"><clr-icon shape="dashboard"></clr-icon> {{ 'Dashboard_builder' | translate }}</a></li>
|
<li><a href="javascript://"><clr-icon shape="dashboard"></clr-icon> {{ 'Dashboard_builder' | translate }}</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="dg-wrapper">
|
<div class="dg-wrapper">
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="clr-col-8">x
|
<div class="clr-col-8">
|
||||||
<h3>{{ 'Dashboard_builder' | translate }}</h3>
|
<h3>{{ 'Dashboard_builder' | translate }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="clr-col-4" style="text-align: right;">
|
<div class="clr-col-4" style="text-align: right;">
|
||||||
<button class="btn btn-success" [routerLink]="['/cns-portal/shield-dashboard']">
|
<button class="btn btn-success" [routerLink]="['/cns-portal/shield-dashboard']">
|
||||||
<clr-icon shape="shield"></clr-icon>Shield Dashboard
|
<clr-icon shape="shield"></clr-icon>Shield Dashboard
|
||||||
</button>
|
</button>
|
||||||
<button id="add" class="btn btn-primary" (click)="gotorunner()">
|
<button id="add" class="btn btn-primary" (click)="gotorunner()">
|
||||||
<clr-icon shape="grid-view"></clr-icon>{{ 'Dashboard_runner' | translate }}
|
<clr-icon shape="grid-view"></clr-icon>{{ 'Dashboard_runner' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<!-- Add Chart Config button -->
|
<button class="btn btn-outline" (click)="onExport()">
|
||||||
<button class="btn btn-primary" (click)="openChartConfig()">
|
<clr-icon shape="export"></clr-icon> {{ 'EXPORT_XLSX' | translate }}
|
||||||
<clr-icon shape="cog"></clr-icon> Chart Config
|
</button>
|
||||||
</button>
|
<button id="add" class="btn btn-primary" (click)="gotoadd()">
|
||||||
<button class="btn btn-outline" (click)="onExport()">
|
<clr-icon shape="plus"></clr-icon> {{ 'ADD' | translate }}
|
||||||
<clr-icon shape="export"></clr-icon> {{ 'EXPORT_XLSX' | translate }}
|
</button>
|
||||||
</button>
|
</div>
|
||||||
<button id="add" class="btn btn-primary" (click)="gotoadd()">
|
</div>
|
||||||
<clr-icon shape="plus"></clr-icon> {{ 'ADD' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading">
|
<clr-datagrid [clrDgLoading]="loading">
|
||||||
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>{{ 'Loading' | translate }} ......
|
<clr-dg-placeholder><ng-template #loadingSpinner><clr-spinner>{{ 'Loading' | translate }} ...... </clr-spinner></ng-template>
|
||||||
</clr-spinner></ng-template>
|
<div *ngIf="error;else loadingSpinner">{{error}}</div></clr-dg-placeholder>
|
||||||
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
|
||||||
</clr-dg-placeholder>
|
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="''">
|
<clr-dg-column [clrDgField]="''">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Go_to' | translate }}
|
{{ 'Go_to' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'dashboard_name'">
|
<clr-dg-column [clrDgField]="'dashboard_name'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Dashboard_Name' | translate }}
|
{{ 'Dashboard_Name' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'description'">
|
<clr-dg-column [clrDgField]="'description'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Description' | translate }}
|
{{ 'Description' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column >
|
||||||
<clr-dg-column [clrDgField]="'secuirity_profile'">
|
<clr-dg-column [clrDgField]="'secuirity_profile'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Security_Profile' | translate }}
|
{{ 'Security_Profile' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column >
|
||||||
<clr-dg-column [clrDgField]="'add_to_home'">
|
<clr-dg-column [clrDgField]="'add_to_home'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Add_to_home' | translate }}
|
{{ 'Add_to_home' | translate }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column>
|
</clr-dg-column >
|
||||||
<!-- <clr-dg-column [clrDgField]="'formType'">
|
<!-- <clr-dg-column [clrDgField]="'formType'">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
Build Status
|
Build Status
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -71,86 +65,81 @@
|
|||||||
Testing
|
Testing
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</clr-dg-column > -->
|
</clr-dg-column > -->
|
||||||
<clr-dg-column [clrDgField]="'action'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-column [clrDgField]="'action'"> <ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'Action' | translate }}
|
{{ 'Action' | translate }}
|
||||||
</ng-container></clr-dg-column>
|
</ng-container></clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let user of data?.slice()?.reverse();" [clrDgItem]="user">
|
<clr-dg-row *clrDgItems="let user of data?.slice()?.reverse();" [clrDgItem]="user">
|
||||||
<clr-dg-cell><span class="label label-light-blue" style="display: inline;margin-left: 10px; cursor: pointer;"
|
<clr-dg-cell><span class="label label-light-blue" style="display: inline;margin-left: 10px; cursor: pointer;" (click)="goToEdit(user.id)"> {{ 'SET_UP' | translate }}</span></clr-dg-cell>
|
||||||
(click)="goToEdit(user.id)"> {{ 'SET_UP' | translate }}</span></clr-dg-cell>
|
<clr-dg-cell>{{user.dashboard_name}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.dashboard_name}}</clr-dg-cell>
|
<clr-dg-cell>{{user.description}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.description}}</clr-dg-cell>
|
<clr-dg-cell>{{user.secuirity_profile}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.secuirity_profile}}</clr-dg-cell>
|
<clr-dg-cell>{{user.add_to_home}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.add_to_home}}</clr-dg-cell>
|
|
||||||
|
|
||||||
<!-- <clr-dg-cell><input type="radio" id="cb1" class="dots" [ngStyle]="{'background-color': user.build == true ? 'green' : 'red'}"></clr-dg-cell>
|
<!-- <clr-dg-cell><input type="radio" id="cb1" class="dots" [ngStyle]="{'background-color': user.build == true ? 'green' : 'red'}"></clr-dg-cell>
|
||||||
<clr-dg-cell>{{user.testing}}</clr-dg-cell> -->
|
<clr-dg-cell>{{user.testing}}</clr-dg-cell> -->
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<!-- <span style="cursor: pointer; padding: 10px; "><clr-icon shape="form" (click)="goToEdit(user.id)" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span> -->
|
<!-- <span style="cursor: pointer; padding: 10px; "><clr-icon shape="form" (click)="goToEdit(user.id)" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span> -->
|
||||||
<a href="javascript:void(0)" style="padding-right: 10px;" role="tooltip" aria-haspopup="true"
|
<a href="javascript:void(0)" style="padding-right: 10px;" role="tooltip" aria-haspopup="true" class="tooltip tooltip-sm tooltip-top-left">
|
||||||
class="tooltip tooltip-sm tooltip-top-left">
|
<span style="cursor: pointer;"><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error" style="color: red;"></clr-icon></span>
|
||||||
<span style="cursor: pointer;"><clr-icon shape="trash" (click)="onDelete(user)" class="red is-error"
|
<span class="tooltip-content">{{ 'Delete' | translate }}</span>
|
||||||
style="color: red;"></clr-icon></span>
|
</a>
|
||||||
<span class="tooltip-content">{{ 'Delete' | translate }}</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<clr-signpost>
|
<clr-signpost>
|
||||||
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success"
|
<span style="cursor: pointer;" clrSignpostTrigger><clr-icon shape="help" class="success" style="color: rgb(0, 130, 236);"></clr-icon></span>
|
||||||
style="color: rgb(0, 130, 236);"></clr-icon></span>
|
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
||||||
<clr-signpost-content [clrPosition]="'left-middle'" *clrIfOpen>
|
<h5 style="margin-top: 0">{{ 'Who_Column' | translate }}</h5>
|
||||||
<h5 style="margin-top: 0">{{ 'Who_Column' | translate }}</h5>
|
<div>{{ 'Account_ID' | translate }}: <code class="clr-code">{{ user.accountId }}</code></div>
|
||||||
<div>{{ 'Account_ID' | translate }}: <code class="clr-code">{{ user.accountId }}</code></div>
|
<div>{{ 'Created_At' | translate }}: <code class="clr-code">{{ user.createdAt | date }}</code></div>
|
||||||
<div>{{ 'Created_At' | translate }}: <code class="clr-code">{{ user.createdAt | date }}</code></div>
|
<div>{{ 'Created_By' | translate }}: <code class="clr-code">{{ user.createdBy }}</code></div>
|
||||||
<div>{{ 'Created_By' | translate }}: <code class="clr-code">{{ user.createdBy }}</code></div>
|
<div>{{ 'Updated_At' | translate }}: <code class="clr-code">{{ user.updatedAt | date }}</code></div>
|
||||||
<div>{{ 'Updated_At' | translate }}: <code class="clr-code">{{ user.updatedAt | date }}</code></div>
|
<div>{{ 'Updated_By' | translate }}: <code class="clr-code">{{ user.updatedBy }}</code></div>
|
||||||
<div>{{ 'Updated_By' | translate }}: <code class="clr-code">{{ user.updatedBy }}</code></div>
|
</clr-signpost-content>
|
||||||
</clr-signpost-content>
|
</clr-signpost>
|
||||||
</clr-signpost>
|
|
||||||
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-action-overflow>
|
<clr-dg-action-overflow>
|
||||||
<button class="action-item" (click)="goToEditData(user.id)">{{ 'Edit' | translate }} <clr-icon shape="edit"
|
<button class="action-item" (click)="goToEditData(user.id)">{{ 'Edit' | translate }} <clr-icon shape="edit" class="is-error"></clr-icon></button>
|
||||||
class="is-error"></clr-icon></button>
|
<!-- <button class="action-item" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button> -->
|
||||||
<!-- <button class="action-item" (click)="onDelete(user)">Delete<clr-icon shape="trash" class="is-error"></clr-icon></button> -->
|
</clr-dg-action-overflow>
|
||||||
</clr-dg-action-overflow>
|
</clr-dg-row>
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{ 'Users_per_page' | translate }}</clr-dg-page-size>
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{ 'Users_per_page' | translate }}</clr-dg-page-size>
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
{{ 'of' | translate }} {{pagination.totalItems}} {{ 'users' | translate }}
|
{{ 'of' | translate }} {{pagination.totalItems}} {{ 'users' | translate }}
|
||||||
</clr-dg-pagination>
|
</clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
</div>
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="addModall" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="s-order-dash-pg">
|
|
||||||
<div class="chart-box" id="word1" (click)="gotoadd()"><br>
|
|
||||||
<img style="margin: auto; display: block;" src="/assets/images/fromscratch.png" height="90" width="90">
|
|
||||||
<h5 class="center"> <b>{{ 'Start_from_scratch' | translate }}</b> </h5>
|
|
||||||
</div>
|
|
||||||
<div class="chart-box" id="word1"><br>
|
|
||||||
<img style="margin: auto; display: block;" src="/assets/images/copytemplate.png" height="90" width="90">
|
|
||||||
<h5 class="center"> <b>{{ 'Import_from_template' | translate }}</b> </h5>
|
|
||||||
</div>
|
|
||||||
<div class="chart-box" id="word1"><br>
|
|
||||||
<img style="margin: auto; display: block;" src="/assets/images/database.png" height="90" width="90">
|
|
||||||
<h5 class="center"> <b>{{ 'Import_from_public_project' | translate }}</b> </h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</clr-modal>
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modalDelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
<clr-modal [(clrModalOpen)]="addModall" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
<div class="modal-body" *ngIf="rowSelected.id">
|
<div class="modal-body">
|
||||||
<h1 class="delete">{{ 'Are_you_sure_want_to_delete' | translate }}</h1>
|
<div class="s-order-dash-pg">
|
||||||
<h2 class="heading">{{rowSelected.id}}</h2>
|
<div class="chart-box" id="word1" (click)="gotoadd()"><br>
|
||||||
<div class="modal-footer">
|
<img style="margin: auto; display: block;" src="/assets/images/fromscratch.png" height="90" width="90">
|
||||||
<button type="button" class="btn btn-outline" (click)="modalDelete = false">{{ 'Cancel' | translate }}</button>
|
<h5 class="center"> <b>{{ 'Start_from_scratch' | translate }}</b> </h5>
|
||||||
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary">{{ 'Delete' | translate }}</button>
|
</div>
|
||||||
</div>
|
<div class="chart-box" id="word1" ><br>
|
||||||
</div>
|
<img style="margin: auto; display: block;" src="/assets/images/copytemplate.png" height="90" width="90">
|
||||||
</clr-modal>
|
<h5 class="center"> <b>{{ 'Import_from_template' | translate }}</b> </h5>
|
||||||
|
</div>
|
||||||
|
<div class="chart-box" id="word1"><br>
|
||||||
|
<img style="margin: auto; display: block;" src="/assets/images/database.png" height="90" width="90">
|
||||||
|
<h5 class="center"> <b>{{ 'Import_from_public_project' | translate }}</b> </h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
|
<clr-modal [(clrModalOpen)]="modalDelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
<div class="modal-body" *ngIf="rowSelected.id">
|
||||||
|
<h1 class="delete">{{ 'Are_you_sure_want_to_delete' | translate }}</h1>
|
||||||
|
<h2 class="heading">{{rowSelected.id}}</h2>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline" (click)="modalDelete = false">{{ 'Cancel' | translate }}</button>
|
||||||
|
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary" >{{ 'Delete' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
@@ -24,7 +24,6 @@ export class AllnewdashComponent implements OnInit {
|
|||||||
projectname;
|
projectname;
|
||||||
projectId;
|
projectId;
|
||||||
error;
|
error;
|
||||||
chartConfigManagerOpen = false;
|
|
||||||
constructor(
|
constructor(
|
||||||
private router : Router,
|
private router : Router,
|
||||||
private route: ActivatedRoute,private dashboardService : DashboardService,
|
private route: ActivatedRoute,private dashboardService : DashboardService,
|
||||||
@@ -122,9 +121,4 @@ export class AllnewdashComponent implements OnInit {
|
|||||||
// this.router.navigate(['../editdashn'],{relativeTo:this.route});
|
// this.router.navigate(['../editdashn'],{relativeTo:this.route});
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Add method to open chart configuration manager
|
|
||||||
openChartConfig(): void {
|
|
||||||
this.router.navigate(['../chart-types'],{relativeTo:this.route});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,630 +0,0 @@
|
|||||||
<div class="chart-config-manager">
|
|
||||||
<h2>Chart Configuration Manager</h2>
|
|
||||||
|
|
||||||
<!-- Chart Types Section -->
|
|
||||||
<clr-tabs>
|
|
||||||
<clr-tab>
|
|
||||||
<button clrTabLink>Chart Types</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Chart Types</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTypeForm = true">
|
|
||||||
Add Chart Type
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTypeForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartType.name" name="chartTypeName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartType.description" name="chartTypeDescription"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newChartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createChartType()" [disabled]="!newChartType.name">Save</button>
|
|
||||||
<button class="btn" (click)="showAddChartTypeForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Description</clr-dg-column>
|
|
||||||
<clr-dg-column>Status</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartType of chartTypes" [clrDgItem]="chartType">
|
|
||||||
<clr-dg-cell>{{chartType.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.displayName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.description}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{chartType.isActive ? 'Active' : 'Inactive'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTypeForEdit(chartType)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartType(chartType.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="onChartTypeSelect(chartType)">
|
|
||||||
<cds-icon shape="eye"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTypes.length}} chart types
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartType">
|
|
||||||
<h4>Edit Chart Type</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartType.name" name="editChartTypeName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartType.displayName" name="editChartTypeDisplayName" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartType.description" name="editChartTypeDescription"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedChartType.isActive" name="editChartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateChartType()" [disabled]="!selectedChartType.name">Update</button>
|
|
||||||
<button class="btn" (click)="selectedChartType = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- UI Components Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>UI Components</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>UI Components for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddUiComponentForm = true">
|
|
||||||
Add UI Component
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddUiComponentForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentName" name="componentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentType" name="componentType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.displayLabel" name="displayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.placeholder" name="placeholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newUiComponent.sortOrder" name="sortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newUiComponent.isRequired" name="isRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createUiComponent()" [disabled]="!newUiComponent.componentName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddUiComponentForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- UI Components Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Component Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Component Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Placeholder</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let uiComponent of uiComponents" [clrDgItem]="uiComponent">
|
|
||||||
<clr-dg-cell>{{uiComponent.componentName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.componentType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.displayLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.placeholder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="uiComponent.isRequired" [class.label-danger]="!uiComponent.isRequired">
|
|
||||||
{{uiComponent.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectUiComponentForEdit(uiComponent)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteUiComponent(uiComponent.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{uiComponents.length}} UI components
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedUiComponent">
|
|
||||||
<h4>Edit UI Component</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentName" name="editComponentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentType" name="editComponentType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.displayLabel" name="editDisplayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.placeholder" name="editPlaceholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedUiComponent.sortOrder" name="editSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedUiComponent.isRequired" name="editIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateUiComponent()" [disabled]="!selectedUiComponent.componentName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedUiComponent = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Component Properties Section -->
|
|
||||||
<clr-tab *ngIf="selectedUiComponent">
|
|
||||||
<button clrTabLink>Component Properties</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Properties for {{selectedUiComponent?.componentName}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddComponentPropertyForm = true">
|
|
||||||
Add Property
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddComponentPropertyForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyName" name="propertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyValue" name="propertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyType" name="propertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createComponentProperty()" [disabled]="!newComponentProperty.propertyName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddComponentPropertyForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Property Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Value</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let property of componentProperties" [clrDgItem]="property">
|
|
||||||
<clr-dg-cell>{{property.propertyName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyValue}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectComponentPropertyForEdit(property)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteComponentProperty(property.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{componentProperties.length}} properties
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedComponentProperty">
|
|
||||||
<h4>Edit Component Property</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyName" name="editPropertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyValue" name="editPropertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyType" name="editPropertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateComponentProperty()" [disabled]="!selectedComponentProperty.propertyName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedComponentProperty = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Chart Templates Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Chart Templates</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Templates for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTemplateForm = true">
|
|
||||||
Add Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTemplateForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartTemplate.templateName" name="templateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateHtml" name="templateHtml" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateCss" name="templateCss" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newChartTemplate.isDefault" name="isDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createChartTemplate()" [disabled]="!newChartTemplate.templateName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddChartTemplateForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Templates Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Template Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Is Default</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let template of chartTemplates" [clrDgItem]="template">
|
|
||||||
<clr-dg-cell>{{template.templateName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="template.isDefault" [class.label-danger]="!template.isDefault">
|
|
||||||
{{template.isDefault ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTemplateForEdit(template)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartTemplate(template.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTemplates.length}} templates
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartTemplate">
|
|
||||||
<h4>Edit Chart Template</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartTemplate.templateName" name="editTemplateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateHtml" name="editTemplateHtml" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateCss" name="editTemplateCss" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedChartTemplate.isDefault" name="editIsDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateChartTemplate()" [disabled]="!selectedChartTemplate.templateName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedChartTemplate = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Dynamic Fields</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Dynamic Fields for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddDynamicFieldForm = true">
|
|
||||||
Add Field
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddDynamicFieldForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldName" name="fieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldType" name="fieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newDynamicField.fieldOptions" name="fieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newDynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createDynamicField()" [disabled]="!newDynamicField.fieldName">Save</button>
|
|
||||||
<button class="btn" (click)="showAddDynamicFieldForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid>
|
|
||||||
<clr-dg-column>Field Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Show in UI</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let field of dynamicFields" [clrDgItem]="field">
|
|
||||||
<clr-dg-cell>{{field.fieldName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.isRequired" [class.label-danger]="!field.isRequired">
|
|
||||||
{{field.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.showInUi" [class.label-danger]="!field.showInUi">
|
|
||||||
{{field.showInUi ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectDynamicFieldForEdit(field)">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteDynamicField(field.id)">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{dynamicFields.length}} fields
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedDynamicField">
|
|
||||||
<h4>Edit Dynamic Field</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldName" name="editFieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldLabel" name="editFieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldType" name="editFieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedDynamicField.fieldOptions" name="editFieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedDynamicField.sortOrder" name="editFieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.isRequired" name="editFieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.showInUi" name="editFieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateDynamicField()" [disabled]="!selectedDynamicField.fieldName">Update</button>
|
|
||||||
<button class="btn" (click)="selectedDynamicField = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
</clr-tabs>
|
|
||||||
</div>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
.chart-config-manager {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
min-height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-success {
|
|
||||||
background-color: #318700;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-danger {
|
|
||||||
background-color: #e62200;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,658 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { environment } from 'src/environments/environment';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
|
|
||||||
interface ChartType {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
isActive: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UiComponent {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
componentName: string;
|
|
||||||
componentType: string;
|
|
||||||
displayLabel: string;
|
|
||||||
placeholder: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ComponentProperty {
|
|
||||||
id: number;
|
|
||||||
component: UiComponent;
|
|
||||||
propertyName: string;
|
|
||||||
propertyValue: string;
|
|
||||||
propertyType: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChartTemplate {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
templateName: string;
|
|
||||||
templateHtml: string;
|
|
||||||
templateCss: string;
|
|
||||||
isDefault: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DynamicField {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
fieldName: string;
|
|
||||||
fieldLabel: string;
|
|
||||||
fieldType: string;
|
|
||||||
fieldOptions: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
showInUi: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-config-manager',
|
|
||||||
templateUrl: './chart-config-manager.component.html',
|
|
||||||
styleUrls: ['./chart-config-manager.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartConfigManagerComponent implements OnInit {
|
|
||||||
// Chart Types
|
|
||||||
chartTypes: ChartType[] = [];
|
|
||||||
selectedChartType: ChartType | null = null;
|
|
||||||
newChartType: Partial<ChartType> = {};
|
|
||||||
showAddChartTypeForm = false;
|
|
||||||
chartTypeLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
uiComponents: UiComponent[] = [];
|
|
||||||
selectedUiComponent: UiComponent | null = null;
|
|
||||||
newUiComponent: Partial<UiComponent> = {};
|
|
||||||
showAddUiComponentForm = false;
|
|
||||||
uiComponentLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Component Properties
|
|
||||||
componentProperties: ComponentProperty[] = [];
|
|
||||||
selectedComponentProperty: ComponentProperty | null = null;
|
|
||||||
newComponentProperty: Partial<ComponentProperty> = {};
|
|
||||||
showAddComponentPropertyForm = false;
|
|
||||||
componentPropertyLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Chart Templates
|
|
||||||
chartTemplates: ChartTemplate[] = [];
|
|
||||||
selectedChartTemplate: ChartTemplate | null = null;
|
|
||||||
newChartTemplate: Partial<ChartTemplate> = {};
|
|
||||||
showAddChartTemplateForm = false;
|
|
||||||
chartTemplateLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Dynamic Fields
|
|
||||||
dynamicFields: DynamicField[] = [];
|
|
||||||
selectedDynamicField: DynamicField | null = null;
|
|
||||||
newDynamicField: Partial<DynamicField> = {};
|
|
||||||
showAddDynamicFieldForm = false;
|
|
||||||
dynamicFieldLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// API base URL
|
|
||||||
private apiUrl = environment.apiUrl || 'http://localhost:8080/api';
|
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.loadChartTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Type Methods
|
|
||||||
loadChartTypes(): void {
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<ChartType[]>(`${this.apiUrl}/chart-types`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTypes = data;
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
this.showError('Error loading chart types: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartType(): void {
|
|
||||||
if (!this.newChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.post<ChartType>(`${this.apiUrl}/chart-types`, this.newChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTypes.push(data);
|
|
||||||
this.newChartType = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showSuccess('Chart type created successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart type:', error);
|
|
||||||
this.showError('Error creating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartType(): void {
|
|
||||||
if (!this.selectedChartType || !this.selectedChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<ChartType>(`${this.apiUrl}/chart-types/${this.selectedChartType.id}`, this.selectedChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTypes.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTypes[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.showSuccess('Chart type updated successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart type:', error);
|
|
||||||
this.showError('Error updating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartType(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart type?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/chart-types/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTypes = this.chartTypes.filter(ct => ct.id !== id);
|
|
||||||
// Also clear related data if the deleted chart type was selected
|
|
||||||
if (this.selectedChartType && this.selectedChartType.id === id) {
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.uiComponents = [];
|
|
||||||
this.chartTemplates = [];
|
|
||||||
this.dynamicFields = [];
|
|
||||||
}
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTypeForEdit(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = { ...chartType };
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI Component Methods
|
|
||||||
loadUiComponents(chartTypeId: number): void {
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<UiComponent[]>(`${this.apiUrl}/ui-components/chart-type/${chartTypeId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents = data;
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading UI components:', error);
|
|
||||||
this.showError('Error loading UI components: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createUiComponent(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const uiComponentData = {
|
|
||||||
...this.newUiComponent,
|
|
||||||
chartType: { id: this.selectedChartType.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<UiComponent>(`${this.apiUrl}/ui-components`, uiComponentData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents.push(data);
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showSuccess('UI component created successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating UI component:', error);
|
|
||||||
this.showError('Error creating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUiComponent(): void {
|
|
||||||
if (!this.selectedUiComponent || !this.selectedUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<UiComponent>(`${this.apiUrl}/ui-components/${this.selectedUiComponent.id}`, this.selectedUiComponent).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.uiComponents.findIndex(uc => uc.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.uiComponents[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.showSuccess('UI component updated successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating UI component:', error);
|
|
||||||
this.showError('Error updating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUiComponent(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this UI component?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/ui-components/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.uiComponents = this.uiComponents.filter(uc => uc.id !== id);
|
|
||||||
if (this.selectedUiComponent && this.selectedUiComponent.id === id) {
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('UI component deleted successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting UI component:', error);
|
|
||||||
this.showError('Error deleting UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectUiComponentForEdit(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = { ...uiComponent };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Property Methods
|
|
||||||
loadComponentProperties(componentId: number): void {
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<ComponentProperty[]>(`${this.apiUrl}/component-properties/component/${componentId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties = data;
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading component properties:', error);
|
|
||||||
this.showError('Error loading component properties: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createComponentProperty(): void {
|
|
||||||
if (!this.selectedUiComponent) {
|
|
||||||
this.showError('Please select a UI component first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const componentPropertyData = {
|
|
||||||
...this.newComponentProperty,
|
|
||||||
component: { id: this.selectedUiComponent.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<ComponentProperty>(`${this.apiUrl}/component-properties`, componentPropertyData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties.push(data);
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showSuccess('Component property created successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating component property:', error);
|
|
||||||
this.showError('Error creating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateComponentProperty(): void {
|
|
||||||
if (!this.selectedComponentProperty || !this.selectedComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<ComponentProperty>(`${this.apiUrl}/component-properties/${this.selectedComponentProperty.id}`, this.selectedComponentProperty).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.componentProperties.findIndex(cp => cp.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.componentProperties[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.showSuccess('Component property updated successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating component property:', error);
|
|
||||||
this.showError('Error updating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComponentProperty(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this component property?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/component-properties/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.componentProperties = this.componentProperties.filter(cp => cp.id !== id);
|
|
||||||
if (this.selectedComponentProperty && this.selectedComponentProperty.id === id) {
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('Component property deleted successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting component property:', error);
|
|
||||||
this.showError('Error deleting component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectComponentPropertyForEdit(componentProperty: ComponentProperty): void {
|
|
||||||
this.selectedComponentProperty = { ...componentProperty };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Template Methods
|
|
||||||
loadChartTemplates(chartTypeId: number): void {
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<ChartTemplate[]>(`${this.apiUrl}/chart-templates/chart-type/${chartTypeId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates = data;
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart templates:', error);
|
|
||||||
this.showError('Error loading chart templates: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartTemplate(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const chartTemplateData = {
|
|
||||||
...this.newChartTemplate,
|
|
||||||
chartType: { id: this.selectedChartType.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<ChartTemplate>(`${this.apiUrl}/chart-templates`, chartTemplateData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates.push(data);
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showSuccess('Chart template created successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart template:', error);
|
|
||||||
this.showError('Error creating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartTemplate(): void {
|
|
||||||
if (!this.selectedChartTemplate || !this.selectedChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<ChartTemplate>(`${this.apiUrl}/chart-templates/${this.selectedChartTemplate.id}`, this.selectedChartTemplate).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTemplates.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTemplates[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.showSuccess('Chart template updated successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart template:', error);
|
|
||||||
this.showError('Error updating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartTemplate(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart template?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/chart-templates/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTemplates = this.chartTemplates.filter(ct => ct.id !== id);
|
|
||||||
if (this.selectedChartTemplate && this.selectedChartTemplate.id === id) {
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('Chart template deleted successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart template:', error);
|
|
||||||
this.showError('Error deleting chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTemplateForEdit(chartTemplate: ChartTemplate): void {
|
|
||||||
this.selectedChartTemplate = { ...chartTemplate };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamic Field Methods
|
|
||||||
loadDynamicFields(chartTypeId: number): void {
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.get<DynamicField[]>(`${this.apiUrl}/dynamic-fields/chart-type/${chartTypeId}`).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields = data;
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading dynamic fields:', error);
|
|
||||||
this.showError('Error loading dynamic fields: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createDynamicField(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
const dynamicFieldData = {
|
|
||||||
...this.newDynamicField,
|
|
||||||
chartType: { id: this.selectedChartType.id }
|
|
||||||
};
|
|
||||||
|
|
||||||
this.http.post<DynamicField>(`${this.apiUrl}/dynamic-fields`, dynamicFieldData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields.push(data);
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.showSuccess('Dynamic field created successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating dynamic field:', error);
|
|
||||||
this.showError('Error creating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDynamicField(): void {
|
|
||||||
if (!this.selectedDynamicField || !this.selectedDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.put<DynamicField>(`${this.apiUrl}/dynamic-fields/${this.selectedDynamicField.id}`, this.selectedDynamicField).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.dynamicFields.findIndex(df => df.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.dynamicFields[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
this.showSuccess('Dynamic field updated successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating dynamic field:', error);
|
|
||||||
this.showError('Error updating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteDynamicField(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this dynamic field?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.http.delete(`${this.apiUrl}/dynamic-fields/${id}`).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.dynamicFields = this.dynamicFields.filter(df => df.id !== id);
|
|
||||||
if (this.selectedDynamicField && this.selectedDynamicField.id === id) {
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
}
|
|
||||||
this.showSuccess('Dynamic field deleted successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting dynamic field:', error);
|
|
||||||
this.showError('Error deleting dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectDynamicFieldForEdit(dynamicField: DynamicField): void {
|
|
||||||
this.selectedDynamicField = { ...dynamicField };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
onChartTypeSelect(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = chartType;
|
|
||||||
this.loadUiComponents(chartType.id);
|
|
||||||
this.loadChartTemplates(chartType.id);
|
|
||||||
this.loadDynamicFields(chartType.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetForms(): void {
|
|
||||||
this.newChartType = {};
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
<div class="chart-config-manager">
|
|
||||||
<h2>Chart Configuration Manager</h2>
|
|
||||||
|
|
||||||
<!-- Test message to verify component is loading -->
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">Chart Config Manager Component Loaded Successfully</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Section -->
|
|
||||||
<clr-tab>
|
|
||||||
<button clrTabLink>Chart Types</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Chart Types</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTypeForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Chart Type
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTypeForm">
|
|
||||||
<app-chart-type-form
|
|
||||||
[chartType]="newChartType"
|
|
||||||
(save)="createChartType()"
|
|
||||||
(cancel)="showAddChartTypeForm = false">
|
|
||||||
</app-chart-type-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Description</clr-dg-column>
|
|
||||||
<clr-dg-column>Status</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartType of chartTypes" [clrDgItem]="chartType">
|
|
||||||
<clr-dg-cell>{{chartType.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.displayName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.description}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{chartType.isActive ? 'Active' : 'Inactive'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTypeForEdit(chartType)" title="Edit" [disabled]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTypeLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTypeLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartType(chartType.id)" title="Delete" [disabled]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTypeLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTypeLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="onChartTypeSelect(chartType)" title="View Details" [disabled]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTypeLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTypeLoadingState !== ClrLoadingState.LOADING" shape="eye"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTypes.length}} chart type(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Type Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartType">
|
|
||||||
<h4>Edit Chart Type</h4>
|
|
||||||
<app-chart-type-form
|
|
||||||
[chartType]="selectedChartType"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateChartType()"
|
|
||||||
(cancel)="selectedChartType = null">
|
|
||||||
</app-chart-type-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- UI Components Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>UI Components</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>UI Components for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddUiComponentForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add UI Component
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddUiComponentForm">
|
|
||||||
<app-ui-component-form
|
|
||||||
[uiComponent]="newUiComponent"
|
|
||||||
(save)="createUiComponent()"
|
|
||||||
(cancel)="showAddUiComponentForm = false">
|
|
||||||
</app-ui-component-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- UI Components Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Component Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Component Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let uiComponent of uiComponents" [clrDgItem]="uiComponent">
|
|
||||||
<clr-dg-cell>{{uiComponent.componentName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.componentType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.displayLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="uiComponent.isRequired" [class.label-danger]="!uiComponent.isRequired">
|
|
||||||
{{uiComponent.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell class="action-cell">
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectUiComponentForEdit(uiComponent)" title="Edit" [disabled]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="uiComponentLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="uiComponentLoadingState !== ClrLoadingState.LOADING" shape="pencil" aria-label="Edit"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteUiComponent(uiComponent.id)" title="Delete" [disabled]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="uiComponentLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="uiComponentLoadingState !== ClrLoadingState.LOADING" shape="trash" aria-label="Delete"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="onUiComponentSelect(uiComponent)" title="View Properties" [disabled]="uiComponentLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="uiComponentLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="uiComponentLoadingState !== ClrLoadingState.LOADING" shape="eye" aria-label="View Properties"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{uiComponents.length}} UI component(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit UI Component Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedUiComponent">
|
|
||||||
<h4>Edit UI Component</h4>
|
|
||||||
<app-ui-component-form
|
|
||||||
[uiComponent]="selectedUiComponent"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateUiComponent()"
|
|
||||||
(cancel)="selectedUiComponent = null">
|
|
||||||
</app-ui-component-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Component Properties Section -->
|
|
||||||
<clr-tab *ngIf="selectedUiComponent">
|
|
||||||
<button clrTabLink>Component Properties</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Properties for {{selectedUiComponent?.componentName}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddComponentPropertyForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Property
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddComponentPropertyForm">
|
|
||||||
<app-component-property-form
|
|
||||||
[componentProperty]="newComponentProperty"
|
|
||||||
(save)="createComponentProperty()"
|
|
||||||
(cancel)="showAddComponentPropertyForm = false">
|
|
||||||
</app-component-property-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Property Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Value</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let property of componentProperties" [clrDgItem]="property">
|
|
||||||
<clr-dg-cell>{{property.propertyName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyValue}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectComponentPropertyForEdit(property)" title="Edit" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteComponentProperty(property.id)" title="Delete" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{componentProperties.length}} propertie(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedComponentProperty">
|
|
||||||
<h4>Edit Component Property</h4>
|
|
||||||
<app-component-property-form
|
|
||||||
[componentProperty]="selectedComponentProperty"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateComponentProperty()"
|
|
||||||
(cancel)="selectedComponentProperty = null">
|
|
||||||
</app-component-property-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Chart Templates Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Chart Templates</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Templates for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddChartTemplateForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddChartTemplateForm">
|
|
||||||
<app-chart-template-form
|
|
||||||
[chartTemplate]="newChartTemplate"
|
|
||||||
(save)="createChartTemplate()"
|
|
||||||
(cancel)="showAddChartTemplateForm = false">
|
|
||||||
</app-chart-template-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Templates Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="chartTemplateLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Template Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Is Default</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let template of chartTemplates" [clrDgItem]="template">
|
|
||||||
<clr-dg-cell>{{template.templateName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="template.isDefault" [class.label-danger]="!template.isDefault">
|
|
||||||
{{template.isDefault ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{template.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{template.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTemplateForEdit(template)" title="Edit" [disabled]="chartTemplateLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTemplateLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTemplateLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartTemplate(template.id)" title="Delete" [disabled]="chartTemplateLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="chartTemplateLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="chartTemplateLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTemplates.length}} template(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Template Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedChartTemplate">
|
|
||||||
<h4>Edit Chart Template</h4>
|
|
||||||
<app-chart-template-form
|
|
||||||
[chartTemplate]="selectedChartTemplate"
|
|
||||||
[isEdit]="true"
|
|
||||||
(save)="updateChartTemplate()"
|
|
||||||
(cancel)="selectedChartTemplate = null">
|
|
||||||
</app-chart-template-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Section -->
|
|
||||||
<clr-tab *ngIf="selectedChartType">
|
|
||||||
<button clrTabLink>Dynamic Fields</button>
|
|
||||||
<clr-tab-content>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Dynamic Fields for {{selectedChartType?.name}}</h3>
|
|
||||||
<button class="btn btn-sm btn-primary" (click)="showAddDynamicFieldForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Field
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddDynamicFieldForm">
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldName" name="fieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldType" name="fieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newDynamicField.fieldOptions" name="fieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newDynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="createDynamicField()" [disabled]="!newDynamicField.fieldName || dynamicFieldLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="dynamicFieldLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
<button class="btn" (click)="showAddDynamicFieldForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="dynamicFieldLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Field Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Show in UI</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let field of dynamicFields" [clrDgItem]="field">
|
|
||||||
<clr-dg-cell>{{field.fieldName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.fieldType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.isRequired" [class.label-danger]="!field.isRequired">
|
|
||||||
{{field.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="field.showInUi" [class.label-danger]="!field.showInUi">
|
|
||||||
{{field.showInUi ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{field.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectDynamicFieldForEdit(field)" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteDynamicField(field.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{dynamicFields.length}} field(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Dynamic Field Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedDynamicField">
|
|
||||||
<h4>Edit Dynamic Field</h4>
|
|
||||||
<form clrForm>
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldName" name="editFieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldLabel" name="editFieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldType" name="editFieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedDynamicField.fieldOptions" name="editFieldOptions"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedDynamicField.sortOrder" name="editFieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.isRequired" name="editFieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.showInUi" name="editFieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" (click)="updateDynamicField()" [disabled]="!selectedDynamicField.fieldName || dynamicFieldLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="dynamicFieldLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
<button class="btn" (click)="selectedDynamicField = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</clr-tab-content>
|
|
||||||
</clr-tab>
|
|
||||||
</div>
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
.chart-config-manager {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
min-height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-success {
|
|
||||||
background-color: #318700;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label-danger {
|
|
||||||
background-color: #e62200;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.required {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-tab-content {
|
|
||||||
padding: 15px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-cell {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons .btn-icon {
|
|
||||||
min-width: 36px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure icons are visible
|
|
||||||
cds-icon {
|
|
||||||
display: inline-block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-spinner {
|
|
||||||
margin: 0;
|
|
||||||
}cds-icon {
|
|
||||||
display: inline-block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-spinner {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
@@ -1,710 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartTypeService } from '../chart-type-manager/chart-type.service';
|
|
||||||
import { UiComponentService } from './ui-component.service';
|
|
||||||
import { ComponentPropertyService } from './component-property.service';
|
|
||||||
import { ChartTemplateService } from './chart-template.service';
|
|
||||||
import { DynamicFieldService } from './dynamic-field.service';
|
|
||||||
|
|
||||||
export interface ChartType {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
isActive: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UiComponent {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
componentName: string;
|
|
||||||
componentType: string;
|
|
||||||
displayLabel: string;
|
|
||||||
placeholder: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ComponentProperty {
|
|
||||||
id: number;
|
|
||||||
component: UiComponent;
|
|
||||||
propertyName: string;
|
|
||||||
propertyValue: string;
|
|
||||||
propertyType: string;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChartTemplate {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
templateName: string;
|
|
||||||
templateHtml: string;
|
|
||||||
templateCss: string;
|
|
||||||
isDefault: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DynamicField {
|
|
||||||
id: number;
|
|
||||||
chartType: ChartType;
|
|
||||||
fieldName: string;
|
|
||||||
fieldLabel: string;
|
|
||||||
fieldType: string;
|
|
||||||
fieldOptions: string;
|
|
||||||
isRequired: boolean;
|
|
||||||
showInUi: boolean;
|
|
||||||
sortOrder: number;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-config-manager',
|
|
||||||
templateUrl: './chart-config-manager.component.html',
|
|
||||||
styleUrls: ['./chart-config-manager.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartConfigManagerComponent implements OnInit {
|
|
||||||
// Chart Types
|
|
||||||
chartTypes: ChartType[] = [];
|
|
||||||
selectedChartType: ChartType | null = null;
|
|
||||||
newChartType: Partial<ChartType> = {};
|
|
||||||
showAddChartTypeForm = false;
|
|
||||||
chartTypeLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// UI Components
|
|
||||||
uiComponents: UiComponent[] = [];
|
|
||||||
selectedUiComponent: UiComponent | null = null;
|
|
||||||
newUiComponent: Partial<UiComponent> = {};
|
|
||||||
showAddUiComponentForm = false;
|
|
||||||
uiComponentLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Component Properties
|
|
||||||
componentProperties: ComponentProperty[] = [];
|
|
||||||
selectedComponentProperty: ComponentProperty | null = null;
|
|
||||||
newComponentProperty: Partial<ComponentProperty> = {};
|
|
||||||
showAddComponentPropertyForm = false;
|
|
||||||
componentPropertyLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Chart Templates
|
|
||||||
chartTemplates: ChartTemplate[] = [];
|
|
||||||
selectedChartTemplate: ChartTemplate | null = null;
|
|
||||||
newChartTemplate: Partial<ChartTemplate> = {};
|
|
||||||
showAddChartTemplateForm = false;
|
|
||||||
chartTemplateLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Dynamic Fields
|
|
||||||
dynamicFields: DynamicField[] = [];
|
|
||||||
selectedDynamicField: DynamicField | null = null;
|
|
||||||
newDynamicField: Partial<DynamicField> = {};
|
|
||||||
showAddDynamicFieldForm = false;
|
|
||||||
dynamicFieldLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private uiComponentService: UiComponentService,
|
|
||||||
private componentPropertyService: ComponentPropertyService,
|
|
||||||
private chartTemplateService: ChartTemplateService,
|
|
||||||
private dynamicFieldService: DynamicFieldService
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
console.log('ChartConfigManagerComponent initialized');
|
|
||||||
this.loadChartTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Type Methods
|
|
||||||
loadChartTypes(): void {
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
console.log('Loading chart types...');
|
|
||||||
this.chartTypeService.getAllChartTypes().subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
console.log('Chart types loaded:', data);
|
|
||||||
this.chartTypes = data;
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
this.showError('Error loading chart types: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartType(): void {
|
|
||||||
if (!this.newChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.createChartType(this.newChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTypes.push(data);
|
|
||||||
this.newChartType = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showSuccess('Chart type created successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart type:', error);
|
|
||||||
this.showError('Error creating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartType(): void {
|
|
||||||
if (!this.selectedChartType || !this.selectedChartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.updateChartType(this.selectedChartType.id, this.selectedChartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTypes.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTypes[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.showSuccess('Chart type updated successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart type:', error);
|
|
||||||
this.showError('Error updating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartType(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart type? This will also delete all related UI components, templates, and fields.')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.deleteChartType(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTypes = this.chartTypes.filter(ct => ct.id !== id);
|
|
||||||
// Clear related data if the deleted chart type was selected
|
|
||||||
if (this.selectedChartType && this.selectedChartType.id === id) {
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.uiComponents = [];
|
|
||||||
this.chartTemplates = [];
|
|
||||||
this.dynamicFields = [];
|
|
||||||
}
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTypeForEdit(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = { ...chartType };
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI Component Methods
|
|
||||||
loadUiComponents(chartTypeId: number): void {
|
|
||||||
if (!chartTypeId) {
|
|
||||||
this.uiComponents = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.getUiComponentsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents = data;
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading UI components:', error);
|
|
||||||
this.showError('Error loading UI components: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createUiComponent(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete chartType object with only the ID
|
|
||||||
const chartTypeWithId = {
|
|
||||||
id: this.selectedChartType.id,
|
|
||||||
name: '',
|
|
||||||
displayName: '',
|
|
||||||
description: '',
|
|
||||||
isActive: true,
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
const uiComponentData = {
|
|
||||||
...this.newUiComponent,
|
|
||||||
chartType: chartTypeWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.uiComponentService.createUiComponent(uiComponentData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents.push(data);
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showSuccess('UI component created successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating UI component:', error);
|
|
||||||
this.showError('Error creating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUiComponent(): void {
|
|
||||||
if (!this.selectedUiComponent || !this.selectedUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.updateUiComponent(this.selectedUiComponent.id, this.selectedUiComponent).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.uiComponents.findIndex(uc => uc.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.uiComponents[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.showSuccess('UI component updated successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating UI component:', error);
|
|
||||||
this.showError('Error updating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUiComponent(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this UI component?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.deleteUiComponent(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.uiComponents = this.uiComponents.filter(uc => uc.id !== id);
|
|
||||||
this.showSuccess('UI component deleted successfully');
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting UI component:', error);
|
|
||||||
this.showError('Error deleting UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.uiComponentLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectUiComponentForEdit(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = { ...uiComponent };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Property Methods
|
|
||||||
loadComponentProperties(componentId: number): void {
|
|
||||||
if (!componentId) {
|
|
||||||
this.componentProperties = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.getComponentPropertiesByComponent(componentId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties = data;
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading component properties:', error);
|
|
||||||
this.showError('Error loading component properties: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createComponentProperty(): void {
|
|
||||||
if (!this.selectedUiComponent) {
|
|
||||||
this.showError('Please select a UI component first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete component object with only the ID
|
|
||||||
const componentWithId = {
|
|
||||||
id: this.selectedUiComponent.id
|
|
||||||
} as UiComponent;
|
|
||||||
|
|
||||||
const componentPropertyData = {
|
|
||||||
...this.newComponentProperty,
|
|
||||||
component: componentWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.componentPropertyService.createComponentProperty(componentPropertyData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties.push(data);
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showSuccess('Component property created successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating component property:', error);
|
|
||||||
this.showError('Error creating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateComponentProperty(): void {
|
|
||||||
if (!this.selectedComponentProperty || !this.selectedComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.updateComponentProperty(this.selectedComponentProperty.id, this.selectedComponentProperty).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.componentProperties.findIndex(cp => cp.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.componentProperties[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.showSuccess('Component property updated successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating component property:', error);
|
|
||||||
this.showError('Error updating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComponentProperty(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this component property?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.deleteComponentProperty(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.componentProperties = this.componentProperties.filter(cp => cp.id !== id);
|
|
||||||
this.showSuccess('Component property deleted successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting component property:', error);
|
|
||||||
this.showError('Error deleting component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectComponentPropertyForEdit(componentProperty: ComponentProperty): void {
|
|
||||||
this.selectedComponentProperty = { ...componentProperty };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Template Methods
|
|
||||||
loadChartTemplates(chartTypeId: number): void {
|
|
||||||
if (!chartTypeId) {
|
|
||||||
this.chartTemplates = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.getChartTemplatesByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates = data;
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart templates:', error);
|
|
||||||
this.showError('Error loading chart templates: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartTemplate(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Remove the chartType from the template data since we're passing it as a parameter
|
|
||||||
const chartTemplateData = { ...this.newChartTemplate };
|
|
||||||
// Remove chartType property if it exists
|
|
||||||
delete (chartTemplateData as any).chartType;
|
|
||||||
|
|
||||||
this.chartTemplateService.createChartTemplate(chartTemplateData, this.selectedChartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates.push(data);
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showSuccess('Chart template created successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart template:', error);
|
|
||||||
this.showError('Error creating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartTemplate(): void {
|
|
||||||
if (!this.selectedChartTemplate || !this.selectedChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.updateChartTemplate(this.selectedChartTemplate.id, this.selectedChartTemplate).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTemplates.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTemplates[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.showSuccess('Chart template updated successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart template:', error);
|
|
||||||
this.showError('Error updating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartTemplate(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart template?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.deleteChartTemplate(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTemplates = this.chartTemplates.filter(ct => ct.id !== id);
|
|
||||||
this.showSuccess('Chart template deleted successfully');
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart template:', error);
|
|
||||||
this.showError('Error deleting chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTemplateLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTemplateForEdit(chartTemplate: ChartTemplate): void {
|
|
||||||
this.selectedChartTemplate = { ...chartTemplate };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamic Field Methods
|
|
||||||
loadDynamicFields(chartTypeId: number): void {
|
|
||||||
if (!chartTypeId) {
|
|
||||||
this.dynamicFields = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.getDynamicFieldsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields = data;
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading dynamic fields:', error);
|
|
||||||
this.showError('Error loading dynamic fields: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createDynamicField(): void {
|
|
||||||
if (!this.selectedChartType) {
|
|
||||||
this.showError('Please select a chart type first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Remove the chartType from the dynamic field data since we're passing it as a parameter
|
|
||||||
const dynamicFieldData = { ...this.newDynamicField };
|
|
||||||
// Remove chartType property if it exists
|
|
||||||
delete (dynamicFieldData as any).chartType;
|
|
||||||
|
|
||||||
this.dynamicFieldService.createDynamicField(dynamicFieldData, this.selectedChartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields.push(data);
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.showSuccess('Dynamic field created successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating dynamic field:', error);
|
|
||||||
this.showError('Error creating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDynamicField(): void {
|
|
||||||
if (!this.selectedDynamicField || !this.selectedDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.updateDynamicField(this.selectedDynamicField.id, this.selectedDynamicField).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.dynamicFields.findIndex(df => df.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.dynamicFields[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
this.showSuccess('Dynamic field updated successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating dynamic field:', error);
|
|
||||||
this.showError('Error updating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteDynamicField(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this dynamic field?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.deleteDynamicField(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.dynamicFields = this.dynamicFields.filter(df => df.id !== id);
|
|
||||||
this.showSuccess('Dynamic field deleted successfully');
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting dynamic field:', error);
|
|
||||||
this.showError('Error deleting dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.dynamicFieldLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectDynamicFieldForEdit(dynamicField: DynamicField): void {
|
|
||||||
this.selectedDynamicField = { ...dynamicField };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
onChartTypeSelect(chartType: ChartType): void {
|
|
||||||
this.selectedChartType = chartType;
|
|
||||||
this.loadUiComponents(chartType.id);
|
|
||||||
this.loadChartTemplates(chartType.id);
|
|
||||||
this.loadDynamicFields(chartType.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
onUiComponentSelect(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = uiComponent;
|
|
||||||
this.loadComponentProperties(uiComponent.id);
|
|
||||||
// Scroll to the Component Properties tab
|
|
||||||
setTimeout(() => {
|
|
||||||
const element = document.querySelector('clr-tab[ng-reflect-ng-if="true"] button[clr-tab-link]:nth-child(3)');
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetForms(): void {
|
|
||||||
this.newChartType = {};
|
|
||||||
this.newUiComponent = {};
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.newChartTemplate = {};
|
|
||||||
this.newDynamicField = {};
|
|
||||||
this.showAddChartTypeForm = false;
|
|
||||||
this.showAddUiComponentForm = false;
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showAddChartTemplateForm = false;
|
|
||||||
this.showAddDynamicFieldForm = false;
|
|
||||||
this.selectedChartType = null;
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { ChartTemplate } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ChartTemplateService {
|
|
||||||
private chartTemplatesUrl = 'api/chart-templates';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all chart templates for a chart type
|
|
||||||
getChartTemplatesByChartType(chartTypeId: number): Observable<ChartTemplate[]> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/chart-type/${chartTypeId}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get chart template by ID
|
|
||||||
getChartTemplateById(id: number): Observable<ChartTemplate> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/${id}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new chart template with optional chart type ID as parameter
|
|
||||||
createChartTemplate(chartTemplate: Partial<ChartTemplate>, chartTypeId?: number): Observable<ChartTemplate> {
|
|
||||||
let url = this.chartTemplatesUrl;
|
|
||||||
if (chartTypeId) {
|
|
||||||
url = `${this.chartTemplatesUrl}?chartTypeId=${chartTypeId}`;
|
|
||||||
}
|
|
||||||
return this.apiRequest.post(url, chartTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update chart template
|
|
||||||
updateChartTemplate(id: number, chartTemplate: ChartTemplate): Observable<ChartTemplate> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/${id}`;
|
|
||||||
return this.apiRequest.put(url, chartTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete chart template
|
|
||||||
deleteChartTemplate(id: number): Observable<void> {
|
|
||||||
const url = `${this.chartTemplatesUrl}/${id}`;
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { ComponentProperty } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ComponentPropertyService {
|
|
||||||
private componentPropertiesUrl = 'api/component-properties';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all component properties for a component
|
|
||||||
getComponentPropertiesByComponent(componentId: number): Observable<ComponentProperty[]> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/component/${componentId}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get component property by ID
|
|
||||||
getComponentPropertyById(id: number): Observable<ComponentProperty> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/${id}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new component property
|
|
||||||
createComponentProperty(componentProperty: Partial<ComponentProperty>): Observable<ComponentProperty> {
|
|
||||||
return this.apiRequest.post(this.componentPropertiesUrl, componentProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update component property
|
|
||||||
updateComponentProperty(id: number, componentProperty: ComponentProperty): Observable<ComponentProperty> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/${id}`;
|
|
||||||
return this.apiRequest.put(url, componentProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete component property
|
|
||||||
deleteComponentProperty(id: number): Observable<void> {
|
|
||||||
const url = `${this.componentPropertiesUrl}/${id}`;
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable, forkJoin } from 'rxjs';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { ChartType, UiComponent, ComponentProperty, ChartTemplate, DynamicField } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class DynamicChartLoaderService {
|
|
||||||
private chartTypesUrl = 'api/chart-types';
|
|
||||||
private uiComponentsUrl = 'api/ui-components';
|
|
||||||
private componentPropertiesUrl = 'api/component-properties';
|
|
||||||
private chartTemplatesUrl = 'api/chart-templates';
|
|
||||||
private dynamicFieldsUrl = 'api/dynamic-fields';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load all chart configurations dynamically
|
|
||||||
* This method fetches all chart types and their associated components, templates, and fields
|
|
||||||
*/
|
|
||||||
loadAllChartConfigurations(): Observable<any> {
|
|
||||||
console.log('Loading all chart configurations dynamically');
|
|
||||||
|
|
||||||
// Load all chart types first
|
|
||||||
return this.apiRequest.get(this.chartTypesUrl).pipe(
|
|
||||||
map(chartTypes => {
|
|
||||||
console.log('Loaded chart types:', chartTypes);
|
|
||||||
return chartTypes;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load complete configuration for a specific chart type
|
|
||||||
* This includes UI components, templates, and dynamic fields
|
|
||||||
*/
|
|
||||||
loadChartConfiguration(chartTypeId: number): Observable<{
|
|
||||||
chartType: ChartType,
|
|
||||||
uiComponents: UiComponent[],
|
|
||||||
templates: ChartTemplate[],
|
|
||||||
dynamicFields: DynamicField[]
|
|
||||||
}> {
|
|
||||||
console.log(`Loading complete configuration for chart type ${chartTypeId}`);
|
|
||||||
|
|
||||||
// Load all related data in parallel
|
|
||||||
return forkJoin({
|
|
||||||
chartType: this.apiRequest.get(`${this.chartTypesUrl}/${chartTypeId}`),
|
|
||||||
uiComponents: this.apiRequest.get(`${this.uiComponentsUrl}/chart-type/${chartTypeId}`),
|
|
||||||
templates: this.apiRequest.get(`${this.chartTemplatesUrl}/chart-type/${chartTypeId}`),
|
|
||||||
dynamicFields: this.apiRequest.get(`${this.dynamicFieldsUrl}/chart-type/${chartTypeId}`)
|
|
||||||
}).pipe(
|
|
||||||
map(result => {
|
|
||||||
console.log(`Loaded complete configuration for chart type ${chartTypeId}:`, result);
|
|
||||||
return result;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load chart template for a specific chart type
|
|
||||||
* This is used to render the chart UI dynamically
|
|
||||||
*/
|
|
||||||
loadChartTemplate(chartTypeId: number): Observable<ChartTemplate[]> {
|
|
||||||
console.log(`Loading chart templates for chart type ${chartTypeId}`);
|
|
||||||
return this.apiRequest.get(`${this.chartTemplatesUrl}/chart-type/${chartTypeId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load UI components for a specific chart type
|
|
||||||
* These define what configuration fields are needed for the chart
|
|
||||||
*/
|
|
||||||
loadUiComponents(chartTypeId: number): Observable<UiComponent[]> {
|
|
||||||
console.log(`Loading UI components for chart type ${chartTypeId}`);
|
|
||||||
return this.apiRequest.get(`${this.uiComponentsUrl}/chart-type/${chartTypeId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load dynamic fields for a specific chart type
|
|
||||||
* These define additional dynamic fields that can be used in the chart
|
|
||||||
*/
|
|
||||||
loadDynamicFields(chartTypeId: number): Observable<DynamicField[]> {
|
|
||||||
console.log(`Loading dynamic fields for chart type ${chartTypeId}`);
|
|
||||||
return this.apiRequest.get(`${this.dynamicFieldsUrl}/chart-type/${chartTypeId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get chart type by name
|
|
||||||
* This is useful for finding a chart type by its name rather than ID
|
|
||||||
*/
|
|
||||||
// getChartTypeByName(name: string): Observable<ChartType | null> {
|
|
||||||
// console.log(`Finding chart type by name: ${name}`);
|
|
||||||
// return this.apiRequest.get(`${this.chartTypesUrl}/byname?chartName=${name}`).pipe(
|
|
||||||
// map((chartTypes: ChartType[]) => {
|
|
||||||
// console.log('Available chart types:', chartTypes);
|
|
||||||
// const chartType = chartTypes.find(ct => ct.name === name);
|
|
||||||
// console.log(`Found chart type for name ${name}:`, chartType);
|
|
||||||
// return chartType || null;
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
getChartTypeByName(name: string): Observable<any> {
|
|
||||||
console.log(`Finding chart type by name: ${name}`);
|
|
||||||
return this.apiRequest.get(`${this.chartTypesUrl}/byname?chartName=${name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load all active chart types
|
|
||||||
* This is used to populate the chart selection in the dashboard editor
|
|
||||||
*/
|
|
||||||
loadActiveChartTypes(): Observable<ChartType[]> {
|
|
||||||
console.log('Loading active chart types');
|
|
||||||
return this.apiRequest.get(`${this.chartTypesUrl}`).pipe(
|
|
||||||
map((chartTypes: ChartType[]) => {
|
|
||||||
const activeChartTypes = chartTypes.filter(ct => ct.isActive);
|
|
||||||
console.log('Loaded active chart types:', activeChartTypes);
|
|
||||||
return activeChartTypes;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { DynamicField } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class DynamicFieldService {
|
|
||||||
private dynamicFieldsUrl = 'api/dynamic-fields';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all dynamic fields for a chart type
|
|
||||||
getDynamicFieldsByChartType(chartTypeId: number): Observable<DynamicField[]> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/chart-type/${chartTypeId}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get dynamic field by ID
|
|
||||||
getDynamicFieldById(id: number): Observable<DynamicField> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/${id}`;
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new dynamic field with optional chart type ID as parameter
|
|
||||||
createDynamicField(dynamicField: Partial<DynamicField>, chartTypeId?: number): Observable<DynamicField> {
|
|
||||||
let url = this.dynamicFieldsUrl;
|
|
||||||
if (chartTypeId) {
|
|
||||||
url = `${this.dynamicFieldsUrl}?chartTypeId=${chartTypeId}`;
|
|
||||||
}
|
|
||||||
return this.apiRequest.post(url, dynamicField);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update dynamic field
|
|
||||||
updateDynamicField(id: number, dynamicField: DynamicField): Observable<DynamicField> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/${id}`;
|
|
||||||
return this.apiRequest.put(url, dynamicField);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete dynamic field
|
|
||||||
deleteDynamicField(id: number): Observable<void> {
|
|
||||||
const url = `${this.dynamicFieldsUrl}/${id}`;
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<div class="chart-template-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #chartTemplateForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartTemplate.templateName" name="templateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartTemplate.templateHtml" name="templateHtml" rows="5" placeholder="<div class='chart-container'>...</div>"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartTemplate.templateCss" name="templateCss" rows="5" placeholder=".chart-container { ... }"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartTemplate.isDefault" name="isDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartTemplate.templateName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
.chart-template-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { ChartTemplate } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-template-form',
|
|
||||||
templateUrl: './chart-template-form.component.html',
|
|
||||||
styleUrls: ['./chart-template-form.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTemplateFormComponent {
|
|
||||||
@Input() chartTemplate: Partial<ChartTemplate> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<ChartTemplate>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.chartTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<div class="chart-type-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #chartTypeForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.name" name="chartTypeName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartType.description" name="chartTypeDescription" rows="3"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartType.name">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
.chart-type-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { ChartType } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-form',
|
|
||||||
templateUrl: './chart-type-form.component.html',
|
|
||||||
styleUrls: ['./chart-type-form.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeFormComponent {
|
|
||||||
@Input() chartType: Partial<ChartType> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<ChartType>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.chartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<div class="component-property-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #componentPropertyForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="componentProperty.propertyName" name="propertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="componentProperty.propertyValue" name="propertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-select-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<select clrSelect [(ngModel)]="componentProperty.propertyType" name="propertyType">
|
|
||||||
<option value="">Select a type</option>
|
|
||||||
<option *ngFor="let type of propertyTypes" [value]="type">{{ type | titlecase }}</option>
|
|
||||||
</select>
|
|
||||||
</clr-select-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!componentProperty.propertyName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
.component-property-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { ComponentProperty } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-component-property-form',
|
|
||||||
templateUrl: './component-property-form.component.html',
|
|
||||||
styleUrls: ['./component-property-form.component.scss']
|
|
||||||
})
|
|
||||||
export class ComponentPropertyFormComponent {
|
|
||||||
@Input() componentProperty: Partial<ComponentProperty> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<ComponentProperty>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
propertyTypes = [
|
|
||||||
'string',
|
|
||||||
'number',
|
|
||||||
'boolean',
|
|
||||||
'array',
|
|
||||||
'object',
|
|
||||||
'function'
|
|
||||||
];
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.componentProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<div class="dynamic-field-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #dynamicFieldForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="dynamicField.fieldName" name="fieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="dynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-select-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<select clrSelect [(ngModel)]="dynamicField.fieldType" name="fieldType">
|
|
||||||
<option value="">Select a type</option>
|
|
||||||
<option *ngFor="let type of fieldTypes" [value]="type">{{ type | titlecase }}</option>
|
|
||||||
</select>
|
|
||||||
</clr-select-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options (JSON format)</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="dynamicField.fieldOptions" name="fieldOptions" rows="3" placeholder='{"option1": "Option 1", "option2": "Option 2"}'></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="dynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="dynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="dynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Show in UI</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!dynamicField.fieldName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
.dynamic-field-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
font-family: 'Courier New', monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { DynamicField } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-dynamic-field-form',
|
|
||||||
templateUrl: './dynamic-field-form.component.html',
|
|
||||||
styleUrls: ['./dynamic-field-form.component.scss']
|
|
||||||
})
|
|
||||||
export class DynamicFieldFormComponent {
|
|
||||||
@Input() dynamicField: Partial<DynamicField> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<DynamicField>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
fieldTypes = [
|
|
||||||
'text',
|
|
||||||
'number',
|
|
||||||
'email',
|
|
||||||
'password',
|
|
||||||
'date',
|
|
||||||
'select',
|
|
||||||
'checkbox',
|
|
||||||
'radio',
|
|
||||||
'textarea'
|
|
||||||
];
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.dynamicField);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<div class="ui-component-form">
|
|
||||||
<form clrForm (ngSubmit)="onSubmit()" #uiComponentForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="uiComponent.componentName" name="componentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-select-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<select clrSelect [(ngModel)]="uiComponent.componentType" name="componentType">
|
|
||||||
<option value="">Select a type</option>
|
|
||||||
<option *ngFor="let type of componentTypes" [value]="type">{{ type | titlecase }}</option>
|
|
||||||
</select>
|
|
||||||
</clr-select-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="uiComponent.displayLabel" name="displayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="uiComponent.placeholder" name="placeholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="uiComponent.sortOrder" name="sortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="uiComponent.isRequired" name="isRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!uiComponent.componentName">
|
|
||||||
{{ isEdit ? 'Update' : 'Save' }}
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
.ui-component-form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { UiComponent } from '../chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-ui-component-form',
|
|
||||||
templateUrl: './ui-component-form.component.html',
|
|
||||||
styleUrls: ['./ui-component-form.component.scss']
|
|
||||||
})
|
|
||||||
export class UiComponentFormComponent {
|
|
||||||
@Input() uiComponent: Partial<UiComponent> = {};
|
|
||||||
@Input() isEdit = false;
|
|
||||||
@Output() save = new EventEmitter<Partial<UiComponent>>();
|
|
||||||
@Output() cancel = new EventEmitter<void>();
|
|
||||||
|
|
||||||
componentTypes = [
|
|
||||||
'input',
|
|
||||||
'select',
|
|
||||||
'checkbox',
|
|
||||||
'radio',
|
|
||||||
'textarea',
|
|
||||||
'datepicker',
|
|
||||||
'slider',
|
|
||||||
'toggle'
|
|
||||||
];
|
|
||||||
|
|
||||||
onSubmit(): void {
|
|
||||||
this.save.emit(this.uiComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.cancel.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
import { UiComponent } from './chart-config-manager.component';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class UiComponentService {
|
|
||||||
private uiComponentsUrl = 'api/ui-components';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all UI components for a chart type
|
|
||||||
getUiComponentsByChartType(chartTypeId: number): Observable<UiComponent[]> {
|
|
||||||
const url = `${this.uiComponentsUrl}/chart-type/${chartTypeId}`;
|
|
||||||
console.log(`Fetching UI components for chart type ${chartTypeId} from ${url}`);
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get UI component by ID
|
|
||||||
getUiComponentById(id: number): Observable<UiComponent> {
|
|
||||||
const url = `${this.uiComponentsUrl}/${id}`;
|
|
||||||
console.log(`Fetching UI component ${id} from ${url}`);
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new UI component
|
|
||||||
createUiComponent(uiComponent: Partial<UiComponent>): Observable<UiComponent> {
|
|
||||||
console.log('Creating UI component:', uiComponent);
|
|
||||||
return this.apiRequest.post(this.uiComponentsUrl, uiComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update UI component
|
|
||||||
updateUiComponent(id: number, uiComponent: UiComponent): Observable<UiComponent> {
|
|
||||||
const url = `${this.uiComponentsUrl}/${id}`;
|
|
||||||
console.log(`Updating UI component ${id}:`, uiComponent);
|
|
||||||
return this.apiRequest.put(url, uiComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete UI component
|
|
||||||
deleteUiComponent(id: number): Observable<void> {
|
|
||||||
const url = `${this.uiComponentsUrl}/${id}`;
|
|
||||||
console.log(`Deleting UI component ${id}`);
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<div class="add-chart-type-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>Add New Chart Type</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createChartType()" #addChartTypeForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.name" name="chartTypeName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the chart type (e.g., "line-chart", "bar-chart")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
<clr-control-helper>This is the user-friendly name shown in the UI</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartType.description" name="chartTypeDescription" rows="3"></textarea>
|
|
||||||
<clr-control-helper>Provide a detailed description of this chart type and when to use it</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Deactivate chart types that should not be available for selection</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartType.name || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Chart Type
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Chart Types</h4>
|
|
||||||
<p>Chart types define the different visualization options available in the dashboard builder. Each chart type can have:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Associated UI components that define the configuration form</li>
|
|
||||||
<li>Templates that define how the chart is rendered</li>
|
|
||||||
<li>Dynamic fields that capture specific configuration parameters</li>
|
|
||||||
</ul>
|
|
||||||
<p>After creating a chart type, you can configure its components, templates, and fields in the chart configuration manager.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
.add-chart-type-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-add-chart-type',
|
|
||||||
templateUrl: './add-chart-type.component.html',
|
|
||||||
styleUrls: ['./add-chart-type.component.scss']
|
|
||||||
})
|
|
||||||
export class AddChartTypeComponent {
|
|
||||||
chartType: Partial<ChartType> = {
|
|
||||||
isActive: true
|
|
||||||
};
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartType(): void {
|
|
||||||
if (!this.chartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.createChartType(this.chartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.showSuccess('Chart type created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
// Redirect to chart types list after a short delay
|
|
||||||
setTimeout(() => {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}, 1500);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart type:', error);
|
|
||||||
this.showError('Error creating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
<div class="chart-type-fields-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>
|
|
||||||
<button class="btn btn-link back-button" (click)="goBack()">
|
|
||||||
<cds-icon shape="arrow" direction="left"></cds-icon>
|
|
||||||
</button>
|
|
||||||
Dynamic Fields for {{ chartType?.name || 'Chart Type' }}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Dynamic Field Form -->
|
|
||||||
<div class="card" *ngIf="showAddForm">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Add New Dynamic Field</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createDynamicField()" #addDynamicFieldForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldName" name="fieldName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the dynamic field (e.g., "chart-title", "x-axis-label")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldLabel" name="fieldLabel" />
|
|
||||||
<clr-control-helper>User-friendly label shown in the configuration form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newDynamicField.fieldType" name="fieldType" />
|
|
||||||
<clr-control-helper>Type of the field (e.g., "string", "number", "boolean")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newDynamicField.fieldOptions" name="fieldOptions" rows="3"></textarea>
|
|
||||||
<clr-control-helper>JSON options for the field (for select fields, etc.)</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newDynamicField.sortOrder" name="fieldSortOrder" />
|
|
||||||
<clr-control-helper>Order in which fields appear in the form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.isRequired" name="fieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Mark as required if this field must be filled</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newDynamicField.showInUi" name="fieldShowInUi" />
|
|
||||||
<label>Visible</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Uncheck to hide this field in the configuration form</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newDynamicField.fieldName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Dynamic Field
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dynamic Fields Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Dynamic Fields</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Dynamic Field
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Field Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Field Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Visible</clr-dg-column>
|
|
||||||
<clr-dg-column>Sort Order</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let dynamicField of dynamicFields" [clrDgItem]="dynamicField">
|
|
||||||
<clr-dg-cell>{{dynamicField.fieldName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{dynamicField.fieldLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{dynamicField.fieldType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="dynamicField.isRequired" [class.label-danger]="!dynamicField.isRequired">
|
|
||||||
{{dynamicField.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="dynamicField.showInUi" [class.label-danger]="!dynamicField.showInUi">
|
|
||||||
{{dynamicField.showInUi ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{dynamicField.sortOrder}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectDynamicFieldForEdit(dynamicField)" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteDynamicField(dynamicField.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{dynamicFields.length}} dynamic field(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Dynamic Field Form -->
|
|
||||||
<div class="card" *ngIf="selectedDynamicField">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Edit Dynamic Field</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateDynamicField()" #editDynamicFieldForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldName" name="editFieldName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldLabel" name="editFieldLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Field Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedDynamicField.fieldType" name="editFieldType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Field Options</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedDynamicField.fieldOptions" name="editFieldOptions" rows="3"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedDynamicField.sortOrder" name="editFieldSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.isRequired" name="editFieldIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Show in UI</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedDynamicField.showInUi" name="editFieldShowInUi" />
|
|
||||||
<label>Visible</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedDynamicField.fieldName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Dynamic Field
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedDynamicField = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Dynamic Fields</h4>
|
|
||||||
<p>Dynamic fields define the configurable parameters for a chart type. Each field represents:</p>
|
|
||||||
<ul>
|
|
||||||
<li>A configuration option that can be set when creating or editing a chart</li>
|
|
||||||
<li>Metadata like type, label, and validation rules</li>
|
|
||||||
<li>Visibility and requirement settings</li>
|
|
||||||
</ul>
|
|
||||||
<p>Dynamic fields allow you to create flexible chart configurations that can be customized per instance.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
.chart-type-fields-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
padding: 0;
|
|
||||||
cds-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
import { DynamicField } from '../chart-config/chart-config-manager.component';
|
|
||||||
import { DynamicFieldService } from '../chart-config/dynamic-field.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-fields',
|
|
||||||
templateUrl: './chart-type-fields.component.html',
|
|
||||||
styleUrls: ['./chart-type-fields.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeFieldsComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
dynamicFields: DynamicField[] = [];
|
|
||||||
newDynamicField: Partial<DynamicField> = {
|
|
||||||
isRequired: false,
|
|
||||||
showInUi: true
|
|
||||||
};
|
|
||||||
selectedDynamicField: DynamicField | null = null;
|
|
||||||
showAddForm = false;
|
|
||||||
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private dynamicFieldService: DynamicFieldService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const chartTypeId = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (chartTypeId) {
|
|
||||||
this.loadChartType(chartTypeId);
|
|
||||||
this.loadDynamicFields(chartTypeId);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartType = data;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadDynamicFields(chartTypeId: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.getDynamicFieldsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields = data;
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading dynamic fields:', error);
|
|
||||||
this.showError('Error loading dynamic fields: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createDynamicField(): void {
|
|
||||||
if (!this.chartType || !this.newDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy without the chartType property
|
|
||||||
const fieldData: Partial<DynamicField> = {
|
|
||||||
fieldName: this.newDynamicField.fieldName,
|
|
||||||
fieldLabel: this.newDynamicField.fieldLabel,
|
|
||||||
fieldType: this.newDynamicField.fieldType,
|
|
||||||
fieldOptions: this.newDynamicField.fieldOptions,
|
|
||||||
isRequired: this.newDynamicField.isRequired,
|
|
||||||
showInUi: this.newDynamicField.showInUi,
|
|
||||||
sortOrder: this.newDynamicField.sortOrder
|
|
||||||
};
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.createDynamicField(fieldData, this.chartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.dynamicFields.push(data);
|
|
||||||
this.newDynamicField = { isRequired: false, showInUi: true };
|
|
||||||
this.showAddForm = false;
|
|
||||||
this.showSuccess('Dynamic field created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating dynamic field:', error);
|
|
||||||
this.showError('Error creating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDynamicField(): void {
|
|
||||||
if (!this.selectedDynamicField || !this.selectedDynamicField.fieldName) {
|
|
||||||
this.showError('Field name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Updating dynamic field:', this.selectedDynamicField);
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.updateDynamicField(this.selectedDynamicField.id, this.selectedDynamicField).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.dynamicFields.findIndex(df => df.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.dynamicFields[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedDynamicField = null;
|
|
||||||
this.showSuccess('Dynamic field updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating dynamic field:', error);
|
|
||||||
this.showError('Error updating dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteDynamicField(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this dynamic field?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.dynamicFieldService.deleteDynamicField(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.dynamicFields = this.dynamicFields.filter(df => df.id !== id);
|
|
||||||
this.showSuccess('Dynamic field deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting dynamic field:', error);
|
|
||||||
this.showError('Error deleting dynamic field: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectDynamicFieldForEdit(dynamicField: DynamicField): void {
|
|
||||||
this.selectedDynamicField = { ...dynamicField };
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
if (this.chartType) {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types/edit', this.chartType.id]);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
<div class="chart-type-manager">
|
|
||||||
<h2>Chart Type Management</h2>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Types Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Chart Types</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" [routerLink]="['/cns-portal/dashboardbuilder/chart-types/add']">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Chart Type
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="chartTypeLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Description</clr-dg-column>
|
|
||||||
<clr-dg-column>Status</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartType of chartTypes" [clrDgItem]="chartType">
|
|
||||||
<clr-dg-cell>{{chartType.name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.displayName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.description}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{chartType.isActive ? 'Active' : 'Inactive'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.createdAt ? (chartType.createdAt | date:'short') : 'N/A'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartType.updatedAt ? (chartType.updatedAt | date:'short') : 'N/A'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" [routerLink]="['/cns-portal/dashboardbuilder/chart-types/edit', chartType.id]" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartType(chartType.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTypes.length}} chart type(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
.chart-type-manager {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-manager',
|
|
||||||
templateUrl: './chart-type-manager.component.html',
|
|
||||||
styleUrls: ['./chart-type-manager.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeManagerComponent implements OnInit {
|
|
||||||
chartTypes: ChartType[] = [];
|
|
||||||
chartTypeLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
// Error handling
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
constructor(private chartTypeService: ChartTypeService) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.loadChartTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chart Type Methods
|
|
||||||
loadChartTypes(): void {
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.getAllChartTypes().subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
// Process the data to ensure dates are properly formatted
|
|
||||||
this.chartTypes = data.map(chartType => ({
|
|
||||||
...chartType,
|
|
||||||
createdAt: this.formatDate(chartType.createdAt),
|
|
||||||
updatedAt: this.formatDate(chartType.updatedAt)
|
|
||||||
}));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
this.showError('Error loading chart types: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format date to handle both string and object formats
|
|
||||||
private formatDate(date: any): string {
|
|
||||||
if (!date) return '';
|
|
||||||
|
|
||||||
// If it's already a string, return as is
|
|
||||||
if (typeof date === 'string') {
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's an object, try to convert to string
|
|
||||||
if (typeof date === 'object') {
|
|
||||||
// Handle various date object formats
|
|
||||||
if (date instanceof Date) {
|
|
||||||
return date.toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle ISO string within object
|
|
||||||
if (date.date) {
|
|
||||||
return date.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle other object formats
|
|
||||||
try {
|
|
||||||
return new Date(date).toISOString();
|
|
||||||
} catch {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartType(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart type?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.deleteChartType(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTypes = this.chartTypes.filter(ct => ct.id !== id);
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.chartTypeLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-page',
|
|
||||||
template: `
|
|
||||||
<div class="chart-type-page">
|
|
||||||
<app-chart-type-manager></app-chart-type-manager>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
styles: [`
|
|
||||||
.chart-type-page {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.chart-type-page {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`]
|
|
||||||
})
|
|
||||||
export class ChartTypePageComponent { }
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
<div class="chart-type-templates-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>
|
|
||||||
<button class="btn btn-link back-button" (click)="goBack()">
|
|
||||||
<cds-icon shape="arrow" direction="left"></cds-icon>
|
|
||||||
</button>
|
|
||||||
Chart Templates for {{ chartType?.name || 'Chart Type' }}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Chart Template Form -->
|
|
||||||
<div class="card" *ngIf="showAddForm">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Add New Chart Template</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createChartTemplate()" #addChartTemplateForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newChartTemplate.templateName" name="templateName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the chart template</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateHtml" name="templateHtml" rows="5"></textarea>
|
|
||||||
<clr-control-helper>HTML structure for rendering the chart</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="newChartTemplate.templateCss" name="templateCss" rows="5"></textarea>
|
|
||||||
<clr-control-helper>CSS styling for the chart template</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newChartTemplate.isDefault" name="isDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Mark as default template for this chart type</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newChartTemplate.templateName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Chart Template
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Templates Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Chart Templates</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Chart Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Template Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Default</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let chartTemplate of chartTemplates" [clrDgItem]="chartTemplate">
|
|
||||||
<clr-dg-cell>{{chartTemplate.templateName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="chartTemplate.isDefault" [class.label-danger]="!chartTemplate.isDefault">
|
|
||||||
{{chartTemplate.isDefault ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartTemplate.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{chartTemplate.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectChartTemplateForEdit(chartTemplate)" title="Edit">
|
|
||||||
<cds-icon shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteChartTemplate(chartTemplate.id)" title="Delete">
|
|
||||||
<cds-icon shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{chartTemplates.length}} chart template(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Chart Template Form -->
|
|
||||||
<div class="card" *ngIf="selectedChartTemplate">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Edit Chart Template</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateChartTemplate()" #editChartTemplateForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Template Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedChartTemplate.templateName" name="editTemplateName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>HTML Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateHtml" name="editTemplateHtml" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>CSS Template</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="selectedChartTemplate.templateCss" name="editTemplateCss" rows="5"></textarea>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Default</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedChartTemplate.isDefault" name="editIsDefault" />
|
|
||||||
<label>Default Template</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedChartTemplate.templateName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Chart Template
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedChartTemplate = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Chart Templates</h4>
|
|
||||||
<p>Chart templates define how a chart of this type is rendered. Each template includes:</p>
|
|
||||||
<ul>
|
|
||||||
<li>HTML structure that defines the chart layout</li>
|
|
||||||
<li>CSS styling that controls the appearance</li>
|
|
||||||
<li>A default flag to indicate the primary template</li>
|
|
||||||
</ul>
|
|
||||||
<p>Templates allow you to have multiple visual representations for the same chart type.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
.chart-type-templates-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
padding: 0;
|
|
||||||
cds-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
import { ChartTemplateService } from '../chart-config/chart-template.service';
|
|
||||||
import { ChartTemplate } from '../chart-config/chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-templates',
|
|
||||||
templateUrl: './chart-type-templates.component.html',
|
|
||||||
styleUrls: ['./chart-type-templates.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeTemplatesComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
chartTemplates: ChartTemplate[] = [];
|
|
||||||
newChartTemplate: Partial<ChartTemplate> = {
|
|
||||||
isDefault: false
|
|
||||||
};
|
|
||||||
selectedChartTemplate: ChartTemplate | null = null;
|
|
||||||
showAddForm = false;
|
|
||||||
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private chartTemplateService: ChartTemplateService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const chartTypeId = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (chartTypeId) {
|
|
||||||
this.loadChartType(chartTypeId);
|
|
||||||
this.loadChartTemplates(chartTypeId);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartType = data;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartTemplates(chartTypeId: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.getChartTemplatesByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates = data;
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart templates:', error);
|
|
||||||
this.showError('Error loading chart templates: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createChartTemplate(): void {
|
|
||||||
if (!this.chartType || !this.newChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy without the chartType property
|
|
||||||
const templateData: Partial<ChartTemplate> = {
|
|
||||||
templateName: this.newChartTemplate.templateName,
|
|
||||||
templateHtml: this.newChartTemplate.templateHtml,
|
|
||||||
templateCss: this.newChartTemplate.templateCss,
|
|
||||||
isDefault: this.newChartTemplate.isDefault
|
|
||||||
};
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.createChartTemplate(templateData, this.chartType.id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartTemplates.push(data);
|
|
||||||
this.newChartTemplate = { isDefault: false };
|
|
||||||
this.showAddForm = false;
|
|
||||||
this.showSuccess('Chart template created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating chart template:', error);
|
|
||||||
this.showError('Error creating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartTemplate(): void {
|
|
||||||
if (!this.selectedChartTemplate || !this.selectedChartTemplate.templateName) {
|
|
||||||
this.showError('Template name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.updateChartTemplate(this.selectedChartTemplate.id, this.selectedChartTemplate).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.chartTemplates.findIndex(ct => ct.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.chartTemplates[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedChartTemplate = null;
|
|
||||||
this.showSuccess('Chart template updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart template:', error);
|
|
||||||
this.showError('Error updating chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteChartTemplate(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this chart template?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTemplateService.deleteChartTemplate(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.chartTemplates = this.chartTemplates.filter(ct => ct.id !== id);
|
|
||||||
this.showSuccess('Chart template deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart template:', error);
|
|
||||||
this.showError('Error deleting chart template: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectChartTemplateForEdit(chartTemplate: ChartTemplate): void {
|
|
||||||
this.selectedChartTemplate = { ...chartTemplate };
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
if (this.chartType) {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types/edit', this.chartType.id]);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,312 +0,0 @@
|
|||||||
<div class="chart-type-ui-components-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>
|
|
||||||
<button class="btn btn-link back-button" (click)="goBack()">
|
|
||||||
<cds-icon shape="arrow" direction="left"></cds-icon>
|
|
||||||
</button>
|
|
||||||
UI Components for {{ chartType?.name || 'Chart Type' }}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add UI Component Form -->
|
|
||||||
<div class="card" *ngIf="showAddForm">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Add New UI Component</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="createUiComponent()" #addUiComponentForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentName" name="componentName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the UI component (e.g., "title-config", "axis-config")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.componentType" name="componentType" />
|
|
||||||
<clr-control-helper>Type of the component (e.g., "input", "select", "checkbox")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.displayLabel" name="displayLabel" />
|
|
||||||
<clr-control-helper>User-friendly label shown in the configuration form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newUiComponent.placeholder" name="placeholder" />
|
|
||||||
<clr-control-helper>Placeholder text for input fields</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="newUiComponent.sortOrder" name="sortOrder" />
|
|
||||||
<clr-control-helper>Order in which components appear in the form</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="newUiComponent.isRequired" name="isRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Mark as required if this component must be filled</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newUiComponent.componentName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create UI Component
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- UI Components Grid -->
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>UI Components</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add UI Component
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Component Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Component Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Display Label</clr-dg-column>
|
|
||||||
<clr-dg-column>Required</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let uiComponent of uiComponents" [clrDgItem]="uiComponent">
|
|
||||||
<clr-dg-cell>{{uiComponent.componentName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.componentType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{uiComponent.displayLabel}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<span class="label" [class.label-success]="uiComponent.isRequired" [class.label-danger]="!uiComponent.isRequired">
|
|
||||||
{{uiComponent.isRequired ? 'Yes' : 'No'}}
|
|
||||||
</span>
|
|
||||||
</clr-dg-cell>
|
|
||||||
<clr-dg-cell class="action-cell">
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectUiComponentForEdit(uiComponent)" title="Edit" [disabled]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="loadingState !== ClrLoadingState.LOADING" shape="pencil" aria-label="Edit"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteUiComponent(uiComponent.id)" title="Delete" [disabled]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="loadingState !== ClrLoadingState.LOADING" shape="trash" aria-label="Delete"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon btn-primary" (click)="onViewComponentProperties(uiComponent)" title="View Properties" [disabled]="loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="loadingState !== ClrLoadingState.LOADING" shape="eye" aria-label="View Properties"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{uiComponents.length}} UI component(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit UI Component Form -->
|
|
||||||
<div class="card" *ngIf="selectedUiComponent && !showAddComponentPropertyForm && !selectedComponentProperty">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Edit UI Component</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateUiComponent()" #editUiComponentForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentName" name="editComponentName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Component Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.componentType" name="editComponentType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Label</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.displayLabel" name="editDisplayLabel" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Placeholder</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedUiComponent.placeholder" name="editPlaceholder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Sort Order</label>
|
|
||||||
<input clrInput type="number" [(ngModel)]="selectedUiComponent.sortOrder" name="editSortOrder" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Required</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="selectedUiComponent.isRequired" name="editIsRequired" />
|
|
||||||
<label>Required</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedUiComponent.componentName || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update UI Component
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedUiComponent = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Section -->
|
|
||||||
<div class="card" *ngIf="selectedUiComponent">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col">
|
|
||||||
<h3>Properties for {{selectedUiComponent?.componentName}}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col" style="text-align: right;">
|
|
||||||
<button class="btn btn-primary" (click)="showAddComponentPropertyForm = true">
|
|
||||||
<cds-icon shape="plus"></cds-icon> Add Property
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="showAddComponentPropertyForm">
|
|
||||||
<form clrForm (ngSubmit)="createComponentProperty()" #addComponentPropertyForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyName" name="propertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyValue" name="propertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="newComponentProperty.propertyType" name="propertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!newComponentProperty.propertyName || componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Create Property
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="showAddComponentPropertyForm = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Component Properties Table -->
|
|
||||||
<div class="card-block">
|
|
||||||
<clr-datagrid [clrDgLoading]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-dg-column>Property Name</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Value</clr-dg-column>
|
|
||||||
<clr-dg-column>Property Type</clr-dg-column>
|
|
||||||
<clr-dg-column>Created At</clr-dg-column>
|
|
||||||
<clr-dg-column>Updated At</clr-dg-column>
|
|
||||||
<clr-dg-column>Actions</clr-dg-column>
|
|
||||||
|
|
||||||
<clr-dg-row *clrDgItems="let property of componentProperties" [clrDgItem]="property">
|
|
||||||
<clr-dg-cell>{{property.propertyName}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyValue}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.propertyType}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.createdAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{property.updatedAt | date:'short'}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="selectComponentPropertyForEdit(property)" title="Edit" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="pencil"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-icon" (click)="deleteComponentProperty(property.id)" title="Delete" [disabled]="componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
<cds-icon *ngIf="componentPropertyLoadingState !== ClrLoadingState.LOADING" shape="trash"></cds-icon>
|
|
||||||
</button>
|
|
||||||
</clr-dg-cell>
|
|
||||||
</clr-dg-row>
|
|
||||||
|
|
||||||
<clr-dg-footer>
|
|
||||||
{{componentProperties.length}} propertie(s)
|
|
||||||
</clr-dg-footer>
|
|
||||||
</clr-datagrid>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Edit Component Property Form -->
|
|
||||||
<div class="card-block" *ngIf="selectedComponentProperty">
|
|
||||||
<h4>Edit Component Property</h4>
|
|
||||||
<form clrForm (ngSubmit)="updateComponentProperty()" #editComponentPropertyForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyName" name="editPropertyName" required />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Value</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyValue" name="editPropertyValue" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Property Type</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="selectedComponentProperty.propertyType" name="editPropertyType" />
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!selectedComponentProperty.propertyName || componentPropertyLoadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="componentPropertyLoadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Property
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="selectedComponentProperty = null">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About UI Components</h4>
|
|
||||||
<p>UI components define the configuration form elements for a chart type. Each component represents:</p>
|
|
||||||
<ul>
|
|
||||||
<li>A form field that appears when configuring a chart of this type</li>
|
|
||||||
<li>Metadata like label, placeholder, and validation rules</li>
|
|
||||||
<li>An order in which they appear in the configuration form</li>
|
|
||||||
</ul>
|
|
||||||
<p>After creating UI components, you can define their properties using the "View Properties" button.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
.chart-type-ui-components-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
padding: 0;
|
|
||||||
cds-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.label-success {
|
|
||||||
background-color: #3d9970;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.label-danger {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-datagrid {
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
clr-dg-cell {
|
|
||||||
&:first-child {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Properties Section Styles
|
|
||||||
.action-cell {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons .btn-icon {
|
|
||||||
min-width: 36px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure icons are visible
|
|
||||||
cds-icon {
|
|
||||||
display: inline-block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
clr-spinner {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.clr-row {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.clr-col {
|
|
||||||
text-align: left !important;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
import { UiComponentService } from '../chart-config/ui-component.service';
|
|
||||||
import { ComponentPropertyService } from '../chart-config/component-property.service';
|
|
||||||
import { UiComponent, ComponentProperty } from '../chart-config/chart-config-manager.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-type-ui-components',
|
|
||||||
templateUrl: './chart-type-ui-components.component.html',
|
|
||||||
styleUrls: ['./chart-type-ui-components.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartTypeUiComponentsComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
uiComponents: UiComponent[] = [];
|
|
||||||
newUiComponent: Partial<UiComponent> = {
|
|
||||||
isRequired: false
|
|
||||||
};
|
|
||||||
selectedUiComponent: UiComponent | null = null;
|
|
||||||
showAddForm = false;
|
|
||||||
|
|
||||||
// Component Properties
|
|
||||||
componentProperties: ComponentProperty[] = [];
|
|
||||||
selectedComponentProperty: ComponentProperty | null = null;
|
|
||||||
newComponentProperty: Partial<ComponentProperty> = {};
|
|
||||||
showAddComponentPropertyForm = false;
|
|
||||||
componentPropertyLoadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private uiComponentService: UiComponentService,
|
|
||||||
private componentPropertyService: ComponentPropertyService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const chartTypeId = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (chartTypeId) {
|
|
||||||
this.loadChartType(chartTypeId);
|
|
||||||
this.loadUiComponents(chartTypeId);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.chartType = data;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadUiComponents(chartTypeId: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.getUiComponentsByChartType(chartTypeId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents = data;
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading UI components:', error);
|
|
||||||
this.showError('Error loading UI components: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createUiComponent(): void {
|
|
||||||
if (!this.chartType || !this.newUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete chartType object with only the ID (following the pattern in chart-config-manager)
|
|
||||||
const chartTypeWithId = {
|
|
||||||
id: this.chartType.id,
|
|
||||||
name: '',
|
|
||||||
displayName: '',
|
|
||||||
description: '',
|
|
||||||
isActive: true,
|
|
||||||
createdAt: '',
|
|
||||||
updatedAt: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
const componentData = {
|
|
||||||
...this.newUiComponent,
|
|
||||||
chartType: chartTypeWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.uiComponentService.createUiComponent(componentData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.uiComponents.push(data);
|
|
||||||
this.newUiComponent = { isRequired: false };
|
|
||||||
this.showAddForm = false;
|
|
||||||
this.showSuccess('UI component created successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating UI component:', error);
|
|
||||||
this.showError('Error creating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUiComponent(): void {
|
|
||||||
if (!this.selectedUiComponent || !this.selectedUiComponent.componentName) {
|
|
||||||
this.showError('Component name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.updateUiComponent(this.selectedUiComponent.id, this.selectedUiComponent).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.uiComponents.findIndex(uc => uc.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.uiComponents[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedUiComponent = null;
|
|
||||||
this.showSuccess('UI component updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating UI component:', error);
|
|
||||||
this.showError('Error updating UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUiComponent(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this UI component?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.uiComponentService.deleteUiComponent(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.uiComponents = this.uiComponents.filter(uc => uc.id !== id);
|
|
||||||
this.showSuccess('UI component deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting UI component:', error);
|
|
||||||
this.showError('Error deleting UI component: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectUiComponentForEdit(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = { ...uiComponent };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component Property Methods
|
|
||||||
loadComponentProperties(componentId: number): void {
|
|
||||||
if (!componentId) {
|
|
||||||
this.componentProperties = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.getComponentPropertiesByComponent(componentId).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties = data;
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading component properties:', error);
|
|
||||||
this.showError('Error loading component properties: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
createComponentProperty(): void {
|
|
||||||
if (!this.selectedUiComponent) {
|
|
||||||
this.showError('Please select a UI component first');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.newComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
|
|
||||||
// Create a complete component object with only the ID
|
|
||||||
const componentWithId = {
|
|
||||||
id: this.selectedUiComponent.id
|
|
||||||
} as UiComponent;
|
|
||||||
|
|
||||||
const componentPropertyData = {
|
|
||||||
...this.newComponentProperty,
|
|
||||||
component: componentWithId
|
|
||||||
};
|
|
||||||
|
|
||||||
this.componentPropertyService.createComponentProperty(componentPropertyData).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
this.componentProperties.push(data);
|
|
||||||
this.newComponentProperty = {};
|
|
||||||
this.showAddComponentPropertyForm = false;
|
|
||||||
this.showSuccess('Component property created successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error creating component property:', error);
|
|
||||||
this.showError('Error creating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateComponentProperty(): void {
|
|
||||||
if (!this.selectedComponentProperty || !this.selectedComponentProperty.propertyName) {
|
|
||||||
this.showError('Property name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.updateComponentProperty(this.selectedComponentProperty.id, this.selectedComponentProperty).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
const index = this.componentProperties.findIndex(cp => cp.id === data.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
this.componentProperties[index] = data;
|
|
||||||
}
|
|
||||||
this.selectedComponentProperty = null;
|
|
||||||
this.showSuccess('Component property updated successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating component property:', error);
|
|
||||||
this.showError('Error updating component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComponentProperty(id: number): void {
|
|
||||||
if (!confirm('Are you sure you want to delete this component property?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.LOADING;
|
|
||||||
this.componentPropertyService.deleteComponentProperty(id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.componentProperties = this.componentProperties.filter(cp => cp.id !== id);
|
|
||||||
this.showSuccess('Component property deleted successfully');
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting component property:', error);
|
|
||||||
this.showError('Error deleting component property: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.componentPropertyLoadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectComponentPropertyForEdit(componentProperty: ComponentProperty): void {
|
|
||||||
this.selectedComponentProperty = { ...componentProperty };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to view properties for a UI component
|
|
||||||
onViewComponentProperties(uiComponent: UiComponent): void {
|
|
||||||
this.selectedUiComponent = uiComponent;
|
|
||||||
this.loadComponentProperties(uiComponent.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
if (this.chartType) {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types/edit', this.chartType.id]);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { ApiRequestService } from 'src/app/services/api/api-request.service';
|
|
||||||
|
|
||||||
export interface ChartType {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
isActive: boolean;
|
|
||||||
createdAt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ChartTypeService {
|
|
||||||
private chartTypesUrl = 'api/chart-types';
|
|
||||||
|
|
||||||
constructor(private apiRequest: ApiRequestService) { }
|
|
||||||
|
|
||||||
// Get all chart types
|
|
||||||
getAllChartTypes(): Observable<ChartType[]> {
|
|
||||||
console.log('Fetching all chart types from', this.chartTypesUrl);
|
|
||||||
return this.apiRequest.get(this.chartTypesUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get chart type by ID
|
|
||||||
getChartTypeById(id: number): Observable<ChartType> {
|
|
||||||
const url = `${this.chartTypesUrl}/${id}`;
|
|
||||||
console.log(`Fetching chart type ${id} from ${url}`);
|
|
||||||
return this.apiRequest.get(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new chart type
|
|
||||||
createChartType(chartType: Partial<ChartType>): Observable<ChartType> {
|
|
||||||
console.log('Creating chart type:', chartType);
|
|
||||||
return this.apiRequest.post(this.chartTypesUrl, chartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update chart type
|
|
||||||
updateChartType(id: number, chartType: ChartType): Observable<ChartType> {
|
|
||||||
const url = `${this.chartTypesUrl}/${id}`;
|
|
||||||
console.log(`Updating chart type ${id}:`, chartType);
|
|
||||||
return this.apiRequest.put(url, chartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete chart type
|
|
||||||
deleteChartType(id: number): Observable<void> {
|
|
||||||
const url = `${this.chartTypesUrl}/${id}`;
|
|
||||||
console.log(`Deleting chart type ${id}`);
|
|
||||||
return this.apiRequest.delete(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
<div class="edit-chart-type-page">
|
|
||||||
<div class="header">
|
|
||||||
<h2>Edit Chart Type</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Loading State -->
|
|
||||||
<div *ngIf="loadingState === ClrLoadingState.LOADING" class="loading-spinner">
|
|
||||||
<clr-spinner clrMedium></clr-spinner>
|
|
||||||
<span>Loading chart type...</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Error and Success Messages -->
|
|
||||||
<div class="alert alert-danger" *ngIf="errorMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="errorMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ errorMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-success" *ngIf="successMessage">
|
|
||||||
<button type="button" class="close" aria-label="Close" (click)="successMessage = null">
|
|
||||||
<cds-icon shape="close"></cds-icon>
|
|
||||||
</button>
|
|
||||||
<div class="alert-item">
|
|
||||||
<span class="alert-text">{{ successMessage }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Type Form -->
|
|
||||||
<div class="card" *ngIf="chartType && loadingState !== ClrLoadingState.LOADING">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="chart-type-header">
|
|
||||||
<h3>{{ chartType.name }}</h3>
|
|
||||||
<span class="label" [class.label-success]="chartType.isActive" [class.label-danger]="!chartType.isActive">
|
|
||||||
{{ chartType.isActive ? 'Active' : 'Inactive' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<form clrForm (ngSubmit)="updateChartType()" #editChartTypeForm="ngForm">
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Name <span class="required">*</span></label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.name" name="chartTypeName" required />
|
|
||||||
<clr-control-helper>Enter a unique name for the chart type (e.g., "line-chart", "bar-chart")</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-input-container>
|
|
||||||
<label>Display Name</label>
|
|
||||||
<input clrInput type="text" [(ngModel)]="chartType.displayName" name="chartTypeDisplayName" />
|
|
||||||
<clr-control-helper>This is the user-friendly name shown in the UI</clr-control-helper>
|
|
||||||
</clr-input-container>
|
|
||||||
|
|
||||||
<clr-textarea-container>
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea clrTextarea [(ngModel)]="chartType.description" name="chartTypeDescription" rows="3"></textarea>
|
|
||||||
<clr-control-helper>Provide a detailed description of this chart type and when to use it</clr-control-helper>
|
|
||||||
</clr-textarea-container>
|
|
||||||
|
|
||||||
<clr-checkbox-container>
|
|
||||||
<label>Is Active</label>
|
|
||||||
<clr-checkbox-wrapper>
|
|
||||||
<input type="checkbox" clrCheckbox [(ngModel)]="chartType.isActive" name="chartTypeIsActive" />
|
|
||||||
<label>Active</label>
|
|
||||||
</clr-checkbox-wrapper>
|
|
||||||
<clr-control-helper>Deactivate chart types that should not be available for selection</clr-control-helper>
|
|
||||||
</clr-checkbox-container>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn btn-primary" type="submit" [disabled]="!chartType.name || loadingState === ClrLoadingState.LOADING">
|
|
||||||
<clr-spinner *ngIf="loadingState === ClrLoadingState.LOADING" clrSmall clrInline></clr-spinner>
|
|
||||||
Update Chart Type
|
|
||||||
</button>
|
|
||||||
<button class="btn" type="button" (click)="onCancel()">Cancel</button>
|
|
||||||
<button class="btn btn-danger" type="button" (click)="onDelete()" style="margin-left: auto;">
|
|
||||||
<cds-icon shape="trash"></cds-icon> Delete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Related Entities Management -->
|
|
||||||
<div class="card" *ngIf="chartType && loadingState !== ClrLoadingState.LOADING">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Related Configuration</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<div class="related-entities-grid">
|
|
||||||
<div class="entity-card">
|
|
||||||
<div class="entity-header">
|
|
||||||
<cds-icon shape="view-list" size="24"></cds-icon>
|
|
||||||
<h4>UI Components</h4>
|
|
||||||
</div>
|
|
||||||
<p>Manage the UI components that define the configuration form for this chart type.</p>
|
|
||||||
<button class="btn btn-sm btn-link" [routerLink]="['/cns-portal/dashboardbuilder/chart-types', chartType.id, 'ui-components']">
|
|
||||||
Manage UI Components
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="entity-card">
|
|
||||||
<div class="entity-header">
|
|
||||||
<cds-icon shape="template" size="24"></cds-icon>
|
|
||||||
<h4>Chart Templates</h4>
|
|
||||||
</div>
|
|
||||||
<p>Manage the templates that define how this chart type is rendered.</p>
|
|
||||||
<button class="btn btn-sm btn-link" [routerLink]="['/cns-portal/dashboardbuilder/chart-types', chartType.id, 'templates']">
|
|
||||||
Manage Templates
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="entity-card">
|
|
||||||
<div class="entity-header">
|
|
||||||
<cds-icon shape="form" size="24"></cds-icon>
|
|
||||||
<h4>Dynamic Fields</h4>
|
|
||||||
</div>
|
|
||||||
<p>Manage the dynamic fields that capture specific configuration parameters.</p>
|
|
||||||
<button class="btn btn-sm btn-link" [routerLink]="['/cns-portal/dashboardbuilder/chart-types', chartType.id, 'fields']">
|
|
||||||
Manage Fields
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Chart Type Details -->
|
|
||||||
<div class="card" *ngIf="chartType && loadingState !== ClrLoadingState.LOADING">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3>Chart Type Details</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-block">
|
|
||||||
<div class="details-grid">
|
|
||||||
<div class="detail-item">
|
|
||||||
<label>ID:</label>
|
|
||||||
<span>{{ chartType.id }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<label>Created At:</label>
|
|
||||||
<span>{{ chartType.createdAt ? (chartType.createdAt | date:'medium') : 'N/A' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<label>Updated At:</label>
|
|
||||||
<span>{{ chartType.updatedAt ? (chartType.updatedAt | date:'medium') : 'N/A' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-section">
|
|
||||||
<h4>About Chart Types</h4>
|
|
||||||
<p>Chart types define the different visualization options available in the dashboard builder. Each chart type can have:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Associated UI components that define the configuration form</li>
|
|
||||||
<li>Templates that define how the chart is rendered</li>
|
|
||||||
<li>Dynamic fields that capture specific configuration parameters</li>
|
|
||||||
</ul>
|
|
||||||
<p>After creating a chart type, you can configure its components, templates, and fields using the management links above.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
.edit-chart-type-page {
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
h2 {
|
|
||||||
color: #0079b8;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-spinner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 20px;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
.card-block {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
.chart-type-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related-entities-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
|
|
||||||
.entity-card {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
|
|
||||||
.entity-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
cds-icon {
|
|
||||||
color: #0079b8;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
min-height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-link {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.details-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
||||||
gap: 15px;
|
|
||||||
|
|
||||||
.detail-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
padding: 8px;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
border-left: 4px solid #0079b8;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
margin-top: 0;
|
|
||||||
color: #0079b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.form-actions {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-type-header {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start !important;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.related-entities-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { ClrLoadingState } from '@clr/angular';
|
|
||||||
import { ChartType, ChartTypeService } from './chart-type.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-edit-chart-type',
|
|
||||||
templateUrl: './edit-chart-type.component.html',
|
|
||||||
styleUrls: ['./edit-chart-type.component.scss']
|
|
||||||
})
|
|
||||||
export class EditChartTypeComponent implements OnInit {
|
|
||||||
chartType: ChartType | null = null;
|
|
||||||
loadingState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
||||||
errorMessage: string | null = null;
|
|
||||||
successMessage: string | null = null;
|
|
||||||
|
|
||||||
// Make ClrLoadingState available to template
|
|
||||||
readonly ClrLoadingState = ClrLoadingState;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private chartTypeService: ChartTypeService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
const id = Number(this.route.snapshot.paramMap.get('id'));
|
|
||||||
if (id) {
|
|
||||||
this.loadChartType(id);
|
|
||||||
} else {
|
|
||||||
this.showError('Invalid chart type ID');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
private showError(message: string): void {
|
|
||||||
this.errorMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.errorMessage = null;
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
private showSuccess(message: string): void {
|
|
||||||
this.successMessage = message;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.successMessage = null;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadChartType(id: number): void {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.getChartTypeById(id).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
// Process the data to ensure dates are properly formatted
|
|
||||||
this.chartType = {
|
|
||||||
...data,
|
|
||||||
createdAt: this.formatDate(data.createdAt),
|
|
||||||
updatedAt: this.formatDate(data.updatedAt)
|
|
||||||
};
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
this.showError('Error loading chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format date to handle both string and object formats
|
|
||||||
private formatDate(date: any): string {
|
|
||||||
if (!date) return '';
|
|
||||||
|
|
||||||
// If it's already a string, return as is
|
|
||||||
if (typeof date === 'string') {
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's an object, try to convert to string
|
|
||||||
if (typeof date === 'object') {
|
|
||||||
// Handle various date object formats
|
|
||||||
if (date instanceof Date) {
|
|
||||||
return date.toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle ISO string within object
|
|
||||||
if (date.date) {
|
|
||||||
return date.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle other object formats
|
|
||||||
try {
|
|
||||||
return new Date(date).toISOString();
|
|
||||||
} catch {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
updateChartType(): void {
|
|
||||||
if (!this.chartType || !this.chartType.name) {
|
|
||||||
this.showError('Chart type name is required');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.updateChartType(this.chartType.id, this.chartType).subscribe({
|
|
||||||
next: (data) => {
|
|
||||||
// Process the data to ensure dates are properly formatted
|
|
||||||
this.chartType = {
|
|
||||||
...data,
|
|
||||||
createdAt: this.formatDate(data.createdAt),
|
|
||||||
updatedAt: this.formatDate(data.updatedAt)
|
|
||||||
};
|
|
||||||
this.showSuccess('Chart type updated successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
// Redirect to chart types list after a short delay
|
|
||||||
setTimeout(() => {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}, 1500);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error updating chart type:', error);
|
|
||||||
this.showError('Error updating chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onCancel(): void {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}
|
|
||||||
|
|
||||||
onDelete(): void {
|
|
||||||
if (!this.chartType) return;
|
|
||||||
|
|
||||||
if (confirm('Are you sure you want to delete this chart type? This action cannot be undone.')) {
|
|
||||||
this.loadingState = ClrLoadingState.LOADING;
|
|
||||||
this.chartTypeService.deleteChartType(this.chartType.id).subscribe({
|
|
||||||
next: () => {
|
|
||||||
this.showSuccess('Chart type deleted successfully');
|
|
||||||
this.loadingState = ClrLoadingState.SUCCESS;
|
|
||||||
// Redirect to chart types list after a short delay
|
|
||||||
setTimeout(() => {
|
|
||||||
this.router.navigate(['/cns-portal/dashboardbuilder/chart-types']);
|
|
||||||
}, 1500);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error deleting chart type:', error);
|
|
||||||
this.showError('Error deleting chart type: ' + (error.error?.message || error.message || 'Unknown error'));
|
|
||||||
this.loadingState = ClrLoadingState.ERROR;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<div class="chart-config-modal">
|
|
||||||
<h3>Chart Configuration Manager</h3>
|
|
||||||
<app-chart-config-manager></app-chart-config-manager>
|
|
||||||
</div>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
.chart-config-modal {
|
|
||||||
padding: 20px;
|
|
||||||
max-height: 80vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-chart-config-modal',
|
|
||||||
templateUrl: './chart-config-modal.component.html',
|
|
||||||
styleUrls: ['./chart-config-modal.component.scss']
|
|
||||||
})
|
|
||||||
export class ChartConfigModalComponent {
|
|
||||||
// This component will be used to display the chart configuration manager in a modal
|
|
||||||
}
|
|
||||||
@@ -17,9 +17,6 @@
|
|||||||
<div style="display: inline; float: right;">
|
<div style="display: inline; float: right;">
|
||||||
<!-- <button class="btn btn-primary">Build</button>
|
<!-- <button class="btn btn-primary">Build</button>
|
||||||
<button class="btn btn-primary" (click)="onSchedule()">Schedule</button> -->
|
<button class="btn btn-primary" (click)="onSchedule()">Schedule</button> -->
|
||||||
<button class="btn btn-success" (click)="testDynamicChartCreation()" style="margin-left: 10px;" *ngIf="!fromRunner">
|
|
||||||
<clr-icon shape="test"></clr-icon> Test Dynamic Chart
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ import { CompactFilterComponent } from '../common-filter';
|
|||||||
import { FilterService } from '../common-filter/filter.service';
|
import { FilterService } from '../common-filter/filter.service';
|
||||||
// Add the UnifiedChartComponent import
|
// Add the UnifiedChartComponent import
|
||||||
import { UnifiedChartComponent } from '../gadgets/unified-chart';
|
import { UnifiedChartComponent } from '../gadgets/unified-chart';
|
||||||
// Add the DynamicChartLoaderService import
|
|
||||||
import { DynamicChartLoaderService } from '../chart-config/dynamic-chart-loader.service';
|
|
||||||
|
|
||||||
function isNullArray(arr) {
|
function isNullArray(arr) {
|
||||||
return !Array.isArray(arr) || arr.length === 0;
|
return !Array.isArray(arr) || arr.length === 0;
|
||||||
@@ -60,7 +58,6 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
// Add availableKeys property for compact filter
|
// Add availableKeys property for compact filter
|
||||||
availableKeys: string[] = [];
|
availableKeys: string[] = [];
|
||||||
|
|
||||||
// Initialize with default widgets and update dynamically
|
|
||||||
WidgetsMock: WidgetModel[] = [
|
WidgetsMock: WidgetModel[] = [
|
||||||
{
|
{
|
||||||
name: 'Common Filter',
|
name: 'Common Filter',
|
||||||
@@ -78,26 +75,26 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
name: 'Line Chart',
|
name: 'Line Chart',
|
||||||
identifier: 'line_chart'
|
identifier: 'line_chart'
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// name: 'Bar Chart',
|
name: 'Bar Chart',
|
||||||
// identifier: 'bar_chart'
|
identifier: 'bar_chart'
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// name: 'Pie Chart',
|
name: 'Pie Chart',
|
||||||
// identifier: 'pie_chart'
|
identifier: 'pie_chart'
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// name: 'Polar Area Chart',
|
name: 'Polar Area Chart',
|
||||||
// identifier: 'polar_area_chart'
|
identifier: 'polar_area_chart'
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// name: 'Bubble Chart',
|
name: 'Bubble Chart',
|
||||||
// identifier: 'bubble_chart'
|
identifier: 'bubble_chart'
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// name: 'Scatter Chart',
|
name: 'Scatter Chart',
|
||||||
// identifier: 'scatter_chart'
|
identifier: 'scatter_chart'
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
name: 'Dynamic Chart',
|
name: 'Dynamic Chart',
|
||||||
identifier: 'dynamic_chart'
|
identifier: 'dynamic_chart'
|
||||||
@@ -134,16 +131,16 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
|
|
||||||
protected componentCollection = [
|
protected componentCollection = [
|
||||||
{ name: "Common Filter", componentInstance: CommonFilterComponent },
|
{ name: "Common Filter", componentInstance: CommonFilterComponent },
|
||||||
{ name: "Line Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Line Chart", componentInstance: LineChartComponent },
|
||||||
{ name: "Doughnut Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Doughnut Chart", componentInstance: DoughnutChartComponent },
|
||||||
{ name: "Radar Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Radar Chart", componentInstance: RadarChartComponent },
|
||||||
{ name: "Bar Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Bar Chart", componentInstance: BarChartComponent },
|
||||||
{ name: "Pie Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Pie Chart", componentInstance: PieChartComponent },
|
||||||
{ name: "Polar Area Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Polar Area Chart", componentInstance: PolarChartComponent },
|
||||||
{ name: "Bubble Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Bubble Chart", componentInstance: BubbleChartComponent },
|
||||||
{ name: "Scatter Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Scatter Chart", componentInstance: ScatterChartComponent },
|
||||||
{ name: "Dynamic Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Dynamic Chart", componentInstance: DynamicChartComponent },
|
||||||
{ name: "Financial Chart", componentInstance: UnifiedChartComponent },
|
{ name: "Financial Chart", componentInstance: FinancialChartComponent },
|
||||||
{ name: "To Do Chart", componentInstance: ToDoChartComponent },
|
{ name: "To Do Chart", componentInstance: ToDoChartComponent },
|
||||||
{ name: "Grid View", componentInstance: GridViewComponent },
|
{ name: "Grid View", componentInstance: GridViewComponent },
|
||||||
{ name: "Compact Filter", componentInstance: CompactFilterComponent },
|
{ name: "Compact Filter", componentInstance: CompactFilterComponent },
|
||||||
@@ -209,9 +206,6 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
// Add drilldown column data property
|
// Add drilldown column data property
|
||||||
drilldownColumnData = []; // Add drilldown column data property
|
drilldownColumnData = []; // Add drilldown column data property
|
||||||
|
|
||||||
// Add chart types property for dynamic chart selection
|
|
||||||
chartTypes: any[] = [];
|
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute,
|
constructor(private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private dashboardService: Dashboard3Service,
|
private dashboardService: Dashboard3Service,
|
||||||
@@ -220,8 +214,7 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
private datastoreService: DatastoreService,
|
private datastoreService: DatastoreService,
|
||||||
private alertService: AlertsService,
|
private alertService: AlertsService,
|
||||||
private sureconnectService: SureconnectService,
|
private sureconnectService: SureconnectService,
|
||||||
private filterService: FilterService,
|
private filterService: FilterService) { } // Add SureconnectService and FilterService to constructor
|
||||||
private dynamicChartLoader: DynamicChartLoaderService) { } // Add SureconnectService, FilterService, and DynamicChartLoaderService to constructor
|
|
||||||
|
|
||||||
// Add property to track if coming from dashboard runner
|
// Add property to track if coming from dashboard runner
|
||||||
fromRunner: boolean = false;
|
fromRunner: boolean = false;
|
||||||
@@ -306,9 +299,6 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
apiUrl: ['']
|
apiUrl: ['']
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load chart types for dynamic chart selection
|
|
||||||
this.loadChartTypesForSelection();
|
|
||||||
|
|
||||||
// Load sureconnect data first, then load dashboard data
|
// Load sureconnect data first, then load dashboard data
|
||||||
this.loadSureconnectData();
|
this.loadSureconnectData();
|
||||||
|
|
||||||
@@ -316,37 +306,6 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
this.loadCommonFilterData();
|
this.loadCommonFilterData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add method to load all chart types for dynamic selection
|
|
||||||
loadChartTypesForSelection() {
|
|
||||||
console.log('Loading chart types for selection');
|
|
||||||
this.dynamicChartLoader.loadActiveChartTypes().subscribe({
|
|
||||||
next: (chartTypes) => {
|
|
||||||
console.log('Loaded chart types for selection:', chartTypes);
|
|
||||||
this.chartTypes = chartTypes;
|
|
||||||
|
|
||||||
// Convert each chart type to a WidgetModel
|
|
||||||
const newWidgets = chartTypes.map(ct => ({
|
|
||||||
name: ct.displayName || ct.name,
|
|
||||||
// identifier: ct.name.toLowerCase().replace(/\s+/g, '_')
|
|
||||||
identifier: `${ct.name.toLowerCase().replace(/\s+/g, '_')}_chart`
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Filter out duplicates by identifier
|
|
||||||
const existingIds = new Set(this.WidgetsMock.map(w => w.identifier));
|
|
||||||
const uniqueNewWidgets = newWidgets.filter(w => !existingIds.has(w.identifier));
|
|
||||||
|
|
||||||
// Append unique new widgets to WidgetsMock
|
|
||||||
this.WidgetsMock = [...this.WidgetsMock, ...uniqueNewWidgets];
|
|
||||||
|
|
||||||
console.log('Updated WidgetsMock:', this.WidgetsMock);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types for selection:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Add method to load sureconnect data
|
// Add method to load sureconnect data
|
||||||
loadSureconnectData() {
|
loadSureconnectData() {
|
||||||
this.sureconnectService.getAll().subscribe((data: any[]) => {
|
this.sureconnectService.getAll().subscribe((data: any[]) => {
|
||||||
@@ -434,27 +393,6 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Map chart names to unified chart types
|
|
||||||
const chartTypeMap = {
|
|
||||||
'Radar Chart': 'radar',
|
|
||||||
'Line Chart': 'line',
|
|
||||||
'Doughnut Chart': 'doughnut',
|
|
||||||
'Bar Chart': 'bar',
|
|
||||||
'Pie Chart': 'pie',
|
|
||||||
'Polar Area Chart': 'polar',
|
|
||||||
'Bubble Chart': 'bubble',
|
|
||||||
'Scatter Chart': 'scatter',
|
|
||||||
'Dynamic Chart': 'line',
|
|
||||||
'Financial Chart': 'line'
|
|
||||||
};
|
|
||||||
|
|
||||||
// If this is a chart, set the chartType property
|
|
||||||
if (chartTypeMap.hasOwnProperty(dashboard.name)) {
|
|
||||||
dashboard.chartType = chartTypeMap[dashboard.name];
|
|
||||||
// Keep the original name instead of changing it to "Unified Chart"
|
|
||||||
// dashboard.name = "Unified Chart";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure compact filter configuration properties are properly initialized
|
// Ensure compact filter configuration properties are properly initialized
|
||||||
if (dashboard.component === 'Compact Filter' || dashboard.name === 'Compact Filter') {
|
if (dashboard.component === 'Compact Filter' || dashboard.name === 'Compact Filter') {
|
||||||
// Make sure all compact filter properties exist
|
// Make sure all compact filter properties exist
|
||||||
@@ -479,30 +417,6 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Map unified chart types back to chart names for serialization
|
|
||||||
const chartNameMap = {
|
|
||||||
'radar': 'Radar Chart',
|
|
||||||
'line': 'Line Chart',
|
|
||||||
'doughnut': 'Doughnut Chart',
|
|
||||||
'bar': 'Bar Chart',
|
|
||||||
'pie': 'Pie Chart',
|
|
||||||
'polar': 'Polar Area Chart',
|
|
||||||
'bubble': 'Bubble Chart',
|
|
||||||
'scatter': 'Scatter Chart'
|
|
||||||
// Removed hardcoded heatmap entry to make it fully dynamic
|
|
||||||
};
|
|
||||||
|
|
||||||
// If this is a unified chart, set the name back to the appropriate chart name
|
|
||||||
if (dashboard.name === 'Unified Chart' && dashboard.chartType && chartNameMap.hasOwnProperty(dashboard.chartType)) {
|
|
||||||
dashboard.name = chartNameMap[dashboard.chartType];
|
|
||||||
}
|
|
||||||
// Also handle the case where the chart already has the correct name
|
|
||||||
else if (dashboard.chartType && chartNameMap.hasOwnProperty(dashboard.chartType) &&
|
|
||||||
dashboard.name === chartNameMap[dashboard.chartType]) {
|
|
||||||
// The name is already correct, no need to change it
|
|
||||||
dashboard.component = "Unified Chart";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure compact filter configuration properties are preserved
|
// Ensure compact filter configuration properties are preserved
|
||||||
if (dashboard.name === 'Compact Filter') {
|
if (dashboard.name === 'Compact Filter') {
|
||||||
// Make sure all compact filter properties exist
|
// Make sure all compact filter properties exist
|
||||||
@@ -514,7 +428,6 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add method to get available fields for a filter dropdown (excluding already selected fields)
|
// Add method to get available fields for a filter dropdown (excluding already selected fields)
|
||||||
getAvailableFields(filters: any[], currentIndex: number, allFields: string[]): string[] {
|
getAvailableFields(filters: any[], currentIndex: number, allFields: string[]): string[] {
|
||||||
if (!filters || !allFields) {
|
if (!filters || !allFields) {
|
||||||
@@ -546,11 +459,9 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
//this._ds.updateDashboard(this.dashboardId, parsed).subscribe();
|
//this._ds.updateDashboard(this.dashboardId, parsed).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop = (ev) => {
|
onDrop(ev) {
|
||||||
console.log("on drop event ", ev);
|
|
||||||
const componentType = ev.dataTransfer.getData("widgetIdentifier");
|
const componentType = ev.dataTransfer.getData("widgetIdentifier");
|
||||||
// Safely calculate maxChartId, handling cases where chartid might be NaN or missing
|
// Safely calculate maxChartId, handling cases where chartid might be NaN or missing
|
||||||
console.log("on drop ", componentType);
|
|
||||||
let maxChartId = 0;
|
let maxChartId = 0;
|
||||||
if (this.dashboardArray && this.dashboardArray.length > 0) {
|
if (this.dashboardArray && this.dashboardArray.length > 0) {
|
||||||
const validChartIds = this.dashboardArray
|
const validChartIds = this.dashboardArray
|
||||||
@@ -562,28 +473,106 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (componentType) {
|
switch (componentType) {
|
||||||
// Handle all chart types by converting them to unified charts
|
|
||||||
case "radar_chart":
|
case "radar_chart":
|
||||||
// Use dynamic chart creation for all chart types
|
return this.dashboardArray.push({
|
||||||
return this.createDynamicChart('radar', maxChartId);
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: RadarChartComponent,
|
||||||
|
name: "Radar Chart"
|
||||||
|
});
|
||||||
case "line_chart":
|
case "line_chart":
|
||||||
return this.createDynamicChart('line', maxChartId);
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 7,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: LineChartComponent,
|
||||||
|
name: "Line Chart"
|
||||||
|
});
|
||||||
case "doughnut_chart":
|
case "doughnut_chart":
|
||||||
return this.createDynamicChart('doughnut', maxChartId);
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: DoughnutChartComponent,
|
||||||
|
name: "Doughnut Chart"
|
||||||
|
});
|
||||||
case "bar_chart":
|
case "bar_chart":
|
||||||
return this.createDynamicChart('bar', maxChartId);
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: BarChartComponent,
|
||||||
|
name: "Bar Chart"
|
||||||
|
});
|
||||||
case "pie_chart":
|
case "pie_chart":
|
||||||
return this.createDynamicChart('pie', maxChartId);
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: PieChartComponent,
|
||||||
|
name: "Pie Chart"
|
||||||
|
});
|
||||||
case "polar_area_chart":
|
case "polar_area_chart":
|
||||||
return this.createDynamicChart('polar', maxChartId);
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: PolarChartComponent,
|
||||||
|
name: "Polar Area Chart"
|
||||||
|
});
|
||||||
case "bubble_chart":
|
case "bubble_chart":
|
||||||
return this.createDynamicChart('bubble', maxChartId);
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: BubbleChartComponent,
|
||||||
|
name: "Bubble Chart"
|
||||||
|
});
|
||||||
case "scatter_chart":
|
case "scatter_chart":
|
||||||
return this.createDynamicChart('scatter', maxChartId);
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: ScatterChartComponent,
|
||||||
|
name: "Scatter Chart"
|
||||||
|
});
|
||||||
case "dynamic_chart":
|
case "dynamic_chart":
|
||||||
return this.createDynamicChart('line', maxChartId); // Default to line for dynamic chart
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: DynamicChartComponent,
|
||||||
|
name: "Dynamic Chart"
|
||||||
|
});
|
||||||
case "financial_chart":
|
case "financial_chart":
|
||||||
return this.createDynamicChart('line', maxChartId); // Default to line for financial chart
|
return this.dashboardArray.push({
|
||||||
|
cols: 5,
|
||||||
|
rows: 6,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
chartid: maxChartId + 1,
|
||||||
|
component: FinancialChartComponent,
|
||||||
|
name: "Financial Chart"
|
||||||
|
});
|
||||||
case "to_do_chart":
|
case "to_do_chart":
|
||||||
return this.dashboardArray.push({
|
return this.dashboardArray.push({
|
||||||
cols: 5,
|
cols: 5,
|
||||||
@@ -630,16 +619,21 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
name: "Grid View"
|
name: "Grid View"
|
||||||
});
|
});
|
||||||
case "unified_chart":
|
case "unified_chart":
|
||||||
return this.createDynamicChart('bar', maxChartId); // Default to bar for unified chart
|
return this.dashboardArray.push({
|
||||||
default:
|
cols: 5,
|
||||||
// Handle any other chart types dynamically
|
rows: 6,
|
||||||
// Extract chart type name from identifier (e.g., "heatmap_chart" -> "heatmap")
|
x: 0,
|
||||||
const chartTypeName = componentType.replace('_chart', '');
|
y: 0,
|
||||||
console.log('Creating dynamic chart of type:', chartTypeName);
|
chartid: maxChartId + 1,
|
||||||
console.log('Display name for chart:', this.getChartDisplayName(chartTypeName));
|
component: UnifiedChartComponent,
|
||||||
|
name: "Unified Chart",
|
||||||
// Use dynamic chart creation for all chart types
|
// Add default configuration for unified chart
|
||||||
return this.createDynamicChart(chartTypeName, maxChartId);
|
chartType: 'bar',
|
||||||
|
xAxis: '',
|
||||||
|
yAxis: '',
|
||||||
|
table: '',
|
||||||
|
connection: undefined
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeItem(item) {
|
removeItem(item) {
|
||||||
@@ -1053,10 +1047,7 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For UnifiedChartComponent, pass chart properties with chartType
|
// For UnifiedChartComponent, pass chart properties with chartType
|
||||||
// Check if the component is UnifiedChartComponent dynamically
|
if (item.name === 'Unified Chart') {
|
||||||
if (item.component === UnifiedChartComponent ||
|
|
||||||
(item.component && item.component.name === 'UnifiedChartComponent') ||
|
|
||||||
item.name === 'Unified Chart') {
|
|
||||||
const unifiedChartInputs = {
|
const unifiedChartInputs = {
|
||||||
chartType: item.chartType || 'bar',
|
chartType: item.chartType || 'bar',
|
||||||
xAxis: item.xAxis,
|
xAxis: item.xAxis,
|
||||||
@@ -1951,266 +1942,4 @@ export class EditnewdashComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add method to apply dynamic template to a chart
|
|
||||||
applyDynamicTemplate(chartItem: any, template: any) {
|
|
||||||
console.log('Applying dynamic template to chart:', chartItem, template);
|
|
||||||
|
|
||||||
// Apply HTML template
|
|
||||||
if (template.templateHtml) {
|
|
||||||
// In a real implementation, you would dynamically render the HTML template
|
|
||||||
// For now, we'll just log it
|
|
||||||
console.log('HTML Template:', template.templateHtml);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply CSS styles
|
|
||||||
if (template.templateCss) {
|
|
||||||
// In a real implementation, you would dynamically apply the CSS styles
|
|
||||||
// For now, we'll just log it
|
|
||||||
console.log('CSS Template:', template.templateCss);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the chart item with template applied
|
|
||||||
return {
|
|
||||||
...chartItem,
|
|
||||||
template: template
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add method to test dynamic chart creation
|
|
||||||
testDynamicChartCreation() {
|
|
||||||
console.log('Testing dynamic chart creation');
|
|
||||||
|
|
||||||
// Show a success message to the user
|
|
||||||
alert('Dynamic chart test started. Check the browser console for detailed output.');
|
|
||||||
|
|
||||||
// Load all chart types
|
|
||||||
this.dynamicChartLoader.loadAllChartConfigurations().subscribe({
|
|
||||||
next: (chartTypes) => {
|
|
||||||
console.log('Loaded chart types:', chartTypes);
|
|
||||||
|
|
||||||
// Find bar chart type
|
|
||||||
const barChartType = chartTypes.find((ct: any) => ct.name === 'bar');
|
|
||||||
if (barChartType) {
|
|
||||||
console.log('Found bar chart type:', barChartType);
|
|
||||||
|
|
||||||
// Load configuration for bar chart
|
|
||||||
this.dynamicChartLoader.loadChartConfiguration(barChartType.id).subscribe({
|
|
||||||
next: (config) => {
|
|
||||||
console.log('Loaded bar chart configuration:', config);
|
|
||||||
|
|
||||||
// Create a test chart item
|
|
||||||
const chartItem = {
|
|
||||||
cols: 5,
|
|
||||||
rows: 6,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
chartid: 100,
|
|
||||||
component: UnifiedChartComponent,
|
|
||||||
name: 'Test Dynamic Bar Chart',
|
|
||||||
chartType: 'bar',
|
|
||||||
xAxis: '',
|
|
||||||
yAxis: '',
|
|
||||||
table: '',
|
|
||||||
connection: undefined,
|
|
||||||
// Add dynamic fields from configuration
|
|
||||||
dynamicFields: config.dynamicFields || []
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('Created test chart item:', chartItem);
|
|
||||||
|
|
||||||
// If we have templates, apply the default one
|
|
||||||
if (config.templates && config.templates.length > 0) {
|
|
||||||
const defaultTemplate = config.templates.find((t: any) => t.isDefault) || config.templates[0];
|
|
||||||
if (defaultTemplate) {
|
|
||||||
console.log('Applying default template:', defaultTemplate);
|
|
||||||
const chartWithTemplate = this.applyDynamicTemplate(chartItem, defaultTemplate);
|
|
||||||
console.log('Chart with template:', chartWithTemplate);
|
|
||||||
|
|
||||||
// Show success message
|
|
||||||
alert('Dynamic chart test completed successfully! Check console for details.');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Show success message even without templates
|
|
||||||
alert('Dynamic chart test completed successfully! No templates found. Check console for details.');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading bar chart configuration:', error);
|
|
||||||
alert('Error loading bar chart configuration. Check console for details.');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.warn('Bar chart type not found');
|
|
||||||
alert('Bar chart type not found in the database.');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
alert('Error loading chart types. Check console for details.');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add method to load dynamic chart configuration
|
|
||||||
loadDynamicChartConfiguration(chartTypeId: number) {
|
|
||||||
console.log(`Loading dynamic chart configuration for chart type ${chartTypeId}`);
|
|
||||||
this.dynamicChartLoader.loadChartConfiguration(chartTypeId).subscribe({
|
|
||||||
next: (config) => {
|
|
||||||
console.log('Loaded dynamic chart configuration:', config);
|
|
||||||
// Here you would apply the configuration to the UI
|
|
||||||
// For example, populate form fields, set up templates, etc.
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading dynamic chart configuration:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add method to create a dynamic chart with configuration from database
|
|
||||||
createDynamicChart = (chartTypeName: string, maxChartId: number) => {
|
|
||||||
console.log(`Creating dynamic chart of type: ${chartTypeName}`);
|
|
||||||
|
|
||||||
// First, get the chart type by name
|
|
||||||
this.dynamicChartLoader.getChartTypeByName(chartTypeName).subscribe({
|
|
||||||
next: (chartType) => {
|
|
||||||
if (chartType) {
|
|
||||||
console.log(`Found chart type:`, chartType);
|
|
||||||
|
|
||||||
// Load the complete configuration for this chart type
|
|
||||||
this.dynamicChartLoader.loadChartConfiguration(chartType.id).subscribe({
|
|
||||||
next: (config) => {
|
|
||||||
console.log(`Loaded configuration for ${chartTypeName}:`, config);
|
|
||||||
|
|
||||||
// Create the chart item with dynamic configuration
|
|
||||||
const chartItem = {
|
|
||||||
cols: 5,
|
|
||||||
rows: 6,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
chartid: maxChartId + 1,
|
|
||||||
component: UnifiedChartComponent,
|
|
||||||
name: chartType.displayName || chartTypeName,
|
|
||||||
chartType: chartType.name,
|
|
||||||
xAxis: '',
|
|
||||||
yAxis: '',
|
|
||||||
table: '',
|
|
||||||
connection: undefined,
|
|
||||||
// Add any dynamic fields from the configuration
|
|
||||||
dynamicFields: config.dynamicFields || []
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add UI components as configuration properties
|
|
||||||
if (config.uiComponents && config.uiComponents.length > 0) {
|
|
||||||
config.uiComponents.forEach(component => {
|
|
||||||
chartItem[component.componentName] = '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dashboardArray.push(chartItem);
|
|
||||||
console.log(`Created dynamic chart:`, chartItem);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error(`Error loading configuration for ${chartTypeName}:`, error);
|
|
||||||
// Fallback to default configuration
|
|
||||||
this.createDefaultChart(chartTypeName, this.getChartDisplayName(chartTypeName));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.warn(`Chart type ${chartTypeName} not found, using default configuration`);
|
|
||||||
this.createDefaultChart(chartTypeName, this.getChartDisplayName(chartTypeName));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading configuration for chart type:', error);
|
|
||||||
// Fallback to default configuration
|
|
||||||
this.createDefaultChart(chartTypeName, this.getChartDisplayName(chartTypeName));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback method to create default chart configuration
|
|
||||||
createDefaultChart = (chartTypeName: string, chartDisplayName: string) => {
|
|
||||||
console.log(`Creating default chart for ${chartTypeName}`);
|
|
||||||
|
|
||||||
// Map chart type names to chart types - making it fully dynamic
|
|
||||||
const chartTypeMap = {
|
|
||||||
'bar': 'bar',
|
|
||||||
'line': 'line',
|
|
||||||
'pie': 'pie',
|
|
||||||
'doughnut': 'doughnut',
|
|
||||||
'radar': 'radar',
|
|
||||||
'polar': 'polar',
|
|
||||||
'bubble': 'bubble',
|
|
||||||
'scatter': 'scatter'
|
|
||||||
// Removed hardcoded heatmap entry to make it fully dynamic
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the chart type from the name - default to bubble for unknown chart types
|
|
||||||
const chartType = chartTypeMap[chartTypeName.toLowerCase()] || 'bubble';
|
|
||||||
|
|
||||||
// Safely calculate maxChartId, handling cases where chartid might be NaN or missing
|
|
||||||
let maxChartId = 0;
|
|
||||||
if (this.dashboardArray && this.dashboardArray.length > 0) {
|
|
||||||
const validChartIds = this.dashboardArray
|
|
||||||
.map(item => item.chartid)
|
|
||||||
.filter(chartid => typeof chartid === 'number' && !isNaN(chartid));
|
|
||||||
|
|
||||||
if (validChartIds.length > 0) {
|
|
||||||
maxChartId = Math.max(...validChartIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartItem = {
|
|
||||||
cols: 5,
|
|
||||||
rows: 6,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
chartid: maxChartId + 1,
|
|
||||||
component: UnifiedChartComponent,
|
|
||||||
name: chartDisplayName,
|
|
||||||
chartType: chartType,
|
|
||||||
xAxis: '',
|
|
||||||
yAxis: '',
|
|
||||||
table: '',
|
|
||||||
connection: undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
this.dashboardArray.push(chartItem);
|
|
||||||
console.log('Created default chart:', chartItem);
|
|
||||||
|
|
||||||
// Update the dashboard collection
|
|
||||||
this.dashboardCollection.dashboard = this.dashboardArray.slice();
|
|
||||||
|
|
||||||
// Force gridster to refresh
|
|
||||||
if (this.options && this.options.api) {
|
|
||||||
this.options.api.optionsChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to get display name for chart type - making it fully dynamic
|
|
||||||
getChartDisplayName = (chartTypeName: string): string => {
|
|
||||||
const displayNameMap = {
|
|
||||||
'bar': 'Bar Chart',
|
|
||||||
'line': 'Line Chart',
|
|
||||||
'pie': 'Pie Chart',
|
|
||||||
'doughnut': 'Doughnut Chart',
|
|
||||||
'radar': 'Radar Chart',
|
|
||||||
'polar': 'Polar Area Chart',
|
|
||||||
'bubble': 'Bubble Chart',
|
|
||||||
'scatter': 'Scatter Chart'
|
|
||||||
// Removed hardcoded heatmap entry to make it fully dynamic
|
|
||||||
};
|
|
||||||
|
|
||||||
// For unknown chart types, create a display name by capitalizing the first letter and adding ' Chart'
|
|
||||||
const displayName = displayNameMap[chartTypeName.toLowerCase()];
|
|
||||||
if (displayName) {
|
|
||||||
return displayName;
|
|
||||||
} else {
|
|
||||||
// Capitalize first letter and add ' Chart'
|
|
||||||
return chartTypeName.charAt(0).toUpperCase() + chartTypeName.slice(1) + ' Chart';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,36 +13,114 @@
|
|||||||
<h4>{{ charttitle }}</h4>
|
<h4>{{ charttitle }}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filter toggle icon -->
|
|
||||||
<div class="filter-toggle-icon" *ngIf="hasFilters()" (click)="toggleFilters()">
|
|
||||||
<clr-icon shape="filter" size="24"
|
|
||||||
[style.color]="showFilters ? '#007cba' : '#666'"
|
|
||||||
title="Toggle Filters">
|
|
||||||
</clr-icon>
|
|
||||||
<span>
|
|
||||||
{{ showFilters ? 'Hide Filters' : 'Show Filters' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Render different chart types based on chartType input -->
|
<!-- Render different chart types based on chartType input -->
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<!-- Dynamic Chart Container - uses extracted dynamic options and styles but static template -->
|
<!-- Bar Chart -->
|
||||||
<div *ngIf="dynamicTemplate || chartType" class="chart-canvas-container">
|
<div *ngIf="chartType === 'bar'">
|
||||||
<canvas baseChart
|
<canvas baseChart
|
||||||
[datasets]="chartData"
|
[data]="chartData"
|
||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
[chartType]="chartType || 'bar'"
|
[chartType]="'bar'"
|
||||||
(chartClick)="chartClicked($event)"
|
(chartClick)="chartClicked($event)"
|
||||||
(chartHover)="chartHovered($event)">
|
(chartHover)="chartHovered($event)">
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fallback for when no chart type is specified -->
|
<!-- Line Chart -->
|
||||||
<div *ngIf="!dynamicTemplate && !chartType" class="chart-canvas-container">
|
<div *ngIf="chartType === 'line'">
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="chartData"
|
||||||
|
[labels]="chartLabels"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="'line'"
|
||||||
|
(chartClick)="chartClicked($event)"
|
||||||
|
(chartHover)="chartHovered($event)">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pie Chart -->
|
||||||
|
<div *ngIf="chartType === 'pie'">
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="chartData"
|
||||||
|
[labels]="chartLabels"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="'pie'"
|
||||||
|
(chartClick)="chartClicked($event)"
|
||||||
|
(chartHover)="chartHovered($event)">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Doughnut Chart -->
|
||||||
|
<div *ngIf="chartType === 'doughnut'">
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="chartData"
|
||||||
|
[labels]="chartLabels"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="'doughnut'"
|
||||||
|
(chartClick)="chartClicked($event)"
|
||||||
|
(chartHover)="chartHovered($event)">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bubble Chart -->
|
||||||
|
<div *ngIf="chartType === 'bubble'">
|
||||||
|
<canvas baseChart
|
||||||
|
[datasets]="bubbleChartData"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="'bubble'"
|
||||||
|
(chartClick)="chartClicked($event)"
|
||||||
|
(chartHover)="chartHovered($event)">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Radar Chart -->
|
||||||
|
<div *ngIf="chartType === 'radar'">
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="chartData"
|
||||||
|
[labels]="chartLabels"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="'radar'"
|
||||||
|
(chartClick)="chartClicked($event)"
|
||||||
|
(chartHover)="chartHovered($event)">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Polar Area Chart -->
|
||||||
|
<div *ngIf="chartType === 'polar'">
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="chartData"
|
||||||
|
[labels]="chartLabels"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="'polarArea'"
|
||||||
|
(chartClick)="chartClicked($event)"
|
||||||
|
(chartHover)="chartHovered($event)">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scatter Chart -->
|
||||||
|
<div *ngIf="chartType === 'scatter'">
|
||||||
<canvas baseChart
|
<canvas baseChart
|
||||||
[datasets]="chartData"
|
[datasets]="chartData"
|
||||||
|
[options]="chartOptions"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="'scatter'"
|
||||||
|
(chartClick)="chartClicked($event)"
|
||||||
|
(chartHover)="chartHovered($event)">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Default/Unknown Chart Type -->
|
||||||
|
<div *ngIf="!['bar', 'line', 'pie', 'doughnut', 'bubble', 'radar', 'polar', 'scatter'].includes(chartType)">
|
||||||
|
<canvas baseChart
|
||||||
|
[data]="chartData"
|
||||||
[labels]="chartLabels"
|
[labels]="chartLabels"
|
||||||
[options]="chartOptions"
|
[options]="chartOptions"
|
||||||
[legend]="chartLegend"
|
[legend]="chartLegend"
|
||||||
@@ -53,23 +131,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Collapsible Base Filters -->
|
<!-- Base Filters -->
|
||||||
<div class="filters-section" *ngIf="baseFilters && baseFilters.length > 0 && showFilters">
|
<div class="filters-section" *ngIf="baseFilters && baseFilters.length > 0">
|
||||||
<h5>Filters</h5>
|
<h5>Filters</h5>
|
||||||
<div class="filters-container">
|
<div class="filters-container">
|
||||||
<div class="filter-item" *ngFor="let filter of baseFilters; let i = index">
|
<div class="filter-item" *ngFor="let filter of baseFilters; let i = index">
|
||||||
<!-- Text Filter -->
|
<!-- Text Filter -->
|
||||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
<div *ngIf="filter.type === 'text'" class="filter-text">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)"
|
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)"
|
||||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
class="form-control" placeholder="Enter {{ filter.field }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dropdown Filter -->
|
<!-- Dropdown Filter -->
|
||||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<select [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)" class="form-control">
|
<select [(ngModel)]="filter.value" (ngModelChange)="onBaseFilterChange(filter)" class="form-control">
|
||||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
<option value="">Select {{ filter.field }}</option>
|
||||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||||
{{ option }}
|
{{ option }}
|
||||||
</option>
|
</option>
|
||||||
@@ -78,15 +156,12 @@
|
|||||||
|
|
||||||
<!-- Multiselect Filter -->
|
<!-- Multiselect Filter -->
|
||||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="multiselect-container">
|
<div class="multiselect-container">
|
||||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'base')">
|
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'base')">
|
||||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
|
||||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
<span *ngIf="getSelectedOptionsCount(filter) > 0">
|
||||||
{{ filter.value }}
|
{{ getSelectedOptionsCount(filter) }} selected
|
||||||
</span>
|
|
||||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
|
||||||
{{ filter.value.length }} selected
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'base')">
|
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'base')">
|
||||||
@@ -103,18 +178,18 @@
|
|||||||
|
|
||||||
<!-- Date Range Filter -->
|
<!-- Date Range Filter -->
|
||||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="date-range-inputs">
|
<div class="date-range-inputs">
|
||||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||||
class="form-control" placeholder="Start Date">
|
class="form-control" placeholder="Start Date">
|
||||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||||
class="form-control" placeholder="End Date">
|
class="form-control" placeholder="End Date">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Toggle Filter -->
|
<!-- Toggle Filter -->
|
||||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="toggle-switch">
|
<div class="toggle-switch">
|
||||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||||
id="toggle-{{ filter.field }}">
|
id="toggle-{{ filter.field }}">
|
||||||
@@ -128,22 +203,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Drilldown Filters -->
|
<!-- Drilldown Filters -->
|
||||||
<div class="filters-section" *ngIf="drilldownFilters && drilldownFilters.length > 0 && currentDrilldownLevel > 0 && showFilters">
|
<div class="filters-section" *ngIf="drilldownFilters && drilldownFilters.length > 0 && currentDrilldownLevel > 0">
|
||||||
<h5>Drilldown Filters</h5>
|
<h5>Drilldown Filters</h5>
|
||||||
<div class="filters-container">
|
<div class="filters-container">
|
||||||
<div class="filter-item" *ngFor="let filter of drilldownFilters; let i = index">
|
<div class="filter-item" *ngFor="let filter of drilldownFilters; let i = index">
|
||||||
<!-- Text Filter -->
|
<!-- Text Filter -->
|
||||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
<div *ngIf="filter.type === 'text'" class="filter-text">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)"
|
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)"
|
||||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
class="form-control" placeholder="Enter {{ filter.field }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dropdown Filter -->
|
<!-- Dropdown Filter -->
|
||||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<select [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)" class="form-control">
|
<select [(ngModel)]="filter.value" (ngModelChange)="onDrilldownFilterChange(filter)" class="form-control">
|
||||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
<option value="">Select {{ filter.field }}</option>
|
||||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||||
{{ option }}
|
{{ option }}
|
||||||
</option>
|
</option>
|
||||||
@@ -152,15 +227,12 @@
|
|||||||
|
|
||||||
<!-- Multiselect Filter -->
|
<!-- Multiselect Filter -->
|
||||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="multiselect-container">
|
<div class="multiselect-container">
|
||||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'drilldown')">
|
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'drilldown')">
|
||||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
|
||||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
<span *ngIf="getSelectedOptionsCount(filter) > 0">
|
||||||
{{ filter.value }}
|
{{ getSelectedOptionsCount(filter) }} selected
|
||||||
</span>
|
|
||||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
|
||||||
{{ filter.value.length }} selected
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'drilldown')">
|
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'drilldown')">
|
||||||
@@ -177,18 +249,18 @@
|
|||||||
|
|
||||||
<!-- Date Range Filter -->
|
<!-- Date Range Filter -->
|
||||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="date-range-inputs">
|
<div class="date-range-inputs">
|
||||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||||
class="form-control" placeholder="Start Date">
|
class="form-control" placeholder="Start Date">
|
||||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||||
class="form-control" placeholder="End Date">
|
class="form-control" placeholder="End Date">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Toggle Filter -->
|
<!-- Toggle Filter -->
|
||||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="toggle-switch">
|
<div class="toggle-switch">
|
||||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||||
id="drilldown-toggle-{{ filter.field }}">
|
id="drilldown-toggle-{{ filter.field }}">
|
||||||
@@ -202,22 +274,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Layer Filters -->
|
<!-- Layer Filters -->
|
||||||
<div class="filters-section" *ngIf="hasActiveLayerFilters() && showFilters">
|
<div class="filters-section" *ngIf="hasActiveLayerFilters()">
|
||||||
<h5>Layer Filters</h5>
|
<h5>Layer Filters</h5>
|
||||||
<div class="filters-container">
|
<div class="filters-container">
|
||||||
<div class="filter-item" *ngFor="let filter of getActiveLayerFilters(); let i = index">
|
<div class="filter-item" *ngFor="let filter of getActiveLayerFilters(); let i = index">
|
||||||
<!-- Text Filter -->
|
<!-- Text Filter -->
|
||||||
<div *ngIf="!filter.type || filter.type === 'text'" class="filter-text">
|
<div *ngIf="filter.type === 'text'" class="filter-text">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)"
|
<input type="text" [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)"
|
||||||
class="form-control" placeholder="Enter {{ filter.field || 'value' }}">
|
class="form-control" placeholder="Enter {{ filter.field }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dropdown Filter -->
|
<!-- Dropdown Filter -->
|
||||||
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
<div *ngIf="filter.type === 'dropdown'" class="filter-dropdown">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<select [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)" class="form-control">
|
<select [(ngModel)]="filter.value" (ngModelChange)="onLayerFilterChange(filter)" class="form-control">
|
||||||
<option value="">Select {{ filter.field || 'value' }}</option>
|
<option value="">Select {{ filter.field }}</option>
|
||||||
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
<option *ngFor="let option of getFilterOptions(filter)" [value]="option">
|
||||||
{{ option }}
|
{{ option }}
|
||||||
</option>
|
</option>
|
||||||
@@ -226,15 +298,12 @@
|
|||||||
|
|
||||||
<!-- Multiselect Filter -->
|
<!-- Multiselect Filter -->
|
||||||
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
<div *ngIf="filter.type === 'multiselect'" class="filter-multiselect">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="multiselect-container">
|
<div class="multiselect-container">
|
||||||
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'layer')">
|
<div class="multiselect-display" (click)="toggleMultiselect(filter, 'layer')">
|
||||||
<span *ngIf="!filter.value || (Array.isArray(filter.value) && filter.value.length === 0)">Select {{ filter.field || 'options' }}</span>
|
<span *ngIf="getSelectedOptionsCount(filter) === 0">Select {{ filter.field }}</span>
|
||||||
<span *ngIf="filter.value && !Array.isArray(filter.value)">
|
<span *ngIf="getSelectedOptionsCount(filter) > 0">
|
||||||
{{ filter.value }}
|
{{ getSelectedOptionsCount(filter) }} selected
|
||||||
</span>
|
|
||||||
<span *ngIf="filter.value && Array.isArray(filter.value) && filter.value.length > 0">
|
|
||||||
{{ filter.value.length }} selected
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'layer')">
|
<div class="multiselect-dropdown" *ngIf="isMultiselectOpen(filter, 'layer')">
|
||||||
@@ -251,18 +320,18 @@
|
|||||||
|
|
||||||
<!-- Date Range Filter -->
|
<!-- Date Range Filter -->
|
||||||
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
<div *ngIf="filter.type === 'date-range'" class="filter-date-range">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="date-range-inputs">
|
<div class="date-range-inputs">
|
||||||
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeInputChange(filter, 'start', $event)"
|
<input type="date" [(ngModel)]="filter.value.start" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||||
class="form-control" placeholder="Start Date">
|
class="form-control" placeholder="Start Date">
|
||||||
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeInputChange(filter, 'end', $event)"
|
<input type="date" [(ngModel)]="filter.value.end" (ngModelChange)="onDateRangeChange(filter, filter.value)"
|
||||||
class="form-control" placeholder="End Date">
|
class="form-control" placeholder="End Date">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Toggle Filter -->
|
<!-- Toggle Filter -->
|
||||||
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
<div *ngIf="filter.type === 'toggle'" class="filter-toggle">
|
||||||
<label>{{ filter.field || 'Filter ' + (i + 1) }}</label>
|
<label>{{ filter.field }}</label>
|
||||||
<div class="toggle-switch">
|
<div class="toggle-switch">
|
||||||
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
<input type="checkbox" [(ngModel)]="filter.value" (ngModelChange)="onToggleChange(filter, $event.target.checked)"
|
||||||
id="layer-toggle-{{ filter.field }}">
|
id="layer-toggle-{{ filter.field }}">
|
||||||
@@ -276,7 +345,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Clear Filters Button -->
|
<!-- Clear Filters Button -->
|
||||||
<div class="clear-filters" *ngIf="hasActiveFilters() && showFilters">
|
<div class="clear-filters" *ngIf="hasActiveFilters()">
|
||||||
<button class="btn btn-sm btn-outline" (click)="clearAllFilters()">
|
<button class="btn btn-sm btn-outline" (click)="clearAllFilters()">
|
||||||
Clear All Filters
|
Clear All Filters
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -30,49 +30,7 @@
|
|||||||
.chart-wrapper {
|
.chart-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: calc(100% - 100px);
|
height: calc(100% - 100px);
|
||||||
min-height: 400px;
|
min-height: 300px;
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-canvas-container {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-toggle-icon {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
padding: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
clr-icon {
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #007cba !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
margin-left: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover span {
|
|
||||||
color: #007cba;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters-section {
|
.filters-section {
|
||||||
@@ -288,6 +246,7 @@
|
|||||||
100% { transform: rotate(360deg); }
|
100% { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.filters-container {
|
.filters-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -300,8 +259,4 @@
|
|||||||
.chart-wrapper {
|
.chart-wrapper {
|
||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-canvas-container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges, ViewChild, ElementRef, Renderer2 } from '@angular/core';
|
import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges, ViewChild } from '@angular/core';
|
||||||
import { Dashboard3Service } from '../../../../../../services/builder/dashboard3.service';
|
import { Dashboard3Service } from '../../../../../../services/builder/dashboard3.service';
|
||||||
import { FilterService } from '../../common-filter/filter.service';
|
import { FilterService } from '../../common-filter/filter.service';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { BaseChartDirective } from 'ng2-charts';
|
import { BaseChartDirective } from 'ng2-charts';
|
||||||
import { ChartConfiguration, ChartDataset } from 'chart.js';
|
import { ChartConfiguration, ChartDataset } from 'chart.js';
|
||||||
import { DynamicChartLoaderService } from '../../chart-config/dynamic-chart-loader.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-unified-chart',
|
selector: 'app-unified-chart',
|
||||||
@@ -62,9 +61,6 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
currentDrilldownLevel: number = 0;
|
currentDrilldownLevel: number = 0;
|
||||||
originalChartData: any = {};
|
originalChartData: any = {};
|
||||||
|
|
||||||
// Filter visibility toggle
|
|
||||||
showFilters: boolean = false;
|
|
||||||
|
|
||||||
// Flag to prevent infinite loops
|
// Flag to prevent infinite loops
|
||||||
private isFetchingData: boolean = false;
|
private isFetchingData: boolean = false;
|
||||||
|
|
||||||
@@ -76,151 +72,9 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
private documentClickHandler: ((event: MouseEvent) => void) | null = null;
|
private documentClickHandler: ((event: MouseEvent) => void) | null = null;
|
||||||
private filtersInitialized: boolean = false;
|
private filtersInitialized: boolean = false;
|
||||||
|
|
||||||
// Dynamic template properties
|
|
||||||
dynamicTemplate: string = '';
|
|
||||||
dynamicStyles: string = '';
|
|
||||||
dynamicOptions: any = null;
|
|
||||||
|
|
||||||
// Properties to hold extracted values from dynamic template
|
|
||||||
extractedChartType: string = '';
|
|
||||||
extractedDatasetsBinding: string = '';
|
|
||||||
extractedLabelsBinding: string = '';
|
|
||||||
extractedOptionsBinding: string = '';
|
|
||||||
extractedLegendBinding: string = '';
|
|
||||||
extractedChartClickBinding: string = '';
|
|
||||||
extractedChartHoverBinding: string = '';
|
|
||||||
|
|
||||||
// Add setter to log when dynamicTemplate changes
|
|
||||||
setDynamicTemplate(value: string) {
|
|
||||||
console.log('Setting dynamic template:', value);
|
|
||||||
this.dynamicTemplate = value;
|
|
||||||
|
|
||||||
// Extract values from the dynamic template
|
|
||||||
this.extractTemplateValues(value);
|
|
||||||
|
|
||||||
// Apply dynamic options if they were extracted
|
|
||||||
if (this.dynamicOptions) {
|
|
||||||
this.mergeDynamicOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply dynamic styles if they were extracted
|
|
||||||
if (this.dynamicStyles) {
|
|
||||||
this.applyDynamicStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger change detection to ensure the template is rendered
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log('Dynamic template updated in DOM');
|
|
||||||
// Check if the dynamic template container exists
|
|
||||||
const dynamicContainer = this.el.nativeElement.querySelector('.dynamic-template-container');
|
|
||||||
console.log('Dynamic template container:', dynamicContainer);
|
|
||||||
if (dynamicContainer) {
|
|
||||||
console.log('Dynamic container innerHTML:', dynamicContainer.innerHTML);
|
|
||||||
}
|
|
||||||
// Check if the canvas element exists in the DOM
|
|
||||||
const canvasElements = this.el.nativeElement.querySelectorAll('canvas');
|
|
||||||
console.log('Canvas elements found in DOM:', canvasElements.length);
|
|
||||||
if (canvasElements.length > 0) {
|
|
||||||
console.log('First canvas element:', canvasElements[0]);
|
|
||||||
// Check if it has the baseChart directive processed
|
|
||||||
const firstCanvas = canvasElements[0];
|
|
||||||
console.log('Canvas has baseChart directive processed:', firstCanvas.classList.contains('chartjs-render-monitor'));
|
|
||||||
} else {
|
|
||||||
console.log('No canvas elements found - checking if template was inserted but not processed');
|
|
||||||
// Check if there's HTML content in the dynamic container
|
|
||||||
if (dynamicContainer) {
|
|
||||||
const htmlContent = dynamicContainer.innerHTML;
|
|
||||||
console.log('HTML content in dynamic container:', htmlContent);
|
|
||||||
if (htmlContent && htmlContent.includes('canvas')) {
|
|
||||||
console.log('Canvas tag found in HTML but not processed by Angular');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract values from dynamic template HTML
|
|
||||||
private extractTemplateValues(template: string): void {
|
|
||||||
console.log('Extracting values from template:', template);
|
|
||||||
|
|
||||||
// Reset extracted values
|
|
||||||
this.extractedChartType = this.chartType || '';
|
|
||||||
this.extractedDatasetsBinding = 'chartData';
|
|
||||||
this.extractedLabelsBinding = 'chartLabels';
|
|
||||||
this.extractedOptionsBinding = 'chartOptions';
|
|
||||||
this.extractedLegendBinding = 'chartLegend';
|
|
||||||
this.extractedChartClickBinding = 'chartClicked($event)';
|
|
||||||
this.extractedChartHoverBinding = 'chartHovered($event)';
|
|
||||||
|
|
||||||
if (!template) {
|
|
||||||
console.log('No template to extract values from');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the template to extract bindings
|
|
||||||
// Look for [chartType] binding
|
|
||||||
const chartTypeMatch = template.match(/\[chartType\]="([^"]+)"/);
|
|
||||||
if (chartTypeMatch && chartTypeMatch[1]) {
|
|
||||||
this.extractedChartType = chartTypeMatch[1];
|
|
||||||
console.log('Extracted chartType binding:', this.extractedChartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for [datasets] binding
|
|
||||||
const datasetsMatch = template.match(/\[datasets\]="([^"]+)"/);
|
|
||||||
if (datasetsMatch && datasetsMatch[1]) {
|
|
||||||
this.extractedDatasetsBinding = datasetsMatch[1];
|
|
||||||
console.log('Extracted datasets binding:', this.extractedDatasetsBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for [labels] binding
|
|
||||||
const labelsMatch = template.match(/\[labels\]="([^"]+)"/);
|
|
||||||
if (labelsMatch && labelsMatch[1]) {
|
|
||||||
this.extractedLabelsBinding = labelsMatch[1];
|
|
||||||
console.log('Extracted labels binding:', this.extractedLabelsBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for [options] binding
|
|
||||||
const optionsMatch = template.match(/\[options\]="([^"]+)"/);
|
|
||||||
if (optionsMatch && optionsMatch[1]) {
|
|
||||||
this.extractedOptionsBinding = optionsMatch[1];
|
|
||||||
console.log('Extracted options binding:', this.extractedOptionsBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for [legend] binding
|
|
||||||
const legendMatch = template.match(/\[legend\]="([^"]+)"/);
|
|
||||||
if (legendMatch && legendMatch[1]) {
|
|
||||||
this.extractedLegendBinding = legendMatch[1];
|
|
||||||
console.log('Extracted legend binding:', this.extractedLegendBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for (chartClick) binding
|
|
||||||
const chartClickMatch = template.match(/\(chartClick\)="([^"]+)"/);
|
|
||||||
if (chartClickMatch && chartClickMatch[1]) {
|
|
||||||
this.extractedChartClickBinding = chartClickMatch[1];
|
|
||||||
console.log('Extracted chartClick binding:', this.extractedChartClickBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for (chartHover) binding
|
|
||||||
const chartHoverMatch = template.match(/\(chartHover\)="([^"]+)"/);
|
|
||||||
if (chartHoverMatch && chartHoverMatch[1]) {
|
|
||||||
this.extractedChartHoverBinding = chartHoverMatch[1];
|
|
||||||
console.log('Extracted chartHover binding:', this.extractedChartHoverBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract CSS styles if present in the template
|
|
||||||
const styleMatch = template.match(/<style[^>]*>([\s\S]*?)<\/style>/i);
|
|
||||||
if (styleMatch && styleMatch[1]) {
|
|
||||||
this.dynamicStyles = styleMatch[1];
|
|
||||||
console.log('Extracted CSS styles:', this.dynamicStyles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private dashboardService: Dashboard3Service,
|
private dashboardService: Dashboard3Service,
|
||||||
private filterService: FilterService,
|
private filterService: FilterService
|
||||||
private dynamicChartLoader: DynamicChartLoaderService,
|
|
||||||
private renderer: Renderer2,
|
|
||||||
private el: ElementRef
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -231,54 +85,13 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Log initial input values for debugging
|
|
||||||
console.log('UnifiedChartComponent ngOnInit - initial input values:', {
|
|
||||||
chartType: this.chartType,
|
|
||||||
xAxis: this.xAxis,
|
|
||||||
yAxis: this.yAxis,
|
|
||||||
table: this.table,
|
|
||||||
baseFilters: this.baseFilters,
|
|
||||||
drilldownFilters: this.drilldownFilters,
|
|
||||||
drilldownLayers: this.drilldownLayers
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if filters are available
|
|
||||||
console.log('Has filters in ngOnInit:', this.hasFilters());
|
|
||||||
|
|
||||||
// Initialize filter values if they haven't been initialized yet
|
|
||||||
if (!this.filtersInitialized) {
|
|
||||||
this.initializeFilterValues();
|
|
||||||
this.filtersInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize chart options with default structure
|
|
||||||
this.initializeChartOptions();
|
this.initializeChartOptions();
|
||||||
|
|
||||||
// Load dynamic template and options for this chart type
|
|
||||||
this.loadDynamicChartConfig();
|
|
||||||
|
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
console.log('UnifiedChartComponent input changes:', changes);
|
console.log('UnifiedChartComponent input changes:', changes);
|
||||||
|
|
||||||
// Log chartType changes specifically
|
|
||||||
if (changes.chartType) {
|
|
||||||
console.log('Chart type changed from', changes.chartType.previousValue, 'to', changes.chartType.currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log all input values for debugging
|
|
||||||
console.log('Current input values:', {
|
|
||||||
chartType: this.chartType,
|
|
||||||
xAxis: this.xAxis,
|
|
||||||
yAxis: this.yAxis,
|
|
||||||
table: this.table,
|
|
||||||
baseFilters: this.baseFilters,
|
|
||||||
drilldownFilters: this.drilldownFilters,
|
|
||||||
drilldownLayers: this.drilldownLayers
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize filter values if they haven't been initialized yet
|
// Initialize filter values if they haven't been initialized yet
|
||||||
if (!this.filtersInitialized && (changes.baseFilters || changes.drilldownFilters || changes.drilldownLayers)) {
|
if (!this.filtersInitialized && (changes.baseFilters || changes.drilldownFilters || changes.drilldownLayers)) {
|
||||||
this.initializeFilterValues();
|
this.initializeFilterValues();
|
||||||
@@ -301,45 +114,10 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange;
|
const drilldownYAxisChanged = changes.drilldownYAxis && !changes.drilldownYAxis.firstChange;
|
||||||
const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange;
|
const drilldownLayersChanged = changes.drilldownLayers && !changes.drilldownLayers.firstChange;
|
||||||
|
|
||||||
// Log base filters changes for debugging
|
|
||||||
if (baseFiltersChanged) {
|
|
||||||
console.log('Base filters changed:', changes.baseFilters);
|
|
||||||
console.log('Current base filters:', this.baseFilters);
|
|
||||||
// Log detailed information about each filter
|
|
||||||
if (this.baseFilters && Array.isArray(this.baseFilters)) {
|
|
||||||
this.baseFilters.forEach((filter, index) => {
|
|
||||||
console.log(`Base filter ${index} details:`, {
|
|
||||||
field: filter.field,
|
|
||||||
value: filter.value,
|
|
||||||
type: filter.type,
|
|
||||||
options: filter.options
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also log when baseFilters is not changed but we still have filters
|
|
||||||
if (!baseFiltersChanged && this.baseFilters && this.baseFilters.length > 0) {
|
|
||||||
console.log('Base filters present but not changed, logging current state:');
|
|
||||||
this.baseFilters.forEach((filter, index) => {
|
|
||||||
console.log(`Base filter ${index} details:`, {
|
|
||||||
field: filter.field,
|
|
||||||
value: filter.value,
|
|
||||||
type: filter.type,
|
|
||||||
options: filter.options
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load dynamic template and options if chart type changed
|
|
||||||
if (chartTypeChanged) {
|
|
||||||
this.loadDynamicChartConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only fetch data if the actual chart configuration changed and we're not already fetching
|
// Only fetch data if the actual chart configuration changed and we're not already fetching
|
||||||
if (!this.isFetchingData && (chartTypeChanged || xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownFiltersChanged ||
|
if (!this.isFetchingData && (chartTypeChanged || xAxisChanged || yAxisChanged || tableChanged || connectionChanged || baseFiltersChanged || drilldownFiltersChanged ||
|
||||||
drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged ||
|
drilldownEnabledChanged || drilldownApiUrlChanged || drilldownXAxisChanged || drilldownYAxisChanged ||
|
||||||
drilldownLayersChanged)) {
|
drilldownLayersChanged)) {
|
||||||
console.log('Chart configuration changed, fetching new data');
|
console.log('Chart configuration changed, fetching new data');
|
||||||
this.initializeChartOptions();
|
this.initializeChartOptions();
|
||||||
this.fetchChartData();
|
this.fetchChartData();
|
||||||
@@ -348,18 +126,7 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
// Update legend visibility if it changed
|
// Update legend visibility if it changed
|
||||||
if (changes.chartlegend !== undefined) {
|
if (changes.chartlegend !== undefined) {
|
||||||
this.chartLegend = changes.chartlegend.currentValue;
|
this.chartLegend = changes.chartlegend.currentValue;
|
||||||
// Ensure chartOptions and required structures exist before accessing legend
|
this.chartOptions.plugins.legend.display = this.chartLegend;
|
||||||
if (!this.chartOptions) {
|
|
||||||
this.chartOptions = {};
|
|
||||||
}
|
|
||||||
if (!this.chartOptions.plugins) {
|
|
||||||
this.chartOptions.plugins = {};
|
|
||||||
}
|
|
||||||
if (!this.chartOptions.plugins.legend) {
|
|
||||||
this.chartOptions.plugins.legend = { display: this.chartLegend };
|
|
||||||
} else {
|
|
||||||
this.chartOptions.plugins.legend.display = this.chartLegend;
|
|
||||||
}
|
|
||||||
console.log('Chart legend changed to:', this.chartLegend);
|
console.log('Chart legend changed to:', this.chartLegend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,199 +141,13 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load dynamic chart configuration (template, styles, and options) for the current chart type
|
|
||||||
private loadDynamicChartConfig(): void {
|
|
||||||
if (!this.chartType) {
|
|
||||||
console.log('No chart type specified, skipping dynamic chart config loading');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Loading dynamic chart configuration for chart type: ${this.chartType}`);
|
|
||||||
console.log('Current dynamic template:', this.dynamicTemplate);
|
|
||||||
|
|
||||||
// Get chart type by name and load its configuration
|
|
||||||
console.log('Calling getChartTypeByName with:', this.chartType);
|
|
||||||
this.dynamicChartLoader.getChartTypeByName(this.chartType).subscribe({
|
|
||||||
next: (chartType) => {
|
|
||||||
console.log('Received chart type by name :', chartType);
|
|
||||||
if (chartType) {
|
|
||||||
console.log('Found chart type:', chartType);
|
|
||||||
|
|
||||||
// Load the complete configuration for this chart type
|
|
||||||
console.log('Loading chart configuration for chart type ID:', chartType.id);
|
|
||||||
this.dynamicChartLoader.loadChartConfiguration(chartType.id).subscribe({
|
|
||||||
next: (config) => {
|
|
||||||
console.log('Received chart configuration:', config);
|
|
||||||
console.log('Loaded chart configuration:', config);
|
|
||||||
|
|
||||||
// Apply the first template if available (for CSS styles)
|
|
||||||
if (config.templates && config.templates.length > 0) {
|
|
||||||
const defaultTemplate = config.templates.find(t => t.isDefault) || config.templates[0];
|
|
||||||
if (defaultTemplate) {
|
|
||||||
const templateHtml = defaultTemplate.templateHtml || '';
|
|
||||||
console.log('Template HTML to be set:', templateHtml);
|
|
||||||
this.setDynamicTemplate(templateHtml);
|
|
||||||
this.dynamicStyles = defaultTemplate.templateCss || '';
|
|
||||||
|
|
||||||
// Apply styles to the component
|
|
||||||
this.applyDynamicStyles();
|
|
||||||
|
|
||||||
console.log('Applied dynamic template and styles', {
|
|
||||||
template: this.dynamicTemplate,
|
|
||||||
styles: this.dynamicStyles
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('No templates found for chart type:', this.chartType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply dynamic options if available
|
|
||||||
console.log('Checking for dynamic fields:', config.dynamicFields);
|
|
||||||
if (config.dynamicFields && config.dynamicFields.length > 0) {
|
|
||||||
// Find the field that contains chart options
|
|
||||||
const optionsField = config.dynamicFields.find(field =>
|
|
||||||
field.fieldName === 'chartOptions' || field.fieldName.toLowerCase().includes('options'));
|
|
||||||
|
|
||||||
if (!optionsField) {
|
|
||||||
console.log('No chartOptions field found in dynamic fields');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optionsField && optionsField.fieldOptions) {
|
|
||||||
try {
|
|
||||||
this.dynamicOptions = JSON.parse(optionsField.fieldOptions);
|
|
||||||
console.log('Applied dynamic chart options:', this.dynamicOptions);
|
|
||||||
|
|
||||||
// Merge dynamic options with current chart options
|
|
||||||
this.mergeDynamicOptions();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error parsing dynamic chart options:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log('No dynamic fields found for chart type:', this.chartType);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart configuration:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log(`Chart type ${this.chartType} not found in database`);
|
|
||||||
// Log available chart types for debugging
|
|
||||||
console.log('Available chart types in database:');
|
|
||||||
this.dynamicChartLoader.loadAllChartConfigurations().subscribe({
|
|
||||||
next: (chartTypes) => {
|
|
||||||
console.log('All chart types:', chartTypes);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart types:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('Error loading chart type:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge dynamic options with current chart options
|
|
||||||
private mergeDynamicOptions(): void {
|
|
||||||
if (this.dynamicOptions) {
|
|
||||||
console.log('Merging dynamic options with existing chart options:', {
|
|
||||||
existing: this.chartOptions,
|
|
||||||
dynamic: this.dynamicOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Deep merge dynamic options with existing chart options
|
|
||||||
this.chartOptions = this.deepMerge(this.chartOptions, this.dynamicOptions);
|
|
||||||
|
|
||||||
console.log('Merged chart options:', this.chartOptions);
|
|
||||||
|
|
||||||
// If we have a chart instance, update it
|
|
||||||
if (this.chart) {
|
|
||||||
this.chart.options = this.chartOptions;
|
|
||||||
this.chart.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method for deep merging objects
|
|
||||||
private deepMerge(target: any, source: any): any {
|
|
||||||
const result = { ...target };
|
|
||||||
|
|
||||||
for (const key in source) {
|
|
||||||
if (source.hasOwnProperty(key)) {
|
|
||||||
if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
|
|
||||||
// Recursively merge objects
|
|
||||||
result[key] = this.deepMerge(result[key] || {}, source[key]);
|
|
||||||
} else {
|
|
||||||
// Override with source value
|
|
||||||
result[key] = source[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply dynamic styles to the component
|
|
||||||
private applyDynamicStyles(): void {
|
|
||||||
// Remove any previously applied dynamic styles
|
|
||||||
const existingStyles = this.el.nativeElement.querySelectorAll('.dynamic-chart-styles');
|
|
||||||
existingStyles.forEach((style: HTMLElement) => {
|
|
||||||
style.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Apply new styles if available
|
|
||||||
if (this.dynamicStyles) {
|
|
||||||
const styleElement = this.renderer.createElement('style');
|
|
||||||
this.renderer.setAttribute(styleElement, 'class', 'dynamic-chart-styles');
|
|
||||||
this.renderer.setProperty(styleElement, 'textContent', this.dynamicStyles);
|
|
||||||
this.renderer.appendChild(this.el.nativeElement, styleElement);
|
|
||||||
console.log('Applied dynamic styles to component');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize chart after dynamic template is rendered
|
|
||||||
private initializeDynamicChart(): void {
|
|
||||||
// This is a complex issue - Angular directives in dynamically inserted HTML
|
|
||||||
// don't get processed automatically. We would need to use a different approach
|
|
||||||
// such as creating components dynamically or using a different template mechanism.
|
|
||||||
console.log('Initializing dynamic chart - this is where we would handle chart initialization');
|
|
||||||
|
|
||||||
// NOTE: The baseChart directive in dynamically inserted HTML via [innerHTML]
|
|
||||||
// will not be processed by Angular. This is a limitation of Angular's change detection.
|
|
||||||
// Possible solutions:
|
|
||||||
// 1. Use Angular's dynamic component creation API
|
|
||||||
// 2. Modify the approach to use a different template mechanism
|
|
||||||
// 3. Keep the canvas element in the static template and only load options dynamically
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if filters are available
|
|
||||||
hasFilters(): boolean {
|
|
||||||
const hasBaseFilters = this.baseFilters && this.baseFilters.length > 0;
|
|
||||||
console.log('Checking for filters - baseFilters:', this.baseFilters, 'hasBaseFilters:', hasBaseFilters);
|
|
||||||
return hasBaseFilters;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle filter visibility
|
|
||||||
toggleFilters(): void {
|
|
||||||
console.log('Toggling filters. Current state:', this.showFilters);
|
|
||||||
console.log('Base filters available:', this.hasFilters());
|
|
||||||
this.showFilters = !this.showFilters;
|
|
||||||
console.log('New state:', this.showFilters);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize filter values with proper default values based on type
|
// Initialize filter values with proper default values based on type
|
||||||
private initializeFilterValues(): void {
|
private initializeFilterValues(): void {
|
||||||
console.log('Initializing filter values');
|
console.log('Initializing filter values');
|
||||||
console.log('Base filters before initialization:', this.baseFilters);
|
|
||||||
|
|
||||||
// Initialize base filters
|
// Initialize base filters
|
||||||
if (this.baseFilters && Array.isArray(this.baseFilters)) {
|
if (this.baseFilters) {
|
||||||
this.baseFilters.forEach((filter, index) => {
|
this.baseFilters.forEach(filter => {
|
||||||
console.log(`Processing base filter ${index}:`, filter);
|
|
||||||
if (filter.value === undefined || filter.value === null) {
|
if (filter.value === undefined || filter.value === null) {
|
||||||
switch (filter.type) {
|
switch (filter.type) {
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
@@ -581,22 +162,13 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
default:
|
default:
|
||||||
filter.value = '';
|
filter.value = '';
|
||||||
}
|
}
|
||||||
console.log(`Initialized base filter ${index} value to:`, filter.value);
|
|
||||||
} else {
|
|
||||||
console.log(`Base filter ${index} already has value:`, filter.value);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// Initialize as empty array if not provided
|
|
||||||
this.baseFilters = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Base filters after initialization:', this.baseFilters);
|
|
||||||
|
|
||||||
// Initialize drilldown filters
|
// Initialize drilldown filters
|
||||||
if (this.drilldownFilters && Array.isArray(this.drilldownFilters)) {
|
if (this.drilldownFilters) {
|
||||||
this.drilldownFilters.forEach((filter, index) => {
|
this.drilldownFilters.forEach(filter => {
|
||||||
console.log(`Processing drilldown filter ${index}:`, filter);
|
|
||||||
if (filter.value === undefined || filter.value === null) {
|
if (filter.value === undefined || filter.value === null) {
|
||||||
switch (filter.type) {
|
switch (filter.type) {
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
@@ -611,23 +183,15 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
default:
|
default:
|
||||||
filter.value = '';
|
filter.value = '';
|
||||||
}
|
}
|
||||||
console.log(`Initialized drilldown filter ${index} value to:`, filter.value);
|
|
||||||
} else {
|
|
||||||
console.log(`Drilldown filter ${index} already has value:`, filter.value);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// Initialize as empty array if not provided
|
|
||||||
this.drilldownFilters = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize layer filters
|
// Initialize layer filters
|
||||||
if (this.drilldownLayers && Array.isArray(this.drilldownLayers)) {
|
if (this.drilldownLayers) {
|
||||||
this.drilldownLayers.forEach((layer, layerIndex) => {
|
this.drilldownLayers.forEach(layer => {
|
||||||
console.log(`Processing drilldown layer ${layerIndex}:`, layer);
|
if (layer.filters) {
|
||||||
if (layer.filters && Array.isArray(layer.filters)) {
|
layer.filters.forEach((filter: any) => {
|
||||||
layer.filters.forEach((filter, filterIndex) => {
|
|
||||||
console.log(`Processing layer ${layerIndex} filter ${filterIndex}:`, filter);
|
|
||||||
if (filter.value === undefined || filter.value === null) {
|
if (filter.value === undefined || filter.value === null) {
|
||||||
switch (filter.type) {
|
switch (filter.type) {
|
||||||
case 'multiselect':
|
case 'multiselect':
|
||||||
@@ -642,16 +206,10 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
default:
|
default:
|
||||||
filter.value = '';
|
filter.value = '';
|
||||||
}
|
}
|
||||||
console.log(`Initialized layer ${layerIndex} filter ${filterIndex} value to:`, filter.value);
|
|
||||||
} else {
|
|
||||||
console.log(`Layer ${layerIndex} filter ${filterIndex} already has value:`, filter.value);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// Initialize as empty array if not provided
|
|
||||||
this.drilldownLayers = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Filter values initialized:', {
|
console.log('Filter values initialized:', {
|
||||||
@@ -663,24 +221,6 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
// Initialize chart options based on chart type
|
// Initialize chart options based on chart type
|
||||||
private initializeChartOptions(): void {
|
private initializeChartOptions(): void {
|
||||||
// Initialize with default structure to ensure plugins.legend exists
|
|
||||||
this.chartOptions = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
position: 'top'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we have dynamic options, use them instead of the default ones
|
|
||||||
if (this.dynamicOptions) {
|
|
||||||
this.mergeDynamicOptions();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.chartType) {
|
switch (this.chartType) {
|
||||||
case 'bar':
|
case 'bar':
|
||||||
this.initializeBarChartOptions();
|
this.initializeBarChartOptions();
|
||||||
@@ -715,8 +255,6 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.chartOptions = {
|
this.chartOptions = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
barPercentage: 0.6, // Reduced from 0.8 to create more spacing between bars
|
|
||||||
categoryPercentage: 0.8, // Reduced from 0.9 to create more spacing between categories
|
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
ticks: {
|
ticks: {
|
||||||
@@ -738,9 +276,7 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
font: {
|
font: {
|
||||||
size: 12
|
size: 12
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Add some padding to the y-axis to prevent bars from touching the top
|
|
||||||
suggestedMax: 10
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
@@ -750,21 +286,11 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
labels: {
|
labels: {
|
||||||
font: {
|
font: {
|
||||||
size: 12
|
size: 12
|
||||||
},
|
}
|
||||||
// Add padding to legend items
|
|
||||||
padding: 20
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
enabled: true,
|
enabled: true
|
||||||
// Improve tooltip appearance
|
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
||||||
titleFont: {
|
|
||||||
size: 14
|
|
||||||
},
|
|
||||||
bodyFont: {
|
|
||||||
size: 12
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
layout: {
|
layout: {
|
||||||
@@ -774,21 +300,6 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
right: 15,
|
right: 15,
|
||||||
top: 15
|
top: 15
|
||||||
}
|
}
|
||||||
},
|
|
||||||
// Add bar chart specific options
|
|
||||||
indexAxis: 'x', // Horizontal bars
|
|
||||||
elements: {
|
|
||||||
bar: {
|
|
||||||
borderWidth: 2, // Increased border width for better visibility
|
|
||||||
borderSkipped: false, // Show all borders for better separation
|
|
||||||
// Add border color to make bars more distinct
|
|
||||||
borderColor: 'rgba(255, 255, 255, 0.8)' // White border for better separation
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Animation settings for smoother transitions
|
|
||||||
animation: {
|
|
||||||
duration: 1000,
|
|
||||||
easing: 'easeInOutQuart'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -923,7 +434,7 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
mode: 'point',
|
mode: 'point',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function (context: any) {
|
label: function(context: any) {
|
||||||
const point: any = context.raw;
|
const point: any = context.raw;
|
||||||
if (point && point.hasOwnProperty('y') && point.hasOwnProperty('r')) {
|
if (point && point.hasOwnProperty('y') && point.hasOwnProperty('r')) {
|
||||||
const yValue = parseFloat(point.y);
|
const yValue = parseFloat(point.y);
|
||||||
@@ -1033,11 +544,6 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.noDataAvailable = false;
|
this.noDataAvailable = false;
|
||||||
|
|
||||||
// Ensure chart options are initialized
|
|
||||||
if (!this.chartOptions) {
|
|
||||||
this.initializeChartOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Starting fetchChartData for chart type:', this.chartType);
|
console.log('Starting fetchChartData for chart type:', this.chartType);
|
||||||
|
|
||||||
// If we're in drilldown mode, fetch the appropriate drilldown data
|
// If we're in drilldown mode, fetch the appropriate drilldown data
|
||||||
@@ -1426,8 +932,8 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
// If we have the expected bubble data format, return it as is
|
// If we have the expected bubble data format, return it as is
|
||||||
if (data && data.length > 0 && data[0].data && data[0].data.length > 0 &&
|
if (data && data.length > 0 && data[0].data && data[0].data.length > 0 &&
|
||||||
typeof data[0].data[0] === 'object' && data[0].data[0].hasOwnProperty('x') &&
|
typeof data[0].data[0] === 'object' && data[0].data[0].hasOwnProperty('x') &&
|
||||||
data[0].data[0].hasOwnProperty('y') && data[0].data[0].hasOwnProperty('r')) {
|
data[0].data[0].hasOwnProperty('y') && data[0].data[0].hasOwnProperty('r')) {
|
||||||
console.log('Data is already in bubble format, returning as is');
|
console.log('Data is already in bubble format, returning as is');
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -1609,17 +1115,17 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
const hue2rgb = (p: number, q: number, t: number) => {
|
const hue2rgb = (p: number, q: number, t: number) => {
|
||||||
if (t < 0) t += 1;
|
if (t < 0) t += 1;
|
||||||
if (t > 1) t -= 1;
|
if (t > 1) t -= 1;
|
||||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
if (t < 1/6) return p + (q - p) * 6 * t;
|
||||||
if (t < 1 / 2) return q;
|
if (t < 1/2) return q;
|
||||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||||
return p;
|
return p;
|
||||||
};
|
};
|
||||||
|
|
||||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||||
const p = 2 * l - q;
|
const p = 2 * l - q;
|
||||||
r = hue2rgb(p, q, h + 1 / 3);
|
r = hue2rgb(p, q, h + 1/3);
|
||||||
g = hue2rgb(p, q, h);
|
g = hue2rgb(p, q, h);
|
||||||
b = hue2rgb(p, q, h - 1 / 3);
|
b = hue2rgb(p, q, h - 1/3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1744,9 +1250,9 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
if (layerIndex < this.drilldownLayers.length) {
|
if (layerIndex < this.drilldownLayers.length) {
|
||||||
drilldownConfig = this.drilldownLayers[layerIndex];
|
drilldownConfig = this.drilldownLayers[layerIndex];
|
||||||
hasDrilldownConfig = drilldownConfig.enabled &&
|
hasDrilldownConfig = drilldownConfig.enabled &&
|
||||||
!!drilldownConfig.apiUrl &&
|
!!drilldownConfig.apiUrl &&
|
||||||
!!drilldownConfig.xAxis &&
|
!!drilldownConfig.xAxis &&
|
||||||
!!drilldownConfig.yAxis;
|
!!drilldownConfig.yAxis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1785,7 +1291,7 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public chartHovered(e: any): void {
|
public chartHovered(e: any): void {
|
||||||
// console.log('Chart hovered:', e);
|
console.log('Chart hovered:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to check if chart data is valid
|
// Method to check if chart data is valid
|
||||||
@@ -1840,257 +1346,4 @@ export class UnifiedChartComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there are active filters
|
|
||||||
hasActiveFilters(): boolean {
|
|
||||||
return (this.baseFilters && this.baseFilters.length > 0) ||
|
|
||||||
(this.drilldownFilters && this.drilldownFilters.length > 0) ||
|
|
||||||
this.hasActiveLayerFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there are active layer filters for current drilldown level
|
|
||||||
hasActiveLayerFilters(): boolean {
|
|
||||||
if (this.currentDrilldownLevel > 1 && this.drilldownLayers && this.drilldownLayers.length > 0) {
|
|
||||||
const layerIndex = this.currentDrilldownLevel - 2; // -2 because level 1 is base drilldown
|
|
||||||
return layerIndex < this.drilldownLayers.length &&
|
|
||||||
this.drilldownLayers[layerIndex].filters &&
|
|
||||||
this.drilldownLayers[layerIndex].filters.length > 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get active layer filters for current drilldown level
|
|
||||||
getActiveLayerFilters(): any[] {
|
|
||||||
if (this.currentDrilldownLevel > 1 && this.drilldownLayers && this.drilldownLayers.length > 0) {
|
|
||||||
const layerIndex = this.currentDrilldownLevel - 2; // -2 because level 1 is base drilldown
|
|
||||||
if (layerIndex < this.drilldownLayers.length &&
|
|
||||||
this.drilldownLayers[layerIndex].filters) {
|
|
||||||
return this.drilldownLayers[layerIndex].filters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get filter options for dropdown/multiselect filters
|
|
||||||
getFilterOptions(filter: any): string[] {
|
|
||||||
if (filter.options) {
|
|
||||||
if (Array.isArray(filter.options)) {
|
|
||||||
return filter.options;
|
|
||||||
} else if (typeof filter.options === 'string') {
|
|
||||||
return filter.options.split(',').map(opt => opt.trim()).filter(opt => opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if an option is selected for multiselect filters
|
|
||||||
isOptionSelected(filter: any, option: string): boolean {
|
|
||||||
if (!filter.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(filter.value)) {
|
|
||||||
return filter.value.includes(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter.value === option;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle base filter changes
|
|
||||||
onBaseFilterChange(filter: any): void {
|
|
||||||
console.log('Base filter changed:', filter);
|
|
||||||
// Refresh data when filter changes
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle drilldown filter changes
|
|
||||||
onDrilldownFilterChange(filter: any): void {
|
|
||||||
console.log('Drilldown filter changed:', filter);
|
|
||||||
// Refresh data when filter changes
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle layer filter changes
|
|
||||||
onLayerFilterChange(filter: any): void {
|
|
||||||
console.log('Layer filter changed:', filter);
|
|
||||||
// Refresh data when filter changes
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle multiselect changes
|
|
||||||
onMultiSelectChange(filter: any, option: string, event: any): void {
|
|
||||||
const checked = event.target.checked;
|
|
||||||
|
|
||||||
// Initialize filter.value as array if it's not already
|
|
||||||
if (!Array.isArray(filter.value)) {
|
|
||||||
filter.value = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checked) {
|
|
||||||
// Add option to array if not already present
|
|
||||||
if (!filter.value.includes(option)) {
|
|
||||||
filter.value.push(option);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Remove option from array
|
|
||||||
filter.value = filter.value.filter((item: string) => item !== option);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh data when filter changes
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle date range changes
|
|
||||||
onDateRangeChange(filter: any, event: any): void {
|
|
||||||
// For date range filters, we need to handle the change differently
|
|
||||||
// since we're binding to individual start/end properties
|
|
||||||
if (!filter.value) {
|
|
||||||
filter.value = { start: null, end: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh data when filter changes
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle date range input changes for start/end dates
|
|
||||||
onDateRangeInputChange(filter: any, dateType: string, event: any): void {
|
|
||||||
// Initialize filter.value if it doesn't exist
|
|
||||||
if (!filter.value) {
|
|
||||||
filter.value = { start: null, end: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the specific date type (start or end)
|
|
||||||
filter.value[dateType] = event.target.value;
|
|
||||||
|
|
||||||
// Refresh data when filter changes
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle toggle changes
|
|
||||||
onToggleChange(filter: any, checked: boolean): void {
|
|
||||||
filter.value = checked;
|
|
||||||
// Refresh data when filter changes
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle multiselect dropdown visibility
|
|
||||||
toggleMultiselect(filter: any, context: string): void {
|
|
||||||
const filterId = `${context}-${filter.field}`;
|
|
||||||
if (this.isMultiselectOpen(filter, context)) {
|
|
||||||
this.openMultiselects.delete(filterId);
|
|
||||||
} else {
|
|
||||||
// Close all other multiselects first
|
|
||||||
this.openMultiselects.clear();
|
|
||||||
this.openMultiselects.set(filterId, context);
|
|
||||||
|
|
||||||
// Add document click handler to close dropdown when clicking outside
|
|
||||||
this.addDocumentClickHandler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add document click handler to close dropdowns when clicking outside
|
|
||||||
private addDocumentClickHandler(): void {
|
|
||||||
if (!this.documentClickHandler) {
|
|
||||||
this.documentClickHandler = (event: MouseEvent) => {
|
|
||||||
const target = event.target as HTMLElement;
|
|
||||||
// Check if click is outside any multiselect dropdown
|
|
||||||
if (!target.closest('.multiselect-container')) {
|
|
||||||
this.openMultiselects.clear();
|
|
||||||
this.removeDocumentClickHandler();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use setTimeout to ensure the click event that opened the dropdown doesn't immediately close it
|
|
||||||
setTimeout(() => {
|
|
||||||
document.addEventListener('click', this.documentClickHandler!);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove document click handler
|
|
||||||
private removeDocumentClickHandler(): void {
|
|
||||||
if (this.documentClickHandler) {
|
|
||||||
document.removeEventListener('click', this.documentClickHandler);
|
|
||||||
this.documentClickHandler = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if multiselect dropdown is open
|
|
||||||
isMultiselectOpen(filter: any, context: string): boolean {
|
|
||||||
const filterId = `${context}-${filter.field}`;
|
|
||||||
return this.openMultiselects.has(filterId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get count of selected options for a multiselect filter
|
|
||||||
getSelectedOptionsCount(filter: any): number {
|
|
||||||
if (!filter.value) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(filter.value)) {
|
|
||||||
return filter.value.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Clear all filters
|
|
||||||
clearAllFilters(): void {
|
|
||||||
// Clear base filters
|
|
||||||
if (this.baseFilters) {
|
|
||||||
this.baseFilters.forEach(filter => {
|
|
||||||
if (filter.type === 'multiselect') {
|
|
||||||
filter.value = [];
|
|
||||||
} else if (filter.type === 'date-range') {
|
|
||||||
filter.value = { start: null, end: null };
|
|
||||||
} else if (filter.type === 'toggle') {
|
|
||||||
filter.value = false;
|
|
||||||
} else {
|
|
||||||
filter.value = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear drilldown filters
|
|
||||||
if (this.drilldownFilters) {
|
|
||||||
this.drilldownFilters.forEach(filter => {
|
|
||||||
if (filter.type === 'multiselect') {
|
|
||||||
filter.value = [];
|
|
||||||
} else if (filter.type === 'date-range') {
|
|
||||||
filter.value = { start: null, end: null };
|
|
||||||
} else if (filter.type === 'toggle') {
|
|
||||||
filter.value = false;
|
|
||||||
} else {
|
|
||||||
filter.value = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear layer filters
|
|
||||||
if (this.drilldownLayers) {
|
|
||||||
this.drilldownLayers.forEach(layer => {
|
|
||||||
if (layer.filters) {
|
|
||||||
layer.filters.forEach((filter: any) => {
|
|
||||||
if (filter.type === 'multiselect') {
|
|
||||||
filter.value = [];
|
|
||||||
} else if (filter.type === 'date-range') {
|
|
||||||
filter.value = { start: null, end: null };
|
|
||||||
} else if (filter.type === 'toggle') {
|
|
||||||
filter.value = false;
|
|
||||||
} else {
|
|
||||||
filter.value = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close all multiselect dropdowns
|
|
||||||
this.openMultiselects.clear();
|
|
||||||
|
|
||||||
// Refresh data
|
|
||||||
this.fetchChartData();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
<ol class="breadcrumb breadcrumb-arrow font-trirong mm-breadcrumb">
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']">
|
<a href="javascript://" [routerLink]="['/cns-portal/dashboard/order']">
|
||||||
<clr-icon shape="home"></clr-icon>
|
<clr-icon shape="home"></clr-icon>
|
||||||
@@ -8,236 +8,400 @@
|
|||||||
<a href="">
|
<a href="">
|
||||||
<clr-icon shape="flag"></clr-icon>{{ 'SEQ_GENERATOR' | translate }}</a>
|
<clr-icon shape="flag"></clr-icon>{{ 'SEQ_GENERATOR' | translate }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<div class="dg-wrapper">
|
<section class="sq-hero">
|
||||||
<div class="clr-row">
|
<div class="sq-hero__content">
|
||||||
<div class="clr-col-8">
|
<div class="sq-hero__icon"><clr-icon shape="number-list"></clr-icon></div>
|
||||||
<h3>{{ 'SEQ_GENERATOR' | translate }}</h3>
|
<div>
|
||||||
</div>
|
<h2 class="sq-hero__title">{{ 'SEQ_GENERATOR' | translate }}</h2>
|
||||||
|
<p class="sq-hero__subtitle">{{ 'FND' | translate }}</p>
|
||||||
<div class="clr-col-4" style="text-align: right;">
|
|
||||||
<button class="btn btn-outline" (click)="onExport()">
|
|
||||||
<clr-icon shape="export"></clr-icon> {{ 'EXPORT' | translate }}
|
|
||||||
</button>
|
|
||||||
<button id="add" class="btn btn-primary" (click)="goToAdd()">
|
|
||||||
<clr-icon shape="plus"></clr-icon>{{ 'ADD' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sq-hero__actions">
|
||||||
|
<button class="sq-btn sq-btn-outline" (click)="onExport()">
|
||||||
|
<clr-icon shape="export"></clr-icon> <span class="sq-btn-text">{{ 'EXPORT' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button id="add" class="sq-btn sq-btn-primary" (click)="goToAdd()">
|
||||||
|
<clr-icon shape="plus"></clr-icon><span class="sq-btn-text">{{ 'ADD' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<clr-datagrid [clrDgLoading]="loading" [(clrDgSelected)]="selected">
|
<div class="sq-container">
|
||||||
<clr-dg-placeholder>
|
<div class="sq-toolbar">
|
||||||
<ng-template #loadingSpinner>
|
<div class="sq-toolbar__left">
|
||||||
<clr-spinner>Loading ... </clr-spinner>
|
<div class="sq-search">
|
||||||
</ng-template>
|
<clr-icon shape="search"></clr-icon>
|
||||||
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
<input class="sq-search__input" type="text" [placeholder]="'SEARCH_ALL_FIELDS' | translate" [(ngModel)]="filterText" />
|
||||||
</clr-dg-placeholder>
|
</div>
|
||||||
<clr-dg-column [clrDgField]="'seq_no'">
|
</div>
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<div class="sq-toolbar__right sq-stats">
|
||||||
{{ 'CURRENT_NO' | translate }}
|
<div class="sq-stat">
|
||||||
</ng-container>
|
<div class="sq-stat__value">{{ totalSequences }}</div>
|
||||||
</clr-dg-column>
|
<div class="sq-stat__label">{{ 'TOTAL_SEQUENCES' | translate }}</div>
|
||||||
<clr-dg-column [clrDgField]="'seq_no'">
|
</div>
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<div class="sq-view-toggle">
|
||||||
{{ 'NAME' | translate }}
|
<button class="sq-btn sq-btn-sm" [class.sq-btn-primary]="viewMode==='cards'" (click)="setViewMode('cards')">
|
||||||
</ng-container>
|
<clr-icon shape="view-cards"></clr-icon>
|
||||||
</clr-dg-column>
|
</button>
|
||||||
<clr-dg-column [clrDgField]="'prefix'">
|
<button class="sq-btn sq-btn-sm" [class.sq-btn-primary]="viewMode==='table'" (click)="setViewMode('table')">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-icon shape="table"></clr-icon>
|
||||||
{{ 'PREFIX' | translate }}
|
</button>
|
||||||
</ng-container>
|
</div>
|
||||||
</clr-dg-column>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'seperator'">
|
<clr-datagrid class="sq-grid" [clrDgLoading]="loading" *ngIf="viewMode==='table'" [(clrDgSelected)]="selected">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<clr-dg-placeholder>
|
||||||
{{ 'SEPARATOR' | translate }}
|
<ng-template #loadingSpinner>
|
||||||
</ng-container>
|
<clr-spinner>{{ 'LOADING' | translate }} ... </clr-spinner>
|
||||||
</clr-dg-column>
|
</ng-template>
|
||||||
|
<div *ngIf="error;else loadingSpinner">{{error}}</div>
|
||||||
|
</clr-dg-placeholder>
|
||||||
|
|
||||||
<clr-dg-column [clrDgField]="'digit'">
|
<clr-dg-column [clrDgField]="'current_no'" class="sq-col sq-col--no">
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
{{ 'SEQUENCE_SIZE' | translate }}
|
<span class="sq-col-title">
|
||||||
</ng-container>
|
<clr-icon shape="hashtag"></clr-icon> {{ 'CURRENT_NO' | translate }}
|
||||||
</clr-dg-column>
|
</span>
|
||||||
<!-- <clr-dg-column [clrDgField]="'date_format'">
|
</ng-container>
|
||||||
<ng-container *clrDgHideableColumn="{hidden: false}">
|
</clr-dg-column>
|
||||||
Date Format
|
|
||||||
</ng-container>
|
|
||||||
</clr-dg-column> -->
|
|
||||||
<clr-dg-row *clrDgItems="let user of alldata" [clrDgItem]="user">
|
|
||||||
<clr-dg-cell>{{user.current_no}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{user.sequence_name}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{user.prefix}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{user.seperator}}</clr-dg-cell>
|
|
||||||
<clr-dg-cell>{{user.sequence_size}}</clr-dg-cell>
|
|
||||||
<!-- <clr-dg-cell>{{user.date_format}}</clr-dg-cell> -->
|
|
||||||
<clr-dg-action-overflow>
|
|
||||||
<button class="action-item" (click)="goToEdit(user)"> {{ 'EDIT' | translate }} <clr-icon shape="edit"
|
|
||||||
class="is-error"></clr-icon></button>
|
|
||||||
<button class="action-item" (click)="onDelete(user)"> {{ 'DELETE' | translate }}<clr-icon shape="trash"
|
|
||||||
class="is-error"></clr-icon></button>
|
|
||||||
</clr-dg-action-overflow>
|
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="'sequence_name'" class="sq-col sq-col--name">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="sq-col-title">
|
||||||
|
<clr-icon shape="tag"></clr-icon> {{ 'NAME' | translate }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
</clr-dg-row>
|
<clr-dg-column [clrDgField]="'prefix'" class="sq-col sq-col--prefix">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="sq-col-title">
|
||||||
|
<clr-icon shape="text"></clr-icon> {{ 'PREFIX' | translate }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
<clr-dg-footer>
|
<clr-dg-column [clrDgField]="'seperator'" class="sq-col sq-col--separator">
|
||||||
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">{{ 'USERS_PER_PAGE' | translate
|
<span class="sq-col-title">
|
||||||
}}</clr-dg-page-size>
|
<clr-icon shape="split-string"></clr-icon> {{ 'SEPARATOR' | translate }}
|
||||||
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} of {{pagination.totalItems}} users
|
</span>
|
||||||
</clr-dg-pagination>
|
</ng-container>
|
||||||
</clr-dg-footer>
|
</clr-dg-column>
|
||||||
</clr-datagrid>
|
|
||||||
|
<clr-dg-column [clrDgField]="'sequence_size'" class="sq-col sq-col--size">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="sq-col-title">
|
||||||
|
<clr-icon shape="ruler"></clr-icon> {{ 'SEQUENCE_SIZE' | translate }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="'suffix'" class="sq-col sq-col--suffix">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="sq-col-title">
|
||||||
|
<clr-icon shape="text"></clr-icon> {{ 'SUFFIX' | translate }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="'starting_no'" class="sq-col sq-col--starting">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="sq-col-title">
|
||||||
|
<clr-icon shape="play"></clr-icon> {{ 'STARTING_NO' | translate }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-column [clrDgField]="'sequence_code'" class="sq-col sq-col--code">
|
||||||
|
<ng-container *clrDgHideableColumn="{hidden: false}">
|
||||||
|
<span class="sq-col-title">
|
||||||
|
<clr-icon shape="code"></clr-icon> {{ 'SEQUENCE_CODE' | translate }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</clr-dg-column>
|
||||||
|
|
||||||
|
<clr-dg-row *clrDgItems="let user of filteredSequences" [clrDgItem]="user">
|
||||||
|
<clr-dg-cell>{{user.current_no}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.sequence_name}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.prefix}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.seperator}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.sequence_size}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.suffix}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.starting_no}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{user.sequence_code}}</clr-dg-cell>
|
||||||
|
<clr-dg-action-overflow>
|
||||||
|
<button class="sq-action-item" (click)="goToEdit(user)">
|
||||||
|
{{ 'EDIT' | translate }}
|
||||||
|
<clr-icon shape="edit"></clr-icon>
|
||||||
|
</button>
|
||||||
|
<button class="sq-action-item sq-btn-error" (click)="onDelete(user)">
|
||||||
|
{{ 'DELETE' | translate }}
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
</button>
|
||||||
|
</clr-dg-action-overflow>
|
||||||
|
</clr-dg-row>
|
||||||
|
|
||||||
|
<clr-dg-footer class="sq-grid-footer">
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="10">
|
||||||
|
<clr-dg-page-size [clrPageSizeOptions]="[10,20,50,100]">
|
||||||
|
{{ 'RECORDS_PER_PAGE' | translate }}
|
||||||
|
</clr-dg-page-size>
|
||||||
|
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
|
||||||
|
{{ 'OF' | translate }} {{pagination.totalItems}} {{ 'RECORDS' | translate }}
|
||||||
|
</clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
|
||||||
|
<div class="sq-cards" *ngIf="viewMode==='cards'">
|
||||||
|
<div class="sq-card-item" *ngFor="let user of filteredSequences">
|
||||||
|
<div class="sq-card-item__header">
|
||||||
|
<div class="sq-card-item__icon"><clr-icon shape="number-list"></clr-icon></div>
|
||||||
|
<div class="sq-card-item__title">{{user.sequence_name}}</div>
|
||||||
|
<div class="sq-card-item__badge">{{user.current_no}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="sq-card-item__body">
|
||||||
|
<div class="sq-kv"><span>{{ 'PREFIX' | translate }}</span><strong>{{user.prefix}}</strong></div>
|
||||||
|
<div class="sq-kv"><span>{{ 'SEPARATOR' | translate }}</span><strong>{{user.seperator}}</strong></div>
|
||||||
|
<div class="sq-kv"><span>{{ 'SEQUENCE_SIZE' | translate }}</span><strong>{{user.sequence_size}}</strong></div>
|
||||||
|
<div class="sq-kv" *ngIf="user.suffix"><span>{{ 'SUFFIX' | translate }}</span><strong>{{user.suffix}}</strong></div>
|
||||||
|
<div class="sq-kv" *ngIf="user.starting_no"><span>{{ 'STARTING_NO' | translate }}</span><strong>{{user.starting_no}}</strong></div>
|
||||||
|
<div class="sq-kv" *ngIf="user.sequence_code"><span>{{ 'SEQUENCE_CODE' | translate }}</span><strong>{{user.sequence_code}}</strong></div>
|
||||||
|
<div class="sq-kv" *ngIf="user.demonstration"><span>{{ 'DEMONSTRATION' | translate }}</span><strong>{{user.demonstration}}</strong></div>
|
||||||
|
</div>
|
||||||
|
<div class="sq-card-item__footer">
|
||||||
|
<button class="sq-btn sq-btn-sm sq-btn-outline" (click)="goToEdit(user)">
|
||||||
|
<clr-icon shape="edit"></clr-icon> {{ 'EDIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button class="sq-btn sq-btn-sm sq-btn-outline" (click)="onDelete(user)">
|
||||||
|
<clr-icon shape="trash"></clr-icon> {{ 'DELETE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
<clr-modal class="sq-modal" [(clrModalOpen)]="modaldelete" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="sq-delete-header">
|
||||||
|
<div class="sq-delete-icon">
|
||||||
|
<clr-icon shape="exclamation-triangle" size="48"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<h1 class="sq-delete-title">{{ 'ARE_YOU_SURE_WANT_TO_DELETE' | translate }}?</h1>
|
||||||
|
<p class="sq-delete-subtitle">{{ 'THIS_ACTION_CANNOT_BE_UNDONE' | translate }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="sq-delete-details" *ngIf="rowSelected.id">
|
||||||
|
<div class="sq-delete-detail-item">
|
||||||
|
<span class="sq-delete-detail-label">{{ 'SEQUENCE_ID' | translate }}:</span>
|
||||||
|
<span class="sq-delete-detail-value">{{rowSelected.id}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="sq-delete-detail-item">
|
||||||
|
<span class="sq-delete-detail-label">{{ 'SEQUENCE_NAME' | translate }}:</span>
|
||||||
|
<span class="sq-delete-detail-value">{{rowSelected.sequence_name}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="sq-btn sq-btn-outline" (click)="modaldelete = false">
|
||||||
|
<clr-icon shape="times"></clr-icon>
|
||||||
|
<span>{{ 'CANCEL' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" (click)="delete(rowSelected.id)" class="sq-btn sq-btn-error">
|
||||||
|
<clr-icon shape="trash"></clr-icon>
|
||||||
|
<span>{{ 'DELETE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
|
||||||
<div class="modal-body" *ngIf="rowSelected.id">
|
<clr-modal class="sq-modal" [(clrModalOpen)]="modaladd" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
<h1 class="delete">Are You Sure Want to delete?</h1>
|
<h3 class="modal-title sq-modal-title">
|
||||||
<h2 class="heading">{{rowSelected.id}}</h2>
|
<clr-icon shape="plus"></clr-icon> {{ 'ADD_SEQUENCE_GENERATOR' | translate }}
|
||||||
<div class="modal-footer">
|
</h3>
|
||||||
<button type="button" class="btn btn-outline" (click)="modaldelete = false">Cancel</button>
|
<div class="modal-body">
|
||||||
<button type="submit" (click)="delete(rowSelected.id)" class="btn btn-primary">Delete</button>
|
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()" class="sq-form">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="sequence_name" class="sq-form-label">
|
||||||
|
{{ 'NAME' | translate }}<span class="required-field">*</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" id="sequence_name" class="sq-form-input" name="sequence_name" formControlName="sequence_name">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.sequence_name.errors" class="sq-error-message">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.sequence_name.errors.required">
|
||||||
|
{{ 'THIS_FIELD_IS_REQUIRED' | translate }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="sequence_code" class="sq-form-label">
|
||||||
|
{{ 'SEQUENCE_CODE' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="sequence_code" class="sq-form-input" name="sequence_code" formControlName="sequence_code">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="demonstration" class="sq-form-label">
|
||||||
|
{{ 'DEMONSTRATION' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="demonstration" class="sq-form-input" formControlName="demonstration">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="prefix" class="sq-form-label">
|
||||||
|
{{ 'PREFIX' | translate }}<span class="required-field">*</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" id="prefix" class="sq-form-input" formControlName="prefix" name="prefix">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.prefix.errors" class="sq-error-message">
|
||||||
|
<div *ngIf="submitted && entryForm.controls.prefix.errors.required">
|
||||||
|
{{ 'THIS_FIELD_IS_REQUIRED' | translate }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="suffix" class="sq-form-label">
|
||||||
|
{{ 'SUFFIX' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="suffix" class="sq-form-input" name="suffix" formControlName="suffix">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="seperator" class="sq-form-label">
|
||||||
|
{{ 'SEPARATOR' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="seperator" class="sq-form-input" name="seperator" formControlName="seperator">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="starting_no" class="sq-form-label">
|
||||||
|
{{ 'STARTING_NO' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="number" id="starting_no" class="sq-form-input" name="starting_no" formControlName="starting_no">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="current_no" class="sq-form-label">
|
||||||
|
{{ 'CURRENT_NO' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="number" id="current_no" class="sq-form-input" formControlName="current_no">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="sequence_size" class="sq-form-label">
|
||||||
|
{{ 'SEQUENCE_SIZE' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="number" id="sequence_size" class="sq-form-input" name="sequence_size" formControlName="sequence_size">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sq-form-actions">
|
||||||
|
<button type="button" class="sq-btn sq-btn-outline" (click)="modaladd = false">
|
||||||
|
<clr-icon shape="times"></clr-icon>
|
||||||
|
<span>{{ 'CANCEL' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="sq-btn sq-btn-primary">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
<span>{{ 'ADD' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
|
|
||||||
|
<clr-modal class="sq-modal" [(clrModalOpen)]="modaledit" [clrModalSize]="'lg'" [clrModalStaticBackdrop]="true">
|
||||||
<clr-modal [(clrModalOpen)]="modaladd" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
<h3 class="modal-title sq-modal-title">
|
||||||
<h3 class="modal-title">Add Sequence Genarator</h3>
|
<clr-icon shape="edit"></clr-icon> {{ 'UPDATE_SEQUENCE_GENERATOR' | translate }}
|
||||||
<div class="modal-body">
|
</h3>
|
||||||
<form [formGroup]="entryForm" (ngSubmit)="onSubmit()">
|
<div class="modal-body" *ngIf="rowSelected.id">
|
||||||
<div class="clr-row">
|
<div class="sq-form-help">
|
||||||
<div class="clr-col-sm-12">
|
{{ 'SEQUENCE_ID' | translate }}: <code class="clr-code">{{rowSelected.id}}</code>
|
||||||
<label for="name">Name<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" name="sequence_name" formControlName="sequence_name">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">Sequence Code</label>
|
|
||||||
<input type="text" class="clr-input" name="sequence_code" formControlName="sequence_code">
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-sm-12">
|
|
||||||
<label for="name">Implementation</label>
|
|
||||||
<select name="implementation" formControlName="implementation" class="clr-dropdown">
|
|
||||||
<option>standard</option>
|
|
||||||
<option>shared</option>
|
|
||||||
</select>
|
|
||||||
</div> -->
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Demonstration</label>
|
|
||||||
<input type="text" class="clr-input" formControlName="demonstration">
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-sm-12">
|
|
||||||
<label for="name">Active</label>
|
|
||||||
<input type="checkbox" formControlName="active" name="active" clrCheckbox>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">Prefix<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" formControlName="prefix" name="prefix">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">suffix</label>
|
|
||||||
<input type="text" class="clr-input" name="suffix" formControlName="suffix">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Separator</label>
|
|
||||||
<input type="text" class="clr-input" name="seperator" formControlName="seperator">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Starting No</label>
|
|
||||||
<input type="number" class="clr-input" name="starting_no" formControlName="starting_no">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Current No</label>
|
|
||||||
<input type="number" class="clr-input" formControlName="current_no">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags"> Size</label>
|
|
||||||
<input type="number" class="clr-input" name="sequence_size" formControlName="sequence_size">
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Date Format</label>
|
|
||||||
<input type="text" class="clr-input" name="date_format" formControlName="date_format">
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline" (click)="modaladd = false">Cancel</button>
|
|
||||||
<button type="submit" class="btn btn-primary">ADD</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</clr-modal>
|
|
||||||
|
|
||||||
<clr-modal [(clrModalOpen)]="modaledit" [clrModalSize]="'md'" [clrModalStaticBackdrop]="true">
|
|
||||||
<h3 class="modal-title">Update Sequence Genarator</h3>
|
|
||||||
<div class="modal-body" *ngIf="rowSelected.id">
|
|
||||||
<h2 class="heading">{{rowSelected.id}}</h2>
|
|
||||||
<form (ngSubmit)="onUpdate(rowSelected.id)">
|
|
||||||
<div class="clr-row">
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">Name<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" name="sequence_name" [(ngModel)]="rowSelected.sequence_name">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">Sequence Code</label>
|
|
||||||
<input type="text" class="clr-input" name="sequence_code" [(ngModel)]="rowSelected.sequence_code">
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clr-col-sm-12">
|
|
||||||
<label for="name">Implementation</label>
|
|
||||||
<select name="implementation" class="clr-dropdown" [(ngModel)]="rowSelected.implementation">
|
|
||||||
<option>standard</option>
|
|
||||||
<option>shared</option>
|
|
||||||
</select>
|
|
||||||
</div> -->
|
|
||||||
<!-- <div class="clr-col-sm-12">
|
|
||||||
<label for="name">Active</label>
|
|
||||||
<input type="checkbox" name="active" name="menuId" clrCheckbox [(ngModel)]="rowSelected.active">
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="name">Prefix<span class="required-field">*</span></label>
|
|
||||||
<input type="text" class="clr-input" name="prefix" [(ngModel)]="rowSelected.prefix">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">suffix</label>
|
|
||||||
<input type="text" class="clr-input" name="suffix" [(ngModel)]="rowSelected.suffix">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Separator</label>
|
|
||||||
<input type="text" class="clr-input" name="seperator" [(ngModel)]="rowSelected.seperator">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Starting No</label>
|
|
||||||
<input type="number" class="clr-input" name="starting_no" [(ngModel)]="rowSelected.starting_no">
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Current No</label>
|
|
||||||
<input type="number" class="clr-input" name="current_no" [(ngModel)]="rowSelected.current_no"
|
|
||||||
readonly>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Demonstration</label>
|
|
||||||
<input type="text" class="clr-input" name="demonstration" [(ngModel)]="rowSelected.demonstration"
|
|
||||||
readonly>
|
|
||||||
</div>
|
|
||||||
<div class="clr-col-sm-12">
|
|
||||||
<label for="tags">Sequence Size</label>
|
|
||||||
<input type="number" class="clr-input" name="sequence_size" [(ngModel)]="rowSelected.sequence_size">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline" (click)="modaledit = false">Cancel</button>
|
|
||||||
<button type="submit" class="btn btn-primary">Update</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<form (ngSubmit)="onUpdate(rowSelected.id)" class="sq-form">
|
||||||
|
<div class="clr-row">
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="sequence_name" class="sq-form-label">
|
||||||
|
{{ 'NAME' | translate }}<span class="required-field">*</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" id="sequence_name" class="sq-form-input" name="sequence_name" [(ngModel)]="rowSelected.sequence_name">
|
||||||
|
<div class="sq-error-message" *ngIf="!rowSelected.sequence_name">
|
||||||
|
{{ 'THIS_FIELD_IS_REQUIRED' | translate }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="sequence_code" class="sq-form-label">
|
||||||
|
{{ 'SEQUENCE_CODE' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="sequence_code" class="sq-form-input" name="sequence_code" [(ngModel)]="rowSelected.sequence_code">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="demonstration" class="sq-form-label">
|
||||||
|
{{ 'DEMONSTRATION' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="demonstration" class="sq-form-input" name="demonstration" [(ngModel)]="rowSelected.demonstration" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="prefix" class="sq-form-label">
|
||||||
|
{{ 'PREFIX' | translate }}<span class="required-field">*</span>
|
||||||
|
</label>
|
||||||
|
<input type="text" id="prefix" class="sq-form-input" name="prefix" [(ngModel)]="rowSelected.prefix">
|
||||||
|
<div class="sq-error-message" *ngIf="!rowSelected.prefix">
|
||||||
|
{{ 'THIS_FIELD_IS_REQUIRED' | translate }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="suffix" class="sq-form-label">
|
||||||
|
{{ 'SUFFIX' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="suffix" class="sq-form-input" name="suffix" [(ngModel)]="rowSelected.suffix">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="seperator" class="sq-form-label">
|
||||||
|
{{ 'SEPARATOR' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="text" id="seperator" class="sq-form-input" name="seperator" [(ngModel)]="rowSelected.seperator">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="starting_no" class="sq-form-label">
|
||||||
|
{{ 'STARTING_NO' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="number" id="starting_no" class="sq-form-input" name="starting_no" [(ngModel)]="rowSelected.starting_no">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="current_no" class="sq-form-label">
|
||||||
|
{{ 'CURRENT_NO' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="number" id="current_no" class="sq-form-input" name="current_no" [(ngModel)]="rowSelected.current_no" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
|
<label for="sequence_size" class="sq-form-label">
|
||||||
|
{{ 'SEQUENCE_SIZE' | translate }}
|
||||||
|
</label>
|
||||||
|
<input type="number" id="sequence_size" class="sq-form-input" name="sequence_size" [(ngModel)]="rowSelected.sequence_size">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sq-form-actions">
|
||||||
|
<button type="button" class="sq-btn sq-btn-outline" (click)="modaledit = false">
|
||||||
|
<clr-icon shape="times"></clr-icon>
|
||||||
|
<span>{{ 'CANCEL' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="sq-btn sq-btn-primary">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
<span>{{ 'UPDATE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</clr-modal>
|
</clr-modal>
|
||||||
@@ -1,72 +1,694 @@
|
|||||||
input[type=text],[type=number],
|
@import '../../../../../styles/_design-tokens.scss';
|
||||||
[type=date],
|
|
||||||
[type=password] {
|
// Sequence Generator Styles
|
||||||
|
.sq-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24px 32px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
font-family: var(--theme-font-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-container {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toolbar, search and stats
|
||||||
|
.sq-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-search {
|
||||||
|
position: relative;
|
||||||
|
max-width: 400px;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 12px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: #9ca3af;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 15px 20px;
|
padding: 12px 12px 12px 40px;
|
||||||
// margin: 3px 0;
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
background-color: rgb(255, 255, 255);
|
border-radius: 8px;
|
||||||
display: inline-block;
|
font-size: 14px;
|
||||||
border: 1px solid #ccc;
|
transition: all 200ms ease-out;
|
||||||
border-radius: 4px;
|
background: var(--theme-surface);
|
||||||
box-sizing: border-box;
|
color: var(--theme-text);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-stat {
|
||||||
|
min-width: 110px;
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||||
|
border-radius: var(--theme-border-radius);
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: var(--theme-shadow);
|
||||||
|
|
||||||
|
&__value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-view-toggle {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.sq-btn {
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid columns
|
||||||
|
.sq-col-title {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-col--prefix .sq-col-title clr-icon,
|
||||||
|
.sq-col--separator .sq-col-title clr-icon,
|
||||||
|
.sq-col--size .sq-col-title clr-icon {
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data Grid Styles
|
||||||
|
.sq-grid {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
::ng-deep .datagrid {
|
||||||
|
.datagrid-head {
|
||||||
|
background: var(--theme-background);
|
||||||
|
|
||||||
|
.datagrid-column {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-row {
|
||||||
|
transition: background-color 150ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-cell {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datagrid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
padding: 16px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-grid-footer {
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card View Styles
|
||||||
|
.sq-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-card-item {
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 12px;
|
||||||
|
margin-right: 16px;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__badge {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-kv {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--theme-text);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form Styles
|
||||||
|
.sq-form {
|
||||||
|
.clr-row {
|
||||||
|
margin: 0 -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clr-col-md-6,
|
||||||
|
.clr-col-sm-12 {
|
||||||
|
padding: 0 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-form-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-form-input,
|
||||||
|
.sq-form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--theme-text);
|
||||||
|
background: var(--theme-surface);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--theme-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-form-select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 12px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px 12px;
|
||||||
|
padding-right: 40px;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-error-message {
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.required-field {
|
.required-field {
|
||||||
color: red;
|
color: var(--theme-error, #ef4444);
|
||||||
font-size: 18px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.green {
|
.sq-form-help {
|
||||||
background-color: rgb(156, 231, 156);
|
color: var(--theme-text-secondary);
|
||||||
color: black;
|
font-size: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.blue {
|
.sq-form-actions {
|
||||||
background-color: #57abcf; //rgb(82, 87, 161);
|
display: flex;
|
||||||
color: black;
|
gap: 12px;
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 24px;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-title {
|
// Modern Button Styles using ThemeService
|
||||||
text-align: center;
|
.sq-btn {
|
||||||
width: 150px;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
text-decoration: none;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: var(--theme-font-primary);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid var(--theme-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizes
|
||||||
|
&.sq-btn-sm {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sq-btn-md {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sq-btn-lg {
|
||||||
|
padding: 16px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variants
|
||||||
|
&.sq-btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: bold;
|
border-color: var(--theme-primary);
|
||||||
background-color: rgba(63, 122, 231, 0.863);
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
//color: rgb(24, 13, 13);
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary, #0284c7) 0%, var(--theme-accent, #7c3aed) 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sq-btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(100, 116, 139, 0.1);
|
||||||
|
border-color: var(--theme-secondary);
|
||||||
|
color: var(--theme-secondary);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sq-btn-error {
|
||||||
|
background: var(--theme-error, #ef4444);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--theme-error, #ef4444);
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-error-dark, #dc2626);
|
||||||
|
border-color: var(--theme-error-dark, #dc2626);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(239, 68, 68, 0.3), 0 4px 6px -2px rgba(239, 68, 68, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sq-btn-ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--theme-background);
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
// Action items in datagrid
|
||||||
//background-color:rgb(170, 169, 169);
|
.sq-action-item {
|
||||||
font-weight: bold;
|
@extend .sq-btn;
|
||||||
|
@extend .sq-btn-ghost;
|
||||||
|
@extend .sq-btn-sm;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sq-btn-error {
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
color: var(--theme-error, #ef4444);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-content {
|
// Delete Modal Styles
|
||||||
text-align: left;
|
.sq-delete-header {
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.delete,
|
.sq-delete-icon {
|
||||||
.heading {
|
color: var(--theme-error, #ef4444);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-delete-title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--theme-text);
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-delete-subtitle {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-delete-details {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-delete-detail-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-delete-detail-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-delete-detail-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal Styles
|
||||||
|
.sq-modal-title clr-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
color: var(--theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .modal-header {
|
||||||
|
background: linear-gradient(135deg, var(--theme-primary) 0%, var(--theme-accent) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 16px 16px 0 0 !important;
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .modal-body {
|
||||||
|
padding: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .modal-footer {
|
||||||
|
padding: 20px 24px !important;
|
||||||
|
background: var(--theme-background);
|
||||||
|
border-radius: 0 0 16px 16px !important;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Breadcrumb
|
||||||
|
.mm-breadcrumb {
|
||||||
|
color: var(--theme-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive adjustments
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sq-hero {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section p {
|
&__content {
|
||||||
background-color: rgb(206, 201, 201);
|
flex-direction: column;
|
||||||
padding: 10px;
|
text-align: center;
|
||||||
font-size: 18px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
&__actions {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-search {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-stats {
|
||||||
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 5px 5px;
|
}
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.ng-invalid.ng-touched {
|
.sq-cards {
|
||||||
border-color: red;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error_mess {
|
.sq-form-actions {
|
||||||
color: red;
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-btn {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sq-card-item__footer {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { SequenceService } from 'src/app/services/api/sequence.service';
|
import { SequenceService } from 'src/app/services/api/sequence.service';
|
||||||
import { ExcelService } from 'src/app/services/excel.service';
|
import { ExcelService } from 'src/app/services/excel.service';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sequencegenarator',
|
selector: 'app-sequencegenarator',
|
||||||
templateUrl: './sequencegenarator.component.html',
|
templateUrl: './sequencegenarator.component.html',
|
||||||
@@ -22,27 +24,70 @@ export class SequencegenaratorComponent implements OnInit {
|
|||||||
modaladd = false;
|
modaladd = false;
|
||||||
modaledit = false;
|
modaledit = false;
|
||||||
submitted = false;
|
submitted = false;
|
||||||
|
filterText = '';
|
||||||
|
// Changed default view to cards
|
||||||
|
viewMode: 'table' | 'cards' = 'cards';
|
||||||
|
|
||||||
public entryForm: FormGroup;
|
public entryForm: FormGroup;
|
||||||
constructor(private seqservice: SequenceService, private router: Router, private toastr: ToastrService, private _fb: FormBuilder,
|
|
||||||
private route: ActivatedRoute, private excel: ExcelService) { }
|
constructor(
|
||||||
|
private seqservice: SequenceService,
|
||||||
|
private router: Router,
|
||||||
|
private toastr: ToastrService,
|
||||||
|
private _fb: FormBuilder,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private excel: ExcelService,
|
||||||
|
private themeService: ThemeService
|
||||||
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
// Ensure theme variables are applied to the view
|
||||||
|
this.themeService.currentTheme$.subscribe(() => {
|
||||||
|
// CSS variables are updated globally; no extra handling needed here
|
||||||
|
});
|
||||||
|
|
||||||
this.getall();
|
this.getall();
|
||||||
this.entryForm = this._fb.group({
|
this.entryForm = this._fb.group({
|
||||||
prefix: [null],
|
prefix: [null, Validators.required],
|
||||||
sequence_size: [null],
|
sequence_size: [null],
|
||||||
suffix: [null],
|
suffix: [null],
|
||||||
starting_no: [null],
|
starting_no: [null],
|
||||||
seperator: [null],
|
seperator: [null],
|
||||||
|
sequence_name: [null, Validators.required],
|
||||||
sequence_name: [null],
|
|
||||||
sequence_code: [null],
|
sequence_code: [null],
|
||||||
demonstration: [null],
|
demonstration: [null],
|
||||||
|
|
||||||
current_no: [null]
|
current_no: [null]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats & filtering helpers for UI only
|
||||||
|
get totalSequences(): number {
|
||||||
|
return ((this.alldata as unknown as any[]) || []).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
get filteredSequences(): any[] {
|
||||||
|
const items: any[] = (this.alldata as unknown as any[]) || [];
|
||||||
|
const text = (this.filterText || '').toLowerCase();
|
||||||
|
if (!text) { return items; }
|
||||||
|
|
||||||
|
// Search across all relevant fields
|
||||||
|
return items.filter(s => (
|
||||||
|
(s?.sequence_name || '').toLowerCase().includes(text) ||
|
||||||
|
(s?.prefix || '').toLowerCase().includes(text) ||
|
||||||
|
(s?.sequence_code || '').toLowerCase().includes(text) ||
|
||||||
|
(s?.suffix || '').toLowerCase().includes(text) ||
|
||||||
|
(s?.seperator || '').toLowerCase().includes(text) ||
|
||||||
|
(s?.sequence_size?.toString() || '').includes(text) ||
|
||||||
|
(s?.starting_no?.toString() || '').includes(text) ||
|
||||||
|
(s?.current_no?.toString() || '').includes(text) ||
|
||||||
|
(s?.demonstration || '').toLowerCase().includes(text)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewMode(mode: 'table' | 'cards') {
|
||||||
|
this.viewMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
getall() {
|
getall() {
|
||||||
this.seqservice.getAll().subscribe((data) => {
|
this.seqservice.getAll().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@@ -57,27 +102,8 @@ export class SequencegenaratorComponent implements OnInit {
|
|||||||
if (error) {
|
if (error) {
|
||||||
this.error = "No data Available OR server Error";
|
this.error = "No data Available OR server Error";
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// onSubmit(){
|
|
||||||
// console.log(this.entryForm.value);
|
|
||||||
// this.seqservice.create(this.entryForm.value).subscribe((data)=>{
|
|
||||||
// console.log(data);
|
|
||||||
// if (data) {
|
|
||||||
// this.toastr.success('Added successfully');
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// (error) => {
|
|
||||||
// console.log('Error in adding data...',+error);
|
|
||||||
// if(error){
|
|
||||||
// this.toastr.error('Not added Data');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// });
|
|
||||||
// this.modaladd=false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
console.log(this.entryForm.value);
|
console.log(this.entryForm.value);
|
||||||
@@ -86,10 +112,8 @@ export class SequencegenaratorComponent implements OnInit {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.onCreate();
|
this.onCreate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.modaladd = false;
|
this.modaladd = false;
|
||||||
this.seqservice.create(this.entryForm.value).subscribe(
|
this.seqservice.create(this.entryForm.value).subscribe(
|
||||||
@@ -115,22 +139,24 @@ export class SequencegenaratorComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
goToAdd() {
|
goToAdd() {
|
||||||
this.modaladd = true;
|
this.modaladd = true;
|
||||||
|
this.submitted = false;
|
||||||
|
// Reset form
|
||||||
|
this.entryForm.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
onExport() {
|
onExport() {
|
||||||
this.excel.exportAsExcelFile(this.alldata, 'user_',
|
this.excel.exportAsExcelFile(this.alldata, 'sequence_',
|
||||||
moment().format('YYYYMMDD_HHmmss'))
|
moment().format('YYYYMMDD_HHmmss'))
|
||||||
}
|
}
|
||||||
|
|
||||||
goToEdit(row) {
|
goToEdit(row) {
|
||||||
this.rowSelected = row;
|
this.rowSelected = { ...row }; // Create a copy to avoid direct binding issues
|
||||||
console.log(row)
|
console.log(row)
|
||||||
this.modaledit = true;
|
this.modaledit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDelete(row) {
|
onDelete(row) {
|
||||||
this.rowSelected = row;
|
this.rowSelected = row;
|
||||||
this.modaldelete = true;
|
this.modaldelete = true;
|
||||||
@@ -161,8 +187,8 @@ export class SequencegenaratorComponent implements OnInit {
|
|||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdate(id) {
|
onUpdate(id) {
|
||||||
this.modaledit = false;
|
this.modaledit = false;
|
||||||
this.seqservice.update(id, this.rowSelected).subscribe((data) => {
|
this.seqservice.update(id, this.rowSelected).subscribe((data) => {
|
||||||
|
|||||||
@@ -1,38 +1,74 @@
|
|||||||
<clr-main-container>
|
<clr-main-container class="modern-layout">
|
||||||
<clr-header class="header-5">
|
<clr-header class="header-5 modern-header">
|
||||||
<div class="branding">
|
<div class="branding modern-branding">
|
||||||
<a href="javascript://" class="nav-link">
|
<a href="javascript://" class="nav-link modern-nav-link">
|
||||||
<a href="#" class="logo">
|
<div class="logo-container">
|
||||||
<img class="img" src="assets/images/icon/micrologo.png" alt="" height="50" width="50">
|
<div class="logo-wrapper">
|
||||||
</a>
|
<img class="logo-img" src="assets/images/icon/micrologo.png" alt="Logo" height="40" width="40">
|
||||||
<div style="width:40px;margin-right:10px;">
|
<div class="logo-glow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="sysparameter?.company_Display_Name !== null; else OPtitle">
|
<div class="brand-text">
|
||||||
<span class="title">{{ sysparameter?.company_Display_Name }}</span>
|
<ng-container *ngIf="sysparameter?.company_Display_Name !== null; else OPtitle">
|
||||||
</ng-container>
|
<span class="company-title">{{ sysparameter?.company_Display_Name }}</span>
|
||||||
<ng-template #OPtitle>
|
<span class="company-subtitle">Enterprise Platform</span>
|
||||||
<span class="title">CloudnSure</span>
|
</ng-container>
|
||||||
</ng-template>
|
<ng-template #OPtitle>
|
||||||
|
<span class="company-title">CloudnSure</span>
|
||||||
|
<span class="company-subtitle">Enterprise Platform</span>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="header-nav" [clr-nav-level]="1">
|
<div class="header-nav modern-header-nav" [clr-nav-level]="1">
|
||||||
<a href="javascript://" id="icon" class=" nav-link nav-icon" routerLinkActive="active" routerLink="/cns-portal/dashboard" (click)="loadMenuByMenuGroup()">
|
<a href="javascript://" class="nav-link nav-icon modern-nav-icon" routerLinkActive="active"
|
||||||
<clr-icon shape="home"></clr-icon>
|
routerLink="/cns-portal/dashboard" (click)="loadMenuByMenuGroup()">
|
||||||
|
<div class="nav-icon-wrapper">
|
||||||
|
<clr-icon shape="home"></clr-icon>
|
||||||
|
<span class="nav-tooltip">Home</span>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="javascript://" id="icon" class=" nav-link nav-icon" routerLinkActive="active" routerLink="/cns-portal/dashboardrunner" >
|
<a href="javascript://" class="nav-link nav-icon modern-nav-icon" routerLinkActive="active"
|
||||||
<clr-icon shape="grid-view" solid></clr-icon>
|
routerLink="/cns-portal/dashboardrunner">
|
||||||
|
<div class="nav-icon-wrapper">
|
||||||
|
<clr-icon shape="grid-view" solid></clr-icon>
|
||||||
|
<span class="nav-tooltip">Dashboard</span>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="javascript://" id="icon" class=" nav-link nav-icon" routerLinkActive="active" routerLink="/cns-portal/rerunner/all" (click)="getName()">
|
<a href="javascript://" class="nav-link nav-icon modern-nav-icon" routerLinkActive="active"
|
||||||
<clr-icon shape="file-group" solid="true"></clr-icon>
|
routerLink="/cns-portal/shield-dashboard">
|
||||||
|
<div class="nav-icon-wrapper">
|
||||||
|
<clr-icon shape="shield" solid></clr-icon>
|
||||||
|
<span class="nav-tooltip">Shield Dashboard</span>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="javascript://" id="icon" class=" nav-link nav-icon" routerLinkActive="active" routerLink="/cns-portal/setupicon" (click)="toggleSetupMenus()">
|
<a href="javascript://" class="nav-link nav-icon modern-nav-icon" routerLinkActive="active"
|
||||||
<clr-icon shape="cog" solid="true"></clr-icon>
|
routerLink="/cns-portal/rerunner/all" (click)="getName()">
|
||||||
|
<div class="nav-icon-wrapper">
|
||||||
|
<clr-icon shape="file-group" solid="true"></clr-icon>
|
||||||
|
<span class="nav-tooltip">Reports</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="javascript://" class="nav-link nav-icon modern-nav-icon" routerLinkActive="active"
|
||||||
|
routerLink="/cns-portal/setupicon" (click)="toggleSetupMenus()">
|
||||||
|
<div class="nav-icon-wrapper">
|
||||||
|
<clr-icon shape="cog" solid="true"></clr-icon>
|
||||||
|
<span class="nav-tooltip">Settings</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="javascript://" class="nav-link nav-icon modern-nav-icon" routerLinkActive="active"
|
||||||
|
routerLink="/cns-portal/theme-customization">
|
||||||
|
<div class="nav-icon-wrapper">
|
||||||
|
<clr-icon shape="palette" solid="true"></clr-icon>
|
||||||
|
<span class="nav-tooltip">Theme Customization</span>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -42,103 +78,158 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="header-actions">
|
<div class="header-actions modern-header-actions">
|
||||||
<!-- <span (click)="restartTour()" style="display: flex;align-items: center;margin-right: 20px;">
|
<clr-dropdown class="modern-user-dropdown">
|
||||||
<clr-icon shape="play"></clr-icon>
|
<button class="modern-user-button" clrDropdownTrigger>
|
||||||
</span> -->
|
<div class="user-avatar">
|
||||||
<clr-dropdown>
|
<div class="avatar-circle">
|
||||||
<button class="nav-text demo-title" clrDropdownTrigger>
|
<clr-icon shape="user"></clr-icon>
|
||||||
<clr-icon shape="user"></clr-icon><label style="line-height: 60px; cursor: pointer;"> {{userName}}</label>
|
</div>
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
<div class="user-status-indicator"></div>
|
||||||
|
</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<span class="user-name">{{userName}}</span>
|
||||||
|
<span class="user-role">Administrator</span>
|
||||||
|
</div>
|
||||||
|
<clr-icon shape="caret down" class="dropdown-arrow"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown-menu *clrIfOpen clrPosition="bottom-right">
|
<clr-dropdown-menu *clrIfOpen clrPosition="bottom-right" class="modern-dropdown-menu">
|
||||||
<a href="javascript://" clrDropdownItem routerLink="/cns-portal/about">About</a>
|
<div class="dropdown-header">
|
||||||
<a href="javascript://" clrDropdownItem routerLink="profile-settings">Profile Settings</a>
|
<div class="user-profile">
|
||||||
|
<div class="profile-avatar">
|
||||||
|
<clr-icon shape="user"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="profile-info">
|
||||||
|
<span class="profile-name">{{userName}}</span>
|
||||||
|
<span class="profile-email">admin@cloudnsure.com</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
|
||||||
<!-- Language Dropdown Section -->
|
<a href="javascript://" clrDropdownItem routerLink="/cns-portal/about" class="modern-dropdown-item">
|
||||||
<clr-dropdown class="sub-dropdown">
|
<clr-icon shape="info-circle"></clr-icon>
|
||||||
<button clrDropdownTrigger style="display: flex; align-items: center; padding: 8px; width: 100%; background: none; border: none; cursor: pointer;">
|
<span>About</span>
|
||||||
<clr-icon shape="language" class="language-icon" style="margin-right: 5px;"></clr-icon>
|
</a>
|
||||||
<label style="margin: 0;">{{ 'Choose language' | translate }}</label>
|
<a href="javascript://" clrDropdownItem routerLink="profile-settings" class="modern-dropdown-item">
|
||||||
<clr-icon shape="caret down" style="margin-left: auto;"></clr-icon>
|
<clr-icon shape="cog"></clr-icon>
|
||||||
|
<span>Profile Settings</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
|
||||||
|
<!-- Language Dropdown Section -->
|
||||||
|
<clr-dropdown class="sub-dropdown modern-language-dropdown">
|
||||||
|
<button clrDropdownTrigger class="modern-language-button">
|
||||||
|
<clr-icon shape="language" class="language-icon"></clr-icon>
|
||||||
|
<span>{{ 'Choose language' | translate }}</span>
|
||||||
|
<clr-icon shape="caret down" class="dropdown-arrow"></clr-icon>
|
||||||
</button>
|
</button>
|
||||||
<clr-dropdown-menu *clrIfOpen clrPosition="right">
|
<clr-dropdown-menu *clrIfOpen clrPosition="right" class="modern-language-menu">
|
||||||
<a href="javascript://" clrDropdownItem (click)="switchLanguage('en')" class="lang-item" style="display: flex; align-items: center; padding: 8px 12px;">
|
<a href="javascript://" clrDropdownItem (click)="switchLanguage('en')" class="modern-lang-item">
|
||||||
<clr-icon shape="globe" class="lang-icon"></clr-icon> English
|
<clr-icon shape="globe" class="lang-icon"></clr-icon>
|
||||||
|
<span>English</span>
|
||||||
|
<div class="lang-flag">🇺🇸</div>
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript://" clrDropdownItem (click)="switchLanguage('hi')" class="lang-item" style="display: flex; align-items: center; padding: 8px 12px;">
|
<a href="javascript://" clrDropdownItem (click)="switchLanguage('hi')" class="modern-lang-item">
|
||||||
<clr-icon shape="globe" class="lang-icon"></clr-icon> Hindi
|
<clr-icon shape="globe" class="lang-icon"></clr-icon>
|
||||||
|
<span>हिन्दी</span>
|
||||||
|
<div class="lang-flag">🇮🇳</div>
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript://" clrDropdownItem (click)="switchLanguage('ta')" class="lang-item" style="display: flex; align-items: center; padding: 8px 12px;">
|
<a href="javascript://" clrDropdownItem (click)="switchLanguage('ta')" class="modern-lang-item">
|
||||||
<clr-icon shape="globe" class="lang-icon"></clr-icon> Tamil
|
<clr-icon shape="globe" class="lang-icon"></clr-icon>
|
||||||
|
<span>தமிழ்</span>
|
||||||
|
<div class="lang-flag">🇮🇳</div>
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript://" clrDropdownItem (click)="switchLanguage('pa')" class="lang-item" style="display: flex; align-items: center; padding: 8px 12px;">
|
<a href="javascript://" clrDropdownItem (click)="switchLanguage('pa')" class="modern-lang-item">
|
||||||
<clr-icon shape="globe" class="lang-icon"></clr-icon> Punjabi
|
<clr-icon shape="globe" class="lang-icon"></clr-icon>
|
||||||
|
<span>ਪੰਜਾਬੀ</span>
|
||||||
|
<div class="lang-flag">🇮🇳</div>
|
||||||
</a>
|
</a>
|
||||||
<a href="javascript://" clrDropdownItem (click)="switchLanguage('ml')" class="lang-item" style="display: flex; align-items: center; padding: 8px 12px;">
|
<a href="javascript://" clrDropdownItem (click)="switchLanguage('ml')" class="modern-lang-item">
|
||||||
<clr-icon shape="globe" class="lang-icon"></clr-icon> Malayalam
|
<clr-icon shape="globe" class="lang-icon"></clr-icon>
|
||||||
|
<span>മലയാളം</span>
|
||||||
|
<div class="lang-flag">🇮🇳</div>
|
||||||
</a>
|
</a>
|
||||||
</clr-dropdown-menu>
|
</clr-dropdown-menu>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
<!-- <a href="javascript://" clrDropdownItem routerLink="myworkspace">My Workspaces</a> -->
|
|
||||||
<a href="javascript://" clrDropdownItem routerLink="/cns-portal/passwordreset">Change Password</a>
|
|
||||||
<a href="javascript://" clrDropdownItem (click)="onLogout()">Log out</a>
|
|
||||||
|
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
|
||||||
|
<a href="javascript://" clrDropdownItem routerLink="/cns-portal/passwordreset" class="modern-dropdown-item">
|
||||||
|
<clr-icon shape="key"></clr-icon>
|
||||||
|
<span>Change Password</span>
|
||||||
|
</a>
|
||||||
|
<a href="javascript://" clrDropdownItem (click)="onLogout()" class="modern-dropdown-item logout-item">
|
||||||
|
<clr-icon shape="logout"></clr-icon>
|
||||||
|
<span>Log out</span>
|
||||||
|
</a>
|
||||||
</clr-dropdown-menu>
|
</clr-dropdown-menu>
|
||||||
</clr-dropdown>
|
</clr-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</clr-header>
|
</clr-header>
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="content-container modern-content-container">
|
||||||
<main class="content-area">
|
<main class="content-area modern-content-area">
|
||||||
<router-outlet></router-outlet>
|
<div class="content-wrapper">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<clr-vertical-nav [clrVerticalNavCollapsible]="true" [(clrVerticalNavCollapsed)]="collapsed" [clr-nav-level]="2">
|
<clr-vertical-nav class="modern-sidebar" [clrVerticalNavCollapsible]="true" [(clrVerticalNavCollapsed)]="collapsed"
|
||||||
|
[clr-nav-level]="2">
|
||||||
|
|
||||||
<!-- <clr-vertical-nav-group *ngIf="!showFolderNames && !showSetupMenus && !showDashNames" routerLinkActive="active">
|
<!-- <clr-vertical-nav-group *ngIf="!showFolderNames && !showSetupMenus && !showDashNames" routerLinkActive="active">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</clr-vertical-nav-group> -->
|
</clr-vertical-nav-group> -->
|
||||||
<div *ngFor="let menu of menus">
|
<div class="sidebar-content">
|
||||||
<!-- NEED MODIFICATION-->
|
<div *ngFor="let menu of menus" class="menu-item-wrapper">
|
||||||
<!-- <div *ngIf="menu.sub_menus.length >=1;then content else other_content"></div> -->
|
<!-- NEED MODIFICATION-->
|
||||||
<div *ngIf="menu.subMenus !== null && menu.subMenus?.length >=1;then content else other_content"></div>
|
<!-- <div *ngIf="menu.sub_menus.length >=1;then content else other_content"></div> -->
|
||||||
<ng-template #content>
|
<div *ngIf="menu.subMenus !== null && menu.subMenus?.length >=1;then content else other_content"></div>
|
||||||
<!-- if sub munu is present -->
|
<ng-template #content>
|
||||||
<clr-vertical-nav-group routerLinkActive="active"> <!--*ngIf="!showFolderNames && !showSetupMenus && !showDashNames" -->
|
<!-- if sub menu is present -->
|
||||||
<clr-icon [attr.shape]="menu.main_menu_icon_name" clrVerticalNavIcon></clr-icon>
|
<clr-vertical-nav-group routerLinkActive="active" class="modern-nav-group">
|
||||||
{{ menu.menuItemDesc }}
|
<div class="nav-group-header">
|
||||||
<!-- SUB NAV-->
|
<clr-icon [attr.shape]="menu.main_menu_icon_name" clrVerticalNavIcon class="nav-group-icon"></clr-icon>
|
||||||
<clr-vertical-nav-group-children *ngFor="let subnav of menu.subMenus">
|
<span class="nav-group-title">{{ menu.menuItemDesc }}</span>
|
||||||
<a clrVerticalNavLink *ngIf="subnav.mvisible == 'true'" routerLinkActive="active" (click)="send(subnav)">
|
<clr-icon shape="angle" class="nav-group-arrow" [class.rotated]="false"></clr-icon>
|
||||||
<!-- <clr-icon [attr.shape]="subnav.mainMenuIconName" clrVerticalNavIcon></clr-icon>'./' + subnav.main_menu_action_name (click)="send(subnav) [ngStyle]="subnav.mvisible == 'true' && { 'display': 'none' }"
|
</div>
|
||||||
[routerLink]=subnav.mvisible == 'true' ? ['./' + subnav.main_menu_action_name] : []-->
|
<!-- SUB NAV-->
|
||||||
{{ subnav.menuItemDesc }}
|
<clr-vertical-nav-group-children class="modern-nav-children">
|
||||||
</a>
|
<ng-container *ngFor="let subnav of menu.subMenus">
|
||||||
</clr-vertical-nav-group-children>
|
<a clrVerticalNavLink *ngIf="subnav.mvisible == 'true'" routerLinkActive="active"
|
||||||
</clr-vertical-nav-group>
|
(click)="send(subnav)" class="modern-nav-link">
|
||||||
<!-- </div> -->
|
<clr-icon [attr.shape]="subnav.mainMenuIconName || 'circle'" clrVerticalNavIcon
|
||||||
</ng-template>
|
class="nav-link-icon"></clr-icon>
|
||||||
<ng-template #other_content>
|
<span class="nav-link-text">{{ subnav.menuItemDesc }}</span>
|
||||||
<!-- if sub menu is not present -->
|
</a>
|
||||||
<a clrVerticalNavLink routerLinkActive="active" [routerLink]="'./'+ menu.main_menu_action_name">
|
</ng-container>
|
||||||
<clr-icon [attr.shape]="menu.main_menu_icon_name" clrVerticalNavIcon></clr-icon>
|
</clr-vertical-nav-group-children>
|
||||||
{{ menu.menuItemDesc }}
|
</clr-vertical-nav-group>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #other_content>
|
||||||
|
<!-- if sub menu is not present -->
|
||||||
|
<a clrVerticalNavLink routerLinkActive="active" [routerLink]="'./'+ menu.main_menu_action_name"
|
||||||
|
class="modern-nav-link-single">
|
||||||
|
<clr-icon [attr.shape]="menu.main_menu_icon_name" clrVerticalNavIcon class="nav-link-icon"></clr-icon>
|
||||||
|
<span class="nav-link-text">{{ menu.menuItemDesc }}</span>
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <div *ngFor="let report of reportNames">
|
<!-- <div *ngFor="let report of reportNames">
|
||||||
<clr-vertical-nav-group *ngIf="showFolderNames && !showSetupMenus" routerLinkActive="active" routerLink="/cns-portal/rerunner/all">
|
<clr-vertical-nav-group *ngIf="showFolderNames && !showSetupMenus" routerLinkActive="active" routerLink="/cns-portal/rerunner/all">
|
||||||
<clr-icon shape="file" clrVerticalNavIcon></clr-icon>{{report}}
|
<clr-icon shape="file" clrVerticalNavIcon></clr-icon>{{report}}
|
||||||
</clr-vertical-nav-group>
|
</clr-vertical-nav-group>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<!-- Display the folder names in the vertical nav bar -->
|
<!-- Display the folder names in the vertical nav bar -->
|
||||||
<!-- <div *ngFor="let report of reportNames">
|
<!-- <div *ngFor="let report of reportNames">
|
||||||
<clr-vertical-nav-group *ngIf="showFolderNames && !showDashNames " routerLink="/cns-portal/rerunner/all">
|
<clr-vertical-nav-group *ngIf="showFolderNames && !showDashNames " routerLink="/cns-portal/rerunner/all">
|
||||||
<clr-icon shape="file" clrVerticalNavIcon></clr-icon>{{report}}
|
<clr-icon shape="file" clrVerticalNavIcon></clr-icon>{{report}}
|
||||||
<a href="javascript://" (click)="filterGridDataByFolder(report?.folderName)">{{ report?.folderName }}</a>
|
<a href="javascript://" (click)="filterGridDataByFolder(report?.folderName)">{{ report?.folderName }}</a>
|
||||||
@@ -146,14 +237,14 @@
|
|||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- <div *ngFor="let dashName of dashNames">
|
<!-- <div *ngFor="let dashName of dashNames">
|
||||||
<clr-vertical-nav-group *ngIf="showDashNames && !showFolderNames && !showSetupMenus" routerLink="/cns-portal/dashboardbuilder/all">
|
<clr-vertical-nav-group *ngIf="showDashNames && !showFolderNames && !showSetupMenus" routerLink="/cns-portal/dashboardbuilder/all">
|
||||||
<clr-icon shape="file" clrVerticalNavIcon></clr-icon>{{dashName}}
|
<clr-icon shape="file" clrVerticalNavIcon></clr-icon>{{dashName}}
|
||||||
|
|
||||||
</clr-vertical-nav-group>
|
</clr-vertical-nav-group>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<!-- <div *ngFor="let dashName of dashNames">
|
<!-- <div *ngFor="let dashName of dashNames">
|
||||||
<clr-vertical-nav-group *ngIf="showDashNames && !showFolderNames">
|
<clr-vertical-nav-group *ngIf="showDashNames && !showFolderNames">
|
||||||
<clr-icon shape="grid-view" clrVerticalNavIcon></clr-icon>{{dashName}}
|
<clr-icon shape="grid-view" clrVerticalNavIcon></clr-icon>{{dashName}}
|
||||||
</clr-vertical-nav-group>
|
</clr-vertical-nav-group>
|
||||||
@@ -162,7 +253,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<clr-vertical-nav-group *ngIf="showSetupMenus && !showDashNames && !showFolderNames" routerLinkActive="active">
|
<clr-vertical-nav-group *ngIf="showSetupMenus && !showDashNames && !showFolderNames" routerLinkActive="active">
|
||||||
<clr-icon shape="cog" clrVerticalNavIcon class="project-visit-tour"></clr-icon>
|
<clr-icon shape="cog" clrVerticalNavIcon class="project-visit-tour"></clr-icon>
|
||||||
Setup
|
Setup
|
||||||
@@ -193,11 +284,11 @@
|
|||||||
</clr-vertical-nav-group> -->
|
</clr-vertical-nav-group> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- Master Menu -->
|
<!-- Master Menu -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <a clrVerticalNavLink routerLinkActive="active" >
|
<!-- <a clrVerticalNavLink routerLinkActive="active" >
|
||||||
<button id="add" class="btn btn-primary" style="width: 200px; position: fixed;bottom: 0; left: 0;" (click)="modaladd()">
|
<button id="add" class="btn btn-primary" style="width: 200px; position: fixed;bottom: 0; left: 0;" (click)="modaladd()">
|
||||||
<clr-icon shape="plus"></clr-icon>ADD A new Team
|
<clr-icon shape="plus"></clr-icon>ADD A new Team
|
||||||
</button> </a> -->
|
</button> </a> -->
|
||||||
@@ -215,15 +306,15 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="clr-col-md-6 clr-col-sm-12" >
|
<div class="clr-col-md-6 clr-col-sm-12">
|
||||||
<label>Name: </label>
|
<label>Name: </label>
|
||||||
<input class="clr-input" type="text" name="name" placeholder="Enter name">
|
<input class="clr-input" type="text" name="name" placeholder="Enter name">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline" (click)="modalteam = false">Cancel</button>
|
<button type="button" class="btn btn-outline" (click)="modalteam = false">Cancel</button>
|
||||||
<button type="submit" class="btn btn-primary" >Add</button>
|
<button type="submit" class="btn btn-primary">Add</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
// ========================================
|
||||||
|
// MODERN LAYOUT COMPONENT STYLES
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
@import '../../../../styles/design-tokens';
|
||||||
|
|
||||||
|
// Legacy form styles (preserved for compatibility)
|
||||||
input[type=text],[type=date], select,textarea {
|
input[type=text],[type=date], select,textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
@@ -7,6 +14,680 @@ input[type=text],[type=date], select,textarea {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
// Modern Layout Styles
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-layout {
|
||||||
|
background: linear-gradient(135deg, $gray-50 0%, $gray-100 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header Styles
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-header {
|
||||||
|
background: linear-gradient(135deg, $primary-600 0%, $primary-700 100%);
|
||||||
|
backdrop-filter: $backdrop-blur-md;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: $shadow-lg;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: $z-sticky;
|
||||||
|
height: $header-height;
|
||||||
|
padding: 0 $space-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-branding {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-4;
|
||||||
|
|
||||||
|
.modern-nav-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-4;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background: $gradient-primary;
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
box-shadow: $shadow-md;
|
||||||
|
transition: all $duration-300 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: $shadow-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-glow {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
left: -2px;
|
||||||
|
right: -2px;
|
||||||
|
bottom: -2px;
|
||||||
|
background: $gradient-primary;
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
opacity: 0;
|
||||||
|
filter: blur(8px);
|
||||||
|
transition: opacity $duration-300 $ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .logo-glow {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.company-title {
|
||||||
|
font-size: $text-xl;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
color: $white;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-subtitle {
|
||||||
|
font-size: $text-xs;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header Navigation
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-header-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-2;
|
||||||
|
margin-left: $space-8;
|
||||||
|
|
||||||
|
.modern-nav-icon {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
background: transparent;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: $white;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
color: $white;
|
||||||
|
box-shadow: $shadow-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
top: -40px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: $gray-900;
|
||||||
|
color: $white;
|
||||||
|
padding: $space-2 $space-3;
|
||||||
|
border-radius: $radius-md;
|
||||||
|
font-size: $text-xs;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
z-index: $z-tooltip;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border: 4px solid transparent;
|
||||||
|
border-top-color: $gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .nav-tooltip {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header Actions
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-4;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-user-dropdown {
|
||||||
|
.modern-user-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
padding: $space-2 $space-4;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
color: $white;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: $shadow-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-circle {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: $white;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-status-indicator {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: $accent-green;
|
||||||
|
border: 2px solid $white;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
color: $white;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-role {
|
||||||
|
font-size: $text-xs;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-arrow {
|
||||||
|
font-size: $text-xs;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
transition: transform $duration-200 $ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .dropdown-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dropdown Menu
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-dropdown-menu {
|
||||||
|
background: $white;
|
||||||
|
border: 1px solid $gray-200;
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
box-shadow: $shadow-xl;
|
||||||
|
padding: $space-2;
|
||||||
|
min-width: 280px;
|
||||||
|
margin-top: $space-2;
|
||||||
|
|
||||||
|
.dropdown-header {
|
||||||
|
padding: $space-4;
|
||||||
|
border-bottom: 1px solid $gray-200;
|
||||||
|
margin-bottom: $space-2;
|
||||||
|
|
||||||
|
.user-profile {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
|
||||||
|
.profile-avatar {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
background: $gradient-primary;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: $white;
|
||||||
|
font-size: $text-lg;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.profile-name {
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-email {
|
||||||
|
font-size: $text-xs;
|
||||||
|
color: $gray-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-divider {
|
||||||
|
height: 1px;
|
||||||
|
background: $gray-200;
|
||||||
|
margin: $space-2 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
padding: $space-3 $space-4;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
color: $gray-700;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $gray-50;
|
||||||
|
color: $gray-900;
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.logout-item {
|
||||||
|
color: $accent-red;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($accent-red, 0.1);
|
||||||
|
color: $accent-red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
font-size: $text-base;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Language Dropdown
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-language-dropdown {
|
||||||
|
.modern-language-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
padding: $space-3 $space-4;
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: $gray-700;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $gray-50;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-icon {
|
||||||
|
font-size: $text-base;
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-arrow {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: $text-xs;
|
||||||
|
color: $gray-400;
|
||||||
|
transition: transform $duration-200 $ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .dropdown-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-language-menu {
|
||||||
|
background: $white;
|
||||||
|
border: 1px solid $gray-200;
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
box-shadow: $shadow-lg;
|
||||||
|
padding: $space-2;
|
||||||
|
min-width: 200px;
|
||||||
|
margin-left: $space-2;
|
||||||
|
|
||||||
|
.modern-lang-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
padding: $space-3 $space-4;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
color: $gray-700;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $primary-50;
|
||||||
|
color: $primary-700;
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-icon {
|
||||||
|
font-size: $text-base;
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-flag {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: $text-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content Container
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-content-container {
|
||||||
|
display: flex;
|
||||||
|
min-height: calc(100vh - #{$header-height});
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-content-area {
|
||||||
|
flex: 1;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
padding: $space-6;
|
||||||
|
min-height: calc(100vh - #{$header-height});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sidebar Styles
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-sidebar {
|
||||||
|
background: rgba(255, 255, 255, 0.98);
|
||||||
|
backdrop-filter: $backdrop-blur-md;
|
||||||
|
border-right: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
box-shadow: $shadow-lg;
|
||||||
|
width: $sidebar-width;
|
||||||
|
transition: all $duration-300 $ease-out;
|
||||||
|
|
||||||
|
&.nav-collapsed {
|
||||||
|
width: $sidebar-width-collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
padding: $space-4 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-wrapper {
|
||||||
|
margin-bottom: $space-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation Groups
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-nav-group {
|
||||||
|
margin-bottom: $space-2;
|
||||||
|
|
||||||
|
.nav-group-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
padding: $space-3 $space-4;
|
||||||
|
color: $gray-700;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
margin: 0 $space-2;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $gray-50;
|
||||||
|
color: $gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-group-icon {
|
||||||
|
font-size: $text-lg;
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-group-title {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-group-arrow {
|
||||||
|
font-size: $text-sm;
|
||||||
|
color: $gray-400;
|
||||||
|
transition: transform $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&.rotated {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-nav-children {
|
||||||
|
margin-top: $space-1;
|
||||||
|
padding-left: $space-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation Links
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-nav-link,
|
||||||
|
.modern-nav-link-single {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-3;
|
||||||
|
padding: $space-3 $space-4;
|
||||||
|
color: $gray-600;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
border-radius: $radius-lg;
|
||||||
|
margin: 0 $space-2;
|
||||||
|
transition: all $duration-200 $ease-out;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $primary-50;
|
||||||
|
color: $primary-700;
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: $primary-100;
|
||||||
|
color: $primary-800;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 3px;
|
||||||
|
height: 20px;
|
||||||
|
background: $primary-600;
|
||||||
|
border-radius: 0 $radius-sm $radius-sm 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link-icon {
|
||||||
|
font-size: $text-base;
|
||||||
|
color: $gray-500;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .nav-link-icon {
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active .nav-link-icon {
|
||||||
|
color: $primary-700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive Design
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-lg) {
|
||||||
|
.modern-header {
|
||||||
|
padding: 0 $space-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-header-nav {
|
||||||
|
margin-left: $space-4;
|
||||||
|
gap: $space-1;
|
||||||
|
|
||||||
|
.modern-nav-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-content-area .content-wrapper {
|
||||||
|
padding: $space-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-sidebar {
|
||||||
|
width: $sidebar-width-collapsed;
|
||||||
|
|
||||||
|
.nav-group-title,
|
||||||
|
.nav-link-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-nav-link,
|
||||||
|
.modern-nav-link-single {
|
||||||
|
justify-content: center;
|
||||||
|
padding: $space-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
.modern-branding .brand-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-user-button .user-info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modern-header-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy styles (preserved for compatibility)
|
||||||
|
// ========================================
|
||||||
|
|
||||||
.center-content {
|
.center-content {
|
||||||
//width: 100%;
|
//width: 100%;
|
||||||
// max-width: 800px;
|
// max-width: 800px;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
import { Component,ViewEncapsulation, OnInit, Input } from '@angular/core';
|
import { Component, ViewEncapsulation, OnInit, Input } from '@angular/core';
|
||||||
import { Router,ActivatedRoute } from '@angular/router';
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
import { LoginService } from '../../../services/api/login.service';
|
import { LoginService } from '../../../services/api/login.service';
|
||||||
import { UserInfoService} from '../../../services/user-info.service';
|
import { UserInfoService } from '../../../services/user-info.service';
|
||||||
import { RealnetMenuService } from '../../../services/api/realnet-menu.service';
|
import { RealnetMenuService } from '../../../services/api/realnet-menu.service';
|
||||||
import { Rn_Main_Menu } from '../../../models/builder/Rn_Main_Menu';
|
import { Rn_Main_Menu } from '../../../models/builder/Rn_Main_Menu';
|
||||||
import { MenuGroupService } from '../../../services/admin/menu-group.service';
|
import { MenuGroupService } from '../../../services/admin/menu-group.service';
|
||||||
@@ -20,6 +20,7 @@ import { ProjectSetup } from 'src/app/models/builder/Project_setup';
|
|||||||
import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service';
|
import { Dashboard3Service } from 'src/app/services/builder/dashboard3.service';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-layout',
|
selector: 'app-layout',
|
||||||
@@ -28,38 +29,39 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
})
|
})
|
||||||
export class LayoutComponent implements OnInit {
|
export class LayoutComponent implements OnInit {
|
||||||
// baseUrl= environment.chaturl;
|
// baseUrl= environment.chaturl;
|
||||||
@Input() data:any;
|
@Input() data: any;
|
||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
apps: Array<ProjectSetup> = [];
|
apps: Array<ProjectSetup> = [];
|
||||||
projectsetup;
|
projectsetup;
|
||||||
|
|
||||||
collapsed = true;
|
collapsed = true;
|
||||||
public showAppAlert:boolean = false;
|
public showAppAlert: boolean = false;
|
||||||
modalteam=false;
|
modalteam = false;
|
||||||
public userName: string="";
|
public userName: string = "";
|
||||||
|
|
||||||
private formCode: string ='teacher_form';
|
private formCode: string = 'teacher_form';
|
||||||
public key:string="formCode";
|
public key: string = "formCode";
|
||||||
public storage:Storage = sessionStorage;
|
public storage: Storage = sessionStorage;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private userInfoService:UserInfoService,
|
private userInfoService: UserInfoService,
|
||||||
private realnetMenuService: RealnetMenuService,
|
private realnetMenuService: RealnetMenuService,
|
||||||
private menuGroupService: MenuGroupService,
|
private menuGroupService: MenuGroupService,
|
||||||
private sysparaservice:SysparameterService,
|
private sysparaservice: SysparameterService,
|
||||||
private reportBuilderService: ReportBuilderService,
|
private reportBuilderService: ReportBuilderService,
|
||||||
private dashboardService : Dashboard3Service,
|
private dashboardService: Dashboard3Service,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
|
private themeService: ThemeService,
|
||||||
// private guidedTourService: GuidedTourService,
|
// private guidedTourService: GuidedTourService,
|
||||||
// private tourservice: TourService,
|
// private tourservice: TourService,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
) {
|
) {
|
||||||
this.translate.setDefaultLang('en');
|
this.translate.setDefaultLang('en');
|
||||||
this.userName = this.userInfoService.getUserName();
|
this.userName = this.userInfoService.getUserName();
|
||||||
this.reportBuilderService.getrbDetails().subscribe((data) => {
|
this.reportBuilderService.getrbDetails().subscribe((data) => {
|
||||||
this.gridData = data;
|
this.gridData = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -69,10 +71,10 @@ modalteam=false;
|
|||||||
switchLanguage(language: string): void {
|
switchLanguage(language: string): void {
|
||||||
this.translate.use(language); // Switch to the selected language
|
this.translate.use(language); // Switch to the selected language
|
||||||
}
|
}
|
||||||
user_name : any;
|
user_name: any;
|
||||||
userrole:any;
|
userrole: any;
|
||||||
menus: Rn_Main_Menu[];
|
menus: Rn_Main_Menu[];
|
||||||
menu:any;
|
menu: any;
|
||||||
tourdata;
|
tourdata;
|
||||||
steps;
|
steps;
|
||||||
error;
|
error;
|
||||||
@@ -80,89 +82,89 @@ modalteam=false;
|
|||||||
this.storage.setItem(this.key, this.formCode);
|
this.storage.setItem(this.key, this.formCode);
|
||||||
this.user_name = this.userInfoService.getUserName();
|
this.user_name = this.userInfoService.getUserName();
|
||||||
console.log('user name: ' + this.user_name);
|
console.log('user name: ' + this.user_name);
|
||||||
this.userrole=this.userInfoService.getRoles();
|
this.userrole = this.userInfoService.getRoles();
|
||||||
console.log('user_role',this.userrole);
|
console.log('user_role', this.userrole);
|
||||||
this.udata=this.userInfoService.getUserId();
|
this.udata = this.userInfoService.getUserId();
|
||||||
console.log('user id',this.udata);
|
console.log('user id', this.udata);
|
||||||
this.loadMenuByAccountId();
|
this.loadMenuByAccountId();
|
||||||
this.loadMenuByMenuGroup();
|
this.loadMenuByMenuGroup();
|
||||||
this.getById(1);
|
this.getById(1);
|
||||||
// this.tourservice.getall().subscribe((data)=>{
|
// this.tourservice.getall().subscribe((data)=>{
|
||||||
// this.tourdata=data[1];
|
// this.tourdata=data[1];
|
||||||
// //console.log("tour data",data);
|
// //console.log("tour data",data);
|
||||||
// console.log(this.tourdata);
|
// console.log(this.tourdata);
|
||||||
// //console.log(this.tourdata.tourId);
|
// //console.log(this.tourdata.tourId);
|
||||||
// //console.log(this.tourdata.useOrb);
|
// //console.log(this.tourdata.useOrb);
|
||||||
// //this.steps=JSON.stringify(this.tourdata.steps);
|
// //this.steps=JSON.stringify(this.tourdata.steps);
|
||||||
// // console.log(this.tourdata.steps);
|
// // console.log(this.tourdata.steps);
|
||||||
// //this.dashboardTour.tourId=this.tourdata.tourId;
|
// //this.dashboardTour.tourId=this.tourdata.tourId;
|
||||||
// //console.log(this.dashboardTour.tourId);
|
// //console.log(this.dashboardTour.tourId);
|
||||||
// //this.dashboardTour.useOrb=this.tourdata.useOrb;
|
// //this.dashboardTour.useOrb=this.tourdata.useOrb;
|
||||||
// // this.dashboardTour.steps=this.tourdata.steps;
|
// // this.dashboardTour.steps=this.tourdata.steps;
|
||||||
// });
|
// });
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
showFolderNames: boolean = false; // Variable to control the report names display
|
|
||||||
reportNames: any[] = []; // Store the report names
|
|
||||||
|
|
||||||
// getName() {
|
|
||||||
// this.reportBuilderService.getrbDetails().pipe(
|
|
||||||
// map((data) => data.map((item) => item.folderName))
|
|
||||||
// ).subscribe((reportName) => {
|
|
||||||
// this.reportNames = reportName;
|
|
||||||
// // this.showReportNames = true; // Toggle on
|
|
||||||
// console.log('Report Names:', this.reportNames);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
getName() {
|
|
||||||
if (this.showFolderNames) {
|
|
||||||
// If in report mode, switch to default mode
|
|
||||||
this.showFolderNames = false;
|
|
||||||
} else {
|
|
||||||
// If in default mode, switch to report mode
|
|
||||||
this.reportBuilderService.getrbDetails().pipe(
|
|
||||||
map((data) => data.map((item) => item.folderName))
|
|
||||||
).subscribe(
|
|
||||||
(folderName) => {
|
|
||||||
this.reportNames = folderName;
|
|
||||||
console.log('Report Names:', this.reportNames);
|
|
||||||
this.showFolderNames = true;
|
|
||||||
},);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
filteredGridData: any[] = [];
|
|
||||||
gridData: any[] = [];
|
|
||||||
// Function to filter the data based on the selected folder
|
|
||||||
filterGridDataByFolder(folderName: string) {
|
|
||||||
this.filteredGridData = this.gridData.filter(item => item.folderName === folderName);
|
|
||||||
}
|
|
||||||
|
|
||||||
showDashNames: boolean = false;
|
|
||||||
dashNames: any[] = [];
|
|
||||||
getDashName() {
|
showFolderNames: boolean = false; // Variable to control the report names display
|
||||||
if (this.showDashNames) {
|
reportNames: any[] = []; // Store the report names
|
||||||
// If in report mode, switch to default mode
|
|
||||||
this.showDashNames = false;
|
// getName() {
|
||||||
} else {
|
// this.reportBuilderService.getrbDetails().pipe(
|
||||||
// If in default mode, switch to report mode
|
// map((data) => data.map((item) => item.folderName))
|
||||||
this.dashboardService.getAllDash().pipe(
|
// ).subscribe((reportName) => {
|
||||||
map((data) => data.map((item) => item.dashboard_name))
|
// this.reportNames = reportName;
|
||||||
).subscribe(
|
// // this.showReportNames = true; // Toggle on
|
||||||
(dashboard_name) => {
|
// console.log('Report Names:', this.reportNames);
|
||||||
this.dashNames = dashboard_name;
|
// });
|
||||||
console.log('Dashboard Names:', this.dashNames);
|
|
||||||
// this.showFolderNames = true;
|
// }
|
||||||
this.showDashNames = true;
|
|
||||||
},);
|
|
||||||
|
getName() {
|
||||||
|
if (this.showFolderNames) {
|
||||||
|
// If in report mode, switch to default mode
|
||||||
|
this.showFolderNames = false;
|
||||||
|
} else {
|
||||||
|
// If in default mode, switch to report mode
|
||||||
|
this.reportBuilderService.getrbDetails().pipe(
|
||||||
|
map((data) => data.map((item) => item.folderName))
|
||||||
|
).subscribe(
|
||||||
|
(folderName) => {
|
||||||
|
this.reportNames = folderName;
|
||||||
|
console.log('Report Names:', this.reportNames);
|
||||||
|
this.showFolderNames = true;
|
||||||
|
},);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredGridData: any[] = [];
|
||||||
|
gridData: any[] = [];
|
||||||
|
// Function to filter the data based on the selected folder
|
||||||
|
filterGridDataByFolder(folderName: string) {
|
||||||
|
this.filteredGridData = this.gridData.filter(item => item.folderName === folderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
showDashNames: boolean = false;
|
||||||
|
dashNames: any[] = [];
|
||||||
|
getDashName() {
|
||||||
|
if (this.showDashNames) {
|
||||||
|
// If in report mode, switch to default mode
|
||||||
|
this.showDashNames = false;
|
||||||
|
} else {
|
||||||
|
// If in default mode, switch to report mode
|
||||||
|
this.dashboardService.getAllDash().pipe(
|
||||||
|
map((data) => data.map((item) => item.dashboard_name))
|
||||||
|
).subscribe(
|
||||||
|
(dashboard_name) => {
|
||||||
|
this.dashNames = dashboard_name;
|
||||||
|
console.log('Dashboard Names:', this.dashNames);
|
||||||
|
// this.showFolderNames = true;
|
||||||
|
this.showDashNames = true;
|
||||||
|
},);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -170,11 +172,11 @@ getDashName() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
showSetupMenus: boolean = false;
|
showSetupMenus: boolean = false;
|
||||||
toggleSetupMenus() {
|
toggleSetupMenus() {
|
||||||
this.showSetupMenus = !this.showSetupMenus;
|
this.showSetupMenus = !this.showSetupMenus;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -183,74 +185,74 @@ toggleSetupMenus() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// side nav menu-sub_menu
|
// side nav menu-sub_menu
|
||||||
loadMenuByAccountId() {
|
loadMenuByAccountId() {
|
||||||
this.realnetMenuService.getByAccountId().subscribe(resp => {
|
this.realnetMenuService.getByAccountId().subscribe(resp => {
|
||||||
this.menus = resp;
|
this.menus = resp;
|
||||||
console.log('menu: ', this.menus);
|
console.log('menu: ', this.menus);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMenuByMenuGroup() {
|
loadMenuByMenuGroup() {
|
||||||
this.menuGroupService.getByCurrentUserMenuGroupId2().subscribe(resp => {
|
this.menuGroupService.getByCurrentUserMenuGroupId2().subscribe(resp => {
|
||||||
this.menus = resp;
|
this.menus = resp;
|
||||||
console.log('menus: ', this.menus);
|
console.log('menus: ', this.menus);
|
||||||
},(error) => {
|
}, (error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
if(error){
|
if (error) {
|
||||||
this.error="No data Available OR server Error";
|
this.error = "No data Available OR server Error";
|
||||||
}
|
}
|
||||||
if (error.status === 401) {
|
if (error.status === 401) {
|
||||||
// auto logout if 401 response returned from api
|
// auto logout if 401 response returned from api
|
||||||
// this.authenticationService.logout();
|
// this.authenticationService.logout();
|
||||||
this.toastr.error("Your Token Is Expire Plz login Again")
|
this.toastr.error("Your Token Is Expire Plz login Again")
|
||||||
//location.reload(true);
|
//location.reload(true);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sysparameter;
|
sysparameter;
|
||||||
getById(id: number) {
|
getById(id: number) {
|
||||||
this.sysparaservice.getById(id).subscribe((data) => {
|
this.sysparaservice.getById(id).subscribe((data) => {
|
||||||
this.sysparameter = data;
|
this.sysparameter = data;
|
||||||
console.log("getbyiddata",this.sysparameter);
|
console.log("getbyiddata", this.sysparameter);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* menuGroup: Rn_Menu_Group_Header[];
|
/* menuGroup: Rn_Menu_Group_Header[];
|
||||||
menu_id: number;
|
menu_id: number;
|
||||||
loadMenuGroupData() {
|
loadMenuGroupData() {
|
||||||
this.menuGroupService.getAll().subscribe(resp => {
|
this.menuGroupService.getAll().subscribe(resp => {
|
||||||
this.menuGroup = resp;
|
this.menuGroup = resp;
|
||||||
this.menu_id = this.menuGroup
|
this.menu_id = this.menuGroup
|
||||||
});
|
});
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
navbarSelectionChange(val){
|
navbarSelectionChange(val) {
|
||||||
// console.log(val);
|
// console.log(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeAppAlert(){
|
closeAppAlert() {
|
||||||
this.showAppAlert=false;
|
this.showAppAlert = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isDisabled(input: string): boolean{
|
isDisabled(input: string): boolean {
|
||||||
if(input === null) {
|
if (input === null) {
|
||||||
return true;
|
return true;
|
||||||
} else false;
|
} else false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onLogout() {
|
onLogout() {
|
||||||
this.userInfoService.logout().subscribe((data)=>{
|
this.userInfoService.logout().subscribe((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
});
|
});
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
@@ -260,32 +262,32 @@ isDisabled(input: string): boolean{
|
|||||||
menuFlag1 = false;
|
menuFlag1 = false;
|
||||||
udata;
|
udata;
|
||||||
uid;
|
uid;
|
||||||
message(){
|
message() {
|
||||||
//this.menuFlag = false;
|
//this.menuFlag = false;
|
||||||
// this.menuFlag1 = true;
|
// this.menuFlag1 = true;
|
||||||
// this.menuGroupService.getuser(this.udata).subscribe((data)=>{
|
// this.menuGroupService.getuser(this.udata).subscribe((data)=>{
|
||||||
// console.log(data);
|
// console.log(data);
|
||||||
// })
|
// })
|
||||||
// window.open(`${this.baseUrl}`)
|
// window.open(`${this.baseUrl}`)
|
||||||
//window.location.href = `${this.baseUrl}`;
|
//window.location.href = `${this.baseUrl}`;
|
||||||
//this.udata=this.userInfoService.getUserInfo();
|
//this.udata=this.userInfoService.getUserInfo();
|
||||||
//console.log(this.udata);
|
//console.log(this.udata);
|
||||||
|
|
||||||
this.menuGroupService.save(this.udata);
|
this.menuGroupService.save(this.udata);
|
||||||
|
|
||||||
this.uid=this.udata.userid;
|
this.uid = this.udata.userid;
|
||||||
localStorage.setItem("id", JSON.stringify(this.udata));
|
localStorage.setItem("id", JSON.stringify(this.udata));
|
||||||
//localStorage.setItem('id', this.uid);
|
//localStorage.setItem('id', this.uid);
|
||||||
//window.location.href = (`${this.baseUrl}/` + this.udata), true
|
//window.location.href = (`${this.baseUrl}/` + this.udata), true
|
||||||
//this.router.navigate([`${this.baseUrl}`], { relativeTo: this.route ,queryParams: { id: this.udata }});
|
//this.router.navigate([`${this.baseUrl}`], { relativeTo: this.route ,queryParams: { id: this.udata }});
|
||||||
|
|
||||||
//this.router.navigate(["../sureboard"],{relativeTo: this.route, queryParams: { userid: this.udata }});
|
//this.router.navigate(["../sureboard"],{relativeTo: this.route, queryParams: { userid: this.udata }});
|
||||||
//this.router.navigate([`${this.baseUrl}`],{relativeTo: this.route, queryParams: { userid: this.udata }});
|
//this.router.navigate([`${this.baseUrl}`],{relativeTo: this.route, queryParams: { userid: this.udata }});
|
||||||
//this.udata = this.userInfoService.getUserInfo();
|
//this.udata = this.userInfoService.getUserInfo();
|
||||||
//this.userInfoService.storeUserInfo(JSON.stringify(this.udata.user));
|
//this.userInfoService.storeUserInfo(JSON.stringify(this.udata.user));
|
||||||
}//
|
}//
|
||||||
|
|
||||||
modaladd(){
|
modaladd() {
|
||||||
//this.modalteam=true;
|
//this.modalteam=true;
|
||||||
this.router.navigate(['/cns-portal/myworkspace']);
|
this.router.navigate(['/cns-portal/myworkspace']);
|
||||||
}
|
}
|
||||||
@@ -293,17 +295,17 @@ isDisabled(input: string): boolean{
|
|||||||
me;
|
me;
|
||||||
md;
|
md;
|
||||||
mv;
|
mv;
|
||||||
send(val){
|
send(val) {
|
||||||
console.log(val);
|
console.log(val);
|
||||||
this.menuGroupService.storeaddeditvalues(val);
|
this.menuGroupService.storeaddeditvalues(val);
|
||||||
this.mc=val.mcreate;
|
this.mc = val.mcreate;
|
||||||
this.me=val.medit;
|
this.me = val.medit;
|
||||||
this.md=val.mdelete;
|
this.md = val.mdelete;
|
||||||
this.mv=val.mvisible;
|
this.mv = val.mvisible;
|
||||||
if(this.mv == 'false'){
|
if (this.mv == 'false') {
|
||||||
this.router.navigate(['/**'])
|
this.router.navigate(['/**'])
|
||||||
}
|
}
|
||||||
this.router.navigate(['./'+ val.main_menu_action_name] , { relativeTo: this.route});
|
this.router.navigate(['./' + val.main_menu_action_name], { relativeTo: this.route });
|
||||||
}
|
}
|
||||||
//skipLocationChange: true, value pass params->,queryParams:{mc:this.mc,me:this.me,md:this.md}
|
//skipLocationChange: true, value pass params->,queryParams:{mc:this.mc,me:this.me,md:this.md}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,240 @@
|
|||||||
<ol class="breadcrumb breadcrumb-arrow font-trirong">
|
<!-- Modern Dashboard Welcome Section -->
|
||||||
|
<div class="modern-dashboard">
|
||||||
|
<!-- Welcome Header -->
|
||||||
|
<div class="welcome-section">
|
||||||
|
<div class="welcome-content">
|
||||||
|
<div class="welcome-text">
|
||||||
|
<h1 class="welcome-title animate-fade-in-up">
|
||||||
|
Welcome to CloudnSure
|
||||||
|
<span class="title-accent">Enterprise Platform</span>
|
||||||
|
</h1>
|
||||||
|
<p class="welcome-subtitle animate-fade-in-up stagger-1">
|
||||||
|
Your comprehensive solution for data management, reporting, and business intelligence
|
||||||
|
</p>
|
||||||
|
<div class="welcome-actions animate-fade-in-up stagger-2">
|
||||||
|
<button class="modern-btn btn-primary btn-lg">
|
||||||
|
<clr-icon shape="play"></clr-icon>
|
||||||
|
Get Started
|
||||||
|
</button>
|
||||||
|
<button class="modern-btn btn-outline btn-lg">
|
||||||
|
<clr-icon shape="book"></clr-icon>
|
||||||
|
View Documentation
|
||||||
|
</button>
|
||||||
|
<button class="modern-btn btn-ghost btn-lg" routerLink="/cns-portal/theme-customization">
|
||||||
|
<clr-icon shape="palette"></clr-icon>
|
||||||
|
Customize Theme
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="welcome-visual animate-fade-in-right">
|
||||||
|
<div class="visual-container">
|
||||||
|
<div class="floating-card card-1">
|
||||||
|
<clr-icon shape="analytics"></clr-icon>
|
||||||
|
<span>Analytics</span>
|
||||||
|
</div>
|
||||||
|
<div class="floating-card card-2">
|
||||||
|
<clr-icon shape="file-group"></clr-icon>
|
||||||
|
<span>Reports</span>
|
||||||
|
</div>
|
||||||
|
<div class="floating-card card-3">
|
||||||
|
<clr-icon shape="cog"></clr-icon>
|
||||||
|
<span>Settings</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Quick Stats Cards -->
|
||||||
|
<div class="stats-section">
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="modern-card stats-card animate-fade-in-up stagger-1">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="stats-icon primary">
|
||||||
|
<clr-icon shape="users"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-trend positive">
|
||||||
|
<clr-icon shape="trending-up"></clr-icon>
|
||||||
|
<span>+12%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="stats-value">1,234</div>
|
||||||
|
<div class="stats-label">Active Users</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modern-card stats-card animate-fade-in-up stagger-2">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="stats-icon success">
|
||||||
|
<clr-icon shape="file-group"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-trend positive">
|
||||||
|
<clr-icon shape="trending-up"></clr-icon>
|
||||||
|
<span>+8%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="stats-value">567</div>
|
||||||
|
<div class="stats-label">Reports Generated</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modern-card stats-card animate-fade-in-up stagger-3">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="stats-icon warning">
|
||||||
|
<clr-icon shape="clock"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-trend negative">
|
||||||
|
<clr-icon shape="trending-down"></clr-icon>
|
||||||
|
<span>-3%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="stats-value">2.4s</div>
|
||||||
|
<div class="stats-label">Avg Response Time</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modern-card stats-card animate-fade-in-up stagger-4">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="stats-icon info">
|
||||||
|
<clr-icon shape="shield"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stats-trend positive">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
<span>99.9%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="stats-value">99.9%</div>
|
||||||
|
<div class="stats-label">Uptime</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Quick Actions -->
|
||||||
|
<div class="actions-section">
|
||||||
|
<h2 class="section-title animate-fade-in-up">Quick Actions</h2>
|
||||||
|
<div class="actions-grid">
|
||||||
|
<a href="javascript://" class="modern-card action-card animate-fade-in-up stagger-1 hover-lift">
|
||||||
|
<div class="action-icon">
|
||||||
|
<clr-icon shape="plus-circle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action-content">
|
||||||
|
<h3>Create Report</h3>
|
||||||
|
<p>Build custom reports with our intuitive report builder</p>
|
||||||
|
</div>
|
||||||
|
<div class="action-arrow">
|
||||||
|
<clr-icon shape="angle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="javascript://" class="modern-card action-card animate-fade-in-up stagger-2 hover-lift">
|
||||||
|
<div class="action-icon">
|
||||||
|
<clr-icon shape="upload"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action-content">
|
||||||
|
<h3>Import Data</h3>
|
||||||
|
<p>Upload and process your data files quickly</p>
|
||||||
|
</div>
|
||||||
|
<div class="action-arrow">
|
||||||
|
<clr-icon shape="angle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="javascript://" class="modern-card action-card animate-fade-in-up stagger-3 hover-lift">
|
||||||
|
<div class="action-icon">
|
||||||
|
<clr-icon shape="cog"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action-content">
|
||||||
|
<h3>System Settings</h3>
|
||||||
|
<p>Configure your system preferences and options</p>
|
||||||
|
</div>
|
||||||
|
<div class="action-arrow">
|
||||||
|
<clr-icon shape="angle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="javascript://" class="modern-card action-card animate-fade-in-up stagger-4 hover-lift">
|
||||||
|
<div class="action-icon">
|
||||||
|
<clr-icon shape="help-info"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action-content">
|
||||||
|
<h3>Help & Support</h3>
|
||||||
|
<p>Get help and access our knowledge base</p>
|
||||||
|
</div>
|
||||||
|
<div class="action-arrow">
|
||||||
|
<clr-icon shape="angle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="javascript://" class="modern-card action-card animate-fade-in-up stagger-5 hover-lift" routerLink="/cns-portal/theme-customization">
|
||||||
|
<div class="action-icon">
|
||||||
|
<clr-icon shape="palette"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="action-content">
|
||||||
|
<h3>Theme Customization</h3>
|
||||||
|
<p>Customize colors, fonts, and appearance</p>
|
||||||
|
</div>
|
||||||
|
<div class="action-arrow">
|
||||||
|
<clr-icon shape="angle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Recent Activity -->
|
||||||
|
<div class="activity-section">
|
||||||
|
<h2 class="section-title animate-fade-in-up">Recent Activity</h2>
|
||||||
|
<div class="modern-card activity-card animate-fade-in-up">
|
||||||
|
<div class="activity-list">
|
||||||
|
<div class="activity-item">
|
||||||
|
<div class="activity-icon success">
|
||||||
|
<clr-icon shape="check"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="activity-content">
|
||||||
|
<div class="activity-title">Report generated successfully</div>
|
||||||
|
<div class="activity-time">2 minutes ago</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="activity-item">
|
||||||
|
<div class="activity-icon info">
|
||||||
|
<clr-icon shape="user"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="activity-content">
|
||||||
|
<div class="activity-title">New user registered</div>
|
||||||
|
<div class="activity-time">15 minutes ago</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="activity-item">
|
||||||
|
<div class="activity-icon warning">
|
||||||
|
<clr-icon shape="exclamation-triangle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="activity-content">
|
||||||
|
<div class="activity-title">System maintenance scheduled</div>
|
||||||
|
<div class="activity-time">1 hour ago</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="activity-item">
|
||||||
|
<div class="activity-icon primary">
|
||||||
|
<clr-icon shape="upload"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<div class="activity-content">
|
||||||
|
<div class="activity-title">Data import completed</div>
|
||||||
|
<div class="activity-time">2 hours ago</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Legacy breadcrumb (hidden but preserved for compatibility) -->
|
||||||
|
<ol class="breadcrumb breadcrumb-arrow font-trirong" style="display: none;">
|
||||||
<li><a href="javascript://">Home</a></li>
|
<li><a href="javascript://">Home</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|||||||
@@ -0,0 +1,510 @@
|
|||||||
|
// ========================================
|
||||||
|
// MODERN DASHBOARD COMPONENT STYLES
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
|
||||||
|
@import '../../../../styles/design-tokens';
|
||||||
|
|
||||||
|
|
||||||
|
// Dashboard Container
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.modern-dashboard {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Welcome Section
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.welcome-section {
|
||||||
|
background: linear-gradient(135deg, $primary-500 0%, $primary-700 100%);
|
||||||
|
border-radius: $radius-2xl;
|
||||||
|
margin-bottom: $space-8;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="75" cy="75" r="1" fill="rgba(255,255,255,0.1)"/><circle cx="50" cy="10" r="0.5" fill="rgba(255,255,255,0.05)"/><circle cx="10" cy="60" r="0.5" fill="rgba(255,255,255,0.05)"/><circle cx="90" cy="40" r="0.5" fill="rgba(255,255,255,0.05)"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: $space-12 $space-8;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-lg) {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
gap: $space-8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-text {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 600px;
|
||||||
|
|
||||||
|
.welcome-title {
|
||||||
|
font-size: $text-5xl;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
color: $white;
|
||||||
|
margin-bottom: $space-4;
|
||||||
|
line-height: 1.1;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
font-size: $text-4xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-accent {
|
||||||
|
display: block;
|
||||||
|
font-size: $text-2xl;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
margin-top: $space-2;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
font-size: $text-xl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-subtitle {
|
||||||
|
font-size: $text-xl;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
margin-bottom: $space-8;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
font-size: $text-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: $space-4;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-sm) {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-visual {
|
||||||
|
flex: 0 0 300px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-lg) {
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visual-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
|
||||||
|
.floating-card {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
backdrop-filter: $backdrop-blur-md;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
padding: $space-4;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-2;
|
||||||
|
color: $white;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
animation: float 3s ease-in-out infinite;
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
font-size: $text-2xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.card-1 {
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
animation-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.card-2 {
|
||||||
|
top: 60px;
|
||||||
|
right: 40px;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.card-3 {
|
||||||
|
bottom: 20px;
|
||||||
|
left: 60px;
|
||||||
|
animation-delay: 2s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats Section
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.stats-section {
|
||||||
|
margin-bottom: $space-8;
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: $space-6;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-sm) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: $space-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card {
|
||||||
|
padding: $space-6;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: $gradient-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: $space-4;
|
||||||
|
|
||||||
|
.stats-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: $text-xl;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: rgba($primary-500, 0.1);
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background: rgba($accent-green, 0.1);
|
||||||
|
color: $accent-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: rgba($accent-orange, 0.1);
|
||||||
|
color: $accent-orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
background: rgba($primary-500, 0.1);
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-trend {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-1;
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
|
||||||
|
&.positive {
|
||||||
|
color: $accent-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.negative {
|
||||||
|
color: $accent-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
clr-icon {
|
||||||
|
font-size: $text-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
.stats-value {
|
||||||
|
font-size: $text-3xl;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
color: $gray-900;
|
||||||
|
margin-bottom: $space-1;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-label {
|
||||||
|
font-size: $text-sm;
|
||||||
|
color: $gray-600;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions Section
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.actions-section {
|
||||||
|
margin-bottom: $space-8;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: $text-2xl;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
color: $gray-900;
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: $space-6;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-sm) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: $space-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-4;
|
||||||
|
padding: $space-6;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
border: 1px solid $gray-200;
|
||||||
|
transition: all $duration-300 $ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $primary-300;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: $shadow-xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: $gradient-primary;
|
||||||
|
border-radius: $radius-xl;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: $white;
|
||||||
|
font-size: $text-xl;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: $text-lg;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
color: $gray-900;
|
||||||
|
margin-bottom: $space-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: $text-sm;
|
||||||
|
color: $gray-600;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-arrow {
|
||||||
|
color: $gray-400;
|
||||||
|
font-size: $text-lg;
|
||||||
|
transition: transform $duration-200 $ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .action-arrow {
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activity Section
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
.activity-section {
|
||||||
|
.section-title {
|
||||||
|
font-size: $text-2xl;
|
||||||
|
font-weight: $font-bold;
|
||||||
|
color: $gray-900;
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-card {
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.activity-list {
|
||||||
|
.activity-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $space-4;
|
||||||
|
padding: $space-4 $space-6;
|
||||||
|
border-bottom: 1px solid $gray-100;
|
||||||
|
transition: background-color $duration-200 $ease-out;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $gray-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: $text-base;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background: rgba($accent-green, 0.1);
|
||||||
|
color: $accent-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
background: rgba($primary-500, 0.1);
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: rgba($accent-orange, 0.1);
|
||||||
|
color: $accent-orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: rgba($primary-500, 0.1);
|
||||||
|
color: $primary-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.activity-title {
|
||||||
|
font-size: $text-sm;
|
||||||
|
font-weight: $font-medium;
|
||||||
|
color: $gray-900;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-time {
|
||||||
|
font-size: $text-xs;
|
||||||
|
color: $gray-500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive Design
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-lg) {
|
||||||
|
.welcome-section .welcome-content {
|
||||||
|
padding: $space-8 $space-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-section .stats-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-section .actions-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
.welcome-section .welcome-content {
|
||||||
|
padding: $space-6 $space-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-text .welcome-title {
|
||||||
|
font-size: $text-3xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-text .welcome-subtitle {
|
||||||
|
font-size: $text-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-section .stats-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-section .actions-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-sm) {
|
||||||
|
.modern-dashboard {
|
||||||
|
padding: 0 $space-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-section {
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-section,
|
||||||
|
.actions-section,
|
||||||
|
.activity-section {
|
||||||
|
margin-bottom: $space-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation Keyframes
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ThemeService } from '../../../services/theme.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-main-page',
|
selector: 'app-main-page',
|
||||||
@@ -7,9 +8,13 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
})
|
})
|
||||||
export class MainPageComponent implements OnInit {
|
export class MainPageComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor(private themeService: ThemeService) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
// Initialize theme service
|
||||||
|
this.themeService.currentTheme$.subscribe(theme => {
|
||||||
|
// Theme changes will be automatically applied via CSS variables
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,25 @@
|
|||||||
|
import { Ad10Component } from './BuilderComponents/angulardatatype/Ad10/Ad10.component';
|
||||||
|
|
||||||
|
import { DefatestComponent } from './BuilderComponents/defu/Defatest/Defatest.component';
|
||||||
|
|
||||||
|
|
||||||
|
import { ChildformComponent } from './BuilderComponents/stpkg/Childform/Childform.component';
|
||||||
|
import { DistrictComponent } from './BuilderComponents/testdata/District/District.component';
|
||||||
|
import { StateComponent } from './BuilderComponents/testdata/State/State.component';
|
||||||
|
import { CountryComponent } from './BuilderComponents/testdata/Country/Country.component';
|
||||||
|
import { Ad9Component } from './BuilderComponents/angulardatatype/Ad9/Ad9.component';
|
||||||
|
import { Ad8Component } from './BuilderComponents/angulardatatype/Ad8/Ad8.component';
|
||||||
|
import { Ad7Component } from './BuilderComponents/angulardatatype/Ad7/Ad7.component';
|
||||||
|
import { Ad6Component } from './BuilderComponents/angulardatatype/Ad6/Ad6.component';
|
||||||
|
import { Adv5Component } from './BuilderComponents/angulardatatype/Adv5/Adv5.component';
|
||||||
|
import { Adv4Component } from './BuilderComponents/angulardatatype/Adv4/Adv4.component';
|
||||||
|
import { SupportComponent } from './BuilderComponents/angulardatatype/Support/Support.component';
|
||||||
|
import { Adv3Component } from './BuilderComponents/angulardatatype/Adv3/Adv3.component';
|
||||||
|
import { Dv2Component } from './BuilderComponents/angulardatatype/Dv2/Dv2.component';
|
||||||
|
import { Adv1Component } from './BuilderComponents/angulardatatype/Adv1/Adv1.component';
|
||||||
|
import { Basicp3Component } from './BuilderComponents/angulardatatype/Basicp3/Basicp3.component';
|
||||||
|
import { Basicp2Component } from './BuilderComponents/angulardatatype/Basicp2/Basicp2.component';
|
||||||
|
import { Basicp1Component } from './BuilderComponents/angulardatatype/Basicp1/Basicp1.component';
|
||||||
|
|
||||||
|
|
||||||
import { SequencegenaratorComponent } from './fnd/sequencegenarator/sequencegenarator.component';
|
import { SequencegenaratorComponent } from './fnd/sequencegenarator/sequencegenarator.component';
|
||||||
@@ -38,7 +60,10 @@ import { AccesstypeComponent } from './admin/accesstype/accesstype.component';
|
|||||||
import { ModulesComponent } from './admin/modules/modules.component';
|
import { ModulesComponent } from './admin/modules/modules.component';
|
||||||
|
|
||||||
import { SessionloggerComponent } from './admin/sessionlogger/sessionlogger.component';
|
import { SessionloggerComponent } from './admin/sessionlogger/sessionlogger.component';
|
||||||
|
import { ReportRunnerComponent } from './builder/report-runner/report-runner.component';
|
||||||
import { ReportrunnerallComponent } from './builder/report-runner/reportrunnerall/reportrunnerall.component';
|
import { ReportrunnerallComponent } from './builder/report-runner/reportrunnerall/reportrunnerall.component';
|
||||||
|
import { Reportrunneredit2Component } from './builder/report-runner/reportrunneredit2/reportrunneredit2.component';
|
||||||
|
import { ReportrunnereditComponent } from './builder/report-runner/reportrunneredit/reportrunneredit.component';
|
||||||
import { ReportBuildComponent } from './builder/report-build/report-build.component';
|
import { ReportBuildComponent } from './builder/report-build/report-build.component';
|
||||||
import { ReportbuildallComponent } from './builder/report-build/reportbuildall/reportbuildall.component';
|
import { ReportbuildallComponent } from './builder/report-build/reportbuildall/reportbuildall.component';
|
||||||
import { ReportbuildaddComponent } from './builder/report-build/reportbuildadd/reportbuildadd.component';
|
import { ReportbuildaddComponent } from './builder/report-build/reportbuildadd/reportbuildadd.component';
|
||||||
@@ -84,6 +109,13 @@ import { AllapiregisteryComponent } from './fnd/apiregistery/allapiregistery/all
|
|||||||
import { AddapiregisteryComponent } from './fnd/apiregistery/addapiregistery/addapiregistery.component';
|
import { AddapiregisteryComponent } from './fnd/apiregistery/addapiregistery/addapiregistery.component';
|
||||||
import { EditapiregisteryComponent } from './fnd/apiregistery/editapiregistery/editapiregistery.component';
|
import { EditapiregisteryComponent } from './fnd/apiregistery/editapiregistery/editapiregistery.component';
|
||||||
import { ApiregisterylineComponent } from './fnd/apiregistery/Apiregisteryline/Apiregisteryline.component';
|
import { ApiregisterylineComponent } from './fnd/apiregistery/Apiregisteryline/Apiregisteryline.component';
|
||||||
|
import { Customer_informationComponent } from './BuilderComponents/angulardatatype/Customer_information/Customer_information.component';
|
||||||
|
import { Deployment_typeComponent } from './BuilderComponents/angulardatatype/Deployment_type/Deployment_type.component';
|
||||||
|
import { ManufacturerComponent } from './BuilderComponents/angulardatatype/Manufacturer/Manufacturer.component';
|
||||||
|
import { Order_summaryComponent } from './BuilderComponents/angulardatatype/Order_summary/Order_summary.component';
|
||||||
|
import { ProductComponent } from './BuilderComponents/angulardatatype/Product/Product.component';
|
||||||
|
import { TypesComponent } from './BuilderComponents/angulardatatype/Types/Types.component';
|
||||||
|
import { Test2Component } from './BuilderComponents/testdata/Test2/Test2.component';
|
||||||
import { Token_registeryComponent } from './fnd/Token_registery/Token_registery.component';
|
import { Token_registeryComponent } from './fnd/Token_registery/Token_registery.component';
|
||||||
import { MyworkspaceComponent } from './admin/myworkspace/myworkspace.component';
|
import { MyworkspaceComponent } from './admin/myworkspace/myworkspace.component';
|
||||||
import { ThemeCustomizationComponent } from './theme-customization/theme-customization.component';
|
import { ThemeCustomizationComponent } from './theme-customization/theme-customization.component';
|
||||||
@@ -94,15 +126,10 @@ import { OauthComponent } from './builder/dashboardnew/sureconnect/oauth/oauth.c
|
|||||||
import { QueryComponent } from './superadmin/query/query.component';
|
import { QueryComponent } from './superadmin/query/query.component';
|
||||||
import { QueryaddComponent } from './superadmin/queryadd/queryadd.component';
|
import { QueryaddComponent } from './superadmin/queryadd/queryadd.component';
|
||||||
import { QueryeditComponent } from './superadmin/queryedit/queryedit.component';
|
import { QueryeditComponent } from './superadmin/queryedit/queryedit.component';
|
||||||
import { ChartTypePageComponent } from './builder/dashboardnew/chart-type-manager/chart-type-page.component';
|
|
||||||
import { AddChartTypeComponent } from './builder/dashboardnew/chart-type-manager/add-chart-type.component';
|
|
||||||
import { EditChartTypeComponent } from './builder/dashboardnew/chart-type-manager/edit-chart-type.component';
|
|
||||||
import { ChartTypeUiComponentsComponent } from './builder/dashboardnew/chart-type-manager/chart-type-ui-components.component';
|
|
||||||
import { ChartTypeTemplatesComponent } from './builder/dashboardnew/chart-type-manager/chart-type-templates.component';
|
|
||||||
import { ChartTypeFieldsComponent } from './builder/dashboardnew/chart-type-manager/chart-type-fields.component';
|
|
||||||
import { ReportRunnerComponent } from './builder/report-runner/report-runner.component';
|
|
||||||
import { ReportrunnereditComponent } from './builder/report-runner/reportrunneredit/reportrunneredit.component';
|
|
||||||
import { Reportrunneredit2Component } from './builder/report-runner/reportrunneredit2/reportrunneredit2.component';
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
//Important: The sequence of path is important as the router go over then in sequential manner
|
//Important: The sequence of path is important as the router go over then in sequential manner
|
||||||
{ path: '', redirectTo: '/cns-portal/dashboard', pathMatch: 'full' },
|
{ path: '', redirectTo: '/cns-portal/dashboard', pathMatch: 'full' },
|
||||||
@@ -195,12 +222,6 @@ const routes: Routes = [
|
|||||||
{ path: 'editdata/:id', component: EditformnewdashComponent },
|
{ path: 'editdata/:id', component: EditformnewdashComponent },
|
||||||
{ path: 'editdashn/:id', component: EditnewdashComponent },
|
{ path: 'editdashn/:id', component: EditnewdashComponent },
|
||||||
{ path: 'schedule/:id', component: ScheduleComponent },
|
{ path: 'schedule/:id', component: ScheduleComponent },
|
||||||
{ path: 'chart-types', component: ChartTypePageComponent },
|
|
||||||
{ path: 'chart-types/add', component: AddChartTypeComponent },
|
|
||||||
{ path: 'chart-types/edit/:id', component: EditChartTypeComponent },
|
|
||||||
{ path: 'chart-types/:id/ui-components', component: ChartTypeUiComponentsComponent },
|
|
||||||
{ path: 'chart-types/:id/templates', component: ChartTypeTemplatesComponent },
|
|
||||||
{ path: 'chart-types/:id/fields', component: ChartTypeFieldsComponent },
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -308,8 +329,178 @@ const routes: Routes = [
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Country', component: CountryComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Adv3', component: Adv3Component },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad10', component: Ad10Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Childform', component: ChildformComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'District', component: DistrictComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'State', component: StateComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Country', component: CountryComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad9', component: Ad9Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad8', component: Ad8Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad7', component: Ad7Component },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad6', component: Ad6Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Adv5', component: Adv5Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Support', component: SupportComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Adv3', component: Adv3Component },
|
||||||
|
|
||||||
|
|
||||||
{ path: 'tokenregistery', component: Token_registeryComponent },
|
{ path: 'tokenregistery', component: Token_registeryComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Defatest', component: DefatestComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Country', component: CountryComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Defatest', component: DefatestComponent },
|
||||||
|
|
||||||
|
{ path: 'Test2', component: Test2Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Country', component: CountryComponent },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Test2', component: Test2Component },
|
||||||
|
|
||||||
|
{ path: 'Childform', component: ChildformComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'District', component: DistrictComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'State', component: StateComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Country', component: CountryComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad9', component: Ad9Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad8', component: Ad8Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad7', component: Ad7Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Ad6', component: Ad6Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Adv5', component: Adv5Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Adv4', component: Adv4Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Support', component: SupportComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Adv3', component: Adv3Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Dv2', component: Dv2Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Adv1', component: Adv1Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Basicp3', component: Basicp3Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Basicp2', component: Basicp2Component },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Basicp1', component: Basicp1Component },
|
||||||
|
{ path: 'cust', component: Customer_informationComponent },
|
||||||
|
|
||||||
|
{ path: 'Order_summary', component: Order_summaryComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Types', component: TypesComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Product', component: ProductComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Manufacturer', component: ManufacturerComponent },
|
||||||
|
|
||||||
|
|
||||||
|
{ path: 'Deployment_type', component: Deployment_typeComponent },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{ path: 'Stepper_workflow', component: Stepper_workflowComponent },
|
{ path: 'Stepper_workflow', component: Stepper_workflowComponent },
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user