@memberjunction/ng-explorer-settings 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -454
- package/package.json +18 -18
- package/dist/lib/shared/components/settings-card/settings-card.component.d.ts +0 -27
- package/dist/lib/shared/components/settings-card/settings-card.component.d.ts.map +0 -1
- package/dist/lib/shared/components/settings-card/settings-card.component.js +0 -170
- package/dist/lib/shared/components/settings-card/settings-card.component.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,21 +1,67 @@
|
|
|
1
1
|
# @memberjunction/ng-explorer-settings
|
|
2
2
|
|
|
3
|
-
Angular components for
|
|
3
|
+
Angular components for the Settings section of MemberJunction Explorer. Provides a tabbed settings interface covering general preferences, user management, role management, application management, entity permissions, appearance, notifications, SQL logging, and profile settings.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
The
|
|
7
|
+
The `SettingsComponent` serves as the main container, registered via `@RegisterClass(BaseNavigationComponent, 'Settings')`. It provides a searchable, tabbed interface for administrative and user settings. Each settings area is implemented as a standalone component that can be used independently.
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TD
|
|
11
|
+
SC["SettingsComponent\n(<mj-settings>)"] --> GS["GeneralSettingsComponent"]
|
|
12
|
+
SC --> AI["AccountInfoComponent"]
|
|
13
|
+
SC --> AS["AppearanceSettingsComponent"]
|
|
14
|
+
SC --> APS["ApplicationSettingsComponent"]
|
|
15
|
+
SC --> UM["UserManagementComponent"]
|
|
16
|
+
SC --> RM["RoleManagementComponent"]
|
|
17
|
+
SC --> AM["ApplicationManagementComponent"]
|
|
18
|
+
SC --> EP["EntityPermissionsComponent"]
|
|
19
|
+
SC --> NP["NotificationPreferencesComponent"]
|
|
20
|
+
SC --> SL["SQLLoggingComponent"]
|
|
21
|
+
SC --> UP["UserProfileSettingsComponent"]
|
|
22
|
+
SC --> UAC["UserAppConfigComponent"]
|
|
23
|
+
|
|
24
|
+
UM --> UD["UserDialogComponent"]
|
|
25
|
+
RM --> RD["RoleDialogComponent"]
|
|
26
|
+
AM --> AD["ApplicationDialogComponent"]
|
|
27
|
+
EP --> PD["PermissionDialogComponent"]
|
|
28
|
+
|
|
29
|
+
style SC fill:#7c5295,stroke:#563a6b,color:#fff
|
|
30
|
+
style GS fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
31
|
+
style AI fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
32
|
+
style AS fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
33
|
+
style APS fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
34
|
+
style UM fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
35
|
+
style RM fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
36
|
+
style AM fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
37
|
+
style EP fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
38
|
+
style NP fill:#b8762f,stroke:#8a5722,color:#fff
|
|
39
|
+
style SL fill:#b8762f,stroke:#8a5722,color:#fff
|
|
40
|
+
style UP fill:#b8762f,stroke:#8a5722,color:#fff
|
|
41
|
+
style UAC fill:#b8762f,stroke:#8a5722,color:#fff
|
|
42
|
+
style UD fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
43
|
+
style RD fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
44
|
+
style AD fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
45
|
+
style PD fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
46
|
+
```
|
|
8
47
|
|
|
9
48
|
## Features
|
|
10
49
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
50
|
+
- **Tabbed settings interface** with search across all settings
|
|
51
|
+
- **General settings**: Core application preferences
|
|
52
|
+
- **Account info**: User account details
|
|
53
|
+
- **Appearance settings**: Theme and display preferences
|
|
54
|
+
- **Application settings**: Application-specific configuration
|
|
55
|
+
- **User management**: CRUD for users with dialog-based editing
|
|
56
|
+
- **Role management**: CRUD for roles with dialog-based editing
|
|
57
|
+
- **Application management**: CRUD for applications with dialog-based editing
|
|
58
|
+
- **Entity permissions**: Permission grid integrated from `@memberjunction/ng-entity-permissions`
|
|
59
|
+
- **Notification preferences**: Notification configuration
|
|
60
|
+
- **SQL logging**: SQL query logging viewer
|
|
61
|
+
- **User profile settings**: Profile editing
|
|
62
|
+
- **User app configuration**: Per-user application configuration
|
|
63
|
+
- **Reusable settings card**: `SettingsCardComponent` for consistent card layout
|
|
64
|
+
- **Shared settings module**: Common settings UI elements
|
|
19
65
|
|
|
20
66
|
## Installation
|
|
21
67
|
|
|
@@ -23,461 +69,63 @@ The `@memberjunction/ng-explorer-settings` package provides a complete settings
|
|
|
23
69
|
npm install @memberjunction/ng-explorer-settings
|
|
24
70
|
```
|
|
25
71
|
|
|
26
|
-
##
|
|
72
|
+
## Key Dependencies
|
|
27
73
|
|
|
28
|
-
|
|
74
|
+
| Dependency | Purpose |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `@memberjunction/core`, `@memberjunction/core-entities` | Entity metadata and data |
|
|
77
|
+
| `@memberjunction/ng-base-application` | BaseNavigationComponent |
|
|
78
|
+
| `@memberjunction/ng-entity-permissions` | Entity permission grid |
|
|
79
|
+
| `@memberjunction/ng-entity-form-dialog` | Entity form dialogs |
|
|
80
|
+
| `@memberjunction/ng-simple-record-list` | Simple record CRUD lists |
|
|
81
|
+
| `@memberjunction/ng-join-grid` | Join/relationship grid |
|
|
82
|
+
| `@memberjunction/ng-user-avatar` | User avatar display |
|
|
83
|
+
| `@progress/kendo-angular-*` | Kendo UI components |
|
|
84
|
+
| `@angular/cdk` | Angular CDK utilities |
|
|
29
85
|
|
|
30
|
-
|
|
86
|
+
## Usage
|
|
31
87
|
|
|
32
88
|
```typescript
|
|
33
89
|
import { ExplorerSettingsModule } from '@memberjunction/ng-explorer-settings';
|
|
34
90
|
|
|
35
91
|
@NgModule({
|
|
36
|
-
imports: [
|
|
37
|
-
CommonModule,
|
|
38
|
-
ExplorerSettingsModule,
|
|
39
|
-
// other imports...
|
|
40
|
-
]
|
|
92
|
+
imports: [ExplorerSettingsModule]
|
|
41
93
|
})
|
|
42
|
-
export class
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- `/settings/roles` - Role list view
|
|
73
|
-
- `/settings/role/:id` - Individual role details
|
|
74
|
-
- `/settings/applications` - Application list view
|
|
75
|
-
- `/settings/application/:name` - Individual application details
|
|
76
|
-
- `/settings/entitypermissions` - Entity permission management
|
|
77
|
-
- `/settings/sqllogging` - SQL logging configuration and session management
|
|
78
|
-
|
|
79
|
-
**Example**:
|
|
80
|
-
```typescript
|
|
81
|
-
// Navigate to a specific user
|
|
82
|
-
this.router.navigate(['/settings/user', userId]);
|
|
83
|
-
|
|
84
|
-
// Navigate to roles section
|
|
85
|
-
this.router.navigate(['/settings/roles']);
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### SingleUserComponent
|
|
89
|
-
|
|
90
|
-
Manages individual user details and configurations.
|
|
91
|
-
|
|
92
|
-
**Selector**: `mj-single-user`
|
|
93
|
-
|
|
94
|
-
**Inputs**:
|
|
95
|
-
- `UserID: string` - The ID of the user to display/edit
|
|
96
|
-
|
|
97
|
-
**Features**:
|
|
98
|
-
- User information display and editing
|
|
99
|
-
- User role assignments via embedded grid
|
|
100
|
-
- User views management
|
|
101
|
-
- Integration with entity form dialog for editing
|
|
102
|
-
|
|
103
|
-
**Example**:
|
|
104
|
-
```html
|
|
105
|
-
<mj-single-user [UserID]="selectedUserId"></mj-single-user>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### SingleRoleComponent
|
|
109
|
-
|
|
110
|
-
Manages individual role details and user assignments.
|
|
111
|
-
|
|
112
|
-
**Selector**: `mj-single-role`
|
|
113
|
-
|
|
114
|
-
**Inputs**:
|
|
115
|
-
- `RoleID: string` - The ID of the role to display/edit
|
|
116
|
-
|
|
117
|
-
**Features**:
|
|
118
|
-
- Role information display and editing
|
|
119
|
-
- User assignments to the role
|
|
120
|
-
- Batch operations for user-role assignments
|
|
121
|
-
|
|
122
|
-
**Example**:
|
|
123
|
-
```html
|
|
124
|
-
<mj-single-role [RoleID]="selectedRoleId"></mj-single-role>
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### SingleApplicationComponent
|
|
128
|
-
|
|
129
|
-
Manages application configurations and entity associations.
|
|
130
|
-
|
|
131
|
-
**Selector**: `mj-single-application`
|
|
132
|
-
|
|
133
|
-
**Inputs**:
|
|
134
|
-
- `ApplicationID: string` - The ID of the application to manage
|
|
135
|
-
|
|
136
|
-
**Features**:
|
|
137
|
-
- Application details management
|
|
138
|
-
- Entity associations configuration
|
|
139
|
-
- Bulk entity assignment operations
|
|
140
|
-
|
|
141
|
-
**Example**:
|
|
142
|
-
```html
|
|
143
|
-
<mj-single-application [ApplicationID]="selectedApplicationId"></mj-single-application>
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### UserRolesGridComponent
|
|
147
|
-
|
|
148
|
-
A specialized grid for managing user-role relationships.
|
|
149
|
-
|
|
150
|
-
**Selector**: `mj-user-roles-grid`
|
|
151
|
-
|
|
152
|
-
**Inputs**:
|
|
153
|
-
- `UserID: string` - User ID when in 'Users' mode
|
|
154
|
-
- `RoleID: string` - Role ID when in 'Roles' mode
|
|
155
|
-
- `Mode: 'Users' | 'Roles'` - Determines the grid's perspective
|
|
156
|
-
- `UserRecord: UserEntity | null` - User entity when in 'Users' mode
|
|
157
|
-
- `RoleRecord: RoleEntity | null` - Role entity when in 'Roles' mode
|
|
158
|
-
|
|
159
|
-
**Features**:
|
|
160
|
-
- Checkbox-based role/user selection
|
|
161
|
-
- Batch save/cancel operations
|
|
162
|
-
- Flip all functionality
|
|
163
|
-
- Transaction-based updates
|
|
164
|
-
- Visual indication of pending changes
|
|
165
|
-
|
|
166
|
-
**Example**:
|
|
167
|
-
```html
|
|
168
|
-
<!-- For managing roles assigned to a user -->
|
|
169
|
-
<mj-user-roles-grid
|
|
170
|
-
[UserID]="userId"
|
|
171
|
-
[UserRecord]="userEntity"
|
|
172
|
-
Mode="Users">
|
|
173
|
-
</mj-user-roles-grid>
|
|
174
|
-
|
|
175
|
-
<!-- For managing users assigned to a role -->
|
|
176
|
-
<mj-user-roles-grid
|
|
177
|
-
[RoleID]="roleId"
|
|
178
|
-
[RoleRecord]="roleEntity"
|
|
179
|
-
Mode="Roles">
|
|
180
|
-
</mj-user-roles-grid>
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### ApplicationEntitiesGridComponent
|
|
184
|
-
|
|
185
|
-
Manages entity associations with applications.
|
|
186
|
-
|
|
187
|
-
**Selector**: `mj-application-entities-grid`
|
|
188
|
-
|
|
189
|
-
**Inputs**:
|
|
190
|
-
- `ApplicationID: string` - Application ID when in 'Applications' mode
|
|
191
|
-
- `EntityID: string` - Entity ID when in 'Entities' mode
|
|
192
|
-
- `Mode: 'Applications' | 'Entities'` - Determines the grid's perspective
|
|
193
|
-
- `ApplicationRecord: ApplicationEntity | null` - Application entity
|
|
194
|
-
- `EntityRecord: EntityEntity | null` - Entity record
|
|
195
|
-
|
|
196
|
-
**Features**:
|
|
197
|
-
- Entity-application association management
|
|
198
|
-
- Bulk selection/deselection
|
|
199
|
-
- Transaction-based saves
|
|
200
|
-
- Dirty state tracking
|
|
201
|
-
|
|
202
|
-
**Example**:
|
|
203
|
-
```html
|
|
204
|
-
<mj-application-entities-grid
|
|
205
|
-
[ApplicationID]="appId"
|
|
206
|
-
[ApplicationRecord]="appEntity"
|
|
207
|
-
Mode="Applications">
|
|
208
|
-
</mj-application-entities-grid>
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### SqlLoggingComponent
|
|
212
|
-
|
|
213
|
-
Provides comprehensive SQL logging management for debugging and migration generation.
|
|
214
|
-
|
|
215
|
-
**Selector**: `mj-sql-logging`
|
|
216
|
-
|
|
217
|
-
**Features**:
|
|
218
|
-
- Real-time SQL logging session management
|
|
219
|
-
- Owner-level access control (requires `Type = 'Owner'`)
|
|
220
|
-
- Session configuration with filtering options
|
|
221
|
-
- Live session monitoring with statement counts
|
|
222
|
-
- Multiple concurrent session support
|
|
223
|
-
- Auto-refresh capabilities
|
|
224
|
-
- Integration with MemberJunction's GraphQL API
|
|
225
|
-
|
|
226
|
-
**Key Capabilities**:
|
|
227
|
-
- **Session Creation**: Start new SQL logging sessions with custom options
|
|
228
|
-
- **User Filtering**: Capture SQL statements from specific users only
|
|
229
|
-
- **Format Options**: Standard SQL logs or migration-ready files
|
|
230
|
-
- **Real-time Monitoring**: View active sessions and their progress
|
|
231
|
-
- **Batch Operations**: Stop individual sessions or all sessions at once
|
|
232
|
-
- **Auto-cleanup**: Sessions automatically expire and clean up empty files
|
|
233
|
-
|
|
234
|
-
**Security Requirements**:
|
|
235
|
-
- User must have `Type = 'Owner'` in the Users table
|
|
236
|
-
- SQL logging must be enabled in server configuration
|
|
237
|
-
- Valid authentication required for all operations
|
|
238
|
-
|
|
239
|
-
**Example Usage**:
|
|
240
|
-
```html
|
|
241
|
-
<!-- Include in settings navigation -->
|
|
242
|
-
<mj-sql-logging></mj-sql-logging>
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
**Session Configuration Options**:
|
|
246
|
-
```typescript
|
|
247
|
-
interface SessionOptions {
|
|
248
|
-
fileName?: string; // Custom log file name
|
|
249
|
-
sessionName?: string; // Human-readable session name
|
|
250
|
-
filterToCurrentUser?: boolean; // Filter to current user's SQL only
|
|
251
|
-
formatAsMigration?: boolean; // Format as migration file
|
|
252
|
-
prettyPrint?: boolean; // Format SQL with indentation
|
|
253
|
-
statementTypes?: 'queries' | 'mutations' | 'both'; // SQL types to capture
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
**GraphQL Integration**:
|
|
258
|
-
```typescript
|
|
259
|
-
// The component automatically handles GraphQL operations:
|
|
260
|
-
// - sqlLoggingConfig: Get current configuration
|
|
261
|
-
// - activeSqlLoggingSessions: List active sessions
|
|
262
|
-
// - startSqlLogging: Create new session
|
|
263
|
-
// - stopSqlLogging: Stop specific session
|
|
264
|
-
// - stopAllSqlLogging: Stop all sessions
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
**UI Features**:
|
|
268
|
-
- **Dashboard-style interface** with modern AI dashboard styling
|
|
269
|
-
- **Status indicators** showing configuration state and active sessions
|
|
270
|
-
- **Interactive session cards** with duration, statement counts, and controls
|
|
271
|
-
- **Dialog-based session creation** with comprehensive options
|
|
272
|
-
- **Auto-refresh toggle** for real-time session monitoring
|
|
273
|
-
- **Responsive layout** optimized for desktop use
|
|
274
|
-
|
|
275
|
-
**Access Control**:
|
|
276
|
-
- Non-Owner users see access denied message with permission refresh option
|
|
277
|
-
- Disabled state shown when SQL logging not enabled in server config
|
|
278
|
-
- Clear instructions provided for enabling SQL logging
|
|
279
|
-
|
|
280
|
-
**Error Handling**:
|
|
281
|
-
- Comprehensive error messages for common issues
|
|
282
|
-
- Graceful handling of permission and configuration problems
|
|
283
|
-
- User-friendly notifications for all operations
|
|
284
|
-
- Debug logging for troubleshooting
|
|
285
|
-
|
|
286
|
-
**Integration Notes**:
|
|
287
|
-
- Requires MJServer with SqlLoggingConfigResolver
|
|
288
|
-
- Works with SQLServerDataProvider logging capabilities
|
|
289
|
-
- Follows MemberJunction's security and styling patterns
|
|
290
|
-
- Compatible with modern Angular control flow syntax (`@if`, `@for`)
|
|
291
|
-
|
|
292
|
-
## User Management Features
|
|
293
|
-
|
|
294
|
-
### User Activation/Deactivation
|
|
295
|
-
|
|
296
|
-
The settings module implements a soft-delete pattern for users:
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
// Example implementation in SettingsComponent
|
|
300
|
-
public async toggleUserActivation(record: BaseEntity) {
|
|
301
|
-
try {
|
|
302
|
-
const user = record as UserEntity;
|
|
303
|
-
const currentlyActive = user.IsActive;
|
|
304
|
-
|
|
305
|
-
// Toggle the IsActive flag
|
|
306
|
-
user.IsActive = !currentlyActive;
|
|
307
|
-
|
|
308
|
-
if (await user.Save()) {
|
|
309
|
-
MJNotificationService.Instance.CreateSimpleNotification(
|
|
310
|
-
`User ${user.Name} has been ${currentlyActive ? 'deactivated' : 'activated'} successfully.`,
|
|
311
|
-
'success',
|
|
312
|
-
3000
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error('Error toggling user activation:', error);
|
|
317
|
-
MJNotificationService.Instance.CreateSimpleNotification(
|
|
318
|
-
'An error occurred while toggling user activation.',
|
|
319
|
-
'error',
|
|
320
|
-
5000
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Custom Action Support
|
|
327
|
-
|
|
328
|
-
The user list supports custom actions with configurable icons and tooltips:
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
// Icon function for toggle button
|
|
332
|
-
public getUserToggleIcon(record: BaseEntity): string {
|
|
333
|
-
const user = record as UserEntity;
|
|
334
|
-
return user.IsActive ? 'fa-user-lock' : 'fa-user-check';
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Tooltip function for toggle button
|
|
338
|
-
public getUserToggleTooltip(record: BaseEntity): string {
|
|
339
|
-
const user = record as UserEntity;
|
|
340
|
-
return user.IsActive ? 'Deactivate user' : 'Activate user';
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
## Configuration Options
|
|
345
|
-
|
|
346
|
-
### Navigation Options
|
|
347
|
-
|
|
348
|
-
The settings component defines navigation options:
|
|
349
|
-
|
|
350
|
-
```typescript
|
|
351
|
-
public options = [
|
|
352
|
-
{ label: 'Users', value: SettingsItem.Users },
|
|
353
|
-
{ label: 'Roles', value: SettingsItem.Roles },
|
|
354
|
-
{ label: 'Applications', value: SettingsItem.Applications },
|
|
355
|
-
{ label: 'Entity Permissions', value: SettingsItem.EntityPermissions },
|
|
356
|
-
{ label: 'SQL Logging', value: SettingsItem.SqlLogging }
|
|
357
|
-
];
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### Grid Configuration
|
|
361
|
-
|
|
362
|
-
User roles and application entities grids support various configurations:
|
|
363
|
-
|
|
364
|
-
```typescript
|
|
365
|
-
// Example: Configure grid height
|
|
366
|
-
<mj-user-roles-grid
|
|
367
|
-
[UserID]="userId"
|
|
368
|
-
style="height: 600px;">
|
|
369
|
-
</mj-user-roles-grid>
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
## Dependencies
|
|
373
|
-
|
|
374
|
-
This package depends on several MemberJunction and third-party packages:
|
|
375
|
-
|
|
376
|
-
### MemberJunction Dependencies
|
|
377
|
-
- `@memberjunction/core`: Core functionality and metadata
|
|
378
|
-
- `@memberjunction/core-entities`: Entity definitions
|
|
379
|
-
- `@memberjunction/global`: Global utilities and decorators
|
|
380
|
-
- `@memberjunction/ng-container-directives`: Layout directives
|
|
381
|
-
- `@memberjunction/ng-shared`: Shared Angular components
|
|
382
|
-
- `@memberjunction/ng-notifications`: Notification service
|
|
383
|
-
- `@memberjunction/ng-entity-permissions`: Entity permission components
|
|
384
|
-
- `@memberjunction/ng-base-forms`: Base form components
|
|
385
|
-
- `@memberjunction/ng-entity-form-dialog`: Entity editing dialogs
|
|
386
|
-
- `@memberjunction/ng-user-view-grid`: User view grid component
|
|
387
|
-
- `@memberjunction/ng-simple-record-list`: Record list component
|
|
388
|
-
- `@memberjunction/ng-tabstrip`: Tab navigation component
|
|
389
|
-
- `@memberjunction/graphql-dataprovider`: GraphQL operations for SQL logging
|
|
390
|
-
|
|
391
|
-
### Kendo UI Dependencies
|
|
392
|
-
- `@progress/kendo-angular-dropdowns`: Dropdown components
|
|
393
|
-
- `@progress/kendo-angular-grid`: Grid functionality
|
|
394
|
-
- `@progress/kendo-angular-buttons`: Button components
|
|
395
|
-
- `@progress/kendo-angular-dialog`: Dialog components
|
|
396
|
-
- `@progress/kendo-angular-layout`: Layout utilities
|
|
397
|
-
- `@progress/kendo-angular-indicators`: Loading indicators
|
|
398
|
-
- `@progress/kendo-angular-inputs`: Form input components
|
|
399
|
-
- `@progress/kendo-angular-label`: Label components
|
|
400
|
-
- `@progress/kendo-angular-dialog`: Modal dialogs for session configuration
|
|
401
|
-
|
|
402
|
-
### Angular Dependencies (Peer)
|
|
403
|
-
- `@angular/common`: ^18.0.2
|
|
404
|
-
- `@angular/core`: ^18.0.2
|
|
405
|
-
- `@angular/forms`: ^18.0.2
|
|
406
|
-
- `@angular/router`: ^18.0.2
|
|
407
|
-
|
|
408
|
-
## Integration with MemberJunction
|
|
409
|
-
|
|
410
|
-
### Entity Registration
|
|
411
|
-
|
|
412
|
-
Components register with MemberJunction's class system:
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
@RegisterClass(BaseNavigationComponent, 'Settings')
|
|
416
|
-
export class SettingsComponent extends BaseNavigationComponent {
|
|
417
|
-
// Component implementation
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
### Transaction Support
|
|
422
|
-
|
|
423
|
-
Batch operations use MemberJunction's transaction system:
|
|
424
|
-
|
|
425
|
-
```typescript
|
|
426
|
-
const md = new Metadata();
|
|
427
|
-
const tg = await md.CreateTransactionGroup();
|
|
428
|
-
|
|
429
|
-
for (const record of records) {
|
|
430
|
-
record.TransactionGroup = tg;
|
|
431
|
-
await record.Save();
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
await tg.Submit();
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Metadata Integration
|
|
438
|
-
|
|
439
|
-
Components leverage MemberJunction's metadata system:
|
|
440
|
-
|
|
441
|
-
```typescript
|
|
442
|
-
const md = new Metadata();
|
|
443
|
-
const userEntity = await md.GetEntityObject<UserEntity>('Users');
|
|
444
|
-
const applications = md.Applications;
|
|
445
|
-
const roles = md.Roles;
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
## Build and Development
|
|
449
|
-
|
|
450
|
-
### Building the Package
|
|
94
|
+
export class AppModule {}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The `SettingsComponent` is registered via `@RegisterClass(BaseNavigationComponent, 'Settings')` and is typically loaded through Explorer's navigation system.
|
|
98
|
+
|
|
99
|
+
## Exported API
|
|
100
|
+
|
|
101
|
+
| Export | Type | Description |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `ExplorerSettingsModule` | NgModule | Main settings module |
|
|
104
|
+
| `SettingsComponent` | Component | Main tabbed settings container |
|
|
105
|
+
| `GeneralSettingsComponent` | Component | General preferences |
|
|
106
|
+
| `AccountInfoComponent` | Component | Account information |
|
|
107
|
+
| `AppearanceSettingsComponent` | Component | Theme/display settings |
|
|
108
|
+
| `ApplicationSettingsComponent` | Component | Application preferences |
|
|
109
|
+
| `UserManagementComponent` | Component | User CRUD management |
|
|
110
|
+
| `RoleManagementComponent` | Component | Role CRUD management |
|
|
111
|
+
| `ApplicationManagementComponent` | Component | Application CRUD management |
|
|
112
|
+
| `EntityPermissionsSettingsComponent` | Component | Entity permission management |
|
|
113
|
+
| `NotificationPreferencesComponent` | Component | Notification settings |
|
|
114
|
+
| `SQLLoggingComponent` | Component | SQL logging viewer |
|
|
115
|
+
| `UserProfileSettingsComponent` | Component | Profile editing |
|
|
116
|
+
| `UserAppConfigComponent` | Component | User app configuration |
|
|
117
|
+
| `SettingsCardComponent` | Component | Reusable settings card layout |
|
|
118
|
+
| `UserDialogComponent` | Component | User edit dialog |
|
|
119
|
+
| `RoleDialogComponent` | Component | Role edit dialog |
|
|
120
|
+
| `ApplicationDialogComponent` | Component | Application edit dialog |
|
|
121
|
+
| `PermissionDialogComponent` | Component | Permission edit dialog |
|
|
122
|
+
|
|
123
|
+
## Build
|
|
451
124
|
|
|
452
125
|
```bash
|
|
453
|
-
|
|
454
|
-
npm run build
|
|
455
|
-
|
|
456
|
-
# Or from the repository root
|
|
457
|
-
npm run build -- --filter="@memberjunction/ng-explorer-settings"
|
|
126
|
+
cd packages/Angular/Explorer/explorer-settings && npm run build
|
|
458
127
|
```
|
|
459
128
|
|
|
460
|
-
### TypeScript Configuration
|
|
461
|
-
|
|
462
|
-
The package uses strict TypeScript settings:
|
|
463
|
-
- Target: ES2015
|
|
464
|
-
- Module: ES2020
|
|
465
|
-
- Strict mode enabled
|
|
466
|
-
- Source maps generated
|
|
467
|
-
- Declaration files generated
|
|
468
|
-
|
|
469
|
-
## Best Practices
|
|
470
|
-
|
|
471
|
-
1. **Always use transactions** for batch operations to ensure data consistency
|
|
472
|
-
2. **Leverage entity metadata** instead of hardcoding entity names
|
|
473
|
-
3. **Use the provided navigation methods** for consistent routing behavior
|
|
474
|
-
4. **Handle errors appropriately** with user-friendly notifications
|
|
475
|
-
5. **Follow MemberJunction patterns** for entity instantiation and data loading
|
|
476
|
-
|
|
477
|
-
## Version
|
|
478
|
-
|
|
479
|
-
Current version: 2.43.0
|
|
480
|
-
|
|
481
129
|
## License
|
|
482
130
|
|
|
483
|
-
ISC
|
|
131
|
+
ISC
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-explorer-settings",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "MemberJunction: Reusable Angular components for the settings section of the MJ Explorer App",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -25,23 +25,23 @@
|
|
|
25
25
|
"@angular/router": "21.1.3"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@memberjunction/core": "4.
|
|
29
|
-
"@memberjunction/core-entities": "4.
|
|
30
|
-
"@memberjunction/global": "4.
|
|
31
|
-
"@memberjunction/graphql-dataprovider": "4.
|
|
32
|
-
"@memberjunction/ng-base-application": "4.
|
|
33
|
-
"@memberjunction/ng-base-forms": "4.
|
|
34
|
-
"@memberjunction/ng-code-editor": "4.
|
|
35
|
-
"@memberjunction/ng-container-directives": "4.
|
|
36
|
-
"@memberjunction/ng-entity-form-dialog": "4.
|
|
37
|
-
"@memberjunction/ng-entity-permissions": "4.
|
|
38
|
-
"@memberjunction/ng-join-grid": "4.
|
|
39
|
-
"@memberjunction/ng-notifications": "4.
|
|
40
|
-
"@memberjunction/ng-shared": "4.
|
|
41
|
-
"@memberjunction/ng-shared-generic": "4.
|
|
42
|
-
"@memberjunction/ng-simple-record-list": "4.
|
|
43
|
-
"@memberjunction/ng-tabstrip": "4.
|
|
44
|
-
"@memberjunction/ng-user-avatar": "4.
|
|
28
|
+
"@memberjunction/core": "4.1.0",
|
|
29
|
+
"@memberjunction/core-entities": "4.1.0",
|
|
30
|
+
"@memberjunction/global": "4.1.0",
|
|
31
|
+
"@memberjunction/graphql-dataprovider": "4.1.0",
|
|
32
|
+
"@memberjunction/ng-base-application": "4.1.0",
|
|
33
|
+
"@memberjunction/ng-base-forms": "4.1.0",
|
|
34
|
+
"@memberjunction/ng-code-editor": "4.1.0",
|
|
35
|
+
"@memberjunction/ng-container-directives": "4.1.0",
|
|
36
|
+
"@memberjunction/ng-entity-form-dialog": "4.1.0",
|
|
37
|
+
"@memberjunction/ng-entity-permissions": "4.1.0",
|
|
38
|
+
"@memberjunction/ng-join-grid": "4.1.0",
|
|
39
|
+
"@memberjunction/ng-notifications": "4.1.0",
|
|
40
|
+
"@memberjunction/ng-shared": "4.1.0",
|
|
41
|
+
"@memberjunction/ng-shared-generic": "4.1.0",
|
|
42
|
+
"@memberjunction/ng-simple-record-list": "4.1.0",
|
|
43
|
+
"@memberjunction/ng-tabstrip": "4.1.0",
|
|
44
|
+
"@memberjunction/ng-user-avatar": "4.1.0",
|
|
45
45
|
"@progress/kendo-angular-buttons": "22.0.1",
|
|
46
46
|
"@progress/kendo-angular-dialog": "22.0.1",
|
|
47
47
|
"@progress/kendo-angular-dropdowns": "22.0.1",
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { TemplateRef } from '@angular/core';
|
|
2
|
-
import * as i0 from "@angular/core";
|
|
3
|
-
/**
|
|
4
|
-
* A reusable settings card component providing consistent styling and layout
|
|
5
|
-
* across all settings sections. Follows the modern AI dashboard design patterns.
|
|
6
|
-
*/
|
|
7
|
-
export declare class SettingsCardComponent {
|
|
8
|
-
/** Card title displayed in the header */
|
|
9
|
-
title?: string;
|
|
10
|
-
/** Card subtitle displayed below the title */
|
|
11
|
-
subtitle?: string;
|
|
12
|
-
/** Whether to use floating card style (higher elevation) */
|
|
13
|
-
floating: boolean;
|
|
14
|
-
/** Card size variant */
|
|
15
|
-
size: 'sm' | 'md' | 'lg';
|
|
16
|
-
/** Whether to remove padding from content area */
|
|
17
|
-
noPadding: boolean;
|
|
18
|
-
/** Template for custom header content */
|
|
19
|
-
headerTemplate?: TemplateRef<any>;
|
|
20
|
-
/** Template for header action buttons */
|
|
21
|
-
actionTemplate?: TemplateRef<any>;
|
|
22
|
-
/** Template for footer content */
|
|
23
|
-
footerTemplate?: TemplateRef<any>;
|
|
24
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<SettingsCardComponent, never>;
|
|
25
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SettingsCardComponent, "mj-settings-card", never, { "title": { "alias": "title"; "required": false; }; "subtitle": { "alias": "subtitle"; "required": false; }; "floating": { "alias": "floating"; "required": false; }; "size": { "alias": "size"; "required": false; }; "noPadding": { "alias": "noPadding"; "required": false; }; "headerTemplate": { "alias": "headerTemplate"; "required": false; }; "actionTemplate": { "alias": "actionTemplate"; "required": false; }; "footerTemplate": { "alias": "footerTemplate"; "required": false; }; }, {}, never, ["*"], true, never>;
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=settings-card.component.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"settings-card.component.d.ts","sourceRoot":"","sources":["../../../../../src/lib/shared/components/settings-card/settings-card.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,WAAW,EAAE,MAAM,eAAe,CAAC;;AAG9D;;;GAGG;AACH,qBA0Ca,qBAAqB;IAChC,yCAAyC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,8CAA8C;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,4DAA4D;IACnD,QAAQ,UAAS;IAE1B,wBAAwB;IACf,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAQ;IAEzC,kDAAkD;IACzC,SAAS,UAAS;IAE3B,yCAAyC;IAChC,cAAc,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAE3C,yCAAyC;IAChC,cAAc,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAE3C,kCAAkC;IACzB,cAAc,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;yCAvBhC,qBAAqB;2CAArB,qBAAqB;CAwBjC"}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core';
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
3
|
-
import * as i0 from "@angular/core";
|
|
4
|
-
import * as i1 from "@angular/common";
|
|
5
|
-
const _c0 = ["*"];
|
|
6
|
-
function SettingsCardComponent_Conditional_1_Conditional_1_ng_container_0_Template(rf, ctx) { if (rf & 1) {
|
|
7
|
-
i0.ɵɵelementContainer(0);
|
|
8
|
-
} }
|
|
9
|
-
function SettingsCardComponent_Conditional_1_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
10
|
-
i0.ɵɵtemplate(0, SettingsCardComponent_Conditional_1_Conditional_1_ng_container_0_Template, 1, 0, "ng-container", 4);
|
|
11
|
-
} if (rf & 2) {
|
|
12
|
-
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
13
|
-
i0.ɵɵproperty("ngTemplateOutlet", ctx_r0.headerTemplate);
|
|
14
|
-
} }
|
|
15
|
-
function SettingsCardComponent_Conditional_1_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
16
|
-
i0.ɵɵelementStart(0, "p", 7);
|
|
17
|
-
i0.ɵɵtext(1);
|
|
18
|
-
i0.ɵɵelementEnd();
|
|
19
|
-
} if (rf & 2) {
|
|
20
|
-
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
21
|
-
i0.ɵɵadvance();
|
|
22
|
-
i0.ɵɵtextInterpolate(ctx_r0.subtitle);
|
|
23
|
-
} }
|
|
24
|
-
function SettingsCardComponent_Conditional_1_Conditional_2_Conditional_4_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
25
|
-
i0.ɵɵelementContainer(0);
|
|
26
|
-
} }
|
|
27
|
-
function SettingsCardComponent_Conditional_1_Conditional_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
28
|
-
i0.ɵɵelementStart(0, "div", 8);
|
|
29
|
-
i0.ɵɵtemplate(1, SettingsCardComponent_Conditional_1_Conditional_2_Conditional_4_ng_container_1_Template, 1, 0, "ng-container", 4);
|
|
30
|
-
i0.ɵɵelementEnd();
|
|
31
|
-
} if (rf & 2) {
|
|
32
|
-
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
33
|
-
i0.ɵɵadvance();
|
|
34
|
-
i0.ɵɵproperty("ngTemplateOutlet", ctx_r0.actionTemplate);
|
|
35
|
-
} }
|
|
36
|
-
function SettingsCardComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
37
|
-
i0.ɵɵelementStart(0, "div", 5)(1, "h3", 6);
|
|
38
|
-
i0.ɵɵtext(2);
|
|
39
|
-
i0.ɵɵelementEnd();
|
|
40
|
-
i0.ɵɵtemplate(3, SettingsCardComponent_Conditional_1_Conditional_2_Conditional_3_Template, 2, 1, "p", 7);
|
|
41
|
-
i0.ɵɵelementEnd();
|
|
42
|
-
i0.ɵɵtemplate(4, SettingsCardComponent_Conditional_1_Conditional_2_Conditional_4_Template, 2, 1, "div", 8);
|
|
43
|
-
} if (rf & 2) {
|
|
44
|
-
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
45
|
-
i0.ɵɵadvance(2);
|
|
46
|
-
i0.ɵɵtextInterpolate(ctx_r0.title);
|
|
47
|
-
i0.ɵɵadvance();
|
|
48
|
-
i0.ɵɵconditional(ctx_r0.subtitle ? 3 : -1);
|
|
49
|
-
i0.ɵɵadvance();
|
|
50
|
-
i0.ɵɵconditional(ctx_r0.actionTemplate ? 4 : -1);
|
|
51
|
-
} }
|
|
52
|
-
function SettingsCardComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
53
|
-
i0.ɵɵelementStart(0, "div", 1);
|
|
54
|
-
i0.ɵɵtemplate(1, SettingsCardComponent_Conditional_1_Conditional_1_Template, 1, 1, "ng-container")(2, SettingsCardComponent_Conditional_1_Conditional_2_Template, 5, 3);
|
|
55
|
-
i0.ɵɵelementEnd();
|
|
56
|
-
} if (rf & 2) {
|
|
57
|
-
const ctx_r0 = i0.ɵɵnextContext();
|
|
58
|
-
i0.ɵɵadvance();
|
|
59
|
-
i0.ɵɵconditional(ctx_r0.headerTemplate ? 1 : 2);
|
|
60
|
-
} }
|
|
61
|
-
function SettingsCardComponent_Conditional_4_ng_container_1_Template(rf, ctx) { if (rf & 1) {
|
|
62
|
-
i0.ɵɵelementContainer(0);
|
|
63
|
-
} }
|
|
64
|
-
function SettingsCardComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
65
|
-
i0.ɵɵelementStart(0, "div", 3);
|
|
66
|
-
i0.ɵɵtemplate(1, SettingsCardComponent_Conditional_4_ng_container_1_Template, 1, 0, "ng-container", 4);
|
|
67
|
-
i0.ɵɵelementEnd();
|
|
68
|
-
} if (rf & 2) {
|
|
69
|
-
const ctx_r0 = i0.ɵɵnextContext();
|
|
70
|
-
i0.ɵɵadvance();
|
|
71
|
-
i0.ɵɵproperty("ngTemplateOutlet", ctx_r0.footerTemplate);
|
|
72
|
-
} }
|
|
73
|
-
/**
|
|
74
|
-
* A reusable settings card component providing consistent styling and layout
|
|
75
|
-
* across all settings sections. Follows the modern AI dashboard design patterns.
|
|
76
|
-
*/
|
|
77
|
-
export class SettingsCardComponent {
|
|
78
|
-
/** Card title displayed in the header */
|
|
79
|
-
title;
|
|
80
|
-
/** Card subtitle displayed below the title */
|
|
81
|
-
subtitle;
|
|
82
|
-
/** Whether to use floating card style (higher elevation) */
|
|
83
|
-
floating = false;
|
|
84
|
-
/** Card size variant */
|
|
85
|
-
size = 'md';
|
|
86
|
-
/** Whether to remove padding from content area */
|
|
87
|
-
noPadding = false;
|
|
88
|
-
/** Template for custom header content */
|
|
89
|
-
headerTemplate;
|
|
90
|
-
/** Template for header action buttons */
|
|
91
|
-
actionTemplate;
|
|
92
|
-
/** Template for footer content */
|
|
93
|
-
footerTemplate;
|
|
94
|
-
static ɵfac = function SettingsCardComponent_Factory(t) { return new (t || SettingsCardComponent)(); };
|
|
95
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SettingsCardComponent, selectors: [["mj-settings-card"]], inputs: { title: "title", subtitle: "subtitle", floating: "floating", size: "size", noPadding: "noPadding", headerTemplate: "headerTemplate", actionTemplate: "actionTemplate", footerTemplate: "footerTemplate" }, standalone: true, features: [i0.ɵɵStandaloneFeature], ngContentSelectors: _c0, decls: 5, vars: 10, consts: [[1, "mj-card"], [1, "mj-card-header"], [1, "mj-card-body"], [1, "mj-card-footer"], [4, "ngTemplateOutlet"], [1, "header-content"], [1, "card-title"], [1, "card-subtitle"], [1, "mj-card-actions"]], template: function SettingsCardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
96
|
-
i0.ɵɵprojectionDef();
|
|
97
|
-
i0.ɵɵelementStart(0, "div", 0);
|
|
98
|
-
i0.ɵɵtemplate(1, SettingsCardComponent_Conditional_1_Template, 3, 1, "div", 1);
|
|
99
|
-
i0.ɵɵelementStart(2, "div", 2);
|
|
100
|
-
i0.ɵɵprojection(3);
|
|
101
|
-
i0.ɵɵelementEnd();
|
|
102
|
-
i0.ɵɵtemplate(4, SettingsCardComponent_Conditional_4_Template, 2, 1, "div", 3);
|
|
103
|
-
i0.ɵɵelementEnd();
|
|
104
|
-
} if (rf & 2) {
|
|
105
|
-
i0.ɵɵclassProp("mj-card-floating", ctx.floating)("mj-card-sm", ctx.size === "sm")("mj-card-lg", ctx.size === "lg");
|
|
106
|
-
i0.ɵɵadvance();
|
|
107
|
-
i0.ɵɵconditional(ctx.title || ctx.headerTemplate ? 1 : -1);
|
|
108
|
-
i0.ɵɵadvance();
|
|
109
|
-
i0.ɵɵclassProp("no-padding", ctx.noPadding);
|
|
110
|
-
i0.ɵɵadvance(2);
|
|
111
|
-
i0.ɵɵconditional(ctx.footerTemplate ? 4 : -1);
|
|
112
|
-
} }, dependencies: [CommonModule, i1.NgTemplateOutlet], styles: ["@keyframes _ngcontent-%COMP%_shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n[_nghost-%COMP%] {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n font-size: 14px;\n line-height: 1.4;\n color: #212121;\n}\n[_nghost-%COMP%] *[_ngcontent-%COMP%] {\n box-sizing: border-box;\n}\n\n.settings-card[_ngcontent-%COMP%] {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n padding: 20px;\n}\n.settings-card[_ngcontent-%COMP%]:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n\n.settings-card-floating[_ngcontent-%COMP%] {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n padding: 20px;\n}\n.settings-card-floating[_ngcontent-%COMP%]:hover {\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-2px);\n}\n\n.settings-card-sm[_ngcontent-%COMP%] {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n padding: 16px;\n}\n.settings-card-sm[_ngcontent-%COMP%]:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n\n.settings-card-lg[_ngcontent-%COMP%] {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n padding: 24px;\n}\n.settings-card-lg[_ngcontent-%COMP%]:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n\n.dashboard-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding: 24px 0 16px 0;\n}\n.dashboard-header[_ngcontent-%COMP%] .header-info[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 16px;\n}\n.dashboard-header[_ngcontent-%COMP%] .header-info[_ngcontent-%COMP%] h1[_ngcontent-%COMP%], .dashboard-header[_ngcontent-%COMP%] .header-info[_ngcontent-%COMP%] h2[_ngcontent-%COMP%], .dashboard-header[_ngcontent-%COMP%] .header-info[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-weight: 600;\n color: #212121;\n font-size: 24px;\n line-height: 1.2;\n}\n.dashboard-header[_ngcontent-%COMP%] .header-info[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\n font-size: 18px;\n}\n.dashboard-header[_ngcontent-%COMP%] .header-info[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n.dashboard-header[_ngcontent-%COMP%] .header-controls[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid #e0e0e0;\n}\n.section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%], .section-header[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0;\n font-weight: 500;\n color: #212121;\n font-size: 16px;\n}\n.section-header[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.control-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #ffffff;\n color: #212121;\n border-color: #bdbdbd;\n}\n.control-btn[_ngcontent-%COMP%]:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.control-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #f8f9fa;\n border-color: #2196f3;\n color: #2196f3;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n.control-btn.control-btn-primary[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #2196f3;\n color: #ffffff;\n border-color: #2196f3;\n}\n.control-btn.control-btn-primary[_ngcontent-%COMP%]:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn.control-btn-primary[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn.control-btn-primary[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.control-btn.control-btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #1976d2;\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);\n transform: translateY(-1px);\n}\n.control-btn.control-btn-primary[_ngcontent-%COMP%]:active {\n transform: translateY(0);\n}\n.control-btn.control-btn-danger[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #f44336;\n color: #ffffff;\n border-color: #f44336;\n}\n.control-btn.control-btn-danger[_ngcontent-%COMP%]:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn.control-btn-danger[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn.control-btn-danger[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.control-btn.control-btn-danger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #ea1c0d;\n border-color: #ea1c0d;\n box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3);\n transform: translateY(-1px);\n}\n.control-btn.control-btn-ghost[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: transparent;\n color: #666666;\n border-color: transparent;\n}\n.control-btn.control-btn-ghost[_ngcontent-%COMP%]:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn.control-btn-ghost[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn.control-btn-ghost[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.control-btn.control-btn-ghost[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #f8f9fa;\n color: #2196f3;\n}\n.control-btn.control-btn-sm[_ngcontent-%COMP%] {\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n}\n.control-btn.control-btn-sm[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n.control-btn.control-btn-lg[_ngcontent-%COMP%] {\n height: 44px;\n padding: 0 24px;\n font-size: 14px;\n}\n.control-btn.control-btn-lg[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.action-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: transparent;\n color: #666666;\n border-color: transparent;\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n width: 28px;\n padding: 0;\n}\n.action-btn[_ngcontent-%COMP%]:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.action-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.action-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.action-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #f8f9fa;\n color: #2196f3;\n}\n.action-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n.action-btn.action-btn-primary[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #2196f3;\n color: #ffffff;\n border-color: #2196f3;\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n width: 28px;\n padding: 0;\n}\n.action-btn.action-btn-primary[_ngcontent-%COMP%]:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.action-btn.action-btn-primary[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.action-btn.action-btn-primary[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.action-btn.action-btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #1976d2;\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);\n transform: translateY(-1px);\n}\n.action-btn.action-btn-primary[_ngcontent-%COMP%]:active {\n transform: translateY(0);\n}\n.action-btn.action-btn-primary[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n.action-btn.action-btn-danger[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #f44336;\n color: #ffffff;\n border-color: #f44336;\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n width: 28px;\n padding: 0;\n}\n.action-btn.action-btn-danger[_ngcontent-%COMP%]:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.action-btn.action-btn-danger[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.action-btn.action-btn-danger[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.action-btn.action-btn-danger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #ea1c0d;\n border-color: #ea1c0d;\n box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3);\n transform: translateY(-1px);\n}\n.action-btn.action-btn-danger[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.info-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(33, 150, 243, 0.1);\n color: #1976d2;\n border: 1px solid rgba(33, 150, 243, 0.2);\n}\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(158, 158, 158, 0.1);\n color: #666666;\n border: 1px solid rgba(158, 158, 158, 0.2);\n}\n.status-badge.status-active[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(76, 175, 80, 0.1);\n color: #3d8b40;\n border: 1px solid rgba(76, 175, 80, 0.2);\n}\n.status-badge.status-migration[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(33, 150, 243, 0.1);\n color: #1976d2;\n border: 1px solid rgba(33, 150, 243, 0.2);\n}\n.status-badge.status-warning[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(255, 193, 7, 0.1);\n color: #6d5200;\n border: 1px solid rgba(255, 193, 7, 0.2);\n}\n.status-badge.status-error[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(244, 67, 54, 0.1);\n color: #ea1c0d;\n border: 1px solid rgba(244, 67, 54, 0.2);\n}\n\n.settings-nav[_ngcontent-%COMP%] {\n width: 220px;\n min-width: 220px;\n max-width: 220px;\n background: #ffffff;\n border-right: 1px solid #e0e0e0;\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow-y: auto;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n.settings-nav[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n margin: 0 8px 4px 8px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 400;\n color: #666666;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n position: relative;\n}\n.settings-nav[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]::before {\n content: \"\";\n position: absolute;\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n width: 3px;\n height: 0;\n background: #2196f3;\n border-radius: 0 2px 2px 0;\n transition: height all 0.15s ease;\n}\n.settings-nav[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:hover {\n background: #f8f9fa;\n color: #2196f3;\n transform: translateX(2px);\n}\n.settings-nav[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] {\n background: rgba(33, 150, 243, 0.1);\n color: #2196f3;\n font-weight: 500;\n}\n.settings-nav[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%]::before {\n height: 24px;\n}\n.settings-nav[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n width: 18px;\n text-align: center;\n}\n\n.form-field[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n}\n.form-field[_ngcontent-%COMP%] .field-label[_ngcontent-%COMP%] {\n display: block;\n margin-bottom: 4px;\n font-size: 12px;\n font-weight: 500;\n color: #212121;\n}\n.form-field[_ngcontent-%COMP%] .field-hint[_ngcontent-%COMP%] {\n margin-top: 4px;\n font-size: 11px;\n color: #9e9e9e;\n}\n.form-field[_ngcontent-%COMP%] .field-error[_ngcontent-%COMP%] {\n margin-top: 4px;\n font-size: 11px;\n color: #f44336;\n}\n.form-field[_ngcontent-%COMP%] input[_ngcontent-%COMP%], .form-field[_ngcontent-%COMP%] textarea[_ngcontent-%COMP%], .form-field[_ngcontent-%COMP%] select[_ngcontent-%COMP%] {\n width: 100%;\n height: 36px;\n padding: 0 8px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n background: #ffffff;\n color: #212121;\n transition: all 0.15s ease;\n outline: none;\n}\n.form-field[_ngcontent-%COMP%] input[_ngcontent-%COMP%]::placeholder, .form-field[_ngcontent-%COMP%] textarea[_ngcontent-%COMP%]::placeholder, .form-field[_ngcontent-%COMP%] select[_ngcontent-%COMP%]::placeholder {\n color: #9e9e9e;\n}\n.form-field[_ngcontent-%COMP%] input[_ngcontent-%COMP%]:focus, .form-field[_ngcontent-%COMP%] textarea[_ngcontent-%COMP%]:focus, .form-field[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:focus {\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.2);\n}\n.form-field[_ngcontent-%COMP%] input[_ngcontent-%COMP%]:disabled, .form-field[_ngcontent-%COMP%] textarea[_ngcontent-%COMP%]:disabled, .form-field[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:disabled {\n background: #f8f9fa;\n color: #bdbdbd;\n cursor: not-allowed;\n}\n\n.form-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 16px;\n}\n.form-row[_ngcontent-%COMP%] .form-field[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.checkbox-field[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 16px;\n}\n.checkbox-field[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #212121;\n cursor: pointer;\n margin: 0;\n}\n\n.status-bar[_ngcontent-%COMP%] {\n display: flex;\n gap: 24px;\n padding: 16px;\n background: #fafafa;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n margin-bottom: 16px;\n}\n.status-bar[_ngcontent-%COMP%] .status-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n}\n.status-bar[_ngcontent-%COMP%] .status-item[_ngcontent-%COMP%] .status-label[_ngcontent-%COMP%] {\n color: #666666;\n font-weight: 500;\n}\n.status-bar[_ngcontent-%COMP%] .status-item[_ngcontent-%COMP%] .status-value[_ngcontent-%COMP%] {\n color: #212121;\n font-weight: 600;\n}\n.status-bar[_ngcontent-%COMP%] .status-item[_ngcontent-%COMP%] .status-value.text-success[_ngcontent-%COMP%] {\n color: #4caf50;\n}\n.status-bar[_ngcontent-%COMP%] .status-item[_ngcontent-%COMP%] .status-value.text-warning[_ngcontent-%COMP%] {\n color: #ffc107;\n}\n.status-bar[_ngcontent-%COMP%] .status-item[_ngcontent-%COMP%] .status-value.text-danger[_ngcontent-%COMP%] {\n color: #f44336;\n}\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n gap: 16px;\n padding: 48px;\n text-align: center;\n color: #666666;\n}\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: #9e9e9e;\n margin-bottom: 8px;\n}\n.empty-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: #212121;\n}\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n max-width: 400px;\n line-height: 1.6;\n}\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n gap: 16px;\n padding: 48px;\n}\n.loading-container[_ngcontent-%COMP%] .loading-spinner[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n border: 3px solid #e0e0e0;\n border-top-color: #2196f3;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_spin 1s linear infinite;\n}\n.loading-container[_ngcontent-%COMP%] .loading-text[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666666;\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n to {\n transform: rotate(360deg);\n }\n}\n.text-success[_ngcontent-%COMP%] {\n color: #4caf50 !important;\n}\n\n.text-warning[_ngcontent-%COMP%] {\n color: #ffc107 !important;\n}\n\n.text-danger[_ngcontent-%COMP%] {\n color: #f44336 !important;\n}\n\n.text-info[_ngcontent-%COMP%] {\n color: #00bcd4 !important;\n}\n\n.text-muted[_ngcontent-%COMP%] {\n color: #9e9e9e !important;\n}\n\n.text-primary[_ngcontent-%COMP%] {\n color: #212121 !important;\n}\n\n.text-secondary[_ngcontent-%COMP%] {\n color: #666666 !important;\n}\n\n.mb-0[_ngcontent-%COMP%] {\n margin-bottom: 0 !important;\n}\n\n.mb-1[_ngcontent-%COMP%] {\n margin-bottom: 4px !important;\n}\n\n.mb-2[_ngcontent-%COMP%] {\n margin-bottom: 8px !important;\n}\n\n.mb-3[_ngcontent-%COMP%] {\n margin-bottom: 16px !important;\n}\n\n.mb-4[_ngcontent-%COMP%] {\n margin-bottom: 24px !important;\n}\n\n.mb-5[_ngcontent-%COMP%] {\n margin-bottom: 32px !important;\n}\n\n.mt-0[_ngcontent-%COMP%] {\n margin-top: 0 !important;\n}\n\n.mt-1[_ngcontent-%COMP%] {\n margin-top: 4px !important;\n}\n\n.mt-2[_ngcontent-%COMP%] {\n margin-top: 8px !important;\n}\n\n.mt-3[_ngcontent-%COMP%] {\n margin-top: 16px !important;\n}\n\n.mt-4[_ngcontent-%COMP%] {\n margin-top: 24px !important;\n}\n\n.mt-5[_ngcontent-%COMP%] {\n margin-top: 32px !important;\n}\n\n.d-flex[_ngcontent-%COMP%] {\n display: flex !important;\n}\n\n.d-block[_ngcontent-%COMP%] {\n display: block !important;\n}\n\n.d-inline-block[_ngcontent-%COMP%] {\n display: inline-block !important;\n}\n\n.d-none[_ngcontent-%COMP%] {\n display: none !important;\n}\n\n.flex-1[_ngcontent-%COMP%] {\n flex: 1 !important;\n}\n\n.flex-column[_ngcontent-%COMP%] {\n flex-direction: column !important;\n}\n\n.flex-row[_ngcontent-%COMP%] {\n flex-direction: row !important;\n}\n\n.align-center[_ngcontent-%COMP%] {\n align-items: center !important;\n}\n\n.justify-center[_ngcontent-%COMP%] {\n justify-content: center !important;\n}\n\n.justify-between[_ngcontent-%COMP%] {\n justify-content: space-between !important;\n}\n\n.stats-grid[_ngcontent-%COMP%] {\n display: grid !important;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1.5rem;\n width: 100%;\n}\n@media (max-width: 768px) {\n .stats-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n.stat-card[_ngcontent-%COMP%] {\n margin-right: 10px;\n}\n\n.role-card[_ngcontent-%COMP%] {\n margin-bottom: 5px;\n}\n\n@media (max-width: 768px) {\n .settings-card[_ngcontent-%COMP%] {\n padding: 16px;\n }\n .dashboard-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 16px;\n }\n .dashboard-header[_ngcontent-%COMP%] .header-controls[_ngcontent-%COMP%] {\n width: 100%;\n justify-content: flex-start;\n }\n .form-row[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n}\n[_nghost-%COMP%] {\n display: block;\n width: 100%;\n}\n\n.settings-card[_ngcontent-%COMP%] {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n.settings-card[_ngcontent-%COMP%]:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n.settings-card.settings-card-floating[_ngcontent-%COMP%] {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n}\n.settings-card.settings-card-floating[_ngcontent-%COMP%]:hover {\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-2px);\n}\n.settings-card.settings-card-sm[_ngcontent-%COMP%] {\n padding: 16px;\n}\n.settings-card.settings-card-lg[_ngcontent-%COMP%] {\n padding: 24px;\n}\n\n.card-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n padding: 24px 24px 16px 24px;\n margin: 0;\n}\n.card-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%], .card-header[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0;\n font-weight: 500;\n color: #212121;\n font-size: 16px;\n}\n.card-header[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.card-header[_ngcontent-%COMP%] .header-content[_ngcontent-%COMP%] {\n flex: 1;\n}\n.card-header[_ngcontent-%COMP%] .header-content[_ngcontent-%COMP%] .card-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #212121;\n line-height: 1.2;\n}\n.card-header[_ngcontent-%COMP%] .header-content[_ngcontent-%COMP%] .card-subtitle[_ngcontent-%COMP%] {\n margin: 4px 0 0 0;\n font-size: 12px;\n color: #666666;\n line-height: 1.4;\n}\n.card-header[_ngcontent-%COMP%] .header-actions[_ngcontent-%COMP%] {\n flex-shrink: 0;\n display: flex;\n gap: 8px;\n align-items: flex-start;\n}\n\n.card-content[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n padding: 0 24px 24px 24px;\n}\n.card-content.no-padding[_ngcontent-%COMP%] {\n padding: 0;\n}\n\n.card-footer[_ngcontent-%COMP%] {\n flex-shrink: 0;\n padding: 16px 24px 24px 24px;\n border-top: 1px solid #e0e0e0;\n background: #f8f9fa;\n border-radius: 0 0 8px 8px;\n}\n\n.settings-card-sm[_ngcontent-%COMP%] .card-header[_ngcontent-%COMP%] {\n padding: 16px 16px 8px 16px;\n}\n.settings-card-sm[_ngcontent-%COMP%] .card-header[_ngcontent-%COMP%] .card-title[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n.settings-card-sm[_ngcontent-%COMP%] .card-header[_ngcontent-%COMP%] .card-subtitle[_ngcontent-%COMP%] {\n font-size: 11px;\n}\n.settings-card-sm[_ngcontent-%COMP%] .card-content[_ngcontent-%COMP%] {\n padding: 0 16px 16px 16px;\n}\n.settings-card-sm[_ngcontent-%COMP%] .card-content.no-padding[_ngcontent-%COMP%] {\n padding: 0;\n}\n.settings-card-sm[_ngcontent-%COMP%] .card-footer[_ngcontent-%COMP%] {\n padding: 8px 16px 16px 16px;\n}\n\n.settings-card-lg[_ngcontent-%COMP%] .card-header[_ngcontent-%COMP%] {\n padding: 32px 32px 24px 32px;\n}\n.settings-card-lg[_ngcontent-%COMP%] .card-header[_ngcontent-%COMP%] .card-title[_ngcontent-%COMP%] {\n font-size: 18px;\n}\n.settings-card-lg[_ngcontent-%COMP%] .card-content[_ngcontent-%COMP%] {\n padding: 0 32px 32px 32px;\n}\n.settings-card-lg[_ngcontent-%COMP%] .card-content.no-padding[_ngcontent-%COMP%] {\n padding: 0;\n}\n.settings-card-lg[_ngcontent-%COMP%] .card-footer[_ngcontent-%COMP%] {\n padding: 24px 32px 32px 32px;\n}"] });
|
|
113
|
-
}
|
|
114
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SettingsCardComponent, [{
|
|
115
|
-
type: Component,
|
|
116
|
-
args: [{ selector: 'mj-settings-card', standalone: true, imports: [CommonModule], template: `
|
|
117
|
-
<div class="mj-card"
|
|
118
|
-
[class.mj-card-floating]="floating"
|
|
119
|
-
[class.mj-card-sm]="size === 'sm'"
|
|
120
|
-
[class.mj-card-lg]="size === 'lg'">
|
|
121
|
-
@if (title || headerTemplate) {
|
|
122
|
-
<div class="mj-card-header">
|
|
123
|
-
@if (headerTemplate) {
|
|
124
|
-
<ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
|
|
125
|
-
} @else {
|
|
126
|
-
<div class="header-content">
|
|
127
|
-
<h3 class="card-title">{{ title }}</h3>
|
|
128
|
-
@if (subtitle) {
|
|
129
|
-
<p class="card-subtitle">{{ subtitle }}</p>
|
|
130
|
-
}
|
|
131
|
-
</div>
|
|
132
|
-
@if (actionTemplate) {
|
|
133
|
-
<div class="mj-card-actions">
|
|
134
|
-
<ng-container *ngTemplateOutlet="actionTemplate"></ng-container>
|
|
135
|
-
</div>
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
</div>
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
<div class="mj-card-body" [class.no-padding]="noPadding">
|
|
142
|
-
<ng-content></ng-content>
|
|
143
|
-
</div>
|
|
144
|
-
|
|
145
|
-
@if (footerTemplate) {
|
|
146
|
-
<div class="mj-card-footer">
|
|
147
|
-
<ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
|
|
148
|
-
</div>
|
|
149
|
-
}
|
|
150
|
-
</div>
|
|
151
|
-
`, styles: ["@keyframes shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n:host {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n font-size: 14px;\n line-height: 1.4;\n color: #212121;\n}\n:host * {\n box-sizing: border-box;\n}\n\n.settings-card {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n padding: 20px;\n}\n.settings-card:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n\n.settings-card-floating {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n padding: 20px;\n}\n.settings-card-floating:hover {\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-2px);\n}\n\n.settings-card-sm {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n padding: 16px;\n}\n.settings-card-sm:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n\n.settings-card-lg {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n padding: 24px;\n}\n.settings-card-lg:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n\n.dashboard-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding: 24px 0 16px 0;\n}\n.dashboard-header .header-info {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 16px;\n}\n.dashboard-header .header-info h1, .dashboard-header .header-info h2, .dashboard-header .header-info h3 {\n margin: 0;\n font-weight: 600;\n color: #212121;\n font-size: 24px;\n line-height: 1.2;\n}\n.dashboard-header .header-info h2 {\n font-size: 18px;\n}\n.dashboard-header .header-info h3 {\n font-size: 16px;\n}\n.dashboard-header .header-controls {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.section-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid #e0e0e0;\n}\n.section-header h3, .section-header h4 {\n margin: 0;\n font-weight: 500;\n color: #212121;\n font-size: 16px;\n}\n.section-header h4 {\n font-size: 14px;\n}\n\n.control-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #ffffff;\n color: #212121;\n border-color: #bdbdbd;\n}\n.control-btn:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn i {\n font-size: 14px;\n}\n.control-btn:hover:not(:disabled) {\n background: #f8f9fa;\n border-color: #2196f3;\n color: #2196f3;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n.control-btn.control-btn-primary {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #2196f3;\n color: #ffffff;\n border-color: #2196f3;\n}\n.control-btn.control-btn-primary:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn.control-btn-primary:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn.control-btn-primary i {\n font-size: 14px;\n}\n.control-btn.control-btn-primary:hover:not(:disabled) {\n background: #1976d2;\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);\n transform: translateY(-1px);\n}\n.control-btn.control-btn-primary:active {\n transform: translateY(0);\n}\n.control-btn.control-btn-danger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #f44336;\n color: #ffffff;\n border-color: #f44336;\n}\n.control-btn.control-btn-danger:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn.control-btn-danger:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn.control-btn-danger i {\n font-size: 14px;\n}\n.control-btn.control-btn-danger:hover:not(:disabled) {\n background: #ea1c0d;\n border-color: #ea1c0d;\n box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3);\n transform: translateY(-1px);\n}\n.control-btn.control-btn-ghost {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: transparent;\n color: #666666;\n border-color: transparent;\n}\n.control-btn.control-btn-ghost:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.control-btn.control-btn-ghost:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.control-btn.control-btn-ghost i {\n font-size: 14px;\n}\n.control-btn.control-btn-ghost:hover:not(:disabled) {\n background: #f8f9fa;\n color: #2196f3;\n}\n.control-btn.control-btn-sm {\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n}\n.control-btn.control-btn-sm i {\n font-size: 12px;\n}\n.control-btn.control-btn-lg {\n height: 44px;\n padding: 0 24px;\n font-size: 14px;\n}\n.control-btn.control-btn-lg i {\n font-size: 16px;\n}\n\n.action-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: transparent;\n color: #666666;\n border-color: transparent;\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n width: 28px;\n padding: 0;\n}\n.action-btn:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.action-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.action-btn i {\n font-size: 14px;\n}\n.action-btn:hover:not(:disabled) {\n background: #f8f9fa;\n color: #2196f3;\n}\n.action-btn i {\n font-size: 12px;\n}\n.action-btn.action-btn-primary {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #2196f3;\n color: #ffffff;\n border-color: #2196f3;\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n width: 28px;\n padding: 0;\n}\n.action-btn.action-btn-primary:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.action-btn.action-btn-primary:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.action-btn.action-btn-primary i {\n font-size: 14px;\n}\n.action-btn.action-btn-primary:hover:not(:disabled) {\n background: #1976d2;\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);\n transform: translateY(-1px);\n}\n.action-btn.action-btn-primary:active {\n transform: translateY(0);\n}\n.action-btn.action-btn-primary i {\n font-size: 12px;\n}\n.action-btn.action-btn-danger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0 16px;\n height: 36px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n outline: none;\n white-space: nowrap;\n background: #f44336;\n color: #ffffff;\n border-color: #f44336;\n height: 28px;\n padding: 0 8px;\n font-size: 11px;\n width: 28px;\n padding: 0;\n}\n.action-btn.action-btn-danger:focus {\n outline: 2px solid rgba(33, 150, 243, 0.2);\n outline-offset: 2px;\n}\n.action-btn.action-btn-danger:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n.action-btn.action-btn-danger i {\n font-size: 14px;\n}\n.action-btn.action-btn-danger:hover:not(:disabled) {\n background: #ea1c0d;\n border-color: #ea1c0d;\n box-shadow: 0 2px 8px rgba(244, 67, 54, 0.3);\n transform: translateY(-1px);\n}\n.action-btn.action-btn-danger i {\n font-size: 12px;\n}\n\n.info-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(33, 150, 243, 0.1);\n color: #1976d2;\n border: 1px solid rgba(33, 150, 243, 0.2);\n}\n\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(158, 158, 158, 0.1);\n color: #666666;\n border: 1px solid rgba(158, 158, 158, 0.2);\n}\n.status-badge.status-active {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(76, 175, 80, 0.1);\n color: #3d8b40;\n border: 1px solid rgba(76, 175, 80, 0.2);\n}\n.status-badge.status-migration {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(33, 150, 243, 0.1);\n color: #1976d2;\n border: 1px solid rgba(33, 150, 243, 0.2);\n}\n.status-badge.status-warning {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(255, 193, 7, 0.1);\n color: #6d5200;\n border: 1px solid rgba(255, 193, 7, 0.2);\n}\n.status-badge.status-error {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 8px;\n height: 20px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n background: rgba(244, 67, 54, 0.1);\n color: #ea1c0d;\n border: 1px solid rgba(244, 67, 54, 0.2);\n}\n\n.settings-nav {\n width: 220px;\n min-width: 220px;\n max-width: 220px;\n background: #ffffff;\n border-right: 1px solid #e0e0e0;\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow-y: auto;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n}\n.settings-nav .nav-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n margin: 0 8px 4px 8px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 400;\n color: #666666;\n text-decoration: none;\n cursor: pointer;\n transition: all 0.15s ease;\n position: relative;\n}\n.settings-nav .nav-item::before {\n content: \"\";\n position: absolute;\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n width: 3px;\n height: 0;\n background: #2196f3;\n border-radius: 0 2px 2px 0;\n transition: height all 0.15s ease;\n}\n.settings-nav .nav-item:hover {\n background: #f8f9fa;\n color: #2196f3;\n transform: translateX(2px);\n}\n.settings-nav .nav-item.active {\n background: rgba(33, 150, 243, 0.1);\n color: #2196f3;\n font-weight: 500;\n}\n.settings-nav .nav-item.active::before {\n height: 24px;\n}\n.settings-nav .nav-item i {\n font-size: 14px;\n width: 18px;\n text-align: center;\n}\n\n.form-field {\n margin-bottom: 16px;\n}\n.form-field .field-label {\n display: block;\n margin-bottom: 4px;\n font-size: 12px;\n font-weight: 500;\n color: #212121;\n}\n.form-field .field-hint {\n margin-top: 4px;\n font-size: 11px;\n color: #9e9e9e;\n}\n.form-field .field-error {\n margin-top: 4px;\n font-size: 11px;\n color: #f44336;\n}\n.form-field input, .form-field textarea, .form-field select {\n width: 100%;\n height: 36px;\n padding: 0 8px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n background: #ffffff;\n color: #212121;\n transition: all 0.15s ease;\n outline: none;\n}\n.form-field input::placeholder, .form-field textarea::placeholder, .form-field select::placeholder {\n color: #9e9e9e;\n}\n.form-field input:focus, .form-field textarea:focus, .form-field select:focus {\n border-color: #2196f3;\n box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.2);\n}\n.form-field input:disabled, .form-field textarea:disabled, .form-field select:disabled {\n background: #f8f9fa;\n color: #bdbdbd;\n cursor: not-allowed;\n}\n\n.form-row {\n display: flex;\n gap: 16px;\n}\n.form-row .form-field {\n flex: 1;\n}\n\n.checkbox-field {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 16px;\n}\n.checkbox-field label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: #212121;\n cursor: pointer;\n margin: 0;\n}\n\n.status-bar {\n display: flex;\n gap: 24px;\n padding: 16px;\n background: #fafafa;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n margin-bottom: 16px;\n}\n.status-bar .status-item {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n}\n.status-bar .status-item .status-label {\n color: #666666;\n font-weight: 500;\n}\n.status-bar .status-item .status-value {\n color: #212121;\n font-weight: 600;\n}\n.status-bar .status-item .status-value.text-success {\n color: #4caf50;\n}\n.status-bar .status-item .status-value.text-warning {\n color: #ffc107;\n}\n.status-bar .status-item .status-value.text-danger {\n color: #f44336;\n}\n\n.empty-state {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n gap: 16px;\n padding: 48px;\n text-align: center;\n color: #666666;\n}\n.empty-state i {\n font-size: 48px;\n color: #9e9e9e;\n margin-bottom: 8px;\n}\n.empty-state h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: #212121;\n}\n.empty-state p {\n margin: 0;\n font-size: 14px;\n max-width: 400px;\n line-height: 1.6;\n}\n\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n gap: 16px;\n padding: 48px;\n}\n.loading-container .loading-spinner {\n width: 40px;\n height: 40px;\n border: 3px solid #e0e0e0;\n border-top-color: #2196f3;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n.loading-container .loading-text {\n font-size: 12px;\n color: #666666;\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n.text-success {\n color: #4caf50 !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\n.text-danger {\n color: #f44336 !important;\n}\n\n.text-info {\n color: #00bcd4 !important;\n}\n\n.text-muted {\n color: #9e9e9e !important;\n}\n\n.text-primary {\n color: #212121 !important;\n}\n\n.text-secondary {\n color: #666666 !important;\n}\n\n.mb-0 {\n margin-bottom: 0 !important;\n}\n\n.mb-1 {\n margin-bottom: 4px !important;\n}\n\n.mb-2 {\n margin-bottom: 8px !important;\n}\n\n.mb-3 {\n margin-bottom: 16px !important;\n}\n\n.mb-4 {\n margin-bottom: 24px !important;\n}\n\n.mb-5 {\n margin-bottom: 32px !important;\n}\n\n.mt-0 {\n margin-top: 0 !important;\n}\n\n.mt-1 {\n margin-top: 4px !important;\n}\n\n.mt-2 {\n margin-top: 8px !important;\n}\n\n.mt-3 {\n margin-top: 16px !important;\n}\n\n.mt-4 {\n margin-top: 24px !important;\n}\n\n.mt-5 {\n margin-top: 32px !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-none {\n display: none !important;\n}\n\n.flex-1 {\n flex: 1 !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.align-center {\n align-items: center !important;\n}\n\n.justify-center {\n justify-content: center !important;\n}\n\n.justify-between {\n justify-content: space-between !important;\n}\n\n.stats-grid {\n display: grid !important;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1.5rem;\n width: 100%;\n}\n@media (max-width: 768px) {\n .stats-grid {\n grid-template-columns: repeat(2, 1fr);\n gap: 1rem;\n }\n}\n\n.stat-card {\n margin-right: 10px;\n}\n\n.role-card {\n margin-bottom: 5px;\n}\n\n@media (max-width: 768px) {\n .settings-card {\n padding: 16px;\n }\n .dashboard-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 16px;\n }\n .dashboard-header .header-controls {\n width: 100%;\n justify-content: flex-start;\n }\n .form-row {\n flex-direction: column;\n }\n}\n:host {\n display: block;\n width: 100%;\n}\n\n.settings-card {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n.settings-card:hover {\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-1px);\n}\n.settings-card.settings-card-floating {\n background: #ffffff;\n border-radius: 8px;\n border: 1px solid #e0e0e0;\n overflow: hidden;\n transition: all 0.2s ease;\n box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);\n}\n.settings-card.settings-card-floating:hover {\n box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);\n transform: translateY(-2px);\n}\n.settings-card.settings-card-sm {\n padding: 16px;\n}\n.settings-card.settings-card-lg {\n padding: 24px;\n}\n\n.card-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 8px;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n padding: 24px 24px 16px 24px;\n margin: 0;\n}\n.card-header h3, .card-header h4 {\n margin: 0;\n font-weight: 500;\n color: #212121;\n font-size: 16px;\n}\n.card-header h4 {\n font-size: 14px;\n}\n.card-header .header-content {\n flex: 1;\n}\n.card-header .header-content .card-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #212121;\n line-height: 1.2;\n}\n.card-header .header-content .card-subtitle {\n margin: 4px 0 0 0;\n font-size: 12px;\n color: #666666;\n line-height: 1.4;\n}\n.card-header .header-actions {\n flex-shrink: 0;\n display: flex;\n gap: 8px;\n align-items: flex-start;\n}\n\n.card-content {\n flex: 1;\n min-height: 0;\n padding: 0 24px 24px 24px;\n}\n.card-content.no-padding {\n padding: 0;\n}\n\n.card-footer {\n flex-shrink: 0;\n padding: 16px 24px 24px 24px;\n border-top: 1px solid #e0e0e0;\n background: #f8f9fa;\n border-radius: 0 0 8px 8px;\n}\n\n.settings-card-sm .card-header {\n padding: 16px 16px 8px 16px;\n}\n.settings-card-sm .card-header .card-title {\n font-size: 14px;\n}\n.settings-card-sm .card-header .card-subtitle {\n font-size: 11px;\n}\n.settings-card-sm .card-content {\n padding: 0 16px 16px 16px;\n}\n.settings-card-sm .card-content.no-padding {\n padding: 0;\n}\n.settings-card-sm .card-footer {\n padding: 8px 16px 16px 16px;\n}\n\n.settings-card-lg .card-header {\n padding: 32px 32px 24px 32px;\n}\n.settings-card-lg .card-header .card-title {\n font-size: 18px;\n}\n.settings-card-lg .card-content {\n padding: 0 32px 32px 32px;\n}\n.settings-card-lg .card-content.no-padding {\n padding: 0;\n}\n.settings-card-lg .card-footer {\n padding: 24px 32px 32px 32px;\n}\n"] }]
|
|
152
|
-
}], null, { title: [{
|
|
153
|
-
type: Input
|
|
154
|
-
}], subtitle: [{
|
|
155
|
-
type: Input
|
|
156
|
-
}], floating: [{
|
|
157
|
-
type: Input
|
|
158
|
-
}], size: [{
|
|
159
|
-
type: Input
|
|
160
|
-
}], noPadding: [{
|
|
161
|
-
type: Input
|
|
162
|
-
}], headerTemplate: [{
|
|
163
|
-
type: Input
|
|
164
|
-
}], actionTemplate: [{
|
|
165
|
-
type: Input
|
|
166
|
-
}], footerTemplate: [{
|
|
167
|
-
type: Input
|
|
168
|
-
}] }); })();
|
|
169
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SettingsCardComponent, { className: "SettingsCardComponent" }); })();
|
|
170
|
-
//# sourceMappingURL=settings-card.component.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"settings-card.component.js","sourceRoot":"","sources":["../../../../../src/lib/shared/components/settings-card/settings-card.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAe,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;;;;;IAkBnC,wBAAgE;;;IAAhE,oHAAiD;;;IAAlC,wDAAgC;;;IAK3C,4BAAyB;IAAA,YAAc;IAAA,iBAAI;;;IAAlB,cAAc;IAAd,qCAAc;;;IAKvC,wBAAgE;;;IADlE,8BAA6B;IAC3B,kIAAiD;IACnD,iBAAM;;;IADW,cAAgC;IAAhC,wDAAgC;;;IAPjD,AADF,8BAA4B,YACH;IAAA,YAAW;IAAA,iBAAK;IACvC,wGAAgB;IAGlB,iBAAM;IACN,0GAAsB;;;IALG,eAAW;IAAX,kCAAW;IAClC,cAEC;IAFD,0CAEC;IAEH,cAIC;IAJD,gDAIC;;;IAdL,8BAA4B;IAGxB,AAFF,kGAAsB,qEAEb;IAaX,iBAAM;;;IAfJ,cAcC;IAdD,+CAcC;;;IAUD,wBAAgE;;;IADlE,8BAA4B;IAC1B,sGAAiD;IACnD,iBAAM;;;IADW,cAAgC;IAAhC,wDAAgC;;AAvCzD;;;GAGG;AA2CH,MAAM,OAAO,qBAAqB;IAChC,yCAAyC;IAChC,KAAK,CAAU;IAExB,8CAA8C;IACrC,QAAQ,CAAU;IAE3B,4DAA4D;IACnD,QAAQ,GAAG,KAAK,CAAC;IAE1B,wBAAwB;IACf,IAAI,GAAuB,IAAI,CAAC;IAEzC,kDAAkD;IACzC,SAAS,GAAG,KAAK,CAAC;IAE3B,yCAAyC;IAChC,cAAc,CAAoB;IAE3C,yCAAyC;IAChC,cAAc,CAAoB;IAE3C,kCAAkC;IACzB,cAAc,CAAoB;+EAvBhC,qBAAqB;6DAArB,qBAAqB;;YArC9B,8BAGwC;YACtC,8EAA+B;YAoB/B,8BAAyD;YACvD,kBAAyB;YAC3B,iBAAM;YAEN,8EAAsB;YAKxB,iBAAM;;YA9BD,AADA,AADA,gDAAmC,iCACD,iCACA;YACrC,cAkBC;YAlBD,0DAkBC;YAEyB,cAA8B;YAA9B,2CAA8B;YAIxD,eAIC;YAJD,6CAIC;4BAlCK,YAAY;;iFAuCX,qBAAqB;cA1CjC,SAAS;2BACE,kBAAkB,cAChB,IAAI,WACP,CAAC,YAAY,CAAC,YACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCT;gBAKQ,KAAK;kBAAb,KAAK;YAGG,QAAQ;kBAAhB,KAAK;YAGG,QAAQ;kBAAhB,KAAK;YAGG,IAAI;kBAAZ,KAAK;YAGG,SAAS;kBAAjB,KAAK;YAGG,cAAc;kBAAtB,KAAK;YAGG,cAAc;kBAAtB,KAAK;YAGG,cAAc;kBAAtB,KAAK;;kFAvBK,qBAAqB"}
|