Commit 7620f738 authored by Marco Ortiz's avatar Marco Ortiz

Merge branch 'developer' into 'master'

Developer

See merge request ByteBot/web/bytebot-workspace!2
parents b0367de7 592c0764
Pipeline #313 failed with stages
......@@ -38,6 +38,7 @@
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"vendor/metismenu/metisMenu.js",
"node_modules/popper.js/dist/umd/popper.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js",
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"node_modules/jquery-slimscroll/jquery.slimscroll.min.js",
......@@ -48,7 +49,6 @@
"node_modules/ace-builds/src-min/mode-java.js",
"node_modules/peity/jquery.peity.min.js",
"node_modules/pace-js/pace.min.js",
"node_modules/popper.js/dist/umd/popper.js",
"vendor/inspinia/inspinia.js"
]
},
......
......@@ -1389,9 +1389,12 @@
"integrity": "sha512-Yao5AFq8HKHsyLCLZn15BybqN67cdbgDbVDnnaN54IUaffkwDHj0VMgBp1C/IxOsFsNltsI7LdnW4GEHmQarLQ=="
},
"@ng-bootstrap/ng-bootstrap": {
"version": "6.0.2",
"resolved": "http://192.168.27.7:4873/@ng-bootstrap%2fng-bootstrap/-/ng-bootstrap-6.0.2.tgz",
"integrity": "sha512-8+Dz8GN15zneIA4+mQ7b1j5O+oHsFdhTYQVDGk6eAazHeqrFD0+o8N+pLZL2PZlf98WcHFmXIOqKXrBD8iLl1g=="
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-5.1.5.tgz",
"integrity": "sha512-DpeKD2LOv2LFSzYOOiWd6qv52i7KS2VY9SQs4533OPspJKq7mKo6zZ3jgAV+tPP+Bit1+pF3hkVpfh+rFMCejA==",
"requires": {
"tslib": "^1.9.0"
}
},
"@ng-select/ng-select": {
"version": "3.7.3",
......@@ -1814,41 +1817,36 @@
}
},
"@xdf/commons": {
"version": "1.0.0",
"resolved": "http://192.168.27.7:4873/@xdf%2fcommons/-/commons-1.0.0.tgz",
"integrity": "sha512-K1dJkuU4fJFhW59M/x7RiS8NJ+1K87FIO2nZs3c+3xZJajBNJDBb2rhvID7I43n88D1Yr+cr2aJkxIpp16fgdg==",
"version": "file:../../XDF/ng-byte-framework/dist/xdf-commons/xdf-commons-1.0.0.tgz",
"integrity": "sha512-OGPxnWBkEflVYRBZDmYWZ3tpQImBaasWEA8lPqDxadTzpg/48SmALEURhTenYN1c7+5zMkqU7X1a7Sh/2h0rVw==",
"requires": {
"tslib": "^1.9.0"
}
},
"@xdf/gallery": {
"version": "1.0.4",
"resolved": "http://192.168.27.7:4873/@xdf%2fgallery/-/gallery-1.0.4.tgz",
"integrity": "sha512-d9+/MXHMYy9btBXOpri8MdxO89bvN+XsjTLw2bt0TVzuOtpHXd97cq2ehmZosfy/6e2MTOJ7FHqvlbl27vGK/A==",
"version": "file:../../XDF/ng-byte-framework/dist/xdf-gallery/xdf-gallery-1.0.8.tgz",
"integrity": "sha512-eOUgs9LGPAVJh/pPBwbNfv+3ZlIlmm7YgO01lFzZUD9bzETOxIfj3DpOluRE4WZxL2UBLB+c3uYjczwoEnEPlg==",
"requires": {
"tslib": "^1.9.0"
}
},
"@xdf/layouts": {
"version": "1.0.0",
"resolved": "http://192.168.27.7:4873/@xdf%2flayouts/-/layouts-1.0.0.tgz",
"integrity": "sha512-7z/I3/+VDPVnJfXjQ3ePvgGg9yv5ZTxmIVEjR2sstrrrd/gEhUy8+3CNFkzgusXfxMYPD7E5DedYVjmUOtbskw==",
"version": "file:../../XDF/ng-byte-framework/dist/xdf-layouts/xdf-layouts-1.0.1.tgz",
"integrity": "sha512-yF8ay/2Yc2/o3AjBIcNkMIY0PmKD/OEfu1GHVymfVUWp3F5BrTXYo3Lvy1Nz/E+GEN9aDtGEkyrZe0zpzO+Irg==",
"requires": {
"tslib": "^1.9.0"
}
},
"@xdf/security": {
"version": "1.0.0",
"resolved": "http://192.168.27.7:4873/@xdf%2fsecurity/-/security-1.0.0.tgz",
"integrity": "sha512-anTe2J9dmBsTy2t5lzg3mXek0+/NfOKeU2elPuR+OIWi1UUaCwUnW1EDEA2kgLtujqlmVJFPTKHKpPdRjKwQIw==",
"version": "file:../../XDF/ng-byte-framework/dist/xdf-security/xdf-security-1.0.0.tgz",
"integrity": "sha512-3Arppi1D2R+h9pYzkbAFt19PGcBMQQCYEuikqOScZKgCkxJhroaKPNVEASgy+ZfLogO3MQ3/37f7ukXZeVx2IQ==",
"requires": {
"tslib": "^1.9.0"
}
},
"@xdf/settings": {
"version": "1.0.2",
"resolved": "http://192.168.27.7:4873/@xdf%2fsettings/-/settings-1.0.2.tgz",
"integrity": "sha512-ebaYci2qT45Yu0fHkCEfl/YT5mK6P6Uef20fgXrlKKUJl1+8b3fubRCCnNKxj2xwxAoBY5DG8jhQG9biwFaNSg==",
"version": "file:../../XDF/ng-byte-framework/dist/xdf-settings/xdf-settings-1.0.3.tgz",
"integrity": "sha512-btepMgHDMkABtx1/IUxwEFNMnzH+Iew1s3J52/x8QZ5/XHyjfQcF9MsQXHvoVxP39DSmjnloYDrAMrbyIp9u+Q==",
"requires": {
"tslib": "^1.9.0"
}
......@@ -8985,6 +8983,14 @@
"tslib": "^1.9.0"
}
},
"ngx-material-file-input": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ngx-material-file-input/-/ngx-material-file-input-2.0.0.tgz",
"integrity": "sha512-E8JyFbAZRzCNC96lbkzg+LS4xmcfoZ3Mh4jhuWfAeBngMLlLxdMW2dgZSu8MFO9V2ZQytb1DPjgmvGhJo5TJHg==",
"requires": {
"tslib": "^1.9.0"
}
},
"ngx-moment": {
"version": "3.5.0",
"resolved": "http://192.168.27.7:4873/ngx-moment/-/ngx-moment-3.5.0.tgz",
......@@ -9003,6 +9009,14 @@
"tslib": "^1.9.0"
}
},
"ngx-slick-carousel": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/ngx-slick-carousel/-/ngx-slick-carousel-0.5.1.tgz",
"integrity": "sha512-AQr0xhMkp7lobBidkNH2d0Jk5HT0txQVkRwrw0SmOvsQLdUkfo9t0/V7JFssh3yAkkL6YwWvKy7sofBZqHk0bw==",
"requires": {
"tslib": "^1.10.0"
}
},
"ngx-toastr": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-10.2.0.tgz",
......@@ -9020,6 +9034,14 @@
"tslib": "^1.9.0"
}
},
"ngx-tribute": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/ngx-tribute/-/ngx-tribute-1.5.1.tgz",
"integrity": "sha512-UyBwFfg4x8OyxjCb+F8xmlTEMU787xeXiHV3Q9/pWs+LcOetX6XXDi1dc1SWoLuT3+sgM+TM6p8SvfWyaGc4/Q==",
"requires": {
"tslib": "^1.9.0"
}
},
"nice-try": {
"version": "1.0.5",
"resolved": "http://192.168.27.7:4873/nice-try/-/nice-try-1.0.5.tgz",
......@@ -12807,6 +12829,11 @@
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true
},
"tributejs": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/tributejs/-/tributejs-5.1.3.tgz",
"integrity": "sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ=="
},
"ts-node": {
"version": "7.0.1",
"resolved": "http://192.168.27.7:4873/ts-node/-/ts-node-7.0.1.tgz",
......
......@@ -25,18 +25,18 @@
"@angular/platform-browser-dynamic": "^8.2.14",
"@angular/router": "~8.2.14",
"@babel/compat-data": "^7.8.0",
"@ng-bootstrap/ng-bootstrap": "^6.0.2",
"@ng-bootstrap/ng-bootstrap": "^5.1.5",
"@ng-select/ng-select": "^3.7.3",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "^4.0.0",
"@swimlane/ngx-datatable": "^16.0.3",
"@swimlane/ngx-graph": "^6.2.0",
"@types/jquery": "^3.3.34",
"@xdf/commons": "^1.0.0",
"@xdf/gallery": "^1.0.4",
"@xdf/layouts": "^1.0.0",
"@xdf/security": "^1.0.0",
"@xdf/settings": "^1.0.2",
"@xdf/commons": "file:../../XDF/ng-byte-framework/dist/xdf-commons/xdf-commons-1.0.0.tgz",
"@xdf/gallery": "file:../../XDF/ng-byte-framework/dist/xdf-gallery/xdf-gallery-1.0.8.tgz",
"@xdf/layouts": "file:../../XDF/ng-byte-framework/dist/xdf-layouts/xdf-layouts-1.0.1.tgz",
"@xdf/security": "file:../../XDF/ng-byte-framework/dist/xdf-security/xdf-security-1.0.0.tgz",
"@xdf/settings": "file:../../XDF/ng-byte-framework/dist/xdf-settings/xdf-settings-1.0.3.tgz",
"ace-builds": "^1.4.8",
"angular-highcharts": "^9.0.2",
"angular-marked": "^1.2.2",
......@@ -63,10 +63,13 @@
"ngx-bootstrap": "^4.3.0",
"ngx-color": "^4.1.1",
"ngx-echarts": "^4.2.2",
"ngx-material-file-input": "^2.0.0",
"ngx-moment": "^3.5.0",
"ngx-scrollbar": "^7.1.0",
"ngx-slick-carousel": "^0.5.1",
"ngx-toastr": "^10.0.4",
"ngx-translate-multi-http-loader": "^3.0.0",
"ngx-tribute": "^1.5.1",
"nomnoml": "^0.6.2",
"pace-js": "^1.0.2",
"peity": "^3.3.0",
......@@ -74,6 +77,7 @@
"popper.js": "^1.16.1",
"rxjs": "~6.4.0",
"stompjs": "^2.3.3",
"tributejs": "^5.1.3",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"
},
......
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BaseLayoutComponent, NotFoundComponent } from '@xdf/layouts';
import { BaseLayoutComponent, NotFoundComponent, CustomLayoutComponent } from '@xdf/layouts';
import { AuthGuard, LoginComponent } from '@xdf/security';
import { HomeComponent } from './views/home/home.component';
......@@ -10,17 +10,21 @@ const routes: Routes = [
// Main redirect
{ path: '', redirectTo: 'home', pathMatch: 'full', canActivate: [AuthGuard] },
{
path: '', component: BaseLayoutComponent,
path: '', component: CustomLayoutComponent,
children: [
{ path: 'home', component: HomeComponent, data: { breadcrumb: 'Home' } },
{
path: 'security', canLoad: [AuthGuard],
path: 'security', data: { breadcrumb: 'Seguridad' }, canLoad: [AuthGuard],
loadChildren: () => import('./modules/security/security.module').then(m => m.SecurityModule)
},
{
path: 'settings', canLoad: [AuthGuard],
path: 'settings', data: { breadcrumb: 'Ajustes Generales' }, canLoad: [AuthGuard],
loadChildren: () => import('./modules/settings/settings.module').then(m => m.SettingsModule)
}
},
{
path: 'configuration', data: { breadcrumb: 'Agentes' }, canLoad: [AuthGuard],
loadChildren: () => import('./modules/agent/agent.module').then(m => m.AgentModule)
},
],
canActivate: [AuthGuard]
},
......
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { CommonModule, HashLocationStrategy, LocationStrategy, registerLocaleData } from '@angular/common';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { MAT_DATE_LOCALE, DateAdapter, MAT_DATE_FORMATS, MatPaginatorIntl } from '@angular/material';
// XDF
import { XdfLayoutsModule, SettingsService, ByteSettingsService, SettingsFakeBackendInterceptor } from '@xdf/layouts';
import { XdfSecurityModule, ByteAuthenticationService, AuthGuard, OAuthGuard, OAuthAuthenticationService, ResourceAuthGuard, AuthenticationFakeBackendInterceptor, ProgramsFakeBackendInterceptor } from '@xdf/security';
import { XdfCommonsModule, NotificationService, ToastNotificationService } from '@xdf/commons';
import { AuthenticationService, InitCommonsService } from '@xdf/commons';
import { DATERANGEPICKER_LOCALE, DaterangepickerLocaleService, XdfGalleryModule, CustomMatPaginatorIntl, ConflictErrorDialogService, HttpErrorHandleInterceptor } from '@xdf/gallery';
import { XdfCommonsModule, NotificationService, ToastNotificationService, AuthenticationService, INITSERVICE_OPTIONS, InitCommonsService, ErrorsHandler } from '@xdf/commons';
//Components
import { AppComponent } from './app.component';
import { HomeComponent } from './views/home/home.component';
import { ToastrModule } from 'ngx-toastr';
import localeEs from '@angular/common/locales/es-PE';
import localeEn from '@angular/common/locales/en';
import { MAT_DATE_LOCALE, MatDialog, MatButtonModule, MatDialogModule, MatPaginatorIntl, DateAdapter, MAT_DATE_FORMATS, MatIconModule } from '@angular/material';
import { XdfGalleryModule, CustomMatPaginatorIntl, DATERANGEPICKER_LOCALE, DaterangepickerLocaleService, ConflictErrorDialogService, HttpErrorHandleInterceptor } from '@xdf/gallery';
import { BytebotSettingsService } from './services/bytebot-settings-service';
import { MultiTranslateHttpLoader } from 'ngx-translate-multi-http-loader';
import { AgentModule } from './modules/agent/agent.module';
import { HomeComponent } from './views/home/home.component';
import {
MAT_MOMENT_DATE_FORMATS,
......@@ -31,6 +36,7 @@ import {
MAT_MOMENT_DATE_ADAPTER_OPTIONS,
} from '@angular/material-moment-adapter';
import { CustomProgramsFakeBackendInterceptor } from './interceptors/custom-programs-fake-backend.interceptor';
import { AgentFakeBackendInterceptor } from './interceptors/agent-fake-backend.interceptor';
const INITIAL_LANGUAGE = 'es';
......@@ -62,6 +68,7 @@ export function createTranslateLoader(http: HttpClient) {
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
ToastrModule.forRoot(),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
......@@ -74,7 +81,11 @@ export function createTranslateLoader(http: HttpClient) {
}),
XdfLayoutsModule,
XdfSecurityModule,
XdfGalleryModule
XdfGalleryModule,
MatDialogModule,
MatButtonModule,
AgentModule,
MatIconModule
],
providers: [
{ provide: MAT_DATE_LOCALE, useValue: INITIAL_LANGUAGE },
......@@ -83,24 +94,24 @@ export function createTranslateLoader(http: HttpClient) {
useClass: MomentDateAdapter,
deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS]
},
{provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS},
{ provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
{ provide: MatPaginatorIntl, useClass: CustomMatPaginatorIntl, deps: [TranslateService] },
{ provide: DATERANGEPICKER_LOCALE, useValue: INITIAL_LANGUAGE },
{ provide: DaterangepickerLocaleService, useClass: DaterangepickerLocaleService},
{ provide: DaterangepickerLocaleService, useClass: DaterangepickerLocaleService },
{ provide: LocationStrategy, useClass: HashLocationStrategy },
{ provide: NotificationService, useClass: ToastNotificationService },
{ provide: SettingsService, useClass: ByteSettingsService },
ConflictErrorDialogService,
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorHandleInterceptor, multi: true},
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorHandleInterceptor, multi: true },
// { provide: HTTP_INTERCEPTORS, useClass: ErrorsHandler, multi: true},
{ provide: ResourceAuthGuard, useClass: ResourceAuthGuard},
{ provide: ResourceAuthGuard, useClass: ResourceAuthGuard },
// descomentar estas lineas para OAUTH
{ provide: AuthGuard, useClass: OAuthGuard},
{ provide: AuthGuard, useClass: OAuthGuard },
{ provide: AuthenticationService, useClass: OAuthAuthenticationService },
{ provide: APP_INITIALIZER, useFactory: loginLoaderFactory, deps: [AuthenticationService], multi: true },
......@@ -110,10 +121,9 @@ export function createTranslateLoader(http: HttpClient) {
// { provide: AuthGuard, useClass: AuthGuard},
// { provide: AuthenticationService, useClass: ByteAuthenticationService },
// { provide: HTTP_INTERCEPTORS, useClass: AuthenticationFakeBackendInterceptor, multi: true},
{ provide: HTTP_INTERCEPTORS, useClass: SettingsFakeBackendInterceptor, multi: true},
{ provide: HTTP_INTERCEPTORS, useClass: CustomProgramsFakeBackendInterceptor, multi: true},
//{ provide: HTTP_INTERCEPTORS, useClass: EntidadFakeFakeBackendInterceptor, multi: true},
{ provide: HTTP_INTERCEPTORS, useClass: SettingsFakeBackendInterceptor, multi: true },
//{ provide: HTTP_INTERCEPTORS, useClass: CustomProgramsFakeBackendInterceptor, multi: true },
// { provide: HTTP_INTERCEPTORS, useClass: AgentFakeBackendInterceptor, multi: true },
{ provide: APP_INITIALIZER, useFactory: init_app, deps: [InitCommonsService, TranslateService], multi: true }
......
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard, ResourceAuthGuard } from '@xdf/security';
import { DirtyGuard } from '@xdf/gallery';
import { AgentComponent } from './view/agent/agent.component';
import { AgentDetailComponent } from './view/agent-detail/agent-detail.component';
import { AgentDataForWizardResolver } from './resolver/agent-data-wizard.resolver';
import { AgentDetailResolver } from './resolver/agent-detail.resolver';
import { AgentListComponent } from './view/agent-list/agent-list.component';
import { CountryDataForWizardResolver } from './resolver/country-data-wizard.resolver';
const routes: Routes = [
{
path: 'agent', component: AgentListComponent, canActivate: [AuthGuard, ResourceAuthGuard],
data: {
program: 'CONVERSATIONAL_AGENT',
// breadcrumb: ''
}
},
{
path: 'agent/detail/new',
//component: AgentDetailComponent,
component: AgentComponent,
resolve: {
countryData : CountryDataForWizardResolver
},
canActivate: [AuthGuard, ResourceAuthGuard], canDeactivate: [DirtyGuard],
data:
{
mode: 'create',
program: 'CONVERSATIONAL_AGENT',
breadcrumb: 'Agentes',
backButton: true
}
},
{
path: 'agent/detail/edit/:code',
//component: AgentDetailComponent,
component: AgentComponent,
resolve: {
agentDetail: AgentDetailResolver,
countryData : CountryDataForWizardResolver
},
canActivate: [AuthGuard, ResourceAuthGuard], canDeactivate: [DirtyGuard],
data:
{
mode: 'edit',
program: 'CONVERSATIONAL_AGENT',
breadcrumb: 'Agentes',
backButton: true
}
},{
path: 'agent/detail/view/:code',
//component: AgentDetailComponent,
component: AgentComponent,
resolve: {
agentDetail: AgentDetailResolver,
countryData : CountryDataForWizardResolver
},
canActivate: [AuthGuard, ResourceAuthGuard],
data:
{
mode: 'view',
program: 'CONVERSATIONAL_AGENT',
breadcrumb: 'Agentes',
backButton: true
}
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AgentRoutingModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import {NgxTributeModule} from 'ngx-tribute';
import { MaterialFileInputModule } from 'ngx-material-file-input';
import { XdfGalleryModule } from '@xdf/gallery';
import { MatProgressSpinnerModule, MatDividerModule, MatSidenavModule, MatListModule, MatStepperModule, MatDialogModule, MatMenuModule, MatIconModule, MatSlideToggleModule, MatCheckboxModule, MatRippleModule, MatNativeDateModule, MatChipsModule, MatExpansionModule, MatTooltipModule, MatToolbarModule, MatTabsModule, MatTableModule, MatSortModule, MatSelectModule, MatRadioModule, MatPaginatorModule, MatInputModule, MatFormFieldModule, MatButtonToggleModule, MatButtonModule, MatCardModule, MatAutocompleteModule, MatSnackBarModule, MatProgressBarModule } from '@angular/material';
import { SlickCarouselModule } from 'ngx-slick-carousel';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { AgentRoutingModule } from './agent-routing.module';
import { AceEditorModule } from 'ng2-ace-editor';
import { AgentComponent } from './view/agent/agent.component';
import { AgentDetailComponent } from './view/agent-detail/agent-detail.component';
import { DynamicTranslatePipe } from './pipe/dynamic-translate.pipe';
import { CaGeneralInformationComponent } from './view/components/ca-general-information/ca-general-information.component';
import { CaFrequentQuestionsComponent } from './view/components/ca-frequent-questions/ca-frequent-questions.component';
import { CaDeploymentChannelsComponent } from './view/components/ca-deployment-channels/ca-deployment-channels.component';
import { AgentListComponent } from './view/agent-list/agent-list.component';
import { CaDeploymentChannelsModalComponent } from './view/components/ca-deployment-channels-modal/ca-deployment-channels-modal.component';
import { AgentStatusPipe } from './pipe/agent-status.pipe';
import { CaFileUploadModalComponent } from './view/components/ca-file-upload-modal/ca-file-upload-modal.component';
@NgModule({
entryComponents: [
CaDeploymentChannelsModalComponent,
CaFileUploadModalComponent
],
declarations: [
AgentComponent,
AgentListComponent,
AgentDetailComponent,
DynamicTranslatePipe,
CaGeneralInformationComponent,
CaFrequentQuestionsComponent,
CaDeploymentChannelsComponent,
CaDeploymentChannelsModalComponent,
AgentStatusPipe,
CaFileUploadModalComponent
],
imports: [
FormsModule,
ReactiveFormsModule,
MatAutocompleteModule,
MatCardModule,
MatButtonModule,
MatButtonToggleModule,
MatFormFieldModule,
MatInputModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatRadioModule,
MatSelectModule,
MatSortModule,
MatTabsModule,
MatTableModule,
MatToolbarModule,
MatTooltipModule,
MatExpansionModule,
MatChipsModule,
MatNativeDateModule,
MatRippleModule,
MatCheckboxModule,
MatSlideToggleModule,
MatIconModule,
MatMenuModule,
MatDialogModule,
MatStepperModule,
MatListModule,
MatSidenavModule,
MatDividerModule,
MatSnackBarModule,
NgbModule,
SlickCarouselModule,
TranslateModule,
XdfGalleryModule,
AgentRoutingModule,
CommonModule,
AceEditorModule,
NgxTributeModule,
MatProgressBarModule,
MaterialFileInputModule
],
exports: [
]
})
export class AgentModule { }
import { AgentStatusPipe } from './agent-status.pipe';
describe('AgentStatusPipe', () => {
it('create an instance', () => {
const pipe = new AgentStatusPipe();
expect(pipe).toBeTruthy();
});
});
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'agentStatus'
})
export class AgentStatusPipe implements PipeTransform {
transform(value: any, ...args: any[]): any {
if (value) {
switch (value) {
case 'CR':
return 'label.created';
case 'DP':
return 'label.deployed';
case 'PS':
return 'label.sync-pending';
}
}
return value;
}
}
import { DynamicTranslatePipe } from './dynamic-translate.pipe';
describe('DynamicTranslatePipe', () => {
it('create an instance', () => {
const pipe = new DynamicTranslatePipe();
expect(pipe).toBeTruthy();
});
});
import { Pipe, PipeTransform } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
@Pipe({
name: 'dynamicTranslate'
})
export class DynamicTranslatePipe implements PipeTransform {
constructor(private translateService: TranslateService) {
}
transform(value: any, ...args: any[]): any {
let lang = this.translateService.currentLang ? this.translateService.currentLang : this.translateService.defaultLang;
let traductions;
if (args[0].param) {
if (typeof args[0].param === 'string') {
traductions = JSON.parse(args[0].param);
} else {
traductions = args[0].param;
}
}
if (traductions && traductions[lang]) {
return traductions[lang];
} else {
return value;
}
}
}
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AgentService } from '../service/agent.service';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AgentDataForWizardResolver implements Resolve<any> {
constructor(private service: AgentService) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any>|Promise<any>|any {
let code = route.paramMap.get('code');
return this.service.getDataForWizard(parseInt(code));
}
}
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AgentService } from '../service/agent.service';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AgentDetailResolver implements Resolve<any> {
constructor(private service: AgentService) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any>|Promise<any>|any {
let code = route.paramMap.get('code');
return this.service.getResult(code);
}
}
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AgentService } from '../service/agent.service';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class CountryDataForWizardResolver implements Resolve<any> {
constructor(private service: AgentService) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any>|Promise<any>|any {
return this.service.getCountryDataForWizard();
}
}
import { TestBed } from '@angular/core/testing';
import { AgentService } from './agent.service';
describe('AgentService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: AgentService = TestBed.get(AgentService);
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { DynaDataService } from '@xdf/gallery';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class AgentService extends DynaDataService {
serviceURL = './service/agent';
constructor(private httpClient: HttpClient) {
super(httpClient);
}
getDataForWizard(agentId: number) {
let params = {};
if (agentId) {
params = {
id: agentId
};
}
return this.httpClient.get(this.serviceURL + '/connection/data', {
params: params
});
}
getCountryDataForWizard() {
return this.httpClient.get(this.serviceURL + '/countries');
}
upload(formData) {
return this.httpClient.post<any>(this.serviceURL + '/file-upload', formData, {
reportProgress: true,
observe: 'events'
});
}
getChannelDatForWizard() {
return this.httpClient.get(this.serviceURL + '/channels');
}
synchronize(id: number, user:string) {
return this.httpClient.get(this.serviceURL + '/synchronize/' + id, {
params: {
user
}
});
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AgentDetailComponent } from './agent-detail.component';
describe('AgentDetailComponent', () => {
let component: AgentDetailComponent;
let fixture: ComponentFixture<AgentDetailComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AgentDetailComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AgentDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'byte-agent-detail',
templateUrl: './agent-detail.component.html',
styleUrls: ['./agent-detail.component.scss']
})
export class AgentDetailComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
.toolbar-option {
margin-left: 10px;
margin-right: 10px;
}
$fontcolor: #676a6c;
.content {
width: 100%;
display: flex;
padding-right: 0px !important;
.grid-container,
.filter-panel {
.crud-table {
margin-bottom: 0px;
width: 100%;
}
.form {
padding: 10px 20px 10px 20px;
}
}
.filter-panel {
margin-bottom: 10px;
}
}
.grid-ibox-content {
width: 100%;
}
.btn-group {
.btn {
font-size: 10px;
}
}
.td-view {
cursor: auto !important;
}
.mat-row-auth:hover {
background-color: #f8f9fa;
cursor: pointer !important;
}
.spinner-container {
height: 100%;
width: 100%;
padding-top: 50px;
position: fixed;
}
.spinner-container {
height: 100%;
width: 100%;
padding-top: 50px;
position: fixed;
}
.spinner-container mat-spinner {
margin: -10px auto 0 auto;
}
td {
vertical-align: middle !important;
color: $fontcolor;
font-size: 12px;
}
th {
vertical-align: middle !important;
}
tr.mat-header-row {
height: 50px !important;
}
tr.mat-footer-row,
tr.mat-row {
height: 40px !important;
}
.table-toolbar {
right: 10px;
top: 10px;
button {
font-size: 12px;
}
}
.status-button-bar {
button {
font-size: 16px;
}
padding-top: 8px;
padding-left: 15px;
}
.mat-raised-button {
padding: 0px 10px;
}
.icon-centered-button span.mat-button-wrapper {
display: flex;
}
.icon-centered-button mat-icon {
font-size: 15px;
padding-top: 2px;
}
::ng-deep .mat-sort-header-arrow {
visibility: hidden;
}
::ng-deep .mat-sort-header-button {
.mat-icon {
padding-left: 10px;
font-size: 12px;
padding-top: 5px;
}
}
th {
background: rgb(242, 242, 242);
}
tr.example-detail-row {
height: 0 !important;
}
tr.example-element-row:not(.example-expanded-row):hover {
background: #dedede;
cursor: pointer;
}
tr.example-element-row:not(.example-expanded-row):active {
background: #efefef;
}
.example-element-row td {
border-bottom-width: 0;
}
.example-element-detail {
overflow: hidden;
display: flex;
}
.example-detail-row td {
padding-top: 0px;
padding-bottom: 0px;
border: none !important;
background-color: #f2f2f2;
}
i.state {
font-size: 1.3em;
}
i.fa-key.pull-left {
color: rgba(128, 128, 128, 0.33);
margin-right: 6px;
}
//////
table {
width: 100%;
}
tr.mat-header-row {
height: 25px;
}
tr.mat-footer-row,
tr.mat-row {
height: 40px;
}
.button-grid {
margin: 2px 5px;
mat-icon {
padding-top: 2px;
}
}
.btn-header-new {
margin-top: -4px;
}
.btn-grid-filter {
height: 36px;
width: 85px;
padding-left: 0px;
padding-top: 2px;
}
.mat-row:hover {
cursor: pointer;
background-color: #f8f9fa;
}
.spinner-container {
height: 100%;
width: 100%;
padding-top: 50px;
position: fixed;
}
.spinner-container mat-spinner {
margin: -10px auto 0 auto;
}
.mat-raised-button {
padding: 0px 10px;
}
.icon-centered-button span.mat-button-wrapper {
display: flex;
}
.icon-centered-button mat-icon {
font-size: 15px;
padding-top: 2px;
}
.ibox-content {
padding-right: 0px;
}
.content {
width: 100%;
display: flex;
padding-right: 0px !important;
}
.grid-ibox-content {
width: 100%;
}
.avatar {
vertical-align: middle;
width: 35px;
height: 35px;
border-radius: 50%;
}
::ng-deep .mat-menu-panel {
overflow: hidden !important;
}
.th-avatar {
width: 130px;
}
::ng-deep .text-right > .mat-sort-header-container {
justify-content: flex-end !important;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AgentListComponent } from './agent-list.component';
describe('AgentComponent', () => {
let component: AgentListComponent;
let fixture: ComponentFixture<AgentListComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AgentListComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AgentListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, ViewChild, ViewContainerRef, Input } from '@angular/core';
import { MatPaginator, MatSort } from '@angular/material';
import { ColumnTemplate, DynaDataSource, ConfirmationDialogService, extractRSQL, FieldFilter } from '@xdf/gallery';
import { Pagination, SortField, NotificationType, NotificationService, AuthenticationService } from '@xdf/commons';
import { Router, ActivatedRoute } from '@angular/router';
import { first, tap } from 'rxjs/operators';
import { TagFilter } from '@xdf/gallery/lib/views/gallery/ngx-tags-input/model/tag-filter';
import { TranslateService } from '@ngx-translate/core';
import { AgentService } from '../../service/agent.service';
const columnTemplateArray = [
{
name: 'avatar',
sortable: false,
title: 'agent_avatar',
visible: true,
filtable: true,
style: { width: 'auto' },
type: 'string',
aliasName: null,
queryName: null,
responsive: null,
styles: null,
values: null,
digitsInfo: null,
dateFormat: null,
colStyle: null
},
{
name: 'id',
sortable: true,
title: 'agent_code',
visible: true,
filtable: true,
style: { width: '75px' },
type: 'number',
aliasName: null,
queryName: null,
responsive: null,
styles: null,
values: null,
digitsInfo: null,
dateFormat: null,
colStyle: null
},
{
name: 'name',
sortable: true,
title: 'agent_name',
visible: true,
filtable: true,
style: { width: 'auto' },
type: 'string',
aliasName: null,
queryName: null,
responsive: null,
styles: null,
values: null,
digitsInfo: null,
dateFormat: null,
colStyle: null
},
{
name: 'version',
sortable: true,
title: 'agent_publish_version',
visible: true,
filtable: true,
style: { width: 'auto' },
type: 'string',
aliasName: null,
queryName: null,
responsive: null,
styles: null,
values: null,
digitsInfo: null,
dateFormat: null,
colStyle: null
},
{
name: 'status',
sortable: true,
title: 'agent_status',
visible: true,
filtable: true,
style: { width: 'auto' },
type: 'string',
aliasName: null,
queryName: null,
responsive: null,
styles: null,
values: null,
digitsInfo: null,
dateFormat: null,
colStyle: null
},
{
name: 'countryName',
sortable: true,
title: 'agent_country',
visible: true,
filtable: true,
style: { width: 'auto' },
type: 'string',
aliasName: null,
queryName: null,
responsive: null,
styles: null,
values: null,
digitsInfo: null,
dateFormat: null,
colStyle: null
},
{
name: 'timezone',
sortable: true,
title: 'agent_timezone',
visible: true,
filtable: true,
style: { width: 'auto' },
type: 'string',
aliasName: null,
queryName: null,
responsive: null,
styles: null,
values: null,
digitsInfo: null,
dateFormat: null,
colStyle: null
}
];
const fieldFilters = [
new FieldFilter('agent_code', 'id', 'id', 'number', undefined),
new FieldFilter('agent_name', 'name', 'name', 'string', undefined),
new FieldFilter('agent_status', 'status', 'status', 'valpos', ({ 'DP': 'label.deployed', 'PS': 'label.sync-pending' }) as any),
];
@Component({
selector: 'byte-agent-list',
templateUrl: './agent-list.component.html',
styleUrls: ['./agent-list.component.scss']
})
export class AgentListComponent implements OnInit {
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator;
@ViewChild(MatSort, { static: false }) sort: MatSort;
sortColumn: string;
sortDirection: string;
statusQuickFilter: string;
pagingSize = 10;
programIdentifier = 'agent';
columnTemplateArray: ColumnTemplate[];
dataSource: DynaDataSource;
displayedColumns = new Array<string>();
fieldFilters: Array<FieldFilter> = fieldFilters;
filterTags = [];
pagination: Pagination;
public resourceAuth: any;
constructor(
private router: Router,
private route: ActivatedRoute,
private agentService: AgentService,
private notificationService: NotificationService,
private translateService: TranslateService,
protected confirmationDialogService: ConfirmationDialogService,
private authenticationService: AuthenticationService,
protected vcRef: ViewContainerRef
) { }
ngAfterViewInit(): void {
this.sort.sortChange.subscribe(() => {
if (!this.sort.disabled && this.sort.active) {
this.pagination.sortFields = new Array();
const sortField: SortField = new SortField();
sortField.direction = this.sortDirection = this.sort.direction;
sortField.field = this.sortColumn = this.sort.active;
this.pagination.sortFields.push(sortField);
}
this.paginator.pageIndex = 0;
this.dataSource.load(this.pagination);
});
this.paginator.page
.pipe(
tap(() => {
this.pagination.currentPage = this.paginator.pageIndex;
this.pagination.itemsPerPage = this.paginator.pageSize;
this.dataSource.load(this.pagination);
})
)
.subscribe();
}
ngOnInit() {
this.sortColumn = 'id';
this.sortDirection = 'asc';
const authList = this.route.snapshot.paramMap['authorization'];
if (authList) {
this.resourceAuth = new Object();
authList.forEach(option => {
this.resourceAuth[option] = true;
});
}
this.columnTemplateArray = columnTemplateArray;
this.dataSource = new DynaDataSource(this.agentService);
const previousState = JSON.parse(localStorage.getItem(this.programIdentifier + '-state'));
if (previousState) {
const PAG = 'pagination';
const FILTERS = 'filtersTags';
this.pagination = previousState[PAG];
this.filterTags = previousState[FILTERS];
localStorage.removeItem(this.programIdentifier + '-state');
} else {
// Paginación
this.pagination = new Pagination();
this.pagination.currentPage = 0;
this.pagination.itemsPerPage = this.pagingSize;
this.pagination.filterExpression = "id != 0";
// Ordenación por defecto
if (this.sortColumn && this.sortDirection) {
this.pagination.sortFields = new Array();
const sortField: SortField = new SortField();
sortField.direction = this.sortDirection;
sortField.field = this.sortColumn;
this.pagination.sortFields.push(sortField);
}
}
// calculando la columnas a pintar
this.columnTemplateArray.forEach(column => {
this.displayedColumns.push(column.name);
});
this.displayedColumns.push('actions');
this.dataSource.load(this.pagination);
}
onFilter(tags: Array<TagFilter>) {
this.filterTags = tags;
this.pagination.currentPage = 0;
// let tagsTemp = tags.slice();
const tagsTemp = tags.map(a => Object.assign({}, a));
for (let i = 0; i < tagsTemp.length; i++) {
if (tagsTemp[i].column !== 'id') {
tagsTemp[i].value = tagsTemp[i].value;
}
}
this.pagination.filterExpression = extractRSQL(tagsTemp);
if (this.pagination.filterExpression == null || this.pagination.filterExpression == undefined) {
this.pagination.filterExpression = "id != 0";
}
this.dataSource.load(this.pagination);
}
onRefresh() {
this.dataSource.load(this.pagination);
}
delete(item) {
this.confirmationDialogService.loadComponent(
this.vcRef,
'title.delete.confirmation',
'message.delete.confirmation').subscribe(result => {
if (result) {
this.agentService.delete(item.id).subscribe((rslt: any) => {
if (rslt.statusCode != 200) {
this.notificationService.showMessage(
this.translateService.instant(rslt.message), this.translateService.instant('label.error.message.title'), NotificationType.error);
} else {
this.dataSource.load(this.pagination);
}
}
);
}
});
}
edit(item) {
this.router.navigate(['/configuration/agent/detail/edit/' + item.id]);
}
synchronize(item) {
let user = (this.authenticationService.currentUserValue ? this.authenticationService.currentUserValue.username : 'admin');
this.agentService.synchronize(item.id, user).pipe(first()).subscribe(() => {
this.notificationService.showMessage(this.translateService.instant("agent.synchronize.success"), undefined, NotificationType.success);
this.onRefresh();
});
}
view(item) {
this.router.navigate(['/configuration/agent/detail/view/' + item.id]);
}
create() {
this.router.navigate(['/configuration/agent/detail/new']);
}
}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<!-- <div class="ibox-title">
<h5>{{'reconciliation.title' | translate}}</h5>
</div> -->
<div class="ibox-content">
<mat-horizontal-stepper labelPosition="bottom" #stepper [linear]="true">
<mat-step state="info">
<ng-template matStepLabel>
<span class="font-bold">{{'label.general-information.title' | translate}}</span>
<div class="font-size-description" [title]="'label.general-information.description' | translate">
{{'label.general-information.description' | translate}}
</div>
</ng-template>
<byte-ca-general-information [stepper]="stepper" (onNextPage)="updateDirtyStatus($event)"></byte-ca-general-information>
</mat-step>
<mat-step state="questions">
<ng-template matStepLabel>
<span class="font-bold">{{'label.questions.title' | translate}}</span>
<div class="font-size-description" [title]="'label.questions.description' | translate">
{{'label.questions.description' | translate}}
</div>
</ng-template>
<byte-ca-frequent-questions [stepper]="stepper" (onNextPage)="updateDirtyStatus($event)"></byte-ca-frequent-questions>
</mat-step>
<mat-step state="deployment-channels">
<ng-template matStepLabel>
<span class="font-bold">{{'label.deployment-channels.title' | translate}}</span>
<div class="font-size-description" [title]="'label.deployment-channels.description' | translate">
{{'label.deployment-channels.description' | translate}}
</div>
</ng-template>
<byte-ca-deployment-channels [stepper]="stepper" [changes]="generalDirty"></byte-ca-deployment-channels>
</mat-step>
<!--
<mat-step state="summary">
<ng-template matStepLabel>
<span class="font-bold">{{'label.summary.title' | translate}}</span>
<div class="font-size-description" [title]="'label.summary.description' | translate">
{{'label.summary.description' | translate}}
</div>
</ng-template>
<byte-es-summary [stepper]="stepper"></byte-es-summary>
</mat-step>
-->
<ng-template matStepperIcon="info">
<mat-icon>info</mat-icon>
</ng-template>
<ng-template matStepperIcon="questions">
<mat-icon>dns</mat-icon>
</ng-template>
<ng-template matStepperIcon="deployment-channels">
<mat-icon>chat</mat-icon>
</ng-template>
<!--
<ng-template matStepperIcon="summary">
<mat-icon>done_all</mat-icon>
</ng-template>
-->
</mat-horizontal-stepper>
</div>
</div>
</div>
</div>
</div>
::ng-deep .info-block {
text-align: center;
vertical-align: middle;
line-height: 150px;
min-height: 300px;
}
::ng-deep .footer-bar {
margin: 0 10px 10px;
}
::ng-deep .border-all {
border: solid 1px #d0c9c9ab;
}
::ng-deep .panel-viewer-wrapper {
border: 1px solid rgba(0, 0, 0, 0.03);
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
margin: 5px 5px 10px;
}
.font-size-description {
white-space: normal;
font-size: 11px;
}
::ng-deep .mat-step-header[aria-selected="true"] {
/*background: #c3ccff !important;*/
background: #c3ccff9e !important;
border: solid 1px #c3ccff80;
border-radius: 9px !important;
}
::ng-deep .mat-horizontal-stepper-header {
pointer-events: none !important;
}
::ng-deep .mat-card {
border-left: solid 0px #3f51b5;
transition: border-left 0.125s ease-in !important;
&.active {
border-left: solid 5px #3f51b5;
}
}
::ng-deep .panel-viewer-title > .mat-icon {
height: 0px;
width: 15px;
vertical-align: sub;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AgentComponent } from './agent.component';
describe('AgentComponent', () => {
let component: AgentComponent;
let fixture: ComponentFixture<AgentComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AgentComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AgentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, ViewChild, Input, AfterContentChecked, AfterViewInit, ChangeDetectionStrategy, AfterContentInit, ViewContainerRef } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { CaGeneralInformationComponent } from '../components/ca-general-information/ca-general-information.component';
import { MAT_STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper';
import { ActivatedRoute, Router } from '@angular/router';
import { NavigationService } from '@xdf/layouts';
import { IDirty } from '@xdf/gallery';
import { CaFrequentQuestionsComponent } from '../components/ca-frequent-questions/ca-frequent-questions.component';
import { MatStepper } from '@angular/material';
import { CaDeploymentChannelsComponent } from '../components/ca-deployment-channels/ca-deployment-channels.component';
@Component({
selector: 'byte-agent',
templateUrl: './agent.component.html',
styleUrls: ['./agent.component.scss'],
providers: [{
provide: MAT_STEPPER_GLOBAL_OPTIONS, useValue: { displayDefaultIndicatorType: false }
}]
})
export class AgentComponent implements OnInit, IDirty {
@ViewChild(CaGeneralInformationComponent, { static: true })
generalInformationComponent: CaGeneralInformationComponent;
@ViewChild(CaFrequentQuestionsComponent, { static: true })
frequentQuestionsComponent: CaFrequentQuestionsComponent;
@ViewChild(CaDeploymentChannelsComponent, { static: true })
deploymentChannelsComponent: CaDeploymentChannelsComponent;
@ViewChild('stepper', { static: true }) stepper: MatStepper;
agentDetail: any;
isLinear = true;
isNew = true;
resourceAuth: any;
//dirty acumulado
generalDirty: boolean = false;
constructor(
private _vcRef: ViewContainerRef,
private _activatedRoute: ActivatedRoute,
private _backService: NavigationService,
private _router: Router
) {
this._backService.backAnnounced$.subscribe(data => {
this._router.navigate(['/configuration/agent']);
});
}
isDirty(): boolean {
let dirty = false;
dirty = this.generalInformationComponent.isDirty()
|| this.frequentQuestionsComponent.isDirty();
if (dirty) {
dirty = this.deploymentChannelsComponent.isDirty()
}
return dirty;
}
getRef(): ViewContainerRef {
return this._vcRef;
}
ngOnInit() {
this.stepper.selectedIndex = 0;
const authList = this._activatedRoute.snapshot.paramMap['authorization'];
this.resourceAuth = new Object();
if (authList) {
authList.forEach(option => {
this.resourceAuth[option] = true;
});
}
console.log(this.resourceAuth);
this.isNew = this._activatedRoute.snapshot.data.mode === 'create';
if (this.isNew) {
this.agentDetail = {
};
} else {
this.agentDetail = this._activatedRoute.snapshot.data.agentDetail;
}
this.generalInformationComponent.buildForm(this.agentDetail);
this.frequentQuestionsComponent.buildForm(this.agentDetail);
this.deploymentChannelsComponent.buildForm(this.agentDetail);
}
updateDirtyStatus(event: boolean) {
this.generalDirty = this.generalDirty || event;
}
}
<div class="title_section divider">
<div class="hidden-xs" *ngIf="applicationSettings?.logoBase64">
<img class="logo_header_popup"
src="data:image/{{applicationSettings.tipo}};base64,{{applicationSettings.logoBase64}}" />
</div>
<div class="title_header_popup">
<h2 mat-dialog-title>
<span> {{'label.deployment-channels.configuration' | translate}} </span>
</h2>
</div>
</div>
<mat-dialog-content>
<h4>
{{'label.deployment-channels.configuration.description' | translate}}
<!-- -->
</h4>
<div>
<ng-container *ngFor="let channel of channels">
<mat-card class="channel-card" (click)="selectChannel(channel)"
[ngClass]="{ 'active': selectedChannel === channel}" *ngIf="!channel.disabled">
<mat-card-header>
<img [src]="channel.image" mat-card-avatar>
<mat-card-title>{{channel.name}}</mat-card-title>
</mat-card-header>
</mat-card>
</ng-container>
</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button mat-dialog-close>{{'btn.cancel' | translate}}</button>
<button mat-button (click)="ok()" cdkFocusInitial
[disabled]="!selectedChannel">{{'btn.accept' | translate}}</button>
</mat-dialog-actions>
\ No newline at end of file
.icon {
vertical-align: middle;
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 10px;
}
::ng-deep .mat-card-header .mat-card-title {
margin-bottom: 12px;
margin-top: 10px;
}
.channel-card {
cursor: pointer;
margin-bottom: 8px;
transition: border-left 0.125s ease-in;
border-left: solid 0px #3f51b5;
&.active {
border-left: solid 5px #3F51B5 !important;
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CaDeploymentChannelsModalComponent } from './ca-deployment-channels-modal.component';
describe('CaDeploymentChannelsModalComponent', () => {
let component: CaDeploymentChannelsModalComponent;
let fixture: ComponentFixture<CaDeploymentChannelsModalComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CaDeploymentChannelsModalComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CaDeploymentChannelsModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
@Component({
selector: 'byte-ca-deployment-channels-modal',
templateUrl: './ca-deployment-channels-modal.component.html',
styleUrls: ['./ca-deployment-channels-modal.component.scss']
})
export class CaDeploymentChannelsModalComponent implements OnInit {
channels: Array<any>;
selectedChannel: any;
applicationSettings:any;
constructor(
public dialogRef: MatDialogRef<CaDeploymentChannelsModalComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.channels = data.channels;
}
ngOnInit() {
}
ok() {
this.dialogRef.close(this.selectedChannel);
}
selectChannel(channel: any) {
this.selectedChannel = channel;
}
}
<div class="panel-viewer-wrapper">
<div class="panel-viewer-title">
<mat-icon>chat</mat-icon>
{{'label.deployment-channels.title' | translate}}
</div>
<div class="panel-viewer-body">
<!-- <div class="row pb-2 text-center">
<div class="col-12">
<h1>{{'label.deployment-channels' | translate}}</h1>
</div>
</div> -->
<div class="row pb-2 justify-content-center">
<div class="col-md-6 col-sm-12">
<!-- <div class="alert alert-warning" *ngIf="!viewMode">
<i class="fa fa-warning"></i>&nbsp;&nbsp;<span
[innerHTML]="'label.summary.warning' | translate"></span>
</div> -->
<h4 class="mt-3">
<!-- <button class="pull-right" mat-icon-button (click)="addChannel()" *ngIf="!viewMode && isAvaliableChannels">
<i class="fa fa-plus"></i>
</button> -->
<button type="button" class="btn btn-outline-primary btn-sm pull-right" (click)="addChannel()"
*ngIf="!viewMode && (deploymentChannels && deploymentChannels.length) && isAvaliableChannels">
<i class="fa fa-file"></i>
<span class="visible-md-inline visible-lg-inline visible-xl-inline">
{{'Añadir canal' | translate}}
</span>
</button>
<b>{{'label.channels' | translate}}</b>
</h4>
<mat-accordion>
<mat-expansion-panel *ngFor="let channel of deploymentChannels; let i = index"
[expanded]="step === i" (opened)="setStep(i)">
<mat-expansion-panel-header class="right-aligned-header" [collapsedHeight]="'60px'">
<mat-panel-title>
<img [src]="channel.channelImage" class="icon">
<div>
<span style="margin-right: 8px;">{{ channel.channelName }}</span>
<ng-container *ngIf="channel?.suggestTitle && channel?.suggestDetail">
<ng-template #popContent><div [innerHTML]="channel?.suggestDetail"></div></ng-template>
<ng-template #popoverTitle><div [innerHTML]="channel?.suggestTitle"></div></ng-template>
<i class="fa fa-info-circle" placement="top" style="color: rgba(0, 0, 0, 0.54);"
[ngbPopover]="popContent"
[popoverTitle]="popoverTitle" triggers="hover"></i>
</ng-container>
<!-- <small class="text-muted">{{ channel.name }}</small> -->
</div>
</mat-panel-title>
<mat-panel-description>
<!-- <label class="label label-success"
*ngIf="channel.active">{{'label.active' | translate}}</label>
<label class="label label-default"
*ngIf="!channel.active">{{'label.inactive' | translate}}</label> -->
<mat-slide-toggle [(ngModel)]="channel.active" [disabled]="viewMode"
style="margin-top: 0;" [ngModelOptions]="{standalone: true}" color="primary"
(click)="$event.stopPropagation();">
<span *ngIf="channel.active">{{'label.active' | translate}}</span>
<span *ngIf="!channel.active">{{'label.inactive' | translate}}</span>
</mat-slide-toggle>
</mat-panel-description>
</mat-expansion-panel-header>
<div>
<form role="form" novalidate #myForm="ngForm" autocomplete="off">
<div class="row">
<!-- <div class="col-6">
<mat-form-field class="amd-form-control">
<mat-label>{{'label.name' | translate}}</mat-label>
<input matInput maxlength="80" [type]="text" required="true"
[disabled]="viewMode" [(ngModel)]="channel.name"
[ngModelOptions]="{standalone: true}">
</mat-form-field>
</div> -->
<!-- <div class="col-6">
<mat-slide-toggle [(ngModel)]="channel.active" [disabled]="viewMode"
[ngModelOptions]="{standalone: true}" color="primary">
<span *ngIf="channel.active">{{'label.active' | translate}}</span>
<span *ngIf="!channel.active">{{'label.inactive' | translate}}</span>
</mat-slide-toggle>
</div> -->
</div>
<div class="row" *ngFor="let field of channel.parameters">
<div class="col-12">
<mat-form-field class="amd-form-control">
<mat-label>{{field.label | dynamicTranslate: {param: field.traductions } }}:
</mat-label>
<!--
[pattern]="field.regex"
-->
<input matInput [maxlength]="field.maxlength" [(ngModel)]="field.value"
[ngModelOptions]="{standalone: true}" [disabled]="viewMode"
#fieldForm='ngModel' [type]="(field.type ? field.type : 'text')"
[required]="field.required" (ngModelChange)="onChange(fieldForm)"
[pattern]="field?.regex">
<mat-error *ngIf="fieldForm.errors?.pattern">
{{ 'Formato inválido' | translate }}</mat-error>
</mat-form-field>
</div>
</div>
</form>
</div>
<mat-action-row *ngIf="!viewMode">
<button mat-button color="warn" (click)="deleteChannel(i)">
<i
class="fa fa-trash"></i><span>&nbsp;&nbsp;{{'label.delete-channel' | translate}}</span>
</button>
</mat-action-row>
</mat-expansion-panel>
</mat-accordion>
<div *ngIf="!(deploymentChannels && deploymentChannels.length)" class="border-all text-center">
<button type="button" class="btn btn-outline-primary btn-sm" (click)="addChannel()"
*ngIf="!viewMode && isAvaliableChannels">
<i class="fa fa-file"></i>
<span class="visible-md-inline visible-lg-inline visible-xl-inline">
{{'Añadir canal' | translate}}
</span>
</button>
<!-- {{'label.delete-channel' | translate}} -->
</div>
</div>
</div>
</div>
<div class="footer-bar">
<div class="row">
<div class="col-12">
<button mat-stroked-button matStepperPrevious class="pull-left">
<i class="fa fa-chevron-left"></i>
<span class="pl-1">{{'btn.previous' | translate}}</span>
</button>
<button mat-stroked-button color="primary" type="submit" (click)="save()" class="pull-right"
*ngIf="!viewMode">
<span class="pr-1">{{'btn.save' | translate}}</span>
<i class="fa fa-chevron-right"></i>
</button>
<button mat-stroked-button (click)="exit()" class="pull-right" *ngIf="viewMode">
<span class="pr-1">{{'application.options.exit' | translate}}</span>
<i class="fa fa-logout"></i>
</button>
</div>
</div>
</div>
</div>
\ No newline at end of file
.icon {
vertical-align: middle;
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 10px;
}
::ng-deep .right-aligned-header > .mat-content {
justify-content: space-between;
}
::ng-deep .mat-content > mat-panel-title {
flex: 0 0 auto;
}
::ng-deep .mat-content > mat-panel-description {
flex: 0 0 auto;
}
::ng-deep .mat-expansion-indicator {
margin-bottom: 10px;
}
::ng-deep mat-slide-toggle {
margin-top: 14px;
}
.border-all.text-center {
padding: 50px;
border-radius: 4px;
}
.label.label-success {
background-color: #3f51b5;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CaDeploymentChannelsComponent } from './ca-deployment-channels.component';
describe('CaDeploymentChannelsComponent', () => {
let component: CaDeploymentChannelsComponent;
let fixture: ComponentFixture<CaDeploymentChannelsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CaDeploymentChannelsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CaDeploymentChannelsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input, ViewChild } from '@angular/core';
import { MatStepper, MatDialog, ErrorStateMatcher, MatInput } from '@angular/material';
import { CaDeploymentChannelsModalComponent } from '../ca-deployment-channels-modal/ca-deployment-channels-modal.component';
import { AgentService } from '../../../service/agent.service';
import { first } from 'rxjs/operators';
import { NotificationType, NotificationService } from '@xdf/commons';
import { TranslateService } from '@ngx-translate/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Form, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
@Component({
selector: 'byte-ca-deployment-channels',
templateUrl: './ca-deployment-channels.component.html',
styleUrls: ['./ca-deployment-channels.component.scss']
})
export class CaDeploymentChannelsComponent implements OnInit {
@Input() stepper: MatStepper;
@Input() changes: boolean = false;
@ViewChild('fieldForm', {static: false})
fieldFormList: any;
agentDetail: any;
channels: Array<any>;
deploymentChannels: Array<any> = new Array<any>();
dirty: boolean = true;
step = -1;
viewMode: boolean = false;
isAvaliableChannels: boolean = false;
mode: string = 'new';
detailText: string = "Detail";
titleText: string = "Titulo"
constructor(
private activatedRoute: ActivatedRoute,
private agentService: AgentService,
private matDialog: MatDialog,
private translateService: TranslateService,
private notificationService: NotificationService,
private router: Router
) {
this.mode = this.activatedRoute.snapshot.data.mode;
this.viewMode = this.mode === 'view';
}
ngOnInit() {
this.agentService.getChannelDatForWizard().pipe(first()).subscribe((result: any) => {
if (result) {
this.channels = result;
} else {
this.channels = [];
}
this.buildList();
this.buildAvaliableChannels();
});
}
buildForm(agentDetail: any) {
this.agentDetail = agentDetail;
this.deploymentChannels = (agentDetail.deploymentChannels || []);
this.buildList();
}
buildList() {
if (this.channels && this.deploymentChannels) {
for (let deploymentChannel of this.deploymentChannels) {
for (let channel of this.channels) {
if (channel.id === deploymentChannel.channelId) {
deploymentChannel.channelName = channel.name;
deploymentChannel.channelImage = channel.image;
deploymentChannel.active = (deploymentChannel.status === 'AC');
deploymentChannel.suggestTitle = channel.suggestTitle;
deploymentChannel.suggestDetail = channel.suggestDetail;
for (let parameter of deploymentChannel.parameters) {
for (let field of channel.parameters) {
if (parameter.channelParamName === field.name) {
parameter.label = field.label;
parameter.type = field.type;
parameter.required = field.required;
parameter.traductions = field.traductions;
parameter.maxlength = field.maxlength;
parameter.regex = field.regex;
break;
}
}
}
break;
}
}
}
}
}
buildAvaliableChannels() {
this.isAvaliableChannels = false;
mainLoop: for (let channel of this.channels) {
channel.disabled = false;
for (let deploymentChannel of this.deploymentChannels) {
if (channel.id === deploymentChannel.channelId) {
channel.disabled = true;
continue mainLoop;
}
}
if (!channel.disabled) {
this.isAvaliableChannels = true;
}
}
}
addChannel() {
let dialog = this.matDialog.open(CaDeploymentChannelsModalComponent, {
width: '500px',
data: {
message: '',
channels: this.channels
}
});
dialog.afterClosed().subscribe(result => {
if (result) {
let selectedChannel = { ...result };
selectedChannel.channelId = selectedChannel.id;
selectedChannel.channelName = selectedChannel.name;
selectedChannel.channelImage = selectedChannel.image;
selectedChannel.id = undefined;
selectedChannel.name = undefined;
this.deploymentChannels.push(selectedChannel);
this.setStep(this.deploymentChannels.length - 1);
this.buildAvaliableChannels();
}
});
}
deleteChannel(i) {
this.deploymentChannels.splice(i, 1);
this.buildAvaliableChannels();
}
exit() {
this.dirty = false;
this.router.navigate(['/configuration/agent']);
}
isDirty() {
return this.dirty;
}
save() {
let isValid: boolean = this.validate();
if (!isValid) {
if (this.fieldFormList && this.fieldFormList.control) {
this.fieldFormList.control.markAsTouched();
}
return;
}
let agent: any = {};
agent.id = this.agentDetail.id;
agent.name = this.agentDetail.name;
agent.description = this.agentDetail.description;
agent.version = this.agentDetail.version;
agent.countryId = this.agentDetail.countryId;
agent.timezone = this.agentDetail.timezone;
agent.avatar = this.agentDetail.avatar;
agent.language = this.agentDetail.language;
agent.type = this.agentDetail.type;
agent.status = this.agentDetail.status;
agent.deploymentChannels = [];
for (let deploymentChannel of this.deploymentChannels) {
let deploymentChannelTmp: any = {};
deploymentChannelTmp.id = deploymentChannel.id;
deploymentChannelTmp.name = deploymentChannel.name;
deploymentChannelTmp.status = deploymentChannel.active ? 'AC' : 'IN';
deploymentChannelTmp.channelId = deploymentChannel.channelId;
deploymentChannelTmp.parameters = [];
for (let parameter of deploymentChannel.parameters) {
let parameterTmp: any = {};
parameterTmp.id = parameter.id;
parameterTmp.value = parameter.value;
parameterTmp.channelParamName = parameter.channelParamName ? parameter.channelParamName : parameter.name;
deploymentChannelTmp.parameters.push(parameterTmp);
}
agent.deploymentChannels.push(deploymentChannelTmp);
}
agent.frequentQuestions = this.agentDetail.frequentQuestions;
this.agentService.update(agent.id, agent).pipe(first()).subscribe(() => {
if (agent.id) {
this.dirty = false;
this.notificationService.showMessage(this.translateService.instant('agent.edit.success'), null, NotificationType.success);
this.router.navigate(['/configuration/agent']);
} else {
this.dirty = false;
this.notificationService.showMessage(this.translateService.instant('agent.create.success'), null, NotificationType.success);
this.router.navigate(['/configuration/agent']);
}
});
}
onChange(fieldForm) {
if (!fieldForm.control.touched) {
fieldForm.control.markAsTouched();
}
}
setStep(step) {
this.step = step;
}
validate(): boolean {
let valid = true;
mainLoop: for (let deploymentChannel of this.deploymentChannels) {
let deploymentChannelTmp: any = {};
// if (!deploymentChannel.name) {
// valid = false;
// break;
// }
for (let parameter of deploymentChannel.parameters) {
parameter.error = false;
if (!parameter.value) {
valid = false;
break mainLoop;
}
if (parameter.regex) {
let regex = new RegExp(parameter.regex);
if (!regex.test(parameter.value)) {
parameter.error = true;
valid = false;
break mainLoop;
}
}
}
}
if (!valid) {
this.notificationService.showMessage(this.translateService.instant('error.message.complete-form'), undefined, NotificationType.error);
}
return valid;
}
}
<div class="title_section divider">
<div class="hidden-xs" *ngIf="applicationSettings?.logoBase64">
<img class="logo_header_popup"
src="data:image/{{applicationSettings.tipo}};base64,{{applicationSettings.logoBase64}}" />
</div>
<div class="title_header_popup">
<h2 mat-dialog-title>
<i class="fa fa-exclamation-triangle" [hidden]="!fileErrorDetail"></i><span> {{'label.file-upload.title' | translate}} </span>
</h2>
</div>
</div>
<mat-dialog-content class="none-overflow" [hidden]="fileErrorDetail">
<h4>
{{'label.file-upload.description' | translate}}
</h4>
<form [formGroup]="formGroup" autocomplete="off">
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<input matInput [placeholder]="'label.description' | translate" formControlName="description"
maxlength="50" required>
</mat-form-field>
</div>
</div>
<div class="row pt-2" *ngIf="createMode">
<div class="col-12">
<mat-form-field class="amd-form-control">
<ngx-mat-file-input formControlName="file" [placeholder]="'label.file' | translate"
[accept]="'.xls,.xlsx'" required (change)="uploadFile($event.target.files)">
</ngx-mat-file-input>
<mat-icon matSuffix>folder</mat-icon>
</mat-form-field>
</div>
</div>
<div class="row pt-2" *ngIf="fileErrorDetail">
<div class="col-12">
<h4>
<b>Detalle de error</b>
</h4>
</div>
<div class="col-12">
<div class="example-container mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container [matColumnDef]="column" *ngFor="let column of displayedColumns">
<th mat-header-cell *matHeaderCellDef> {{ 'header.' + column | translate}} </th>
<td mat-cell *matCellDef="let element"> {{element[column] | translate}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
</div>
<div class="row pt-2" *ngIf="!createMode">
<div class="col-12">
<mat-form-field class="amd-form-control">
<input matInput [placeholder]="'label.file' | translate" formControlName="filename" required>
<mat-icon matSuffix [matTooltip]="'label.file.tooltip' | translate" matTooltipPosition="above">
help_icon
</mat-icon>
</mat-form-field>
</div>
</div>
</form>
</mat-dialog-content>
<mat-dialog-content class="none-overflow" [hidden]="!fileErrorDetail">
<h4>
{{'label.file-upload.error.description' | translate}}
</h4>
<div class="row pt-2" *ngIf="fileErrorDetail">
<!-- <div class="col-12">
<h4>
<b>{{'label.file-upload.error.subtitle' | translate}}</b>
</h4>
</div> -->
<div class="col-12">
<div class="example-container mat-elevation-z8">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<!-- Position Column -->
<ng-container [matColumnDef]="column" *ngFor="let column of displayedColumns">
<th mat-header-cell *matHeaderCellDef> {{ 'header.' + column | translate}} </th>
<td mat-cell *matCellDef="let element"> {{element[column] | translate}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
</div>
</mat-dialog-content>
<!-- <mat-dialog-actions align="end">
<button mat-button mat-dialog-close>{{'btn.cancel' | translate}}</button>
<button mat-button (click)="retry()" *ngIf="fileErrorDetail">{{'btn.retry' | translate}}</button>
<button mat-button (click)="ok()" cdkFocusInitial *ngIf="!fileErrorDetail"
[disabled]="formGroup.invalid || !enabledOK">{{'btn.accept' | translate}}</button>
</mat-dialog-actions> -->
<div mat-dialog-actions class="border-top">
<button mat-button mat-dialog-close>{{'btn.cancel' | translate}}</button>
<button mat-button (click)="retry()" *ngIf="fileErrorDetail">{{'btn.retry' | translate}}</button>
<button mat-button (click)="ok()" cdkFocusInitial *ngIf="!fileErrorDetail"
[disabled]="formGroup.invalid || !enabledOK">{{'btn.accept' | translate}}</button>
</div>
\ No newline at end of file
.example-container {
max-height: 200px;
overflow: auto;
margin-bottom: 10px
}
table {
width: 100%;
}
.none-overflow{
overflow: hidden;
}
.title_header_popup {
i {
font-size: 28px;
padding-right: 20px;
color: red;
}
}
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CaFileUploadModalComponent } from './ca-file-upload-modal.component';
describe('CaFileUploadModalComponent', () => {
let component: CaFileUploadModalComponent;
let fixture: ComponentFixture<CaFileUploadModalComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CaFileUploadModalComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CaFileUploadModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Inject } from '@angular/core';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
import { HttpEventType, HttpErrorResponse } from '@angular/common/http';
import { of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { AgentService } from '../../../service/agent.service';
import { MatDialogRef, MatTableDataSource, MAT_DIALOG_DATA } from '@angular/material';
import { NotificationService, NotificationType } from '@xdf/commons';
import { TranslateService } from '@ngx-translate/core';
export class FileUploadModel {
id: number;
uuid: string;
filename: string;
status: string;
user: string;
uploadDate: string;
description: string;
}
@Component({
selector: 'byte-ca-file-upload-modal',
templateUrl: './ca-file-upload-modal.component.html',
styleUrls: ['./ca-file-upload-modal.component.scss']
})
export class CaFileUploadModalComponent implements OnInit {
formGroup: FormGroup;
accept = '.xls,.xlsx';
enabledOK = false;
createMode = true;
fileInfo = new FileUploadModel();
fileErrorDetail: any;
dataSource = new MatTableDataSource([]);
displayedColumns: string[] = [];
applicationSettings:any;
constructor(
private formBuilder: FormBuilder,
private agentService: AgentService,
private _notificationService: NotificationService,
private _translateService: TranslateService,
public dialogRef: MatDialogRef<CaFileUploadModalComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) {
this.formGroup = this.formBuilder.group({
description: new FormControl({ value: "", disabled: false }),
file: new FormControl({ value: '', disabled: false }),
filename: new FormControl({ value: '', disabled: true })
});
if (data) {
this.fileInfo = data;
this.formGroup.controls.description.setValue(this.fileInfo.description);
this.formGroup.controls.filename.setValue(this.fileInfo.filename);
this.createMode = false;
this.enabledOK = true;
}
}
ngOnInit() {
}
buildErrorDetail(status, detail) {
this.fileErrorDetail = undefined;
if ("CONTENT_ERROR" === status) {
this.displayedColumns = ['line', 'type', 'value'];
if (detail) {
let fileErrorDetailTemp = [];
for (let line of Object.keys(detail)) {
let object = detail[line];
let type = Object.keys(object)[0];
fileErrorDetailTemp.push({
line: line,
type: type,
value: object[type],
});
}
if (fileErrorDetailTemp) {
this.fileErrorDetail = fileErrorDetailTemp;
this.dataSource.data = fileErrorDetailTemp;
}
}
} else if ("HEADER_ERROR" === status) {
this.displayedColumns = ['header', 'value'];
if (detail) {
let fileErrorDetailTemp = [];
for (let header of Object.keys(detail)) {
let value = detail[header];
fileErrorDetailTemp.push({
header: header,
value: value,
});
}
if (fileErrorDetailTemp) {
this.fileErrorDetail = fileErrorDetailTemp;
this.dataSource.data = fileErrorDetailTemp;
}
}
} else {
this.fileErrorDetail = undefined;
}
}
retry() {
this.fileErrorDetail = undefined;
}
uploadFile(event) {
for (const i in event) {
if (event[i] instanceof File) {
const reader = new FileReader();
reader.onload = () => {
//this.formGroup.controls['imageAvatar'].setValue(reader.result as string);
};
reader.readAsDataURL(event[i]);
let fileSelected = {
data: event[i],
state: 'in',
inProgress: false,
progress: 0,
canRetry: false
}
this.validateFile(fileSelected);
}
}
}
validateFile(file) {
const formData = new FormData();
formData.append('file', file.data);
file.inProgress = true;
this.enabledOK = false;
this.agentService.upload(formData).pipe(
map(event => {
switch (event.type) {
case HttpEventType.UploadProgress:
file.progress = Math.round(event.loaded * 100 / event.total);
break;
case HttpEventType.Response:
return event;
}
}),
catchError((error: HttpErrorResponse) => {
file.inProgress = false;
return of(`${file.data.name} upload failed.`);
})
).subscribe((event: any) => {
if (typeof (event) === 'object') {
this.fileErrorDetail = undefined;
this.enabledOK = false;
let info = event.body;
this.fileInfo = new FileUploadModel();
if (info) {
let status = info.fileValidationResult.status;
if ("OK" === status) {
this.fileInfo.id = info.id;
this.fileInfo.description = this.formGroup.controls.description.value;
this.fileInfo.uuid = info.uuid;
this.fileInfo.filename = info.fileName;
this.fileInfo.status = info.status;
this.fileInfo.user = info.user;
this.fileInfo.uploadDate = info.uploadDate;
this.enabledOK = true;
this.fileErrorDetail = undefined;
} else if ("INCOMPATIBLE_EXTENSION" === status) {
this.formGroup.controls.file.setValue(null);
let message = "label.file.incompatible.extension";
this.enabledOK = false;
this.fileErrorDetail = undefined;
this._notificationService.showMessage(this._translateService.instant(message) + "[" + this.accept + "]", null, NotificationType.error);
} else if ("CONTENT_ERROR" === status) {
this.formGroup.controls.file.setValue(null);
this.enabledOK = false;
this.buildErrorDetail(status, info.fileValidationResult.recordsErrorMap);
} else if ("HEADER_ERROR" === status) {
this.formGroup.controls.file.setValue(null);
this.enabledOK = false;
this.buildErrorDetail(status, info.fileValidationResult.headersErrorMap);
}
} else {
this.enabledOK = false;
this.fileErrorDetail = undefined;
this.formGroup.controls.file.setValue(null);
}
}
});
}
ok() {
this.fileInfo.description = this.formGroup.controls.description.value;
this.dialogRef.close(this.fileInfo);
}
}
<div class="panel-viewer-wrapper">
<div class="panel-viewer-title">
<mat-icon>dns</mat-icon>
{{'label.questions.title' | translate}}
</div>
<div class="panel-viewer-body">
<div class="row pb-4">
<div class="col-1"></div>
<div class="col-10">
<div class="row pt-2" *ngIf="!viewMode" [hidden]="!(dataSource.data && dataSource.data.length)">
<div class="col-12">
<button type="button" class="btn btn-outline-primary btn-sm pull-right" (click)="addFile()">
<i class="fa fa-file-excel-o"></i>
<span class="visible-md-inline visible-lg-inline visible-xl-inline">
{{'btn.new.file' | translate}}
</span>
</button>
</div>
</div>
<div class="border-all text-center" *ngIf="!(dataSource.data && dataSource.data.length)">
<button type="button" class="btn btn-outline-primary btn-sm " (click)="addFile()">
<i class="fa fa-file-excel-o"></i>
<span class="visible-md-inline visible-lg-inline visible-xl-inline">
{{'btn.upload.file' | translate}}
</span>
</button>
</div>
<div class="table-container mat-elevation-z8 p-b-15 mt-3" [hidden]="!(dataSource.data && dataSource.data.length)">
<table mat-table [dataSource]="dataSource" matSort>
<!-- Load ID -->
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="w-id text-right">{{'label.file.id' | translate}}</th>
<td mat-cell *matCellDef="let element" class="w-id text-right"> {{element.id}} </td>
</ng-container>
<!-- Filename -->
<ng-container matColumnDef="filename">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="w-filename">{{'label.file.name' | translate}}
</th>
<td mat-cell *matCellDef="let element" class="w-filename"> {{element.filename}} </td>
</ng-container>
<!-- Description -->
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="w-description">
{{'label.file.description' | translate}}</th>
<td mat-cell *matCellDef="let element" class="w-description"> {{element.description}} </td>
</ng-container>
<!-- Status -->
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="w-status">{{'label.file.status' | translate}}
</th>
<td mat-cell *matCellDef="let element" class="w-status">
<span [ngClass]="getClassStatus(element.status)">{{ getTextStatus(element.status) }}</span>
</td>
</ng-container>
<!-- User -->
<ng-container matColumnDef="user">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="w-user">{{'label.file.user' | translate}}
</th>
<td mat-cell *matCellDef="let element" class="w-user"> {{element.user}} </td>
</ng-container>
<!-- Fecha de carga -->
<ng-container matColumnDef="uploadDate">
<th mat-header-cell *matHeaderCellDef class="w-uploadDate text-right">{{'label.file.upload.date' | translate}}</th>
<td mat-cell *matCellDef="let element" class="w-uploadDate text-right">
<i class="fa fa-calendar" *ngIf="element.uploadDate"></i> {{element.uploadDate}}
</td>
</ng-container>
<!-- Actions -->
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="w-actions"></th>
<td mat-cell *matCellDef="let element; let $index = index" class="w-actions text-center">
<div class="btn-group">
<button type="button" class="btn btn-default btn-sm" (click)="onEditRecord(element, $index)"
[disabled]="!resourceAuth['edit'] || viewMode">
<i class="fa fa-edit"></i>
<span class="visible-md-inline visible-lg-inline visible-xl-inline">
{{'btn.edit' | translate}}
</span>
</button>
<button type="button" class="btn btn-default btn-sm" (click)="onDeleteRecord(element, $index)"
[disabled]="!resourceAuth['delete'] || viewMode">
<i class="fa fa-trash"></i>
<span class="visible-md-inline visible-lg-inline visible-xl-inline">
{{'btn.delete' | translate}}
</span>
</button>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
</div>
<div class="col-1"></div>
</div>
<div class="footer-bar">
<div class="row">
<div class="col-12">
<button mat-stroked-button matStepperPrevious class="pull-left">
<i class="fa fa-chevron-left"></i>
<span class="pl-1">{{'btn.previous' | translate}}</span>
</button>
<button mat-stroked-button (click)="next()" class="pull-right" [disabled]="!dataSource.data.length">
<span class="pr-1">{{'btn.next' | translate}}</span>
<i class="fa fa-chevron-right"></i>
</button>
</div>
</div>
</div>
</div>
\ No newline at end of file
.table-container {
height: 300px;
overflow: auto;
}
th.mat-sort-header-sorted {
color: black;
}
$fontcolor: #676a6c;
.content {
width: 100%;
display: flex;
padding-right: 0px !important;
.grid-container,
.filter-panel {
.crud-table {
margin-bottom: 0px;
width: 100%;
}
.form {
padding: 10px 20px 10px 20px;
}
}
.filter-panel {
margin-bottom: 10px;
}
}
.btn-group {
.btn {
font-size: 10px;
}
}
th.mat-column-actions {
width: 1px !important;
padding-right: 0px !important;
}
td.mat-column-actions {
min-width: 80px;
padding-right: 20px !important;
cursor: default !important;
}
td {
vertical-align: middle !important;
color: $fontcolor;
font-size: 12px;
}
th {
vertical-align: middle !important;
}
tr.mat-header-row {
height: 50px !important;
}
tr.mat-footer-row,
tr.mat-row {
height: 40px !important;
}
.table-toolbar {
right: 10px;
top: 10px;
button {
font-size: 12px;
}
}
.mat-raised-button {
padding: 0px 10px;
}
::ng-deep .mat-sort-header-arrow {
visibility: hidden;
}
::ng-deep .mat-sort-header-button {
.mat-icon {
padding-left: 10px;
font-size: 12px;
padding-top: 5px;
}
}
th {
background: rgb(242, 242, 242);
}
//////
table {
width: 100%;
}
tr.mat-header-row {
height: 25px;
}
tr.mat-footer-row,
tr.mat-row {
height: 40px;
}
.button-grid {
margin: 2px 5px;
mat-icon {
padding-top: 2px;
}
}
.btn-grid-action {
padding: 0 10px;
line-height: 0em;
}
.grid-ibox-content {
width: 100%;
}
td.mat-cell,
td.mat-footer-cell,
th.mat-header-cell {
/* padding: 0; */
border-bottom-width: 1px;
border-bottom-style: solid;
padding: 1px 10px 1px 10px;
}
.p-b-15 {
padding-bottom: 15px;
}
.w-id {
width: 5%;
}
.w-filename {
width: 20%;
}
.w-description {
width: 18%;
}
.w-status {
width: 12%;
}
.w-user {
width: 15%;
}
.w-uploadDate {
width: 10%;
}
.w-actions {
width: 20%;
}
.border-all.text-center {
padding: 50px;
border-radius: 4px;
margin-top: 56px;
}
::ng-deep .text-right > .mat-sort-header-container {
justify-content: flex-end !important;
}
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CaFrequentQuestionsComponent } from './ca-frequent-questions.component';
describe('CaFrequentQuestionsComponent', () => {
let component: CaFrequentQuestionsComponent;
let fixture: ComponentFixture<CaFrequentQuestionsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CaFrequentQuestionsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CaFrequentQuestionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input, ViewContainerRef, ViewChild, Output, EventEmitter } from '@angular/core';
import { MatStepper, MatDialog, MatTableDataSource, MatSort } from '@angular/material';
import { ActivatedRoute, Router } from '@angular/router';
import { animate, trigger, state, transition, style } from '@angular/animations';
import { AuthorizationService } from '@xdf/security';
import { TranslateService } from '@ngx-translate/core';
import { CaFileUploadModalComponent, FileUploadModel } from '../ca-file-upload-modal/ca-file-upload-modal.component';
@Component({
selector: 'byte-ca-frequent-questions',
templateUrl: './ca-frequent-questions.component.html',
styleUrls: ['./ca-frequent-questions.component.scss'],
animations: [
trigger('fadeInOut', [
state('in', style({ opacity: 100 })),
transition('* => void', [
animate(300, style({ opacity: 0 }))
])
])
]
})
export class CaFrequentQuestionsComponent implements OnInit {
@ViewChild(MatSort, { static: false }) sort: MatSort;
@Input() stepper: MatStepper;
@Output() onNextPage: EventEmitter<boolean> = new EventEmitter<boolean>();
public resourceAuth = new Object();
dirty: boolean = false;
accept = '.xls,.xlsx';
agentDetail: any;
displayedColumns: string[] = ['id', 'filename', 'description', 'status', 'user', 'uploadDate', 'actions'];
dataSource = new MatTableDataSource<FileUploadModel>([]);
mapStatus = {
"LO": {
"class": "label label-primary",
"text": "label.status.loaded"
},
"PS": {
"class": "label label-warning",
"text": "label.status.pending"
}
};
files = [];
viewMode: boolean;
constructor(protected route: ActivatedRoute,
protected router: Router,
protected vcRef: ViewContainerRef,
protected authorizationService: AuthorizationService,
private translate: TranslateService,
private _activatedRoute: ActivatedRoute,
private matDialog: MatDialog
) {
this.viewMode = this._activatedRoute.snapshot.data.mode === 'view';
}
ngOnInit() {
const keyAuthorization = 'authorization';
const authList = this.route.snapshot.paramMap[keyAuthorization];
if (authList) {
this.resourceAuth = new Object();
authList.forEach(option => {
this.resourceAuth[option] = true;
});
}
}
isDirty() {
return this.dirty;
}
buildForm(agentDetail: any) {
this.agentDetail = agentDetail;
if (agentDetail.frequentQuestions) {
this.dataSource.data = agentDetail.frequentQuestions;
}
}
setDataForWizard(result: any) {
console.log(result)
/*
this.protocols = result.connectionProtocolOptions;
this.generalProperties = result.generalProperties;
this.alias = result.alias;
this.maxNumberOfConnectionsByExternalSystem = result.maxNumberOfConnectionsByExternalSystem;
this.columnsForEntity = result.columnsForEntity;
*/
}
ngAfterViewInit() {
this.dataSource.sort = this.sort;
}
getClassStatus(status) {
return this.mapStatus[status].class;
}
getTextStatus(status) {
return this.translate.instant(this.mapStatus[status].text);
}
addFile() {
let dialog = this.matDialog.open(CaFileUploadModalComponent, {
width: '600px',
data: null
});
dialog.afterClosed().subscribe(result => {
if (result) {
let fileAdded = { ...result };
let listTemp = this.dataSource.data;
listTemp.push(fileAdded);
this.dataSource.data = listTemp;
this.dirty = true;
}
});
}
onEditRecord(item, index) {
item.editable = true;
let dialog = this.matDialog.open(CaFileUploadModalComponent, {
width: '500px',
data: item
});
dialog.afterClosed().subscribe(result => {
if (result) {
let fileEdited = { ...result };
let listTemp = this.dataSource.data;
listTemp[index] = fileEdited;
this.dataSource.data = listTemp;
}
});
}
saveQuestions(validateForm: boolean) {
let success = true;
this.agentDetail.frequentQuestions = this.dataSource.data;
return success;
}
onDeleteRecord(item, index) {
let listTemp = this.dataSource.data;
listTemp.splice(index, 1);
this.dataSource.data = listTemp;
this.dirty = true;
}
next() {
if (!this.dataSource.data.length) {
return;
}
if (this.viewMode) {
this.stepper.next();
return;
}
if (this.saveQuestions(true)) {
this.onNextPage.emit(this.isDirty());
this.stepper.next();
}
}
}
<div class="panel-viewer-wrapper">
<div class="panel-viewer-title">
<i class="fa fa-info-circle"></i>
{{'label.general-information.title' | translate}}
</div>
<form [formGroup]="formGroup" autocomplete="off">
<div class="panel-viewer-body">
<div class="row pt-3 pb-4">
<div class="col-3"></div>
<div class="col-6 mat-elevation-z3">
<div class="row">
<div class="col-6">
<!-- INICIO :: SECCION AVATAR -->
<div class="row pt-2 h-100">
<div class="col-12">
<label class="form-field-label" #labelcontrol>{{ 'label.avatar' | translate }}
<span aria-hidden="true"
class="mat-placeholder-required mat-form-field-required-marker"> *</span>
</label>
<div class="h-85">
<div class="image-preview">
<div [ngClass]="{'with-image': formGroup.controls['imageAvatar'].value, 'edit-image': formGroup.controls['imageAvatar'].value, 'without-image': !formGroup.controls['imageAvatar'].value}">
<img src="{{ urlBase + formGroup.controls['imageAvatar'].value }}"
style="height: 100%;"
*ngIf="formGroup.controls['imageAvatar'].value">
<div class="image-notfound"
*ngIf="!formGroup.controls['imageAvatar'].value"></div>
</div>
<button type="button" mat-stroked-button (click)="fileInput.click()"
*ngIf="!viewMode" class="btn-upload-image">
<mat-icon>file_upload</mat-icon>
<!-- {{text | translate}} -->
</button>
</div>
</div>
<input type="file" #fileInput name="fileUpload"
(change)="uploadFiles($event.target.files)"
style="display:none;" >
</div>
</div>
<!-- FIN :: SECCION AVATAR -->
</div>
<div class="col-6">
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<input matInput [placeholder]="'label.name' | translate" formControlName="name"
maxlength="50" required>
</mat-form-field>
</div>
</div>
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<textarea matInput cdkTextareaAutosize class="overflow-auto"
[placeholder]="'label.description' | translate" maxlength="200"
formControlName="description" #autosize="cdkTextareaAutosize" cdkAutosizeMinRows="3"
cdkAutosizeMaxRows="6" required></textarea>
</mat-form-field>
</div>
</div>
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<input matInput [placeholder]="'label.version' | translate" formControlName="version"
maxlength="15" required pattern="[0-9]{1,2}[.]{1}[0-9]{1,2}[.]{1}[0-9]{1,2}">
<mat-hint>##.##.##</mat-hint>
</mat-form-field>
</div>
</div>
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<mat-select [placeholder]="'label.country' | translate" formControlName="country"
required (selectionChange)="onCountryChange($event, true)">
<mat-option *ngFor="let country of countries" [value]="country.id">
{{country.name | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<mat-select [placeholder]="'label.timezone' | translate" formControlName="timezone"
required>
<mat-option *ngFor="let timezone of timezones" [value]="timezone">
{{timezone | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<mat-select [placeholder]="'label.language' | translate" formControlName="language" required >
<mat-option *ngFor="let language of languages" [value]="language.code">
{{language.name | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="row pt-2">
<div class="col-12">
<mat-form-field class="amd-form-control">
<mat-select [placeholder]="'label.type' | translate" formControlName="type" required >
<mat-option *ngFor="let type of types" [value]="type.code">
{{type.name | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
</div>
</div>
<div class="col-3"></div>
</div>
<div class="footer-bar">
<div class="row">
<div class="col-12">
<button mat-stroked-button (click)="next()" class="pull-right" [disabled]="formGroup.invalid">
<span class="pr-1">{{'btn.next' | translate}}</span>
<i class="fa fa-chevron-right"></i>
</button>
</div>
</div>
</div>
</div>
</form>
</div>
\ No newline at end of file
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
#file-label {
display: inline-flex;
vertical-align: middle;
font-size: 12px;
line-height: 18px;
}
#file-label mat-icon {
font-size: 18px;
text-align: center;
}
#file-label a {
cursor: pointer;
}
.uploadList {
margin-top: 10px;
}
.uploadfilecontainer {
background-position: center;
line-height: 200px;
margin: 20px auto;
border: 2px dashed #1c8adb;
border-radius: 10px;
button {
vertical-align: middle;
}
}
.uploadfilecontainer:hover {
cursor: pointer;
background-color: #9ecbec !important;
opacity: 0.8;
}
.image-preview {
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
background-image: none;
background-color: white;
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 5px;
}
.image-notfound {
width: 100%;
height: 100%;
// border-radius: 5px;
// border-bottom-color:rgba(0,0,0,.12);
&::after {
content: "photo_size_select_actual";
font-family: "Material Icons";
position: relative;
font-size: 4.5em;
color: rgba(230, 230, 230, 1);
top: calc(50% - 3rem);
left: calc(50% - 2.25rem);
z-index: 0;
}
}
.btn-upload-image {
font-size: 80%;
width: 100%;
border-radius: 0;
border-width: 1px 0 0 0;
border-top-color: rgba(0, 0, 0, 0.12);
}
.form-field-label {
color: rgba(0, 0, 0, 0.54);
font-size: 75%;
}
.with-image {
text-align: center;
height: 100%;
}
.edit-image {
text-align: center;
height: calc(100% - 36px);
}
.without-image {
text-align: inherit;
height: calc(100% - 36px);
}
.h-85 {
height: 85%;
}
.h-100 {
height: 85%;
}
.col-6.mat-elevation-z3 {
padding: 24px;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CaGeneralInformationComponent } from './ca-general-information.component';
describe('CaGeneralInformationComponent', () => {
let component: CaGeneralInformationComponent;
let fixture: ComponentFixture<CaGeneralInformationComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CaGeneralInformationComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CaGeneralInformationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
import { MatStepper } from '@angular/material';
import { ActivatedRoute } from '@angular/router';
import { animate, trigger, state, transition, style } from '@angular/animations';
import { NotificationService, NotificationType } from '@xdf/commons';
import { TranslateService } from '@ngx-translate/core';
export const FILE_TYPE = {
image: /image.*/,
file: /.*/
};
@Component({
selector: 'byte-ca-general-information',
templateUrl: './ca-general-information.component.html',
styleUrls: ['./ca-general-information.component.scss'],
animations: [
trigger('fadeInOut', [
state('in', style({ opacity: 100 })),
transition('* => void', [
animate(300, style({ opacity: 0 }))
])
])
]
})
export class CaGeneralInformationComponent implements OnInit {
@Input() stepper: MatStepper;
@Output() onNextPage: EventEmitter<boolean> = new EventEmitter<boolean>();
agentDetail: any;
formGroup: FormGroup;
viewMode: boolean;
mode: string = 'new';
imageName = "imageAvatar";
urlBase = '';
countries = [];
timezones = [];
languages = [
{
"code": "ES",
"name": "Español"
}
];
types = [
{
"code": "FQ",
"name": "Agente de preguntas frecuentes"
}
];
acceptedImageTypes = ['image/png', 'image/jpeg', 'image/gif'];
acceptedTypes = "PNG, JPEG, GIF";
constructor(
private formBuilder: FormBuilder,
private _activatedRoute: ActivatedRoute,
private _notificationService: NotificationService,
private _translateService: TranslateService
) {
this.mode = this._activatedRoute.snapshot.data.mode;
this.viewMode = this.mode === 'view';
this.countries = this._activatedRoute.snapshot.data.countryData;
this.formGroup = this.formBuilder.group({
name: new FormControl({ value: "", disabled: this.viewMode }),
description: new FormControl({ value: "", disabled: this.viewMode }),
version: new FormControl({ value: "", disabled: this.viewMode }),
imageAvatar: new FormControl({ value: "", disabled: this.viewMode }, { validators: [Validators.required] }),
country: new FormControl({ value: "", disabled: this.viewMode }),
timezone: new FormControl({ value: "", disabled: this.viewMode }),
language: new FormControl({ value: "", disabled: this.viewMode }),
type: new FormControl({ value: "", disabled: this.viewMode })
});
}
ngOnInit() {
}
buildForm(agentDetail: any) {
this.agentDetail = agentDetail;
if (agentDetail) {
this.formGroup.patchValue({
name: agentDetail.name,
description: agentDetail.description,
version: agentDetail.version,
timezone: agentDetail.timezone,
country: agentDetail.countryId,
imageAvatar: agentDetail.avatar,
language: agentDetail.language,
type: agentDetail.type
});
this.onCountryChange(this.formGroup.controls.country);
}
}
saveGeneralInformation(validateForm: boolean): boolean {
if (validateForm && !this.formGroup.valid) {
return false;
}
this.agentDetail.name = this.formGroup.controls.name.value;
this.agentDetail.description = this.formGroup.controls.description.value;
this.agentDetail.version = this.formGroup.controls.version.value;
this.agentDetail.countryId = this.formGroup.controls.country.value;
this.agentDetail.timezone = this.formGroup.controls.timezone.value;
this.agentDetail.avatar = this.formGroup.controls.imageAvatar.value;
this.agentDetail.language = this.formGroup.controls.language.value;
this.agentDetail.type = this.formGroup.controls.type.value;
return true;
}
isDirty() {
return this.formGroup.dirty;
}
uploadFiles(event) {
for (const i in event) {
if (event[i] instanceof File) {
let eventTemp = event[i];
if (this.acceptedImageTypes.includes(eventTemp.type)) {
const reader = new FileReader();
reader.onload = () => {
this.formGroup.controls['imageAvatar'].setValue(reader.result as string);
};
reader.readAsDataURL(event[i]);
} else {
let message = "label.imageAvatar.incompatible.extension";
this._notificationService.showMessage(this._translateService.instant(message) + "[" + this.acceptedTypes + "]", null, NotificationType.error);
}
}
}
}
onCountryChange(obj, deleteTimeZone?: boolean) {
let selectedCountry = obj.value;
this.timezones = [];
if (deleteTimeZone) {
this.formGroup.controls['timezone'].setValue(null);
}
if (selectedCountry) {
for (var index in this.countries) {
if (selectedCountry === this.countries[index].id) {
this.timezones = this.countries[index].timezones;
break;
}
}
}
}
next() {
if (this.viewMode) {
this.stepper.next();
return;
}
if (this.saveGeneralInformation(true)) {
this.onNextPage.emit(this.isDirty());
this.stepper.next();
}
}
}
import { TestBed } from '@angular/core/testing';
import { UploadService } from './upload.service';
describe('UploadService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: UploadService = TestBed.get(UploadService);
expect(service).toBeTruthy();
});
});
import { HttpClient, HttpEvent, HttpErrorResponse, HttpEventType } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class UploadService {
SERVER_URL: string = "./service/agent";
constructor(private httpClient: HttpClient) { }
public upload(formData) {
return this.httpClient.post<any>(this.SERVER_URL, formData, {
reportProgress: true,
observe: 'events'
});
}
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { SettingsService, ApplicationSettings } from '@xdf/layouts';
@Injectable({
providedIn: 'root'
})
export class BytebotSettingsService extends SettingsService {
constructor(private http: HttpClient) {
super();
}
getApplicationSettings(): Observable<ApplicationSettings> {
return this.http.get<ApplicationSettings>('./service/settings-logo/');
}
}
......@@ -32,53 +32,12 @@
},
{
"id": 37,
"name": "piloto",
"label": "menu.piloto",
"name": "CONVERSATIONAL_AGENT",
"label": "menu.parent.agent",
"icon": "<i class=\"fa fa-cog\"></i>",
"fullPath": "/piloto",
"singlePath": "piloto",
"isProgram": false,
"children": [
{
"id": 33,
"name": "entidades",
"label": "entidades.piloto",
"icon": null,
"fullPath": "/piloto/entidades",
"singlePath": "entidades",
"fullPath": "/configuration/agent",
"singlePath": "configuration",
"isProgram": true,
"children": []
},
{
"id": 34,
"name": "estados",
"label": "estados.piloto",
"icon": null,
"fullPath": "/piloto/estados",
"singlePath": "estados",
"isProgram": true,
"children": []
},
{
"id": 35,
"name": "motivos",
"label": "motivos.piloto",
"icon": null,
"fullPath": "/piloto/motivos",
"singlePath": "motivos",
"isProgram": true,
"children": []
},
{
"id": 36,
"name": "categoria_motivo",
"label": "categoria.motivo.piloto",
"icon": null,
"fullPath": "/piloto/categoria-motivo",
"singlePath": "categoria-motivo",
"isProgram": true,
"children": []
}
]
}
]
......@@ -31,10 +31,19 @@
"options": {
"styleClass": "col-xs-3 col-sm-6 col-md-6",
"showLength": true,
"maxLength": 60
"maxLength": 25
},
"validators": [
"required"
"required",
"identifier",
{
"name": "minLength",
"value": 3
},
{
"name": "maxLength",
"value": 25
}
],
"controlType": "text"
}
......@@ -47,7 +56,7 @@
"options": {
"styleClass": "col-md-6",
"showLength": true,
"maxLength": 80,
"maxLength": 30,
"showStrength": false
},
"validators": [
......@@ -96,10 +105,15 @@
"options": {
"styleClass": "col-xs-3 col-sm-6 col-md-6",
"showLength": true,
"maxLength": 80
"maxLength": 30
},
"validators": [
"required"
"required",
"names",
{
"name": "minLength",
"value": 2
}
],
"controlType": "text"
}
......@@ -111,9 +125,15 @@
"options": {
"styleClass": "col-xs-3 col-sm-6 col-md-6",
"showLength": true,
"maxLength": 80
"maxLength": 30
},
"validators": [],
"validators": [
"names",
{
"name": "minLength",
"value": 2
}
],
"controlType": "text"
}
},
......@@ -124,10 +144,15 @@
"options": {
"styleClass": "col-xs-3 col-sm-6 col-md-6",
"showLength": true,
"maxLength": 80
"maxLength": 30
},
"validators": [
"required"
"names",
"required",
{
"name": "minLength",
"value": 2
}
],
"controlType": "text"
}
......@@ -139,10 +164,15 @@
"options": {
"styleClass": "col-xs-3 col-sm-6 col-md-6",
"showLength": true,
"maxLength": 80
"maxLength": 30
},
"validators": [
"required"
"names",
"required",
{
"name": "minLength",
"value": 2
}
],
"controlType": "text"
}
......@@ -168,8 +198,8 @@
"type": "string",
"formOptions": {
"values": {
"01": "Mr",
"02": "Miss"
"01": "Sr.",
"02": "Sra."
},
"options": {
"styleClass": "col-md-3"
......@@ -189,6 +219,7 @@
"required"
],
"options": {
"editabled": false,
"styleClass": "col-md-6"
}
}
......@@ -203,7 +234,11 @@
"styleClass": "col-md-3"
},
"validators": [
"required"
"required",
{
"name": "pattern",
"value": "^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\\s\\./0-9]*$"
}
],
"controlType": "text"
}
......@@ -261,6 +296,7 @@
[
"username", "status"
],
[ "password" ],
[
"defaultRole",
"roleList"
......@@ -301,6 +337,7 @@
"sortColumnDefault": "username",
"sortColumnDirection": "desc",
"pagingSize": 5,
"expandRow": true,
"columns": [
{
"name": "username",
......
......@@ -6,5 +6,6 @@
"entidades",
"estados",
"motivos",
"categoria_motivo"
"categoria_motivo",
"CONVERSATIONAL_AGENT"
]
\ No newline at end of file
{
"HOME": "Start",
"BACK": "Back",
"home.subtitle": "Welcome to XDF Project",
"home.comments": "It is an application skeleton for a typical web app. You can use it to quickly bootstrap your webapp projects and dev environment."
"home.comments": "It is an application skeleton for a typical web app. You can use it to quickly bootstrap your webapp projects and dev environment.",
"btn.previous": "Back",
"label.agent": "Agents",
"agent_avatar": "Avatar",
"agent_code": "ID",
"agent_name": "Name",
"agent_version": "Version",
"agent_status": "Status",
"agent_country": "Country",
"agent_timezone": "Timezone",
"btn.file.upload" : "Upload",
"btn.accept": "Accept",
"label.deployment-channels.title": "Deployment channels",
"label.deployment-channels.description": "Configure the messaging channels that will be used by the agent",
"label.channels": "Configured channels",
"label.active": "Active",
"label.inactive": "Inactive",
"label.name": "Name",
"label.deployment-channels.configuration": "Deployment channels",
"label.deployment-channels.configuration.description": "Select a deployment channel",
"label.status.pending" : "Pending",
"label.status.loaded" : "Loaded",
"label.status.off" : "Disabled",
"label.created": "Created",
"label.deployed": "Deployed",
"label.sync-pending": "Sync pending",
"breadcrumb.agent": "Agents",
"label.general-information.title" : "General information",
"label.general-information.description" : "Register the basic information for the agent",
"label.avatar" : "Avatar",
"label.description" : "Description",
"label.version" : "Version",
"label.country" : "Country",
"label.timezone" : "Timezone",
"label.language" : "Language",
"label.type" : "Agent type",
"label.questions.title" : "Frequent questions",
"label.questions.description" : "Upload files whose content will be used as training for the agent",
"btn.new.file" : "New file",
"label.file.id" : "ID",
"label.file.name" : "Name",
"label.file.description" : "Description",
"label.file.status" : "Status",
"label.file.user" : "User",
"label.file.upload.date" : "Loaded date",
"label.file-upload.title" : "Questions file",
"label.file-upload.description" : "The question file will be used as material training",
"label.file" : "File",
"label.file.incompatible.extension" : "Invalid file. Just accept the following file extensions ",
"label.imageAvatar.incompatible.extension" : "Invalid file. Just accept the followeing image types ",
"label.delete-channel": "Delete channel",
"agent.edit.success": "The agent was successfully edited",
"agent.create.success": "The agent was created successfully",
"agent.synchronize.success": "Synchronization was successful",
"error.message.complete-form": "Complete the form correctly",
"label.file.tooltip" : "For security reasons, you cannot edit uploaded files. Please delete and upload a new file",
"EMPTY.VALUE": "Empty value",
"NOT.FOUND.VALUE": "Value not found",
"header.type": "Column",
"header.header": "Column",
"header.line": "Row",
"header.value": "Value",
"HEADER.REPETEAD": "Repeated column",
"HEADER.NOT.FOUND": "Column not found",
"label.file-upload.error.subtitle": "Error detail",
"label.file-upload.error.description": "An error occurred in the validation of the file.",
"btn.synchronize": "Synchronize"
}
\ No newline at end of file
{
"HOME": "Inicio",
"BACK": "Regresar",
"home.subtitle": "Bienvenido al proyecto XDF",
"home.comments": "Este proyecto presenta una aplicación web típica. Puede usarlo como un marco de referencia para el desarrollo de sus aplicaciones web."
"home.comments": "Este proyecto presenta una aplicación web típica. Puede usarlo como un marco de referencia para el desarrollo de sus aplicaciones web.",
"btn.previous": "Atrás",
"label.agent": "Agentes",
"agent_avatar": "Avatar",
"agent_code": "ID",
"agent_name": "Nombre",
"agent_version": "Versión",
"agent_status": "Estado",
"agent_country": "País",
"agent_timezone": "Zona horaria",
"agent_publish_version": "Versión publicada",
"btn.file.upload": "Cargar",
"btn.accept": "Aceptar",
"label.deployment-channels.title": "Canales de mensajería",
"label.deployment-channels.description": "Configure los canales de mensajería por los cuales interactuará el agente",
"label.channels": "Canales configurados",
"label.active": "Activo",
"label.inactive": "Inactivo",
"label.name": "Nombre",
"label.deployment-channels.configuration": "Canales de mensajería",
"label.deployment-channels.configuration.description": "Seleccione un canal de mensajería",
"label.status.pending": "Pendiente",
"label.status.loaded": "Cargado",
"label.status.off": "En Baja",
"label.created": "Creado",
"label.deployed": "Desplegado",
"label.sync-pending": "Pendiente de sincronización",
"breadcrumb.agent": "Agentes",
"label.general-information.title": "Información general",
"label.general-information.description": "Registre la información correspondiente al agente",
"label.avatar": "Avatar",
"label.description": "Descripción",
"label.version": "Versión",
"label.country": "País",
"label.timezone": "Zona horaria",
"label.language": "Idioma",
"label.type": "Tipo de agente",
"label.questions.title": "Preguntas frecuentes",
"label.questions.description": "Cargue los archivos con las preguntas frecuentes de entrenamiento",
"btn.new.file": "Nuevo archivo",
"label.file.id": "ID",
"label.file.name": "Nombre",
"label.file.description": "Descripción",
"label.file.status": "Estado",
"label.file.user": "Usuario",
"label.file.upload.date": "Fecha de carga",
"label.file-upload.title": "Archivo de preguntas",
"label.file-upload.description": "El archivo de preguntas se utilizará para el entrenamiento del bot",
"label.file": "Archivo",
"label.file.incompatible.extension": "Archivo no válido. Sólo se aceptan archivos con extensión ",
"label.imageAvatar.incompatible.extension": "Archivo no válido. Sólo se aceptan imágenes y con formato ",
"label.delete-channel": "Borrar canal",
"agent.edit.success": "El agente fue editado correctamente",
"agent.create.success": "El agente fue creado correctamente",
"agent.synchronize.success": "La sincronización se realizó correctamente",
"error.message.complete-form": "Complete correctamente el formulario",
"label.file.tooltip": "Por seguridad, no se pueden editar los archivos validados",
"EMPTY.VALUE": "Celda vacía",
"NOT.FOUND.VALUE": "Valor no encontrado",
"header.type": "Columna",
"header.header": "Columna",
"header.line": "Fila",
"header.value": "Valor",
"HEADER.REPETEAD": "Columna repetida",
"HEADER.NOT.FOUND": "Columna no encontrado",
"label.file-upload.error.subtitle": "Detalle de error",
"label.file-upload.error.description": "Ha ocurrido un error en la validación del archivo.",
"btn.retry": "Cargar otro archivo",
"btn.synchronize": "Publicar",
"btn.upload.file": "Subir archivo"
}
\ No newline at end of file
......@@ -54,16 +54,18 @@
"message.error.validateAge": "The age must be over 17 years and less that 100",
"message.error.email": "The email does not comply with the format",
"message.error.required": "The \"{{ label }}\" field is required",
"message.error.min.length": "The \"{{ label }}\" field supports at least \"{{value}}\" characters",
"message.error.max.length": "The \"{{ label }}\" field supports at maximum \"{{value}}\" characters",
"message.error.minlength": "The \"{{ label }}\" field supports at least \"{{requiredLength}}\" characters",
"message.error.maxlength": "The \"{{ label }}\" field supports at maximum \"{{requiredLength}}\" characters",
"message.error.min": "Value must be greater equal that to \"{{ min }}\"",
"message.error.max": "Value must be less equal that to \"{{ value }}\"",
"message.error.pattern": "The value does not meet the regular expression - \"{{ value }}\"",
"message.error.max": "Value must be less equal that to \"{{ max }}\"",
"message.error.pattern": "The \"{{ label }}\" field does not meet the required format",
"message.error.duplicity.name": "The value entered already exists",
"message.error.matDatepickerParse": "The value input is not valid",
"message.error.resource.authorization": "Don't have access to the program",
"message.error.matDatepickerMin": "The date must be greater than {{ min }}",
"message.error.array.required": "You must select at least one item",
"message.error.identifier": "The \"{{ label }}\" field can only contain numbers, letters and underscore",
"message.error.names": "The \"{{ label }}\" field is not valid",
"title.dirty.confirmation": "Confirm",
"title.delete.confirmation": "Delete",
......
......@@ -54,16 +54,18 @@
"message.error.validateAge": "La edad debe ser mayor de 17 años y menor de 100",
"message.error.email": "El email no cumple con el formato",
"message.error.required": "El campo \"{{ label }}\" es requerido",
"message.error.min.length": "El campo \"{{ label }}\" admite como mínimo \"{{ value }}\" caracteres",
"message.error.max.length": "El campo \"{{ label }}\" admite como máximo \"{{ value }}\" caracteres",
"message.error.minlength": "El campo \"{{ label }}\" admite como mínimo \"{{ requiredLength }}\" caracteres",
"message.error.maxlength": "El campo \"{{ label }}\" admite como máximo \"{{ requiredLength }}\" caracteres",
"message.error.min": "El valor debe ser mayor o igual a \"{{ min }}\"",
"message.error.max": "El valor debe ser menor o igual a \"{{ max }}\"",
"message.error.pattern": "El valor no cumple con la expresión regular - \"{{ requiredPattern }}\"",
"message.error.pattern": "El campo \"{{ label }}\" no cumple con el formato requerido",
"message.error.duplicity.name": "El valor ingresado ya existe",
"message.error.matDatepickerParse": "El valor ingresado no es válido",
"message.error.resource.authorization": "No tiene acceso al programa",
"message.error.matDatepickerMin": "La fecha debe ser mayor a {{ min }}",
"message.error.array.required": "Debe seleccionar al menos un elemento",
"message.error.identifier": "El campo \"{{ label }}\" solo puede contener números, letras y guiones",
"message.error.names": "El campo \"{{ label }}\" no es válido",
"title.dirty.confirmation": "Confirmar",
"title.delete.confirmation": "Eliminar",
......
......@@ -12,9 +12,5 @@
"menu.settings.valpos": "Valores posibles",
"menu.business.config": "Configuración",
"menu.ticket.manager": "Gestor de tickets",
"menu.piloto": "Configuración piloto",
"entidades.piloto": "Entidades",
"estados.piloto": "Estados",
"motivos.piloto": "Motivos",
"categoria.motivo.piloto": "Categorías de motivos"
"menu.parent.agent": "Agentes"
}
\ No newline at end of file
......@@ -2,9 +2,9 @@
"login.title": "Login Page",
"breadcrumb.user.role": "User role",
"breadcrumb.user.role.detail": "Detail",
"breadcrumb.user.role.detail": "User role",
"breadcrumb.user": "User",
"breadcrumb.user.detail": "Detail",
"breadcrumb.user.detail": "User",
"control.new": "New",
"control.view": "View",
......@@ -41,14 +41,18 @@
"user_group_personal_info": "Personal information",
"user_group_contact_info": "Contact information",
"user_form_username_placeholder": "Username",
"user_form_password_placeholder": "Password",
"user_form_defaultRole_label": "Main role",
"user_form_defaultRole_suggest": "This role will be used as the default role in the application.",
"user_form_defaultRole_placeholder": "Main role",
"user_form_roleList_label": "Assigned roles",
"user_form_roleList_placeholder": "Assigned roles",
"user_form_firstName_placeholder": "First name",
"user_form_secondName_placeholder": "Second name",
"user_form_firstSurname_placeholder": "Last name",
"user_form_secondSurname_placeholder": "Mother's last name",
"user_form_salutationPrefer_label": "Salutation",
"user_form_salutationPrefer_placeholder": "Salutation",
"user_form_sex_label": "Gender",
"user_form_sex_placeholder": "Gender",
"user_form_birthday_placeholder": "Birthday",
......
......@@ -2,9 +2,9 @@
"login.title": "Identificación de Usuario",
"breadcrumb.user.role": "Rol de usuario",
"breadcrumb.user.role.detail": "Detalle",
"breadcrumb.user.role.detail": "Rol de usuario",
"breadcrumb.user": "Usuario",
"breadcrumb.user.detail": "Detalle",
"breadcrumb.user.detail": "Usuario",
"control.new": "Creación",
"control.view": "Visualizar",
......@@ -41,14 +41,18 @@
"user_group_personal_info": "Datos personales",
"user_group_contact_info": "Datos de contacto",
"user_form_username_placeholder": "Código de usuario",
"user_form_password_placeholder": "Contraseña",
"user_form_defaultRole_label": "Rol principal",
"user_form_defaultRole_suggest": "Este rol será utilizado como rol por defecto en la aplicación.",
"user_form_defaultRole_placeholder": "Rol principal",
"user_form_roleList_label": "Roles asignados",
"user_form_roleList_placeholder": "Roles asignados",
"user_form_firstName_placeholder": "Primer nombre",
"user_form_secondName_placeholder": "Segundo nombre",
"user_form_firstSurname_placeholder": "Apellido paterno",
"user_form_secondSurname_placeholder": "Apellido Materno",
"user_form_salutationPrefer_label": "Saludo",
"user_form_salutationPrefer_placeholder": "Saludo",
"user_form_sex_label": "Género",
"user_form_sex_placeholder": "Género",
"user_form_birthday_placeholder": "Fecha de nacimiento",
......
......@@ -134,3 +134,55 @@ button {
color: #676a6c;
font-weight: 500;
}
/* FIn de estilos XDF */
.mat-button-toggle-label-content {
line-height: 10px !important;
}
.mat-icon {
font-size: 16px;
}
.panel-viewer-wrapper {
border: 1px solid rgba(0, 0, 0, 0.03);
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
margin: 4px 0 10px;
}
.panel-viewer-title {
padding: 12px 0px 12px 20px;
font-size: 13px;
color: rgba(0, 0, 0, 0.54);
background: rgba(0, 0, 0, 0.03);
.mat-icon-button {
line-height: 0px !important;
}
}
.panel-viewer-body {
padding: 10px 30px 10px 30px;
background-color: #ffffff;
}
.amd-form-control {
width: 100%;
}
.cdk-global-scrollblock {
position: fixed;
overflow-y: inherit;
/*width: inherit;*/
width: -moz-available; /* WebKit-based browsers will ignore this. */
width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
width: stretch;
}
// Z-index para la barra de menu
.navbar-static-side {
z-index: 999;
position: absolute;
}
......@@ -12,7 +12,7 @@
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
......
{
"extends": "tslint:recommended",
"rulesDirectory": [
"codelyzer"
],
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warning"
},
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment