Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Commit 8e62a17

Browse files
committed
Merge branch 'develop' into main
2 parents c43eb30 + e767a73 commit 8e62a17

File tree

58 files changed

+2296
-985
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2296
-985
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
10.24.0
1+
lts/fermium
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Injectable } from '@angular/core'
2+
import { CloudAppRestService } from '@exlibris/exl-cloudapp-angular-lib'
3+
import LRU from 'lru-cache'
4+
5+
6+
export interface Location {
7+
code: string
8+
name: string
9+
}
10+
11+
12+
/*
13+
14+
### A note on the caches in AlmaConfigurationService
15+
16+
The caches store promises and not the results. This ensures we only make one request to the Alma API.
17+
The first call creates a promise and sets up the request. Subsequent calls get the same promise.
18+
When the request set up in the first call resolves, the promise resolves for all the callers.
19+
Any subsequent calls return the same promise, which is already resolved.
20+
21+
*/
22+
23+
24+
@Injectable({
25+
providedIn: 'root',
26+
})
27+
export class AlmaConfigurationService {
28+
29+
private _locationCache = new LRU<string, Promise<Location>>(100)
30+
private _locationsForLibraryCache = new LRU<string, Promise<Location[]>>(5)
31+
32+
33+
constructor(
34+
private restService: CloudAppRestService,
35+
) { }
36+
37+
38+
location(libraryCode: string, locationCode: string): Promise<Location | undefined> {
39+
let cacheKey = `${ libraryCode }${ locationCode }`
40+
let locationPromise = this._locationCache.get(cacheKey)
41+
if (!locationPromise) {
42+
let locationsForLibraryPromise = this.locationsForLibrary(libraryCode)
43+
locationPromise = locationsForLibraryPromise.then(locations =>
44+
locations.find(l => l.code == locationCode)
45+
)
46+
this._locationCache.set(cacheKey, locationPromise)
47+
}
48+
return locationPromise
49+
}
50+
51+
52+
locationsForLibrary(libraryCode: string): Promise<Location[]> {
53+
let locationsForLibraryPromise = this._locationsForLibraryCache.get(libraryCode)
54+
if (!locationsForLibraryPromise) {
55+
let respPromise = this.restService.call(`/almaws/v1/conf/libraries/${libraryCode}/locations`).toPromise()
56+
locationsForLibraryPromise = respPromise.then(resp => resp.location)
57+
this._locationsForLibraryCache.set(libraryCode, locationsForLibraryPromise)
58+
}
59+
return locationsForLibraryPromise
60+
}
61+
62+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Injectable } from '@angular/core'
2+
import { AppService } from './app.service'
3+
import { PrintSlipReportWindowService } from './print-slip-report/window.service'
4+
import { RequestedResourcesService } from './requested-resources'
5+
6+
7+
8+
export const WINDOW_KEY = 'appModuleServicesService'
9+
10+
11+
@Injectable({
12+
providedIn: 'root'
13+
})
14+
export class AppModuleServicesService {
15+
16+
static getFromWindow(wnd): AppModuleServicesService {
17+
let s = wnd[WINDOW_KEY]
18+
if (!s) {
19+
throw Error(`No ${ WINDOW_KEY } defined on window`)
20+
}
21+
return s
22+
}
23+
24+
25+
constructor(
26+
public readonly appService: AppService,
27+
public readonly printSlipReportWindowService: PrintSlipReportWindowService,
28+
public readonly requestedResourcesService: RequestedResourcesService,
29+
) { }
30+
31+
32+
setInWindow(wnd) {
33+
Object.defineProperty(wnd, WINDOW_KEY, { enumerable: true, value: this })
34+
}
35+
36+
}
Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
import { NgModule } from '@angular/core'
22
import { RouterModule, Routes } from '@angular/router'
3-
import { ConfigComponent } from './config/config.component'
43
import { MainComponent } from './main/main.component'
5-
import { PrintSlipReportComponent } from './print-slip-report/print-slip-report.component'
64

75

86

97
const routes: Routes = [
10-
{ path: '', component: MainComponent },
11-
{ path: 'config', component: ConfigComponent },
12-
{ path: 'print-slip-report', component: PrintSlipReportComponent }
8+
{
9+
path: '',
10+
component: MainComponent
11+
},
12+
{
13+
path: 'config',
14+
loadChildren: () => import('./config/config.module').then(m => m.PrintSlipReportConfigModule),
15+
},
16+
{
17+
path: 'download-excel-slip-report',
18+
loadChildren: () => import('./download-excel-slip-report/download-excel-slip-report.module').then(m => m.DownloadExcelSlipReportModule)
19+
},
20+
{
21+
path: 'print-slip-report',
22+
loadChildren: () => import('./print-slip-report/print-slip-report.module').then(m => m.PrintSlipReportModule)
23+
},
1324
]
1425

1526

1627
@NgModule({
17-
imports: [RouterModule.forRoot(routes, { useHash: true })],
18-
exports: [RouterModule],
28+
imports: [ RouterModule.forRoot(routes, { useHash: true }) ],
29+
exports: [ RouterModule ],
1930
})
2031
export class AppRoutingModule { }

cloudapp/src/app/app.component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component } from '@angular/core'
2-
import { AppService } from './app.service'
2+
import { InitService } from '@exlibris/exl-cloudapp-angular-lib'
33

44

55

@@ -9,6 +9,9 @@ import { AppService } from './app.service'
99
})
1010
export class AppComponent {
1111

12-
constructor(private appService: AppService) { }
12+
// noinspection JSUnusedLocalSymbols
13+
constructor(
14+
private initService: InitService
15+
) { }
1316

1417
}

cloudapp/src/app/app.module.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,15 @@ import { MaterialModule, getTranslateModule, AlertModule } from '@exlibris/exl-c
88

99
import { AppComponent } from './app.component';
1010
import { AppRoutingModule } from './app-routing.module';
11-
import { CircDeskCodeDefaultsComponent } from './config/circ-desk-code-defaults.component'
12-
import { ColumnOptionComponent } from './column-options/column-option.component';
13-
import { ColumnOptionsListComponent } from './column-options/column-options-list.component';
14-
import { ConfigComponent } from './config/config.component';
11+
import { ColumnOptionsModule } from './column-options'
1512
import { MainComponent } from './main/main.component';
16-
import { PrintSlipReportComponent } from './print-slip-report/print-slip-report.component';
1713

1814

1915

2016
@NgModule({
2117
declarations: [
2218
AppComponent,
23-
CircDeskCodeDefaultsComponent,
24-
ColumnOptionComponent,
25-
ColumnOptionsListComponent,
26-
ConfigComponent,
2719
MainComponent,
28-
PrintSlipReportComponent,
2920
],
3021
imports: [
3122
MaterialModule,
@@ -37,6 +28,7 @@ import { PrintSlipReportComponent } from './print-slip-report/print-slip-report.
3728
FormsModule,
3829
ReactiveFormsModule,
3930
getTranslateModule(),
31+
ColumnOptionsModule,
4032
],
4133
providers: [
4234
{ provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'standard' } },

cloudapp/src/app/app.service.ts

Lines changed: 178 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,190 @@
11
import { Injectable } from '@angular/core'
2-
import { InitService } from '@exlibris/exl-cloudapp-angular-lib'
2+
import { CloudAppEventsService, CloudAppStoreService, InitData } from '@exlibris/exl-cloudapp-angular-lib'
3+
import * as _ from 'lodash'
4+
import { last } from 'rxjs/operators'
5+
import { COLUMNS_DEFINITIONS } from './requested-resources/column-definitions' // Direct to avoid circular dependency
6+
import { ColumnOption } from './column-options'
7+
import { ConfigService } from './config/config.service'
8+
import { SlipReportError } from './slip-report'
39

410

511

12+
const FALLBACK_DEFAULT_CIRC_DESK_CODE = 'DEFAULT_CIRC_DESK'
13+
const STORAGE_KEY = 'last-used-options'
14+
15+
616
@Injectable({
717
providedIn: 'root',
818
})
919
export class AppService {
1020

21+
initData?: InitData
22+
private _libraryCode?: string
23+
private _circDeskCode?: string
24+
columnOptions: ColumnOption[] | undefined = undefined
25+
lastSlipReportError: SlipReportError | undefined = undefined
26+
27+
1128
constructor(
12-
private initService: InitService
13-
) {}
29+
private configService: ConfigService,
30+
private eventsService: CloudAppEventsService,
31+
private storeService: CloudAppStoreService,
32+
) {
33+
this.eventsService.getInitData().subscribe(initData => { this.initData = initData })
34+
}
35+
36+
37+
get circDeskCode(): string {
38+
return this._circDeskCode ?? FALLBACK_DEFAULT_CIRC_DESK_CODE
39+
}
40+
41+
42+
set circDeskCode(c) {
43+
this._circDeskCode = c.trim()
44+
}
45+
46+
47+
get columnOptionsAreCustomised(): boolean {
48+
let selected = this.columnOptions.filter(_ => _.include)
49+
let selectedByDefault = this.defaultColumnOptions.filter(_ => _.include)
50+
let r = (
51+
selected.length !== selectedByDefault.length
52+
|| ! _.every(
53+
_.zip(selected, selectedByDefault),
54+
([a, b]) => {
55+
let r2 = (
56+
a.code === b.code
57+
&& a.hiddenInApp === b.hiddenInApp
58+
&& a.limit === b.limit
59+
)
60+
console.log('columnOptionsAreCustomised inner result', r2)
61+
return r2
62+
}
63+
)
64+
)
65+
console.log('columnOptionsAreCustomised result', r)
66+
return r
67+
}
68+
69+
70+
get defaultCircDeskCode(): string {
71+
let libConfig = this.configService.config?.libraryConfigs?.filter(x => x.libraryCode == this.libraryCode)
72+
return libConfig?.[0]?.defaultCircDeskCode ?? FALLBACK_DEFAULT_CIRC_DESK_CODE
73+
}
74+
75+
76+
get defaultColumnOptions(): ColumnOption[] {
77+
let missingColumnDefinitions = new Map(COLUMNS_DEFINITIONS) // Copy because we are going to mutate it
78+
return [
79+
// Start with the columns in the order they are from the app configuration,
80+
...(
81+
(this.configService.config?.columnDefaults ?? [])
82+
// ... minus any that aren't defined anymore
83+
.filter(c => missingColumnDefinitions.has(c.code))
84+
.map(c => {
85+
let name = missingColumnDefinitions.get(c.code).name
86+
missingColumnDefinitions.delete(c.code)
87+
return { include: false, limit: 0, hiddenInApp: false, ...c, name }
88+
})
89+
),
90+
// Add any columns not in the app configuration, in the order they appear in the column definitions
91+
...(
92+
Array.from(missingColumnDefinitions.values())
93+
.map(c => ({ code: c.code, name: c.name, include: false, limit: 0, hiddenInApp: false }))
94+
)
95+
]
96+
}
97+
98+
99+
get includedColumnOptions() {
100+
return this.columnOptions.filter(c => c.include)
101+
}
102+
103+
104+
get libraryCode(): string {
105+
return this._libraryCode
106+
}
107+
108+
109+
set libraryCode(c) {
110+
this._libraryCode = c.trim()
111+
}
112+
113+
114+
popLastSlipReportError(): SlipReportError | undefined {
115+
let e = this.lastSlipReportError
116+
this.lastSlipReportError = undefined
117+
return e
118+
}
119+
120+
121+
async resetColumnsToDefaults() {
122+
await this.configService.load()
123+
this.columnOptions = this.defaultColumnOptions
124+
}
125+
126+
127+
async loadLastUsed() {
128+
await this.configService.load()
129+
let lastUsedOptions = await this.storeService.get(STORAGE_KEY).toPromise()
130+
131+
this.libraryCode = (
132+
this.initData?.user?.currentlyAtLibCode
133+
? this.initData.user.currentlyAtLibCode
134+
: (lastUsedOptions?.libraryCode ?? '')
135+
)
136+
137+
this.circDeskCode = lastUsedOptions?.circDeskCode ?? this.defaultCircDeskCode
138+
139+
if (!lastUsedOptions?.columnOptions?.length) {
140+
// If there are no last used options saved, reset them
141+
return await this.resetColumnsToDefaults()
142+
}
143+
144+
if (lastUsedOptions?.columnOptions) {
145+
let missingColumnDefinitions = new Map(COLUMNS_DEFINITIONS) // Copy because we are going to mutate it
146+
this.columnOptions = [
147+
// Start with the columns in the order they are from the last used options,
148+
...(
149+
(lastUsedOptions?.columnOptions ?? [])
150+
// ... minus any that aren't defined anymore
151+
.filter(c => missingColumnDefinitions.has(c.code))
152+
.map(c => {
153+
let name = missingColumnDefinitions.get(c.code).name
154+
missingColumnDefinitions.delete(c.code)
155+
return { include: false, limit: 0, hiddenInApp: false, ...c, name }
156+
})
157+
),
158+
// Add any columns not in the app configuration, in the order they appear in the column definitions
159+
...(
160+
Array.from(missingColumnDefinitions.values())
161+
.map(c => ({ code: c.code, name: c.name, include: false, limit: 0, hiddenInApp: false }))
162+
)
163+
]
164+
} else {
165+
this.columnOptions = this.defaultColumnOptions
166+
}
167+
}
168+
169+
170+
async saveLastUsed() {
171+
let lastUsedOptions = {
172+
libraryCode: this.libraryCode,
173+
circDeskCode: this.circDeskCode,
174+
columnOptions: (
175+
this.columnOptionsAreCustomised
176+
? this.columnOptions.map(c => ({
177+
code: c.code, include: c.include, limit: c.limit, hiddenInApp: c.hiddenInApp
178+
}))
179+
: undefined
180+
)
181+
}
182+
if (lastUsedOptions.columnOptions) {
183+
console.log('Saving customised column options')
184+
} else {
185+
console.log('Saving default column options')
186+
}
187+
return this.storeService.set(STORAGE_KEY, lastUsedOptions).toPromise()
188+
}
14189

15190
}

0 commit comments

Comments
 (0)