Skip to content

Commit 722110a

Browse files
committed
feat: features
1 parent bcb19b3 commit 722110a

File tree

31 files changed

+413
-285
lines changed

31 files changed

+413
-285
lines changed

apps/cloud/src/app/@core/services/feature/feature.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class FeatureService {
1919
constructor(private http: HttpClient) {}
2020

2121
getFeatureToggleDefinition() {
22-
return this.http.get(`${this.API_URL}/definition`).toPromise();
22+
return this.http.get<IFeature[]>(`${this.API_URL}/definition`)
2323
}
2424

2525
getParentFeatures(

apps/cloud/src/app/@shared/feature-toggle/feature-toggle.component.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<mat-slide-toggle class="inline-flex" [checked]="enabledFeature(feature)" (change)="featureChanged($event.checked, feature)"></mat-slide-toggle>
1414
</mat-expansion-panel-header>
1515

16-
@if (feature?.children.length > 0) {
16+
@if (feature.children?.length > 0) {
1717
<mat-list displayDensity="cosy">
1818
@for (child of feature.children; track child.code) {
1919
<mat-list-item>
@@ -30,3 +30,9 @@
3030
</mat-expansion-panel>
3131
}
3232
</mat-accordion>
33+
34+
@if (loading()) {
35+
<div class="absolute left-0 top-0 w-full h-full flex justify-center items-center">
36+
<mat-progress-spinner [mode]="'indeterminate'" [diameter]="22" color="accent" />
37+
</div>
38+
}
Lines changed: 108 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { Component, DestroyRef, OnInit, inject } from '@angular/core'
2-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
1+
import { Component, DestroyRef, effect, inject, signal } from '@angular/core'
2+
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'
33
import { MatDialog } from '@angular/material/dialog'
44
import { ActivatedRoute } from '@angular/router'
5-
import { IFeature, IFeatureOrganization, IFeatureToggle } from '@metad/contracts'
5+
import { IFeature, IFeatureOrganization } from '@metad/contracts'
66
import { NgmCountdownConfirmationComponent } from '@metad/ocap-angular/common'
7-
import { combineLatest, firstValueFrom, of } from 'rxjs'
8-
import { distinctUntilChanged, map, shareReplay, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators'
7+
import { derivedFrom } from 'ngxtension/derived-from'
8+
import { injectRouteData } from 'ngxtension/inject-route-data'
9+
import { of, pipe } from 'rxjs'
10+
import { map, switchMap, tap } from 'rxjs/operators'
911
import { environment } from '../../../environments/environment'
1012
import { FeatureService, FeatureStoreService, Store } from '../../@core/services'
1113
import { TranslationBaseComponent } from '../language/translation-base.component'
@@ -15,53 +17,62 @@ import { TranslationBaseComponent } from '../language/translation-base.component
1517
templateUrl: './feature-toggle.component.html',
1618
styleUrls: ['./feature-toggle.component.scss']
1719
})
18-
export class FeatureToggleComponent extends TranslationBaseComponent implements OnInit {
20+
export class FeatureToggleComponent extends TranslationBaseComponent {
1921
private readonly _activatedRoute = inject(ActivatedRoute)
2022
private readonly _featureService = inject(FeatureService)
2123
private readonly _featureStoreService = inject(FeatureStoreService)
2224
private readonly _storeService = inject(Store)
2325
private readonly _matDialog = inject(MatDialog)
2426
readonly destroyRef = inject(DestroyRef)
2527

26-
loading = false
27-
featureToggles = []
28-
featureTogglesDefinitions: IFeatureToggle[] = []
28+
readonly isOrganization = injectRouteData('isOrganization')
2929

30-
public readonly isOrganization$ = this._activatedRoute.data.pipe(
31-
startWith(this._activatedRoute.snapshot.data),
32-
map((data) => data?.isOrganization),
33-
distinctUntilChanged(),
34-
takeUntilDestroyed(),
35-
shareReplay(1)
36-
)
37-
public readonly organization$ = this._storeService.selectedOrganization$
30+
// loading = false
31+
readonly loading = signal(false)
32+
readonly featureToggles = signal([])
33+
// featureTogglesDefinitions: IFeatureToggle[] = []
34+
35+
// public readonly isOrganization$ = this._activatedRoute.data.pipe(
36+
// startWith(this._activatedRoute.snapshot.data),
37+
// map((data) => data?.isOrganization),
38+
// distinctUntilChanged(),
39+
// takeUntilDestroyed(),
40+
// shareReplay(1)
41+
// )
42+
readonly organization = toSignal(this._storeService.selectedOrganization$)
3843

39-
public readonly features$ = this._featureService.getParentFeatures(['children']).pipe(
44+
readonly features$ = this._featureService.getParentFeatures(['children']).pipe(
4045
map(({ items }) => items),
4146
)
42-
43-
public readonly featureTenant$ = this._storeService.featureTenant$
44-
45-
public readonly featureOrganizations$ = this.isOrganization$.pipe(
46-
switchMap((isOrganization) => (isOrganization ? this.organization$ : of(null))),
47-
switchMap((organization) => {
48-
const request = {}
49-
if (organization) {
50-
request['organizationId'] = organization.id
51-
}
52-
return this._featureStoreService.loadFeatureOrganizations(['feature'], request).pipe(map(({ items }) => items))
53-
}),
54-
takeUntilDestroyed(),
55-
shareReplay(1)
47+
// readonly features$ = this._featureService.getFeatureToggleDefinition()
48+
49+
readonly featureTenant = toSignal(this._storeService.featureTenant$)
50+
51+
readonly featureOrganizations = derivedFrom(
52+
[this.isOrganization],
53+
pipe(
54+
switchMap(([isOrganization]) => (isOrganization ? this._storeService.selectedOrganization$ : of(null))),
55+
switchMap((organization) => {
56+
const request = {}
57+
if (organization) {
58+
request['organizationId'] = organization.id
59+
}
60+
return this._featureStoreService.loadFeatureOrganizations(['feature'], request).pipe(map(({ items }) => items))
61+
})
62+
),
63+
{ initialValue: [] }
5664
)
5765

58-
ngOnInit(): void {
59-
combineLatest([this.featureTenant$, this.featureOrganizations$])
60-
.pipe(
61-
withLatestFrom(combineLatest([this.isOrganization$, this.organization$])),
62-
takeUntilDestroyed(this.destroyRef)
63-
)
64-
.subscribe(([[featureTenant, featureOrganizations], [isOrganization, organization]]) => {
66+
constructor() {
67+
super()
68+
69+
this.loading.set(true)
70+
effect(
71+
() => {
72+
const isOrganization = this.isOrganization()
73+
const organization = this.organization()
74+
const featureTenant = this.featureTenant()
75+
const featureOrganizations = this.featureOrganizations()
6576
if (isOrganization && organization) {
6677
this._storeService.featureOrganizations = featureOrganizations
6778
}
@@ -77,75 +88,92 @@ export class FeatureToggleComponent extends TranslationBaseComponent implements
7788
}
7889
})
7990

80-
this.featureToggles = featureToggles
91+
this.featureToggles.set(featureToggles)
92+
console.log(featureToggles)
8193

82-
this.loading = false
83-
})
84-
85-
this._storeService.featureToggles$
86-
.pipe(
87-
tap((toggles) => (this.featureTogglesDefinitions = toggles)),
88-
takeUntilDestroyed(this.destroyRef)
89-
)
90-
.subscribe()
94+
this.loading.set(false)
95+
},
96+
{ allowSignalWrites: true }
97+
)
9198
}
9299

100+
// ngOnInit(): void {
101+
102+
// combineLatest([this.featureTenant$, this.featureOrganizations$])
103+
// .pipe(
104+
// withLatestFrom(combineLatest([this.isOrganization$, this.organization$])),
105+
// takeUntilDestroyed(this.destroyRef)
106+
// )
107+
// .subscribe(([[featureTenant, featureOrganizations], [isOrganization, organization]]) => {
108+
109+
// })
110+
111+
// this._storeService.featureToggles$
112+
// .pipe(
113+
// tap((toggles) => (this.featureTogglesDefinitions = toggles)),
114+
// takeUntilDestroyed(this.destroyRef)
115+
// )
116+
// .subscribe()
117+
// }
118+
93119
getFeatures() {
94120
this._featureStoreService.loadFeatures(['children']).pipe(takeUntilDestroyed(this.destroyRef)).subscribe()
95121
}
96122

97123
async featureChanged(isEnabled: boolean, feature: IFeature) {
98-
const result = await firstValueFrom(
99-
this._matDialog
100-
.open(NgmCountdownConfirmationComponent, {
101-
data: {
102-
recordType: feature.description,
103-
isEnabled: isEnabled
124+
this._matDialog
125+
.open(NgmCountdownConfirmationComponent, {
126+
data: {
127+
recordType: feature.description,
128+
isEnabled: isEnabled
129+
}
130+
})
131+
.afterClosed()
132+
.pipe(
133+
switchMap((result) => {
134+
if (result) {
135+
return this.emitFeatureToggle({ feature, isEnabled: !!isEnabled })
104136
}
105137
})
106-
.afterClosed()
107-
)
108-
109-
if (result) {
110-
await this.emitFeatureToggle({ feature, isEnabled: !!isEnabled })
111-
} else {
112-
if (!environment.IS_ELECTRON) {
113-
window.location.reload()
114-
}
115-
}
138+
)
139+
.subscribe()
116140
}
117141

118-
async emitFeatureToggle({ feature, isEnabled }: { feature: IFeature; isEnabled: boolean }) {
119-
const isOrganization = await firstValueFrom(this.isOrganization$)
120-
const organization = await firstValueFrom(this.organization$)
142+
emitFeatureToggle({ feature, isEnabled }: { feature: IFeature; isEnabled: boolean }) {
143+
const isOrganization = this.isOrganization()
144+
const organization = this.organization()
121145
const { id: featureId } = feature
122146
const request = {
123147
featureId,
148+
feature,
124149
isEnabled
125150
}
126151
if (organization && isOrganization) {
127152
const { id: organizationId } = organization
128153
request['organizationId'] = organizationId
129154
}
130-
await firstValueFrom(this._featureStoreService.changedFeature(request))
131-
if (!environment.IS_ELECTRON) {
132-
window.location.reload()
133-
}
155+
return this._featureStoreService.changedFeature(request).pipe(
156+
tap(() => {
157+
if (!environment.IS_ELECTRON) {
158+
window.location.reload()
159+
}
160+
})
161+
)
134162
}
135163

136164
enabledFeature(row: IFeature) {
137-
const featureOrganization = this.featureToggles.find(
138-
(featureOrganization: IFeatureOrganization) => featureOrganization.featureId === row.id
165+
const featureOrganization = this.featureToggles().find(
166+
(featureOrganization: IFeatureOrganization) => featureOrganization.feature.code === row.code
139167
)
140-
if (featureOrganization && featureOrganization.isEnabled === false) {
168+
if (featureOrganization) {
141169
return featureOrganization.isEnabled
142170
}
143171

144-
const featureToggle = this.featureTogglesDefinitions.find((item: IFeatureToggle) => item.name == row.code)
145-
if (featureToggle) {
146-
return featureToggle.enabled
147-
}
172+
// const featureToggle = this.featureTogglesDefinitions.find((item: IFeatureToggle) => item.code == row.code)
173+
// if (featureToggle) {
174+
// return featureToggle.enabled
175+
// }
148176

149-
return true
177+
return false
150178
}
151179
}

apps/cloud/src/app/features/chat/chat-input/chat-input.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CommonModule } from '@angular/common'
2-
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core'
2+
import { ChangeDetectionStrategy, Component, effect, inject, signal } from '@angular/core'
33
import { toSignal } from '@angular/core/rxjs-interop'
44
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'
55
import { RouterModule } from '@angular/router'
@@ -34,6 +34,12 @@ export class ChatInputComponent {
3434
readonly prompt = toSignal(this.promptControl.valueChanges)
3535
readonly answering = this.chatService.answering
3636

37+
constructor() {
38+
effect(() => {
39+
this.answering() ? this.promptControl.disable() : this.promptControl.enable()
40+
})
41+
}
42+
3743
send() {
3844
this.ask(this.prompt().trim())
3945
}

apps/cloud/src/app/features/chatbi/copilot/schema.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
DataSettingsSchema,
66
OrderBySchema,
77
SlicerSchema,
8-
TimeSlicerSchema
8+
TimeSlicerSchema,
9+
VariableSchema
910
} from '@metad/ocap-core'
1011
import { EChartsOptions } from '@metad/story/story'
1112
import { z } from 'zod'
@@ -31,6 +32,6 @@ export const ChatAnswerSchema = z.object({
3132
top: z.number().optional().describe('The number of top members'),
3233
slicers: z.array(SlicerSchema).optional().describe('The slicers to filter data'),
3334
timeSlicers: z.array(TimeSlicerSchema).optional().describe('The time slicers to filter data'),
34-
variables: z.array(SlicerSchema).optional().describe('The variables to the query of cube'),
35+
variables: z.array(VariableSchema).optional().describe('The variables to the query of cube'),
3536
questions: z.array(z.string().describe('More suggestion prompts, 3 will be enough.')).describe(`Give user more question prompts about how to drilldown other dimensions or one of dimension members, for examples: '分析<某维度1>成员<xxx>在<某维度2>上的<度量>分布'`)
3637
})

0 commit comments

Comments
 (0)