Compare commits
No commits in common. "feature/advanced-reports" and "master" have entirely different histories.
feature/ad
...
master
@ -12,7 +12,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
|
||||
jobs:
|
||||
push-to-svn:
|
||||
runs-on: self-hosted
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
--no-auth-cache \
|
||||
--non-interactive \
|
||||
--trust-server-cert \
|
||||
"${{ secrets.SVN_REPO_URL }}/branches/advanced-reports" svn-branch
|
||||
"${{ secrets.SVN_REPO_URL }}/branches/data-export-api-copy" svn-branch
|
||||
|
||||
- name: Sync files to SVN working copy
|
||||
run: |
|
||||
@ -74,4 +74,4 @@ jobs:
|
||||
--no-auth-cache \
|
||||
--non-interactive \
|
||||
--trust-server-cert \
|
||||
-m "Gitea CI sync from commit ${{ github.sha }} [ci skip]"
|
||||
-m "Gitea CI sync from commit ${{ github.sha }} [ci skip]"
|
||||
6
Development/client/.env
Normal file
6
Development/client/.env
Normal file
@ -0,0 +1,6 @@
|
||||
CURRENT_FILE_PATH=src/locale/messages.xlf
|
||||
TRANSLATED_FILE_PATH_ES=src/locale/messages.es.xlf
|
||||
TRANSLATED_FILE_PATH_PT=src/locale/messages.pt.xlf
|
||||
GOOGLE_LOCATION=global
|
||||
GOOGLE_PROJECT_ID=predictive-fx-392018
|
||||
GOOGLE_APPLICATION_CREDENTIALS=google-cloud.json
|
||||
@ -52,21 +52,6 @@
|
||||
"glob": "**/*",
|
||||
"input": "node_modules/leaflet/dist/images",
|
||||
"output": "/assets/images"
|
||||
},
|
||||
{
|
||||
"glob": "CHANGELOG.md",
|
||||
"input": "docs",
|
||||
"output": "/assets/docs"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "docs/releases",
|
||||
"output": "/assets/docs/releases"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "../server/public/images",
|
||||
"output": "/images"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
@ -4,19 +4,12 @@
|
||||
"license": "COMMERCIAL",
|
||||
"angular-cli": {},
|
||||
"scripts": {
|
||||
"generate-release-manifest": "node scripts/generate-release-manifest.js",
|
||||
"ng": "ng",
|
||||
"prestart-cert": "npm run generate-release-manifest",
|
||||
"start-cert": "ng serve --ssl true --sslKey ~/ssl/server.key --sslCert ~/ssl/server.crt --proxy-config proxy.config.json --host 0.0.0.0 --disableHostCheck",
|
||||
"prestart": "npm run generate-release-manifest",
|
||||
"start": "CHOKIDAR_USEPOLLING=true ng serve --ssl true --proxy-config proxy.config.json --host 0.0.0.0 --disableHostCheck",
|
||||
"prestart-es": "npm run generate-release-manifest",
|
||||
"start-es": "ng serve --ssl true --proxy-config proxy.config.json --host 0.0.0.0 --disableHostCheck --configuration=es",
|
||||
"prestart-pt": "npm run generate-release-manifest",
|
||||
"start-pt": "ng serve --ssl true --proxy-config proxy.config.json --host 0.0.0.0 --disableHostCheck --configuration=pt",
|
||||
"prebuild": "npm run generate-release-manifest",
|
||||
"build": "ng build",
|
||||
"prebuild-prep": "npm run generate-release-manifest",
|
||||
"build-prep": "ng build --aot --localize=false",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
@ -29,9 +22,7 @@
|
||||
"sync-i18n": "npm run build-prep && npm run i18n-extract && npm run i18n-merge",
|
||||
"sync-i18n-w": "npm run build-prep && npm run i18n-extract-w && npm run i18n-merge-w",
|
||||
"pre-translate": "npx translation start && npm run sync-i18n && npx translation translate && npx translation cleanup",
|
||||
"prebuild-prod": "npm run generate-release-manifest",
|
||||
"build-prod": "ng build --prod --localize && cp -R dist/en/* dist/ && rm -R dist/en",
|
||||
"prebuild-prod-window": "npm run generate-release-manifest",
|
||||
"build-prod-window": "ng build --prod --localize && xcopy /E /Y dist\\en\\* dist\\ && rmdir /S /Q dist\\en"
|
||||
},
|
||||
"private": true,
|
||||
@ -67,13 +58,8 @@
|
||||
"geodesy": "^1.1.3",
|
||||
"intl": "^1.2.5",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet-river": "^1.0.1",
|
||||
"marked": "^1.2.9",
|
||||
"mermaid": "^8.14.0",
|
||||
"ngrx-store-localstorage": "^9.0.0",
|
||||
"ngx-captcha": "^8.0.1",
|
||||
"ngx-markdown": "^9.1.1",
|
||||
"polygon-clipping": "^0.15.7",
|
||||
"primeng-lts": "^9.2.8",
|
||||
"quill": "^1.3.7",
|
||||
"rbush": "^3.0.1",
|
||||
@ -124,4 +110,4 @@
|
||||
},
|
||||
"websocket-driver": "0.7.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,12 +11,6 @@
|
||||
"changeOrigin": false,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/images/*": {
|
||||
"target": "https://127.0.0.1:4100",
|
||||
"secure": false,
|
||||
"changeOrigin": false,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/es/uploads/*": {
|
||||
"target": "https://127.0.0.1:4100",
|
||||
"pathRewrite": {
|
||||
@ -22,8 +22,6 @@
|
||||
<input pInputText type="text" (input)="dt.filter($event.target.value, col.field, col.filterMatchMode)"
|
||||
[value]="dt.filters[col.field]?.value">
|
||||
</div>
|
||||
<p-dropdown *ngIf="col.field === ACTIVE" [options]="activeOpts" [style]="{'width':'100%'}" [ngModel]="dt.filters[col.field]?.value" (onChange)="dt.filter($event.value, col.field, 'equals')"></p-dropdown>
|
||||
<p-dropdown *ngIf="col.field === KIND" [options]="kindOpts" [style]="{'width':'100%'}" [ngModel]="dt.filters[col.field]?.value" (onChange)="dt.filter($event.value, col.field, 'equals')"></p-dropdown>
|
||||
<span *ngSwitchDefault></span>
|
||||
</th>
|
||||
</tr>
|
||||
@ -7,7 +7,7 @@ import { User } from '../models/user.model';
|
||||
import * as fromUsers from '../reducers';
|
||||
import * as userActions from '../actions/account.actions';
|
||||
|
||||
import { RoleIds, Roles, globals, OperationalStatus, Labels } from '@app/shared/global';
|
||||
import { RoleIds, globals, OperationalStatus, Labels } from '@app/shared/global';
|
||||
import { BaseComp } from '@app/shared/base/base.component';
|
||||
import { Utils } from '@app/shared/utils';
|
||||
|
||||
@ -21,15 +21,6 @@ export class AccountListComponent extends BaseComp implements OnInit, OnDestroy
|
||||
readonly resolveFieldData = Utils.resolveFieldData;
|
||||
readonly KIND = 'kind';
|
||||
readonly ACTIVE = OperationalStatus.ACTIVE;
|
||||
activeOpts = [
|
||||
{ label: globals.all, value: null },
|
||||
{ label: globals.active, value: true },
|
||||
{ label: globals.notActive, value: false },
|
||||
];
|
||||
kindOpts = [
|
||||
{ label: globals.all, value: null },
|
||||
...Object.entries(Roles).map(([value, label]) => ({ label: label as string, value }))
|
||||
];
|
||||
accounts: Array<User>;
|
||||
isLoading: boolean;
|
||||
currAcc: User;
|
||||
@ -69,16 +69,6 @@ const routes: Routes = [
|
||||
loadChildren: () => import('./tools/tools.module').then(m => m.ToolsModule),
|
||||
runGuardsAndResolvers: 'always',
|
||||
},
|
||||
{
|
||||
path: 'dlq',
|
||||
loadChildren: () => import('./tools/dlq-monitor/dlq-monitor.module').then(m => m.DlqMonitorModule),
|
||||
runGuardsAndResolvers: 'always',
|
||||
},
|
||||
{
|
||||
path: 'dealers',
|
||||
loadChildren: () => import('./dealers/dealers.module').then(m => m.DealersModule),
|
||||
runGuardsAndResolvers: 'always',
|
||||
},
|
||||
{
|
||||
path: 'track',
|
||||
loadChildren: () => import('./track/track.module').then(m => m.TrackModule),
|
||||
@ -100,21 +90,6 @@ const routes: Routes = [
|
||||
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule),
|
||||
runGuardsAndResolvers: 'always'
|
||||
},
|
||||
{
|
||||
path: 'api-keys',
|
||||
loadChildren: () => import('./settings/api-keys/api-keys.module').then(m => m.ApiKeysModule),
|
||||
runGuardsAndResolvers: 'always'
|
||||
},
|
||||
{
|
||||
path: 'release-notes',
|
||||
loadChildren: () => import('./release-notes/release-notes.module').then(m => m.ReleaseNotesModule),
|
||||
runGuardsAndResolvers: 'always'
|
||||
},
|
||||
{
|
||||
path: 'changelog',
|
||||
redirectTo: 'release-notes',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -27,16 +27,7 @@
|
||||
|
||||
<app-topbar></app-topbar>
|
||||
|
||||
<!-- Mobile-only left-edge tab to open/close the navigation panel -->
|
||||
<button id="mobile-menu-tab" (click)="onMenuButtonClick($event)" aria-label="Toggle navigation">
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</button>
|
||||
|
||||
<div class="layout-menu" [ngClass]="{'layout-menu-dark':darkMenu}" (click)="onMenuClick($event)">
|
||||
<div class="menu-user-info" *ngIf="user$ | async as user">
|
||||
<app-inline-profile [user]="user" [expiryWarning]="expiryWarning$ | async"
|
||||
(navigateToSubscription)="onNavigateToManageSubscription()"></app-inline-profile>
|
||||
</div>
|
||||
<app-menu></app-menu>
|
||||
</div>
|
||||
|
||||
@ -405,7 +405,7 @@ export class AppMainComponent implements AfterViewInit, OnDestroy, OnInit, After
|
||||
|
||||
get showFooter() {
|
||||
return (
|
||||
!(['/editMap', '/areas', '/track', '/home']
|
||||
!(['/editMap', '/areas', '/track']
|
||||
.filter(it => {
|
||||
const re = new RegExp(it.toLowerCase(), 'i');
|
||||
return this.router.url.toLocaleLowerCase().match(re);
|
||||
@ -12,7 +12,7 @@ import { SubKeys } from './profile/common';
|
||||
selector: 'app-menu',
|
||||
template: `
|
||||
<ul class="ultima-menu ultima-main-menu clearfix">
|
||||
<li app-menuitem *ngFor="let item of model; let i = index;" [item]="item" [index]="i" [root]="true" [class]="item.badgeClass"></li>
|
||||
<li app-menuitem *ngFor="let item of model; let i = index;" [item]="item" [index]="i" [root]="true"></li>
|
||||
</ul>
|
||||
`
|
||||
})
|
||||
@ -39,18 +39,8 @@ export class AppMenuComponent implements OnInit {
|
||||
const mItems: MenuItem[] = [
|
||||
{ id: 'dashboard', label: $localize`:@@dashboard:Dashboard`, icon: 'dashboard', routerLink: ['/home'] },
|
||||
{ id: 'customers', label: $localize`:@@customers:Customers`, icon: 'assignment_ind', routerLink: ['/customers'] },
|
||||
{ id: 'dealers', label: $localize`:@@dealers:Dealers`, icon: 'store', routerLink: ['/dealers'] },
|
||||
{ id: 'partners', label: $localize`:@@partnerMgnt:Partner Management`, icon: 'business', routerLink: ['/partners'] },
|
||||
{ label: $localize`:@@billing:Billing`, icon: 'monetization_on', routerLink: ['/billing'] },
|
||||
{
|
||||
id: 'tools',
|
||||
label: $localize`:@@tools:Tools`, icon: 'extension',
|
||||
routerLink: ['/tools'],
|
||||
items: [
|
||||
{ id: 'api-keys', label: $localize`:@@apiKeys:API Keys`, icon: 'vpn_key', routerLink: ['/api-keys'] },
|
||||
{ id: 'dlq-monitor', label: $localize`:@@dlqMonitor:DLQ Monitor`, icon: 'bug_report', routerLink: ['/dlq'] }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
label: $localize`:@@settings:Settings`, icon: 'settings',
|
||||
@ -59,7 +49,6 @@ export class AppMenuComponent implements OnInit {
|
||||
{ id: 'subscription', label: $localize`:@@promoManagement:Promo Management`, icon: 'credit_card', routerLink: ['/settings/subscription'] }
|
||||
]
|
||||
},
|
||||
{ id: 'release-notes', label: $localize`:@@releaseNotes:Release Notes`, icon: 'history', routerLink: ['/release-notes'] },
|
||||
];
|
||||
this.model = mItems;
|
||||
}
|
||||
@ -76,9 +65,13 @@ export class AppMenuComponent implements OnInit {
|
||||
{
|
||||
id: 'Help',
|
||||
label: $localize`:@@help:Help`, icon: 'help_outline',
|
||||
items: this.buildHelpMenuItems(),
|
||||
badge: '1'
|
||||
} as any
|
||||
items: [{
|
||||
label: $localize`:@@trainingVideos:Training Videos`,
|
||||
icon: 'video_library',
|
||||
url: 'https://www.youtube.com/watch?v=QjGZan5QdAo&list=PLSMll_kIgHA3eamxiSH0Dgl95v60okMcV',
|
||||
target: '_blank'
|
||||
}]
|
||||
}
|
||||
];
|
||||
this.model = mItems;
|
||||
}
|
||||
@ -120,28 +113,14 @@ export class AppMenuComponent implements OnInit {
|
||||
{
|
||||
id: 'Help',
|
||||
label: $localize`:@@help:Help`, icon: 'help_outline',
|
||||
items: this.buildHelpMenuItems(),
|
||||
badge: '1'
|
||||
} as any
|
||||
)
|
||||
}
|
||||
|
||||
private buildHelpMenuItems(): MenuItem[] {
|
||||
return [
|
||||
{
|
||||
id: 'release-notes',
|
||||
label: $localize`:@@releaseNotes:Release Notes`,
|
||||
icon: 'history',
|
||||
routerLink: ['/release-notes'],
|
||||
badge: '1'
|
||||
} as any,
|
||||
{
|
||||
label: $localize`:@@trainingVideos:Training Videos`,
|
||||
icon: 'video_library',
|
||||
url: 'https://www.youtube.com/watch?v=QjGZan5QdAo&list=PLSMll_kIgHA3eamxiSH0Dgl95v60okMcV',
|
||||
target: '_blank'
|
||||
items: [{
|
||||
label: $localize`:@@trainingVideos:Training Videos`,
|
||||
icon: 'video_library',
|
||||
url: 'https://www.youtube.com/watch?v=QjGZan5QdAo&list=PLSMll_kIgHA3eamxiSH0Dgl95v60okMcV',
|
||||
target: '_blank'
|
||||
}]
|
||||
}
|
||||
];
|
||||
)
|
||||
}
|
||||
|
||||
private addOnlyTrackingItems(mItems: MenuItem[]) {
|
||||
@ -230,10 +209,7 @@ export class AppMenuComponent implements OnInit {
|
||||
items: [
|
||||
{ id: 'upload', label: $localize`:@@uploadJobData:Upload Job Data`, icon: 'cloud_upload', routerLink: ['/tools/upload'] },
|
||||
{ id: 'areaLib', label: $localize`:@@manageAreasLib:Manage Areas Library`, icon: 'folder_special', routerLink: ['/tools/areas'] },
|
||||
{ id: 'settings', label: $localize`:@@settings:Settings`, icon: 'settings', routerLink: ['/tools/settings'] },
|
||||
...( this.authSvc.hasRole([RoleIds.APP])
|
||||
? [{ id: 'api-keys', label: $localize`:@@apiKeys:API Keys`, icon: 'vpn_key', routerLink: ['/api-keys'] }]
|
||||
: [] )
|
||||
{ id: 'settings', label: $localize`:@@settings:Settings`, icon: 'settings', routerLink: ['/tools/settings'] }
|
||||
]
|
||||
}
|
||||
);
|
||||
@ -15,22 +15,20 @@ const DUMP_PKEY = '-1';
|
||||
template: `
|
||||
<ng-container>
|
||||
<a [attr.href]="item.url" (click)="itemClick($event)" *ngIf="!item.routerLink || item.items"
|
||||
(mouseenter)="onMouseEnter()" class="ripplelink"
|
||||
[ngClass]="{'active-menuitem-routerlink': selected, 'has-notify': item.badge && !active }"
|
||||
(mouseenter)="onMouseEnter()" class="ripplelink"
|
||||
[ngClass]="{'active-menuitem-routerlink': selected }"
|
||||
[attr.target]="item.target" [attr.tabindex]="0">
|
||||
<i *ngIf="item.icon" class="material-icons">{{item.icon}}</i>
|
||||
<span class="menu-label-wrap">
|
||||
<span>{{item.label}}</span>
|
||||
<span class="menu-notify-counter" *ngIf="item.badge && !active">{{item.badge}}</span>
|
||||
</span>
|
||||
<span>{{item.label}}</span>
|
||||
<span class="menuitem-badge" *ngIf="item.badge">{{item.badge}}</span>
|
||||
<i class="material-icons submenu-icon" *ngIf="item.items">keyboard_arrow_down</i>
|
||||
</a>
|
||||
<a (click)="itemClick($event)" (mouseenter)="onMouseEnter()" *ngIf="item.routerLink && !item.items"
|
||||
[routerLink]="item.routerLink" routerLinkActive="active-menuitem-routerlink" class="ripplelink"
|
||||
<a (click)="itemClick($event)" (mouseenter)="onMouseEnter()" *ngIf="item.routerLink && !item.items"
|
||||
[routerLink]="item.routerLink" routerLinkActive="active-menuitem-routerlink" class="ripplelink"
|
||||
[routerLinkActiveOptions]="{exact: true}" [attr.target]="item.target" [attr.tabindex]="0">
|
||||
<i *ngIf="item.icon" class="material-icons">{{item.icon}}</i>
|
||||
<span>{{item.label}}</span>
|
||||
<span class="menu-notify-counter menu-notify-inline" *ngIf="item.badge">{{item.badge}}</span>
|
||||
<span class="menuitem-badge" *ngIf="item.badge">{{item.badge}}</span>
|
||||
<i class="material-icons submenu-icon" *ngIf="item.items">keyboard_arrow_down</i>
|
||||
</a>
|
||||
<div class="layout-menu-tooltip">
|
||||
@ -16,7 +16,6 @@ import { ConfirmationService, MessageService } from 'primeng/api';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { ScrollPanelModule } from 'primeng/scrollpanel';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { TooltipModule } from 'primeng/tooltip';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppMainComponent } from './app.main.component';
|
||||
@ -25,9 +24,9 @@ import { AppMenuitemComponent } from './app.menuitem.component';
|
||||
import { AppTopbarComponent } from './app.topbar.component';
|
||||
import { AppInlineProfileComponent } from './app.profile.component';
|
||||
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { ReportComponent } from './report.component';
|
||||
import { PageNotFoundComponent } from './page-not-found.component';
|
||||
import { DashboardModule } from './dashboard/dashboard.module';
|
||||
|
||||
import { StoreModule, ActionsSubject } from '@ngrx/store';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
@ -88,8 +87,7 @@ export function translationsFactory(locale: string) {
|
||||
imports: [
|
||||
BrowserModule, BrowserAnimationsModule, HttpClientModule, GlobalModule,
|
||||
InputTextModule, ButtonModule, MenuModule, ProgressSpinnerModule, ScrollPanelModule,
|
||||
MessagesModule, ToastModule, ConfirmDialogModule, DialogModule, DropdownModule, CheckboxModule, TooltipModule, AppSharedModule,
|
||||
DashboardModule,
|
||||
MessagesModule, ToastModule, ConfirmDialogModule, DialogModule, DropdownModule, CheckboxModule, AppSharedModule,
|
||||
// The store that defines our app state
|
||||
StoreModule.forRoot(reducers, {
|
||||
metaReducers,
|
||||
@ -110,6 +108,7 @@ export function translationsFactory(locale: string) {
|
||||
MapBaseComp,
|
||||
MapEditBaseComp,
|
||||
PageNotFoundComponent,
|
||||
DashboardComponent,
|
||||
AppComponent,
|
||||
AppMainComponent,
|
||||
AppMenuComponent,
|
||||
@ -8,18 +8,15 @@
|
||||
|
||||
.account-summary-info .account-username {
|
||||
margin-right: 0.5em;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.account-summary-info .account-type {
|
||||
margin-right: 0.5em;
|
||||
font-style: italic;
|
||||
opacity: 0.85;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.account-summary-info .account-contact {
|
||||
color: #ffd700;
|
||||
opacity: 0.9;
|
||||
text-align:center;
|
||||
}
|
||||
@ -4,7 +4,6 @@ import { Client } from "../models/client.model";
|
||||
export const FETCH = '[CLIENTS] Fetch clients';
|
||||
export class Fetch implements Action {
|
||||
type: typeof FETCH = FETCH;
|
||||
constructor(readonly payload?: { filters?: string; useCache?: boolean }) { }
|
||||
}
|
||||
|
||||
export const FETCH_SUCCESS = '[CLIENTS] Fetch clients success';
|
||||
@ -0,0 +1,6 @@
|
||||
/*
|
||||
To disable deselect or reselect an item on a table row
|
||||
Ref:https://stackoverflow.com/questions/48675497/how-to-disable-the-option-to-deselect-a-row-on-turbotable-component*/
|
||||
tr.ui-state-highlight {
|
||||
pointer-events: none;
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
<div class="ui-g">
|
||||
<div class="ui-g-12">
|
||||
<div class="card">
|
||||
<p-table #dt [value]="clients" [columns]="cols" selectionMode="single" (onRowSelect)="onRowSelect($event)" [paginator]="true" [rows]="15" [pageLinks]="5" [rowsPerPageOptions]="[15,30,50]" [alwaysShowPaginator]="false" [(selection)]="currClient" dataKey="_id" [resetPageOnSort]="false" stateStorage="session" stateKey="cltb-ops" [responsive]="true">
|
||||
<ng-template pTemplate="caption">
|
||||
<span class="table-caption-1" i18n="@@clientList">Client List</span>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="header" let-columns>
|
||||
<tr>
|
||||
<th *ngFor="let col of columns" [pSortableColumn]="col.field" [width]="col.width">{{col.header}}<p-sortIcon [field]="col.field"></p-sortIcon>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th *ngFor="let col of columns" [ngSwitch]="col.filtered" class="ui-fluid">
|
||||
<div class="input-with-icon" *ngSwitchCase="true">
|
||||
<i class="ui-icon-search"></i>
|
||||
<input pInputText type="text" (input)="dt.filter($event.target.value, col.field, col.filterMatchMode)" [value]="dt.filters[col.field]?.value">
|
||||
</div>
|
||||
<span *ngSwitchDefault></span>
|
||||
</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-client let-rowData let-columns>
|
||||
<tr [pSelectableRow]="client">
|
||||
<td *ngFor="let col of cols">
|
||||
<span class="ui-column-title">{{col.header}}</span>
|
||||
{{resolveFieldData(rowData, col.field)}}
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
<div class="ui-widget-header ui-helper-clearfix toolbar">
|
||||
<button type="button" *ngIf="canWrite" pButton icon="ui-icon-plus" (click)="newClient()" i18n-label="@@new" label="New"></button>
|
||||
<button type="button" pButton icon="ui-icon-edit" [disabled]="!canEdit" (click)="editClient()" i18n-label="@@detail" label="Detail"></button>
|
||||
<button type="button" *ngIf="canWrite" [disabled]="!canEdit" pButton icon="ui-icon-trash" (click)="deleteClient()" i18n-label="@@delete" label="Delete"></button>
|
||||
<div class="float-right">
|
||||
<button type="button" [disabled]="!canEdit" pButton icon="ui-icon-view-list" (click)="toJobList()" i18n-label="@@viewJobs" label="View Jobs"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,111 @@
|
||||
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { Table } from 'primeng/table';
|
||||
|
||||
import { Client } from '../models/client.model';
|
||||
import * as fromClients from '../reducers';
|
||||
import * as clientActions from '../actions/client.actions';
|
||||
|
||||
import { RoleIds, globals } from '../../shared/global';
|
||||
import { JobService } from '../../domain/services/job.service';
|
||||
import { Utils } from 'src/app/shared/utils';
|
||||
import { BaseComp } from 'src/app/shared/base/base.component';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'agm-client-list',
|
||||
templateUrl: './client-list.component.html',
|
||||
styleUrls: ['./client-list.component.css']
|
||||
})
|
||||
export class ClientListComponent extends BaseComp implements OnInit, OnDestroy {
|
||||
resolveFieldData = Utils.resolveFieldData;
|
||||
|
||||
clients: Array<Client>;
|
||||
currClient: Client;
|
||||
|
||||
@ViewChild('dt') dt: Table;
|
||||
|
||||
cols: any[];
|
||||
loading$ = this.store.select(fromClients.isLoading);
|
||||
|
||||
get canWrite(): boolean {
|
||||
return this.authSvc.hasRole([RoleIds.APP, RoleIds.APP_ADM, RoleIds.OFFICER]);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly jobService: JobService,
|
||||
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.cols = [
|
||||
{ field: 'name', header: globals.name, filtered: true, filterMatchMode: 'contains' },
|
||||
{ field: 'username', header: globals.userName, filtered: true, filterMatchMode: 'contains' },
|
||||
{ field: 'address', header: globals.address },
|
||||
{ field: 'contact', header: globals.contact, filtered: true },
|
||||
{ field: 'phone', header: globals.phone + ' ' + $localize`:@@Num:N°`, width: '15%', filtered: true },
|
||||
{ field: 'email', header: globals.email, filtered: true, filterMatchMode: 'contains' }
|
||||
];
|
||||
|
||||
// These reference entity services and logic should apply to logged in logic later
|
||||
this.sub$ = this.store.select(fromClients.getAllClients).subscribe(clients => this.clients = clients);
|
||||
|
||||
this.sub$.add(this.store.select(fromClients.getSelectedClient).subscribe((client) => {
|
||||
this.currClient = client;
|
||||
}));
|
||||
|
||||
this.store.dispatch(new clientActions.Fetch());
|
||||
}
|
||||
|
||||
onRowSelect(event) {
|
||||
this.store.dispatch(new clientActions.Select(event.data));
|
||||
}
|
||||
|
||||
get canEdit() {
|
||||
return (this.currClient && this.currClient._id !== '0');
|
||||
}
|
||||
|
||||
newClient() {
|
||||
this.router.navigate(['client', '0'], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
editClient() {
|
||||
this.router.navigate(['client', this.currClient._id], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
deleteClient() {
|
||||
this.jobService.countByClient(this.currClient._id).subscribe((count) => {
|
||||
return this.confirmDelete(count);
|
||||
});
|
||||
}
|
||||
|
||||
private confirmDelete(count: number) {
|
||||
let msg = globals.confirmDeleteThing.replace('#thing#', globals.client);
|
||||
if (count > 0) {
|
||||
let ref = count === 1 ? $localize`:@@relatedJobSingular:There is #jobs# related job` : $localize`:@@relatedJobPlural:There are #jobs# related jobs`;
|
||||
ref = ref.replace('#jobs#', String(count));
|
||||
msg = ref + '. ' + msg;
|
||||
}
|
||||
|
||||
this.confirmSvc.confirm({
|
||||
message: msg,
|
||||
accept: () => {
|
||||
this.store.dispatch(new clientActions.Delete(this.currClient));
|
||||
this.currClient = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toJobList() {
|
||||
this.router.navigate(['/jobs']);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
|
||||
}
|
||||
@ -13,7 +13,6 @@ import { DropdownModule } from 'primeng/dropdown';
|
||||
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { AccordionModule } from 'primeng/accordion';
|
||||
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
@ -29,7 +28,7 @@ import { AppSharedModule } from '../shared/app-shared.module';
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, TableModule, PaginatorModule, DialogModule, ConfirmDialogModule, ToastModule, MessagesModule, InputTextModule,
|
||||
CheckboxModule, ToolbarModule, ButtonModule, DropdownModule, AccordionModule, AppSharedModule,
|
||||
CheckboxModule, ToolbarModule, ButtonModule, DropdownModule, AppSharedModule,
|
||||
StoreModule.forFeature(fromClients.FEATURE_KEY, fromClients.reducer),
|
||||
EffectsModule.forFeature([ClientEffects]),
|
||||
ClientsRoutingModule
|
||||
@ -10,7 +10,6 @@ import { ClientService } from '@app/domain/services/client.service';
|
||||
import { AuthService } from '@app/domain/services/auth.service';
|
||||
import { AppMessageService } from '@app/shared/app-message.service';
|
||||
import { globals } from '@app/shared/global';
|
||||
import { ClientCacheService } from '@app/domain/services/client-cache.service';
|
||||
|
||||
@Injectable()
|
||||
export class ClientEffects {
|
||||
@ -18,20 +17,15 @@ export class ClientEffects {
|
||||
private readonly actions$: Actions,
|
||||
private readonly clientSvc: ClientService,
|
||||
private readonly authSvc: AuthService,
|
||||
private readonly msgSvc: AppMessageService,
|
||||
private readonly clientCache: ClientCacheService
|
||||
private readonly msgSvc: AppMessageService
|
||||
) {
|
||||
}
|
||||
|
||||
@Effect()
|
||||
loadClients$: Observable<Action> = this.actions$.pipe(
|
||||
ofType<clientActions.Fetch>(clientActions.FETCH),
|
||||
switchMap(({ payload }) =>
|
||||
this.clientSvc.loadClients({
|
||||
byPuid: this.authSvc.user.parent,
|
||||
useCache: payload?.useCache,
|
||||
...(payload?.filters ? { filters: payload.filters } : {})
|
||||
}).pipe(
|
||||
switchMap(() =>
|
||||
this.clientSvc.loadClients({ byPuid: this.authSvc.user.parent }).pipe(
|
||||
map(clients => new clientActions.FetchSuccess(clients)),
|
||||
catchError(err => {
|
||||
this.msgSvc.addFailedMsg(globals.doThingsFailed.replace('#do#', globals.load).replace('#thing#', globals.clients));
|
||||
@ -46,10 +40,7 @@ export class ClientEffects {
|
||||
ofType<clientActions.Create>(clientActions.CREATE),
|
||||
switchMap(({ payload }) =>
|
||||
this.clientSvc.saveClient(payload).pipe(
|
||||
map((client) => {
|
||||
this.clientCache.invalidate();
|
||||
return new clientActions.CreateSuccess(client);
|
||||
}),
|
||||
map((client) => new clientActions.CreateSuccess(client)),
|
||||
catchError(err => {
|
||||
this.msgSvc.addFailedMsg(globals.doThingsFailed.replace('#do#', globals.create).replace('#thing#', globals.client));
|
||||
return of(new clientActions.CreateFailed())
|
||||
@ -63,9 +54,7 @@ export class ClientEffects {
|
||||
ofType<clientActions.Update>(clientActions.UPDATE),
|
||||
switchMap(({ payload }) =>
|
||||
this.clientSvc.saveClient(payload).pipe(
|
||||
map(() => {
|
||||
return new clientActions.UpdateSuccess(payload);
|
||||
}),
|
||||
map(() => new clientActions.UpdateSuccess(payload)),
|
||||
catchError(err => {
|
||||
this.msgSvc.addFailedMsg(globals.doThingsFailed.replace('#do#', globals.save).replace('#thing#', globals.client));
|
||||
return of(new clientActions.UpdateFailed());
|
||||
@ -79,10 +68,7 @@ export class ClientEffects {
|
||||
ofType<clientActions.Delete>(clientActions.DELETE),
|
||||
switchMap(({ payload }) =>
|
||||
this.clientSvc.deleteClient(payload).pipe(
|
||||
map(() => {
|
||||
this.clientCache.invalidate();
|
||||
return new clientActions.DeleteSuccess(payload);
|
||||
}),
|
||||
map(() => new clientActions.DeleteSuccess(payload)),
|
||||
catchError(err => {
|
||||
this.msgSvc.addFailedMsg(globals.doThingsFailed.replace('#do#', globals.delete).replace('#thing#', globals.client));
|
||||
return of(new clientActions.UpdateFailed())
|
||||
@ -4,7 +4,6 @@ import { Customer } from "../models/customer.model";
|
||||
export const FETCH = '[CUSTOMERS] Fetch customers';
|
||||
export class Fetch implements Action {
|
||||
type: typeof FETCH = FETCH;
|
||||
constructor(readonly payload?: { filters?: string; useCache?: boolean }) {}
|
||||
}
|
||||
|
||||
export const FETCH_SUCCESS = '[CUSTOMERS] Fetch customers success';
|
||||
@ -56,20 +56,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dealer Selection -->
|
||||
<div class="ui-g-12 ui-md-6 ui-lg-6 form-row">
|
||||
<span class="form-label-span">Dealer:</span>
|
||||
<p-dropdown id="dealer" name="dealer" formControlName="dealer"
|
||||
[options]="dealerOptions"
|
||||
[style]="{'min-width': '200px'}"
|
||||
placeholder="Select Dealer"
|
||||
[loading]="dealerLoading"
|
||||
[filter]="true"
|
||||
filterBy="label"
|
||||
appendTo="body">
|
||||
</p-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="ui-g-12 ui-md-6 ui-lg-6 form-row">
|
||||
<p-checkbox id="billable" name="billable" formControlName="billable" label="Billable"
|
||||
binary="true"></p-checkbox>
|
||||
@ -104,12 +90,6 @@
|
||||
required="true" i18n-title="@@accessAccount" title="Access Account" showActive="true">
|
||||
</agm-account-editor>
|
||||
</div>
|
||||
|
||||
<!-- API Key Manager (existing customers only) -->
|
||||
<div class="ui-g-12" *ngIf="!isNew">
|
||||
<agm-api-key-manager [ownerId]="customer._id" [toggleable]="true" [collapsed]="true"></agm-api-key-manager>
|
||||
</div>
|
||||
|
||||
<div class="ui-g-12 toolbar padtop1 ui-fluid">
|
||||
<button pButton [disabled]="form.invalid || partnerLoading" type="button" style="width:auto"
|
||||
[icon]="isNew ? 'ui-icon-plus' : 'ui-icon-save'" [label]="isNew ? globals.create : globals.save"
|
||||
@ -6,7 +6,6 @@ import { Customer, Partner } from '../models/customer.model';
|
||||
import * as customerActions from '../actions/customer.actions';
|
||||
import { UserService } from '@app/domain/services/user.service';
|
||||
import { PartnerService } from '@app/partners/services/partner.service';
|
||||
import { Dealer, DealerService } from '@app/dealers/dealer.service';
|
||||
import { BaseComp } from '@app/shared/base/base.component';
|
||||
import { GC, RoleIds, globals, Labels } from '@app/shared/global';
|
||||
import { AGNavSubscription, Trial } from '@app/domain/models/subscription.model';
|
||||
@ -41,10 +40,6 @@ export class CustomerEditComponent extends BaseComp implements OnInit {
|
||||
partnerLoading = false;
|
||||
partnerError: string | null = null;
|
||||
|
||||
// Dealer Selection Properties
|
||||
dealerOptions: SelectItem[] = [];
|
||||
dealerLoading = false;
|
||||
|
||||
private _customer: Customer;
|
||||
get customer(): Customer { return this._customer; }
|
||||
set customer(customer: Customer) {
|
||||
@ -56,8 +51,7 @@ export class CustomerEditComponent extends BaseComp implements OnInit {
|
||||
premium: this.selectedItem.premium,
|
||||
billable: this.selectedItem.billable,
|
||||
trials: this.selectedItem.membership?.trials,
|
||||
partner: this.selectedItem.partner || null,
|
||||
dealer: this.selectedItem.dealer || null
|
||||
partner: this.selectedItem.partner || null
|
||||
});
|
||||
|
||||
// Set partner selection based on customer.partner field, or null if not set
|
||||
@ -73,7 +67,6 @@ export class CustomerEditComponent extends BaseComp implements OnInit {
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly userSvc: UserService,
|
||||
private readonly partnerSvc: PartnerService,
|
||||
private readonly dealerSvc: DealerService,
|
||||
private readonly fb: FormBuilder
|
||||
) {
|
||||
super();
|
||||
@ -90,9 +83,7 @@ export class CustomerEditComponent extends BaseComp implements OnInit {
|
||||
billable: [],
|
||||
trials: [],
|
||||
// Partner form control
|
||||
partner: [null],
|
||||
// Dealer form control
|
||||
dealer: [null]
|
||||
partner: [null]
|
||||
});
|
||||
this.lang = this.authSvc.locale;
|
||||
|
||||
@ -115,7 +106,6 @@ export class CustomerEditComponent extends BaseComp implements OnInit {
|
||||
}
|
||||
// Load partners from service
|
||||
this.loadPartners();
|
||||
this.loadDealers();
|
||||
}
|
||||
});
|
||||
|
||||
@ -181,8 +171,7 @@ export class CustomerEditComponent extends BaseComp implements OnInit {
|
||||
custObj = Object.assign(this.selectedItem, this.form.value.profile, this.form.value.account,
|
||||
{ premium: this.form.value.premium || false },
|
||||
{ billable: this.form.value.billable || false },
|
||||
{ partner: this.form.value.partner || null },
|
||||
{ dealer: this.form.value.dealer?._id || this.form.value.dealer || null });
|
||||
{ partner: this.form.value.partner || null });
|
||||
|
||||
this.membership
|
||||
? custObj = Object.assign(custObj, { membership: updateTrialMembship(this.membership) })
|
||||
@ -220,32 +209,6 @@ export class CustomerEditComponent extends BaseComp implements OnInit {
|
||||
return DateUtils.dateToTS(date);
|
||||
}
|
||||
|
||||
// Dealer Methods
|
||||
private loadDealers(): void {
|
||||
this.dealerLoading = true;
|
||||
this.dealerSvc.getAll().subscribe({
|
||||
next: (dealers: Dealer[]) => {
|
||||
this.dealerOptions = [
|
||||
{ label: 'None', value: null },
|
||||
...dealers
|
||||
.sort((a, b) => a.companyName.localeCompare(b.companyName))
|
||||
.map(d => ({
|
||||
label: d.code
|
||||
? (d.country ? `${d.code} - ${d.companyName} (${d.country})` : `${d.code} - ${d.companyName}`)
|
||||
: (d.country ? `${d.companyName} (${d.country})` : d.companyName),
|
||||
value: d
|
||||
}))
|
||||
];
|
||||
if (this.customer?.dealer) {
|
||||
const match = this.dealerOptions.find(o => o.value && o.value._id === (this.customer.dealer as any)?._id);
|
||||
if (match) { this.form.patchValue({ dealer: match.value }); }
|
||||
}
|
||||
this.dealerLoading = false;
|
||||
},
|
||||
error: () => { this.dealerLoading = false; }
|
||||
});
|
||||
}
|
||||
|
||||
// Partner Methods
|
||||
private loadPartners(): void {
|
||||
this.partnerLoading = true;
|
||||
@ -1,25 +1,15 @@
|
||||
<div class="ui-g">
|
||||
<div class="ui-g-12">
|
||||
<div class="card">
|
||||
<p-accordion styleClass="agm-accordion" [style]="{'display':'block', 'margin-bottom':'0.75rem'}">
|
||||
<p-accordionTab i18n-header="@@searchCustomers" header="Search Customers" [transitionOptions]="'250ms'" [selected]="searchAccordionOpen"
|
||||
(selectedChange)="searchAccordionOpen = $event; onAccordionToggle($event)">
|
||||
<agm-dynamic-filter [filterDefinitions]="customerFilterDefinitions" [locale]="locale" stateKey="customers-list-filters" (filtersSubmit)="onFiltersSubmit($event)"></agm-dynamic-filter>
|
||||
</p-accordionTab>
|
||||
</p-accordion>
|
||||
<p-table #dt [value]="customers" [columns]="cols" selectionMode="single" (onRowSelect)="onRowSelect($event)" [paginator]="true" [rows]="15" [pageLinks]="5" [rowsPerPageOptions]="[10, 15, 30]" [alwaysShowPaginator]="true" [(selection)]="curCust" dataKey="_id" [resetPageOnSort]="false" stateStorage="session" stateKey="ctb-ops" [responsive]="true">
|
||||
<ng-template pTemplate="caption">
|
||||
<div class="ui-g ui-g-nopad cache-ttl-caption">
|
||||
<div class="ui-g-6 cc-field-label cache-ttl-caption-title">
|
||||
<span class="table-caption-1" style="display:block; text-align:left;" i18n="@@customerList">Customer List</span>
|
||||
<div class="ui-g ui-g-nopad">
|
||||
<div class="ui-g-6 cc-field-label">
|
||||
<span class="table-caption-1" i18n="@@customerList">Customer List</span>
|
||||
</div>
|
||||
<div class="ui-g-6 cc-field-label cache-ttl-caption-controls">
|
||||
<input pInputText type="number" min="0" step="1" placeholder="Cache TTL" [(ngModel)]="cacheTtlSeconds"
|
||||
(blur)="updateCacheTtl()" style="width: 3.5rem; margin-right: 8px;">
|
||||
<span class="cache-ttl-help" tabindex="0">
|
||||
<span class="cache-ttl-help-icon">?</span>
|
||||
<span class="cache-ttl-help-text">Controls how long results stay cached after you return to this page. Value is in seconds.</span>
|
||||
</span>
|
||||
<div class="ui-g-6 cc-field-label">
|
||||
<label style="margin-right: 8px;">Self Signup Accounts {{ isSelfSignup ? 'On' : 'Off' }}</label>
|
||||
<p-inputSwitch [(ngModel)]="isSelfSignup" (onChange)="onToggle($event)"></p-inputSwitch>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
@ -40,10 +30,6 @@
|
||||
|
||||
<p-dropdown *ngIf="col.field === PARTNER_NAME" [options]="partners" [style]="{'width':'100%'}" [ngModel]="dt.filters[col.field]?.value" (onChange)="dt.filter($event.value, col.field, 'equals')"></p-dropdown>
|
||||
|
||||
<div class="input-with-icon" *ngIf="col.field === 'contact'">
|
||||
<i class="ui-icon-search"></i>
|
||||
<input pInputText type="text" (input)="dt.filter($event.target.value, col.field, 'contains')" [value]="dt.filters[col.field]?.value">
|
||||
</div>
|
||||
<span *ngSwitchDefault></span>
|
||||
</th>
|
||||
</tr>
|
||||
@ -10,9 +10,6 @@ import * as customerActions from '../actions/customer.actions';
|
||||
import { globals, OperationalStatus } from '@app/shared/global';
|
||||
|
||||
import { BaseComp } from '@app/shared/base/base.component';
|
||||
import { CustomerCacheService } from '@app/domain/services/customer-cache.service';
|
||||
import { ListReturnCacheService } from '@app/domain/services/list-return-cache.service';
|
||||
import { FilterDefinition, FilterChangeEvent } from '@app/shared/dynamic-filter/dynamic-filter.component';
|
||||
|
||||
@Component({
|
||||
selector: 'agm-customer-list',
|
||||
@ -35,20 +32,13 @@ export class CustomerListComponent extends BaseComp implements OnInit, OnDestroy
|
||||
partners: SelectItem[];
|
||||
cols: any[];
|
||||
totalItems;
|
||||
|
||||
searchAccordionOpen = sessionStorage.getItem('customers-list-accordion') === 'true';
|
||||
private lastFiltersQuery: Record<string, any> | undefined;
|
||||
private useCacheOnReturn = false;
|
||||
cacheTtlSeconds: number;
|
||||
customerFilterDefinitions: FilterDefinition[];
|
||||
isSelfSignup = false;
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly customerCache: CustomerCacheService,
|
||||
private readonly listReturnCache: ListReturnCacheService,
|
||||
|
||||
) {
|
||||
super();
|
||||
this.cacheTtlSeconds = Math.round(this.customerCache.getTtlMs() / 1000);
|
||||
this.totalItems = { '=0': '', '=1': '1 ' + $localize`:@@customer:customer`.toLocaleLowerCase(), 'other': $localize`:@@total#Customers:Total: # customers` };
|
||||
|
||||
this.statuses = [
|
||||
@ -66,21 +56,12 @@ export class CustomerListComponent extends BaseComp implements OnInit, OnDestroy
|
||||
{ field: this.ACTIVE, header: globals.active, width: '9%' },
|
||||
{ field: this.PARTNER_NAME, header: globals.partner, width: '9%' }
|
||||
];
|
||||
|
||||
this.customerFilterDefinitions = [
|
||||
{ key: 'name', label: globals.name, dataType: 'text' },
|
||||
{ key: 'username', label: globals.userName, dataType: 'text' },
|
||||
{ key: 'email', label: globals.email, dataType: 'text' },
|
||||
{ key: 'contact', label: globals.contact, dataType: 'text' },
|
||||
{ key: 'createdAt', label: globals.from, dataType: 'date-preset' },
|
||||
{ key: 'selfSignup', label: 'Self Signup', dataType: 'select', options: [
|
||||
{ label: 'True', value: true },
|
||||
{ label: 'False', value: false },
|
||||
]},
|
||||
];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const saved = localStorage.getItem('isSelfSignup');
|
||||
this.isSelfSignup = saved === 'true';
|
||||
|
||||
this.sub$ = this.store.select(fromCustomers.getAllCustomers).subscribe(customers => {
|
||||
this.setCustomersAndPartners(customers);
|
||||
});
|
||||
@ -89,23 +70,12 @@ export class CustomerListComponent extends BaseComp implements OnInit, OnDestroy
|
||||
this.curCust = cust;
|
||||
}));
|
||||
|
||||
this.useCacheOnReturn = this.listReturnCache.startVisit('customers');
|
||||
const savedFilters = sessionStorage.getItem('customers-list-last-filters');
|
||||
if (savedFilters) {
|
||||
try {
|
||||
this.lastFiltersQuery = JSON.parse(savedFilters);
|
||||
} catch (_err) {
|
||||
this.lastFiltersQuery = undefined;
|
||||
}
|
||||
}
|
||||
this.store.dispatch(savedFilters
|
||||
? new customerActions.Fetch({ filters: savedFilters, useCache: this.useCacheOnReturn })
|
||||
: new customerActions.Fetch({ useCache: this.useCacheOnReturn })
|
||||
);
|
||||
this.store.dispatch(new customerActions.Fetch());
|
||||
}
|
||||
|
||||
private setCustomersAndPartners(customers: Customer[]) {
|
||||
this.customers = customers.map(c => ({
|
||||
const filtered = this.isSelfSignup ? customers.filter(c => c.selfSignup) : customers;
|
||||
this.customers = filtered.map(c => ({
|
||||
...c,
|
||||
partnerName: c.partner?.name || null
|
||||
}));
|
||||
@ -119,32 +89,18 @@ export class CustomerListComponent extends BaseComp implements OnInit, OnDestroy
|
||||
];
|
||||
}
|
||||
|
||||
onToggle(event: any): void {
|
||||
this.isSelfSignup = event.checked;
|
||||
localStorage.setItem('isSelfSignup', String(this.isSelfSignup));
|
||||
this.store.select(fromCustomers.getAllCustomers).subscribe(customers => {
|
||||
this.setCustomersAndPartners(customers);
|
||||
});
|
||||
}
|
||||
|
||||
onRowSelect(event) {
|
||||
this.store.dispatch(new customerActions.Select(event.data));
|
||||
}
|
||||
|
||||
onAccordionToggle(expanded: boolean) {
|
||||
sessionStorage.setItem('customers-list-accordion', String(expanded));
|
||||
}
|
||||
|
||||
updateCacheTtl(): void {
|
||||
const ttlMs = this.customerCache.setTtlMs(Number(this.cacheTtlSeconds || 0) * 1000);
|
||||
this.cacheTtlSeconds = Math.round(ttlMs / 1000);
|
||||
}
|
||||
|
||||
onFiltersSubmit(event: FilterChangeEvent) {
|
||||
const q = { ...event.query };
|
||||
const filtersStr = JSON.stringify(q);
|
||||
const prevFilters = sessionStorage.getItem('customers-list-last-filters');
|
||||
if (filtersStr !== prevFilters) {
|
||||
this.customerCache.invalidate();
|
||||
this.useCacheOnReturn = false;
|
||||
}
|
||||
this.lastFiltersQuery = q;
|
||||
sessionStorage.setItem('customers-list-last-filters', filtersStr);
|
||||
this.store.dispatch(new customerActions.Fetch({ filters: filtersStr, useCache: this.useCacheOnReturn }));
|
||||
}
|
||||
|
||||
get canEdit() {
|
||||
return (this.curCust && this.curCust._id !== '0');
|
||||
}
|
||||
@ -154,7 +110,6 @@ export class CustomerListComponent extends BaseComp implements OnInit, OnDestroy
|
||||
}
|
||||
|
||||
editCustomer() {
|
||||
this.listReturnCache.markPending('customers');
|
||||
this.router.navigate(['customer', this.curCust._id], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user