Skip to content

Commit bcf116b

Browse files
committed
Added datasets count, removed navigation observable, fixed types
1 parent 2e99fb7 commit bcf116b

File tree

18 files changed

+240
-270
lines changed

18 files changed

+240
-270
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { HttpErrorResponse } from '@angular/common/http'
2+
import { HttpClient } from '@angular/common/http'
3+
import { inject, Injectable } from '@angular/core'
4+
import type { Observable } from 'rxjs'
5+
import { catchError, map, of, ReplaySubject, tap } from 'rxjs'
6+
import type { CountDatasetsResponse, ListDatasetsResponse } from './dataset.types'
7+
8+
@Injectable({ providedIn: 'root' })
9+
export class DatasetService {
10+
private _httpClient = inject(HttpClient)
11+
12+
private _datasetCount: ReplaySubject<number> = new ReplaySubject<number>(1)
13+
datasetCount$ = this._datasetCount.asObservable()
14+
15+
// TODO watch active org, on change request dataset count
16+
17+
listDatasets(): Observable<ListDatasetsResponse> {
18+
return this._httpClient.get<ListDatasetsResponse>('/api/v3/datasets/').pipe(tap(({ datasets }) => datasets))
19+
}
20+
21+
countDatasets(): Observable<number> {
22+
// TODO input dynamic org id
23+
return this._httpClient.get<CountDatasetsResponse>('/api/v3/datasets/count/?organization_id=1').pipe(
24+
map(({ datasets_count }) => {
25+
// TODO once this takes an organization_id, only update `this._datasetCount` IFF it's the active org
26+
this._datasetCount.next(datasets_count)
27+
return datasets_count
28+
}),
29+
catchError((error: HttpErrorResponse) => {
30+
// TODO toast or alert? also, better fallback value
31+
console.error('Error occurred while counting datasets:', error.error)
32+
return of(-1)
33+
}),
34+
)
35+
}
36+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Subset type
2+
type ImportFile = {
3+
created: string;
4+
modified: string;
5+
deleted: boolean;
6+
import_record: number;
7+
cycle: number;
8+
file: string;
9+
uploaded_filename: string;
10+
cached_first_row: string;
11+
id: number;
12+
}
13+
14+
// Subset type
15+
type Dataset = {
16+
deleted: boolean;
17+
name: string;
18+
app: 'seed';
19+
owner: number;
20+
access_level_instance: number;
21+
start_time: string;
22+
finish_time: string;
23+
created_at: string;
24+
updated_at: number;
25+
last_modified_by: string;
26+
matching_done: boolean;
27+
super_organization: number;
28+
id: number;
29+
model: 'data_importer.importrecord';
30+
importfiles: ImportFile[];
31+
}
32+
33+
export type ListDatasetsResponse = {
34+
status: 'success';
35+
datasets: Dataset[];
36+
}
37+
38+
export type CountDatasetsResponse = {
39+
status: 'success';
40+
datasets_count: number;
41+
}

src/@seed/api/dataset/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './dataset.service'
2+
export * from './dataset.types'

src/@seed/components/navigation/navigation.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import type { NavigationItem } from '@seed/components'
33

44
@Injectable({ providedIn: 'root' })
55
export class SeedNavigationService {
6-
private _componentRegistry: Map<string, any> = new Map<string, any>()
7-
private _navigationStore: Map<string, NavigationItem[]> = new Map<string, any>()
6+
private _componentRegistry = new Map<string, unknown>()
7+
private _navigationStore = new Map<string, NavigationItem[]>()
88

99
/**
1010
* Register navigation component
1111
*/
12-
registerComponent(name: string, component: any): void {
12+
registerComponent(name: string, component: unknown): void {
1313
this._componentRegistry.set(name, component)
1414
}
1515

@@ -24,7 +24,7 @@ export class SeedNavigationService {
2424
* Get navigation component from the registry
2525
*/
2626
getComponent<T>(name: string): T {
27-
return this._componentRegistry.get(name)
27+
return this._componentRegistry.get(name) as T
2828
}
2929

3030
/**

src/@seed/components/navigation/vertical/vertical.component.ts

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,12 @@ export class VerticalNavigationComponent implements OnChanges, OnInit, AfterView
9494
onRefreshed: ReplaySubject<boolean> = new ReplaySubject<boolean>(1)
9595
private _animationsEnabled = false
9696
private _asideOverlay: HTMLElement
97-
private readonly _handleAsideOverlayClick = () => { this.closeAside() }
98-
private readonly _handleOverlayClick = () => { this.close() }
97+
private readonly _handleAsideOverlayClick = () => {
98+
this.closeAside()
99+
}
100+
private readonly _handleOverlayClick = () => {
101+
this.close()
102+
}
99103
private _hovered = false
100104
private _mutationObserver: MutationObserver
101105
private _overlay: HTMLElement
@@ -134,8 +138,7 @@ export class VerticalNavigationComponent implements OnChanges, OnInit, AfterView
134138
/**
135139
* Setter for seedScrollbarDirectives
136140
*/
137-
@ViewChildren(ScrollbarDirective)
138-
set seedScrollbarDirectives(seedScrollbarDirectives: QueryList<ScrollbarDirective>) {
141+
@ViewChildren(ScrollbarDirective) set seedScrollbarDirectives(seedScrollbarDirectives: QueryList<ScrollbarDirective>) {
139142
// Store the directives
140143
this._scrollbarDirectives = seedScrollbarDirectives
141144

@@ -159,31 +162,17 @@ export class VerticalNavigationComponent implements OnChanges, OnInit, AfterView
159162
})
160163
}
161164

162-
/**
163-
* On mouseenter
164-
*
165-
* @private
166-
*/
167-
@HostListener('mouseenter')
168-
private _onMouseenter(): void {
169-
// Enable the animations
165+
@HostListener('mouseenter') private _onMouseenter(): void {
170166
this._enableAnimations()
171167

172-
// Set the hovered
168+
// Set hovered state
173169
this._hovered = true
174170
}
175171

176-
/**
177-
* On mouseleave
178-
*
179-
* @private
180-
*/
181-
@HostListener('mouseleave')
182-
private _onMouseleave(): void {
183-
// Enable the animations
172+
@HostListener('mouseleave') private _onMouseleave(): void {
184173
this._enableAnimations()
185174

186-
// Set the hovered
175+
// Set hovered state
187176
this._hovered = false
188177
}
189178

@@ -304,7 +293,7 @@ export class VerticalNavigationComponent implements OnChanges, OnInit, AfterView
304293
// This fixes the problem by reading the 'top' value from the html element and adding it as a
305294
// 'marginTop' to the navigation itself.
306295
this._mutationObserver = new MutationObserver((mutations) => {
307-
mutations.forEach((mutation) => {
296+
for (const mutation of mutations) {
308297
const mutationTarget = mutation.target as HTMLElement
309298
if (mutation.attributeName === 'class') {
310299
if (mutationTarget.classList.contains('cdk-global-scrollblock')) {
@@ -314,7 +303,7 @@ export class VerticalNavigationComponent implements OnChanges, OnInit, AfterView
314303
this._renderer2.setStyle(this._elementRef.nativeElement, 'margin-top', null)
315304
}
316305
}
317-
})
306+
}
318307
})
319308
this._mutationObserver.observe(this._document.documentElement, {
320309
attributes: true,
@@ -339,15 +328,15 @@ export class VerticalNavigationComponent implements OnChanges, OnInit, AfterView
339328
}
340329
} else {
341330
// Go through all the scrollbar directives
342-
this._scrollbarDirectives.forEach((seedScrollbarDirective) => {
331+
for (const seedScrollbarDirective of this._scrollbarDirectives) {
343332
// Skip if not enabled
344333
if (!seedScrollbarDirective.isEnabled()) {
345334
return
346335
}
347336

348337
// Scroll to the active element
349338
seedScrollbarDirective.scrollToElement('.seed-vertical-navigation-item-active', -120, true)
350-
})
339+
}
351340
}
352341
})
353342
}
@@ -468,33 +457,11 @@ export class VerticalNavigationComponent implements OnChanges, OnInit, AfterView
468457
}
469458
}
470459

471-
/**
472-
* Enable the animations
473-
*
474-
* @private
475-
*/
476460
private _enableAnimations(): void {
477-
// Return if the animations are already enabled
478-
if (this._animationsEnabled) {
479-
return
480-
}
481-
482-
// Enable the animations
483461
this._animationsEnabled = true
484462
}
485463

486-
/**
487-
* Disable the animations
488-
*
489-
* @private
490-
*/
491464
private _disableAnimations(): void {
492-
// Return if the animations are already disabled
493-
if (!this._animationsEnabled) {
494-
return
495-
}
496-
497-
// Disable the animations
498465
this._animationsEnabled = false
499466
}
500467

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const screens: Record<string, string> = {
2+
sm: '600px',
3+
md: '960px',
4+
lg: '1280px',
5+
xl: '1440px',
6+
}
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Types
22
export type Scheme = 'auto' | 'dark' | 'light'
3-
export type Screens = Record<string, string>
43
export type Themes = { id: string; name: string }[]
54

65
/**
@@ -10,7 +9,6 @@ export type Themes = { id: string; name: string }[]
109
export type SEEDConfig = {
1110
layout: string;
1211
scheme: Scheme;
13-
screens: Screens;
1412
theme: 'theme-default';
1513
themes: Themes;
1614
}
Lines changed: 29 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,49 @@
1-
import type { BreakpointState } from '@angular/cdk/layout'
21
import { BreakpointObserver } from '@angular/cdk/layout'
32
import { inject, Injectable } from '@angular/core'
4-
import type { Observable } from 'rxjs'
5-
import { map, ReplaySubject, switchMap } from 'rxjs'
6-
import { ConfigService } from '../config'
3+
import { map, ReplaySubject } from 'rxjs'
4+
import { screens } from '../config/config.screens'
75

86
@Injectable({ providedIn: 'root' })
97
export class MediaWatcherService {
108
private _breakpointObserver = inject(BreakpointObserver)
11-
private _configService = inject(ConfigService)
12-
13-
private _onMediaChange: ReplaySubject<{
14-
matchingAliases: string[];
15-
matchingQueries: any;
16-
}> = new ReplaySubject<{ matchingAliases: string[]; matchingQueries: any }>(1)
9+
private _onMediaChange = new ReplaySubject<{ matchingAliases: string[]; matchingQueries: Record<string, string> }>(1)
1710

1811
constructor() {
19-
this._configService.config$
20-
.pipe(
21-
map((config) => Object.fromEntries(Object.entries(config.screens).map(([alias, screen]) => [alias, `(min-width: ${screen})`]))),
22-
switchMap((screens) =>
23-
this._breakpointObserver.observe(Object.values(screens)).pipe(
24-
map((state) => {
25-
// Prepare the observable values and set their defaults
26-
const matchingAliases: string[] = []
27-
const matchingQueries: any = {}
12+
const mediaQueries = Object.fromEntries(Object.entries(screens).map(([alias, minWidth]) => [alias, `(min-width: ${minWidth})`]))
2813

29-
// Get the matching breakpoints and use them to fill the subject
30-
const matchingBreakpoints = Object.entries(state.breakpoints).filter(([/* query */, matches]) => matches) ?? []
31-
for (const [query] of matchingBreakpoints) {
32-
// Find the alias of the matching query
33-
const matchingAlias = Object.entries(screens).find(([/* alias */, q]) => q === query)[0]
14+
const aliasMap = Object.entries(mediaQueries).reduce<Record<string, string>>((acc, [alias, query]) => ({ ...acc, [query]: alias }), {})
3415

35-
// Add the matching query to the observable values
36-
if (matchingAlias) {
37-
matchingAliases.push(matchingAlias)
38-
matchingQueries[matchingAlias] = query
39-
}
40-
}
41-
42-
// Execute the observable
43-
this._onMediaChange.next({
44-
matchingAliases,
45-
matchingQueries,
46-
})
47-
}),
48-
),
49-
),
16+
this._breakpointObserver
17+
.observe(Object.values(mediaQueries))
18+
.pipe(
19+
map(({ breakpoints }) => {
20+
// Prepare the observable values and set their defaults
21+
const matchingAliases: string[] = []
22+
const matchingQueries: Record<string, string> = {}
23+
24+
for (const [query] of Object.entries(breakpoints).filter(([, isMatch]) => isMatch)) {
25+
// Find the alias of the matching query
26+
const alias = aliasMap[query]
27+
28+
matchingAliases.push(alias)
29+
matchingQueries[alias] = query
30+
}
31+
32+
// Execute the observable
33+
this._onMediaChange.next({
34+
matchingAliases,
35+
matchingQueries,
36+
})
37+
}),
5038
)
5139
.subscribe()
5240
}
5341

54-
get onMediaChange$(): Observable<{
55-
matchingAliases: string[];
56-
matchingQueries: any;
57-
}> {
42+
get onMediaChange$() {
5843
return this._onMediaChange.asObservable()
5944
}
6045

61-
onMediaQueryChange$(query: string | string[]): Observable<BreakpointState> {
46+
onMediaQueryChange$(query: string | string[]) {
6247
return this._breakpointObserver.observe(query)
6348
}
6449
}

src/app/app.config.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,6 @@ export const appConfig: ApplicationConfig = {
9595
seed: {
9696
layout: 'main',
9797
scheme: 'light',
98-
screens: {
99-
sm: '600px',
100-
md: '960px',
101-
lg: '1280px',
102-
xl: '1440px',
103-
},
10498
theme: 'theme-default',
10599
themes: [
106100
{

src/app/app.resolvers.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
import { inject } from '@angular/core'
22
import { forkJoin } from 'rxjs'
3-
// import { DatasetService } from '@seed/api/dataset'
43
import { VersionService } from '@seed/api/version/version.service'
5-
import { NavigationService } from 'app/core/navigation/navigation.service'
64

75
export const initialDataResolver = () => {
8-
// const datasetService = inject(DatasetService)
9-
const navigationService = inject(NavigationService)
106
const versionService = inject(VersionService)
117

12-
// Fork join multiple API endpoint calls to wait all of them to finish
13-
return forkJoin([
14-
versionService.get(),
15-
// datasetService.countDatasets(),
16-
navigationService.get(),
17-
])
8+
// Fork join multiple API endpoint calls to wait on all of them to finish
9+
return forkJoin([versionService.get()])
1810
}

0 commit comments

Comments
 (0)