diff --git a/.deploy/webapp/package.json b/.deploy/webapp/package.json index d9085058e..74982c92c 100644 --- a/.deploy/webapp/package.json +++ b/.deploy/webapp/package.json @@ -67,6 +67,7 @@ "immer": "^10.0.1", "js-yaml": "^4.1.0", "json5": "^2.2.3", + "langchain": "^0.0.152", "lato-font": "^3.0.0", "lodash": "^4.17.21", "lodash-es": "^4.17.21", @@ -94,6 +95,8 @@ "tslib": "^2.3.0", "typeorm": "^0.2.37", "xlsx": "^0.18.5", + "zod": "^3.22.2", + "zod-to-json-schema": "^3.21.4", "zone.js": "0.13.1" }, "devDependencies": { diff --git a/apps/cloud/src/app/features/story/story/story.component.html b/apps/cloud/src/app/features/story/story/story.component.html index 1850b4714..797a7d102 100644 --- a/apps/cloud/src/app/features/story/story/story.component.html +++ b/apps/cloud/src/app/features/story/story/story.component.html @@ -139,3 +139,9 @@
{{error}}
+ + \ No newline at end of file diff --git a/apps/cloud/src/app/features/story/story/story.component.ts b/apps/cloud/src/app/features/story/story/story.component.ts index fecbdf6f3..95ea874e4 100644 --- a/apps/cloud/src/app/features/story/story/story.component.ts +++ b/apps/cloud/src/app/features/story/story/story.component.ts @@ -14,9 +14,10 @@ import { ViewChild } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' +import { CdkDragEnd } from '@angular/cdk/drag-drop' import { ResizerModule } from '@metad/ocap-angular/common' import { NgmDSCoreService, OcapCoreModule } from '@metad/ocap-angular/core' -import { AgentType, isEqual } from '@metad/ocap-core' +import { AgentType, C_MEASURES, isEqual } from '@metad/ocap-core' import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy' import { TranslateModule } from '@ngx-translate/core' import { NgMapPipeModule, NxCoreService, ReversePipe } from '@metad/core' @@ -29,8 +30,10 @@ import { StoryPointType, WidgetComponentType } from '@metad/story/core' +import { WasmAgentService } from '@metad/ocap-angular/wasm-agent' import { NxDesignerModule, NxSettingsPanelService } from '@metad/story/designer' import { NxStoryComponent, NxStoryModule } from '@metad/story/story' +import { StoryExplorerModule } from '@metad/story' import { registerTheme } from 'echarts/core' import { NGXLogger } from 'ngx-logger' import { firstValueFrom } from 'rxjs' @@ -39,9 +42,8 @@ import { MenuCatalog, registerWasmAgentModel, Store } from '../../../@core' import { MaterialModule } from '../../../@shared' import { AppService } from '../../../app.service' import { StoryToolbarComponent } from '../toolbar/toolbar.component' -import { CdkDragEnd } from '@angular/cdk/drag-drop' import { StoryToolbarService } from '../toolbar/toolbar.service' -import { WasmAgentService } from '@metad/ocap-angular/wasm-agent' + type ResponsiveBreakpointType = { name: string; @@ -65,6 +67,7 @@ type ResponsiveBreakpointType = { NxStoryModule, NxDesignerModule, StoryToolbarComponent, + StoryExplorerModule ], selector: 'pac-story', templateUrl: './story.component.html', @@ -156,6 +159,10 @@ export class StoryComponent implements OnInit { readonly pageKey$ = this.route.queryParams.pipe(map((queryParams) => queryParams['pageKey'])) readonly widgetKey$ = this.route.queryParams.pipe(map((queryParams) => queryParams['widgetKey'])) + // Story explorer + showExplorer = signal(false) + explore = signal(null) + /** |-------------------------------------------------------------------------- | Subscriptions (effect) @@ -193,6 +200,11 @@ export class StoryComponent implements OnInit { if (params.$language) { this.storyOptions.locale = params.$language } + + if (params.explore) { + this.showExplorer.set(true) + this.explore.set(JSON.parse(atob(params.explore))) + } }) private themeSub = this.storyService.themeChanging$.pipe(delay(300)).subscribe(async ([prev, current]) => { @@ -320,4 +332,13 @@ export class StoryComponent implements OnInit { onToolbarDragEnded(event: CdkDragEnd) { this.toolbarComponent.calculateRightSide(event) } + + closeExplorer(event) { + this.showExplorer.set(false) + this._router.navigate([], { + relativeTo: this.route, + queryParams: {explore: null}, + queryParamsHandling: 'merge' // remove to replace all query params by provided + }) + } } diff --git a/apps/cloud/src/app/features/story/toolbar/toolbar.component.ts b/apps/cloud/src/app/features/story/toolbar/toolbar.component.ts index c0efde378..5409b4532 100644 --- a/apps/cloud/src/app/features/story/toolbar/toolbar.component.ts +++ b/apps/cloud/src/app/features/story/toolbar/toolbar.component.ts @@ -29,6 +29,7 @@ import { ConfirmUniqueComponent } from '@metad/components/confirm' import { ConfirmCodeEditorComponent } from '@metad/components/editor' import { DeepPartial, IsNilPipe } from '@metad/core' import { + CHARTS, ParametersComponent, PreferencesComponent, QuerySettingComponent, @@ -55,7 +56,7 @@ import { SaveAsTemplateComponent } from '../save-as-template/save-as-template.co import { StoryDetailsComponent } from '../story-details/story-details.component' import { DeviceOrientation, DeviceZooms, EmulatedDevices, StoryScales, downloadStory } from '../types' import { StoryToolbarService } from './toolbar.service' -import { CHARTS, COMPONENTS, PAGES } from './types' +import { COMPONENTS, PAGES } from './types' @Component({ diff --git a/apps/cloud/src/app/features/story/toolbar/types.ts b/apps/cloud/src/app/features/story/toolbar/types.ts index b313b19e5..7f00b4850 100644 --- a/apps/cloud/src/app/features/story/toolbar/types.ts +++ b/apps/cloud/src/app/features/story/toolbar/types.ts @@ -1,613 +1,7 @@ -import { ChartAnnotation, ChartDimensionRoleType, ChartMeasureRoleType, ChartOrient } from '@metad/ocap-core' -import { DeepPartial, NxChartType } from '@metad/core' + import { StoryPoint, StoryPointType, WidgetComponentType } from '@metad/story/core' import { DisplayGrid } from 'angular-gridster2' -export interface ChartGroup { - label: string - charts: { label: string; icon: string; rotate?: boolean; width?: string; value: DeepPartial }[] -} - -export const CHARTS: ChartGroup[] = [ - { - label: 'Comparison', - charts: [ - { - label: NxChartType.Bar, - icon: 'bar.svg', - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.horizontal - }, - dimensions: [{}], - measures: [{}] - } - }, - { - label: NxChartType.Column, - icon: 'column.svg', - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.vertical - }, - dimensions: [{}], - measures: [{}] - } - }, - { - label: NxChartType.ColumnStacked, - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.vertical - }, - dimensions: [ - {}, - { - role: ChartDimensionRoleType.Stacked - } - ], - measures: [{}] - }, - icon: 'column-stacked.svg' - }, - { - label: NxChartType.BarStacked, - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.horizontal - }, - dimensions: [ - {}, - { - role: ChartDimensionRoleType.Stacked - } - ], - measures: [{}] - }, - icon: 'column-stacked.svg', - rotate: true - }, - { - label: NxChartType.BarPolar, - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.horizontal, - variant: 'polar', - chartOptions: { - seriesStyle: { - colorBy: 'data', - roundCap: true - } - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'bar-polar.svg' - }, - { - label: NxChartType.BarPolar + 'Background', - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.horizontal, - variant: 'polar', - chartOptions: { - seriesStyle: { - colorBy: 'data', - roundCap: true, - showBackground: true, - backgroundStyle: {} - } - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'bar-polar-bg.svg' - }, - { - label: NxChartType.Histogram, - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.vertical, - chartOptions: { - seriesStyle: { - barWidth: '99.3%' - } - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'histogram.svg' - }, - { - label: NxChartType.Combination, - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.vertical, - chartOptions: { - seriesStyle: { - } - } - }, - dimensions: [{}], - measures: [ - {}, - { - role: ChartMeasureRoleType.Axis2, - shapeType: 'line' - } - ] - }, - icon: 'combination.svg', - width: '50px', - }, - { - label: NxChartType.Bar + 'Trellis', - value: { - chartType: { - type: NxChartType.Bar, - orient: ChartOrient.vertical, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [ - {}, - { - role: ChartDimensionRoleType.Trellis, - } - ], - measures: [{}] - }, - icon: 'bar-trellis.svg' - }, - { - label: NxChartType.Pie, - value: { - chartType: { - type: NxChartType.Pie, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'pie.svg' - }, - { - label: NxChartType.Doughnut, - value: { - chartType: { - type: NxChartType.Pie, - variant: 'Doughnut', - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'doughnut.svg' - }, - { - label: NxChartType.Doughnut + '2', - value: { - chartType: { - type: NxChartType.Pie, - variant: 'Doughnut', - chartOptions: { - seriesStyle: { - radius: ['80%', '90%'] - } - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'doughnut-2.svg' - }, - { - label: NxChartType.Nightingale, - value: { - chartType: { - type: NxChartType.Pie, - variant: 'Nightingale', - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'nightingale.svg' - }, - { - label: NxChartType.Nightingale + '2', - value: { - chartType: { - type: NxChartType.Pie, - variant: 'Nightingale', - chartOptions: { - seriesStyle: { - radius: [0] - } - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'nightingale-2.svg' - }, - { - label: NxChartType.Waterfall, - value: { - chartType: { - type: NxChartType.Waterfall, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}] - }, - icon: 'waterfall.svg', - width: '40px' - } - ] - }, - { - label: 'Trend', - charts: [ - { - label: NxChartType.Line, - icon: 'line.svg', - width: '50px', - value: { - chartType: { - type: NxChartType.Line, - orient: ChartOrient.vertical - }, - dimensions: [{}], - measures: [{}] - } - }, - { - label: NxChartType.Line + '2', - icon: 'line.svg', - width: '50px', - value: { - chartType: { - type: NxChartType.Line, - orient: ChartOrient.horizontal - }, - dimensions: [{}], - measures: [{}] - }, - rotate: true - }, - { - label: NxChartType.Area, - icon: 'area.svg', - width: '50px', - value: { - chartType: { - type: NxChartType.Line, - orient: ChartOrient.vertical, - chartOptions: { - seriesStyle: { - areaStyle: {} - } - } - }, - dimensions: [{}], - measures: [{}] - } - }, - { - label: NxChartType.AreaStacked, - icon: 'area-stacked.svg', - width: '50px', - value: { - chartType: { - type: NxChartType.Line, - orient: ChartOrient.vertical, - chartOptions: { - seriesStyle: { - areaStyle: {}, - stack: 'normal' - } - } - }, - dimensions: [{}], - measures: [{}, {}] - } - }, - { - label: NxChartType.ThemeRiver, - icon: 'theme-river.svg', - width: '50px', - value: { - chartType: { - type: NxChartType.ThemeRiver, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}] - } - } - ] - }, - { - label: 'Correlation', - charts: [ - { - label: NxChartType.Scatter, - icon: 'scatter.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Scatter, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}, {}] - } - }, - { - label: NxChartType.Bubble, - icon: 'bubble.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Scatter, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [ - {}, - {}, - { - role: ChartMeasureRoleType.Size - } - ] - } - }, - { - label: NxChartType.Heatmap, - icon: 'heatmap.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Heatmap, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}, {}], - measures: [{}] - } - } - ] - }, - { - label: 'Distribution', - charts: [ - { - label: NxChartType.Boxplot, - icon: 'boxplot.png', - width: '40px', - value: { - chartType: { - type: NxChartType.Boxplot, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}, {}], - measures: [{}] - } - }, - { - label: NxChartType.Tree, - icon: 'tree.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Tree, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [ - { - displayHierarchy: true - } - ], - measures: [{}] - } - }, - { - label: NxChartType.Treemap, - icon: 'tree-map.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Treemap, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [ - { - displayHierarchy: true - } - ], - measures: [{}] - } - }, - { - label: NxChartType.Sunburst, - icon: 'sunburst.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Sunburst, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [ - { - displayHierarchy: true - } - ], - measures: [{}] - } - }, - { - label: NxChartType.Sankey, - icon: 'sankey.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Sankey, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [ - { - displayHierarchy: true - } - ], - measures: [{}] - } - }, - { - label: NxChartType.Funnel, - icon: 'funnel.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Funnel, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [], - measures: [{}, {}, {}] - } - } - ] - }, - { - label: 'Geo', - charts: [ - { - label: NxChartType.GeoMap, - icon: 'geomap.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.GeoMap, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}] - } - } - ] - }, - { - label: '3D', - charts: [ - { - label: NxChartType.Bar3D, - icon: 'gl.svg', - width: '60px', - value: { - chartType: { - type: NxChartType.Bar3D, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}, {}, {}], - measures: [{}] - } - }, - { - label: NxChartType.Line3D, - icon: 'gl.svg', - width: '60px', - value: { - chartType: { - type: NxChartType.Line3D, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}, {}, {}], - measures: [{}] - } - }, - { - label: NxChartType.Scatter3D, - icon: 'gl.svg', - width: '60px', - value: { - chartType: { - type: NxChartType.Scatter3D, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}, {}, {}], - measures: [{}] - } - } - ] - }, - { - label: 'Custom', - charts: [ - { - label: NxChartType.Custom, - icon: 'custom.svg', - width: '40px', - value: { - chartType: { - type: NxChartType.Custom, - chartOptions: { - seriesStyle: {} - } - }, - dimensions: [{}], - measures: [{}] - } - } - ] - } -] export const COMPONENTS: { code: string diff --git a/libs/component-angular/advanced-filter/advanced-filter.component.html b/libs/component-angular/advanced-filter/advanced-filter.component.html index a6488ae7b..c989bac4d 100644 --- a/libs/component-angular/advanced-filter/advanced-filter.component.html +++ b/libs/component-angular/advanced-filter/advanced-filter.component.html @@ -80,7 +80,6 @@
(null) - @Input() get entitySet() { - return this.entitySet$.value - } - set entitySet(value) { - this.entitySet$.next(value) - } - private readonly entitySet$ = new BehaviorSubject(null) - @Input() get entityType() { return this.entityType$.value } @@ -215,7 +206,6 @@ export class PropertySelectComponent implements ControlValueAccessor, OnInit, Af @Input() coreService: NxCoreService @Input() dsCoreService: NgmDSCoreService - // @Input() calculationEditor: Type @Input() syntax: Syntax @Input() displayDensity: DisplayDensity | string @Input() disabled: boolean @@ -441,7 +431,7 @@ export class PropertySelectComponent implements ControlValueAccessor, OnInit, Af } if (members?.length) { - property.caption = `${property.caption}:${exclude?' - ':''}${isString(members[0]) ? members[0] : members[0].label || members[0].value}` + property.caption = `${property.caption}:${exclude?' - ':''}${isString(members[0]) ? members[0] : members[0].caption || members[0].value}` if (members.length > 1) { property.caption += `(+${members.length - 1})` } diff --git a/libs/formly/property-select/property-select.component.html b/libs/formly/property-select/property-select.component.html index 3bb31642f..4c59e7688 100644 --- a/libs/formly/property-select/property-select.component.html +++ b/libs/formly/property-select/property-select.component.html @@ -4,7 +4,6 @@ [editable]="true" [showAttributes]="true" [dataSettings]="dataSettings$ | async" - [entitySet]="entitySet$ | async" [entityType]="entityType$ | async" [restrictedDimensions]="restrictedDimensions$ | async" [coreService]="coreService" diff --git a/libs/story-angular/src/lib/explorer/explorer.component.html b/libs/story-angular/src/lib/explorer/explorer.component.html new file mode 100644 index 000000000..907908460 --- /dev/null +++ b/libs/story-angular/src/lib/explorer/explorer.component.html @@ -0,0 +1,173 @@ +
+
+ +
+ +
+ +
+
+ Measures +
+ +
+ + + {{item.caption}} + + +
+
+ +
+
+
+ + {{item.caption}} + +
+
+ +
+
+
+ +
+ + +
+
+
+
{{ 'PAC.KEY_WORDS.Dimensions' | translate: {Default: 'Dimensions'} }}
+
+ + +
+
+ +
+
+
+ {{'FORMLY.CHART.' + group.label | translate: {Default: group.label} }} +
+
+ +
+
+
+ +
+
+ + + + +
+ +
+ + Visual + Options + + + + Table + Chart + + +
+ +
+
+
+
+ +
+ +
\ No newline at end of file diff --git a/libs/story-angular/src/lib/explorer/explorer.component.scss b/libs/story-angular/src/lib/explorer/explorer.component.scss new file mode 100644 index 000000000..e5a58fcf8 --- /dev/null +++ b/libs/story-angular/src/lib/explorer/explorer.component.scss @@ -0,0 +1,10 @@ +:host { + @apply flex flex-col justify-start items-stretch bg-slate-100; +} + +.tag.selected { + @apply text-amber-500 bg-amber-100; +} +.ngm-story-explorer__chart.selected { + @apply ring-violet-500; +} \ No newline at end of file diff --git a/libs/story-angular/src/lib/explorer/explorer.component.ts b/libs/story-angular/src/lib/explorer/explorer.component.ts new file mode 100644 index 000000000..055f7017e --- /dev/null +++ b/libs/story-angular/src/lib/explorer/explorer.component.ts @@ -0,0 +1,285 @@ +import { CdkDrag, CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop' +import { CommonModule } from '@angular/common' +import { Component, EventEmitter, Input, Output, computed, effect, inject, signal } from '@angular/core' +import { toObservable, toSignal } from '@angular/core/rxjs-interop' +import { FormsModule, ReactiveFormsModule } from '@angular/forms' +import { MatButtonModule } from '@angular/material/button' +import { MatButtonToggleModule } from '@angular/material/button-toggle' +import { MatChipsModule } from '@angular/material/chips' +import { MatDividerModule } from '@angular/material/divider' +import { MatListModule } from '@angular/material/list' +import { MatTooltipModule } from '@angular/material/tooltip' +import { NgmPrismHighlightComponent } from '@metad/components/prism' +import { PropertyCapacity, PropertyModule } from '@metad/components/property' +import { NxTableModule } from '@metad/components/table' +import { NxChartType } from '@metad/core' +import { AnalyticalCardModule } from '@metad/ocap-angular/analytical-card' +import { AnalyticalGridModule } from '@metad/ocap-angular/analytical-grid' +import { NgmMemberTreeComponent } from '@metad/ocap-angular/controls' +import { DisplayDensity, NgmDSCoreService, OcapCoreModule } from '@metad/ocap-angular/core' +import { EntityCapacity, NgmEntitySchemaComponent } from '@metad/ocap-angular/entity' +import { + C_MEASURES, + ChartAnnotation, + ChartOrient, + DataSettings, + Dimension, + DisplayBehaviour, + ISlicer, + assignDeepOmitBlank, + cloneDeep, + getEntityDimensions, + getEntityMeasures, + nonNullable, + pick +} from '@metad/ocap-core' +import { WidgetComponentType } from '@metad/story/core' +import { TranslateModule } from '@ngx-translate/core' +import { filter, map, switchMap } from 'rxjs/operators' +import { CHARTS, getChartType } from './types' + +@Component({ + standalone: true, + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + TranslateModule, + MatChipsModule, + MatButtonModule, + MatListModule, + MatButtonToggleModule, + MatDividerModule, + MatTooltipModule, + DragDropModule, + NxTableModule, + NgmPrismHighlightComponent, + OcapCoreModule, + NgmEntitySchemaComponent, + NgmMemberTreeComponent, + AnalyticalCardModule, + AnalyticalGridModule, + PropertyModule + ], + selector: 'ngm-story-explorer', + templateUrl: 'explorer.component.html', + styleUrls: ['explorer.component.scss'] +}) +export class StoryExplorerComponent { + EntityCapacity = EntityCapacity + DisplayDensity = DisplayDensity + PropertyCapacity = PropertyCapacity + ComponentType = WidgetComponentType + + private readonly dsCoreService = inject(NgmDSCoreService) + + @Input() + get data() { + return this._data() + } + set data(value: any) { + this._data.set(value) + if (value) { + const chartAnnotation = value?.dataSettings?.chartAnnotation + if (chartAnnotation) { + this.component.set({ + component: WidgetComponentType.AnalyticalCard, + label: getChartType(chartAnnotation.chartType)?.label as NxChartType, + dataSettings: value.dataSettings + }) + this.rows.set(chartAnnotation.dimensions) + this.columns.set(chartAnnotation.measures) + } + } + + } + private readonly _data = signal<{ dataSettings: DataSettings; chartAnnotation: ChartAnnotation }>(null) + + @Output() closed = new EventEmitter() + + dimensions = signal<{ dimension: Dimension; caption: string; hierarchies: Dimension[] }[]>([]) + + readonly entityType = toSignal( + toObservable(this._data).pipe( + filter(nonNullable), + switchMap(({ dataSettings }) => + this.dsCoreService + .selectEntitySet(dataSettings.dataSource, dataSettings.entitySet) + .pipe(map(({ entityType }) => entityType)) + ) + ) + ) + + readonly measureList = computed(() => { + return getEntityMeasures(this.entityType()) + }) + readonly dataSettings = computed(() => { + return pick(this.data?.dataSettings, 'dataSource', 'entitySet') as DataSettings + }) + readonly dataSettingsChart = computed(() => { + const chartAnnotation = assignDeepOmitBlank( + { + ...cloneDeep(this.component()?.dataSettings?.chartAnnotation ?? this.data?.chartAnnotation ?? {}) + }, + { + dimensions: this.rows().map((row) => ({ + ...row, + chartOptions: { + dataZoom: { + type: 'inside' + } + } + })), + measures: [ + ...this.columns(), + ...this.measures().map((measure) => ({ + dimension: C_MEASURES, + measure, + formatting: { + shortNumber: true + } + })) + ] + }, + 5 + ) + + chartAnnotation.dimensions = chartAnnotation.dimensions.filter((d) => d.dimension) + chartAnnotation.measures = chartAnnotation.measures.filter((d) => d.dimension) + + console.log('chartAnnotation:', chartAnnotation) + return { + ...(this.data?.dataSettings ?? {}), + chartAnnotation + } + }) + readonly chartOptions = signal({ + legend: { + show: true + } + }) + readonly dataSettingsGrid = computed(() => { + const analytics = { + ...(this.data?.analytics ?? {}), + rows: this.rows(), + columns: this.measures().map((measure) => ({ + dimension: C_MEASURES, + measure + })) + } + console.log('analytics:', analytics) + return { + ...(this.data?.dataSettings ?? {}), + analytics + } + }) + + readonly _slicers = computed(() => { + return Object.values(this.slicers()).map((slicer) => slicer) + }) + + readonly rows = signal([]) + readonly columns = signal([]) + readonly measures = signal([]) + readonly slicers = signal<{ [name: string]: ISlicer }>({}) + readonly component = signal({ + label: NxChartType.Bar, + component: WidgetComponentType.AnalyticalCard, + dataSettings: { + chartAnnotation: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.horizontal + }, + dimensions: [{}], + measures: [{}] + } + } + }) + + view: 'table' | 'chart' = 'table' + visualPanel: 'visual' | 'options' = 'visual' + charts = CHARTS.map((item) => cloneDeep(item) as any) + + constructor() { + effect( + () => { + if (this.entityType()) { + this.dimensions.set( + getEntityDimensions(this.entityType()).map(({ name, caption, hierarchies }) => ({ + dimension: { + dimension: name, + displayBehaviour: DisplayBehaviour.descriptionOnly + }, + caption, + hierarchies: hierarchies.map((hierarchy) => ({ + dimension: name, + hierarchy: hierarchy.name, + caption: hierarchy.caption + })) + })) + ) + } + }, + { allowSignalWrites: true } + ) + } + + back() { + this.closed.emit() + } + + trackByDim(index, item) { + return item?.dimension?.dimension + } + trackByIndex(index, item) { + return index + } + + toggleHierarchy(hierarchy: string, { dimension }) { + const index = this.dimensions().findIndex((d) => d.dimension.dimension === dimension.dimension) + this.dimensions.set([ + ...this.dimensions().slice(0, index), + { + ...this.dimensions()[index], + dimension: { + ...this.dimensions()[index].dimension, + hierarchy: hierarchy + } + }, + ...this.dimensions().slice(index + 1) + ]) + } + + dropRowPredicate(item: CdkDrag) { + return true + } + + dropRow(event: CdkDragDrop) { + this.rows.set([...this.rows(), { ...event.item.data }]) + } + + dropColumn(event: CdkDragDrop) { + this.columns.set([...this.columns(), { ...event.item.data }]) + } + + onRowChange(row: Dimension, i: number) { + this.rows.set([...this.rows().slice(0, i), row, ...this.rows().slice(i + 1)]) + } + + onMeasuresChange(measures: string[]) { + this.measures.set(measures) + } + + onSlicersChange(slicer: ISlicer, dimension: string) { + this.slicers.set({ + ...this.slicers(), + [dimension]: slicer + }) + } + + createWidget(widget: any) { + console.log(widget) + this.component.set(widget) + } +} diff --git a/libs/story-angular/src/lib/explorer/explorer.module.ts b/libs/story-angular/src/lib/explorer/explorer.module.ts new file mode 100644 index 000000000..5a5757d0a --- /dev/null +++ b/libs/story-angular/src/lib/explorer/explorer.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core' +import { StoryExplorerComponent } from './explorer.component'; + +@NgModule({ + declarations: [], + imports: [ + StoryExplorerComponent + ], + exports: [ + StoryExplorerComponent + ] +}) +export class StoryExplorerModule {} diff --git a/libs/story-angular/src/lib/explorer/index.ts b/libs/story-angular/src/lib/explorer/index.ts new file mode 100644 index 000000000..0f7a52c80 --- /dev/null +++ b/libs/story-angular/src/lib/explorer/index.ts @@ -0,0 +1,3 @@ +export * from './types' +export * from './explorer.module' +export * from './explorer.component' \ No newline at end of file diff --git a/libs/story-angular/src/lib/explorer/types.ts b/libs/story-angular/src/lib/explorer/types.ts new file mode 100644 index 000000000..f2ffedc2d --- /dev/null +++ b/libs/story-angular/src/lib/explorer/types.ts @@ -0,0 +1,617 @@ +import { ChartAnnotation, ChartDimensionRoleType, ChartMeasureRoleType, ChartOrient, ChartType } from '@metad/ocap-core' +import { DeepPartial, NxChartType } from '@metad/core' + +export interface ChartGroup { + label: string + charts: { label: string; icon: string; rotate?: boolean; width?: string; value: DeepPartial }[] +} + +export const CHARTS: ChartGroup[] = [ + { + label: 'Comparison', + charts: [ + { + label: NxChartType.Bar, + icon: 'bar.svg', + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.horizontal + }, + dimensions: [{}], + measures: [{}] + } + }, + { + label: NxChartType.Column, + icon: 'column.svg', + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.vertical + }, + dimensions: [{}], + measures: [{}] + } + }, + { + label: NxChartType.ColumnStacked, + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.vertical + }, + dimensions: [ + {}, + { + role: ChartDimensionRoleType.Stacked + } + ], + measures: [{}] + }, + icon: 'column-stacked.svg' + }, + { + label: NxChartType.BarStacked, + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.horizontal + }, + dimensions: [ + {}, + { + role: ChartDimensionRoleType.Stacked + } + ], + measures: [{}] + }, + icon: 'column-stacked.svg', + rotate: true + }, + { + label: NxChartType.BarPolar, + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.horizontal, + variant: 'polar', + chartOptions: { + seriesStyle: { + colorBy: 'data', + roundCap: true + } + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'bar-polar.svg' + }, + { + label: NxChartType.BarPolar + 'Background', + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.horizontal, + variant: 'polar', + chartOptions: { + seriesStyle: { + colorBy: 'data', + roundCap: true, + showBackground: true, + backgroundStyle: {} + } + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'bar-polar-bg.svg' + }, + { + label: NxChartType.Histogram, + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.vertical, + chartOptions: { + seriesStyle: { + barWidth: '99.3%' + } + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'histogram.svg' + }, + { + label: NxChartType.Combination, + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.vertical, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [ + {}, + { + role: ChartMeasureRoleType.Axis2, + shapeType: 'line' + } + ] + }, + icon: 'combination.svg', + width: '50px' + }, + { + label: NxChartType.Bar + 'Trellis', + value: { + chartType: { + type: NxChartType.Bar, + orient: ChartOrient.vertical, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [ + {}, + { + role: ChartDimensionRoleType.Trellis + } + ], + measures: [{}] + }, + icon: 'bar-trellis.svg' + }, + { + label: NxChartType.Pie, + value: { + chartType: { + type: NxChartType.Pie, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'pie.svg' + }, + { + label: NxChartType.Doughnut, + value: { + chartType: { + type: NxChartType.Pie, + variant: 'Doughnut', + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'doughnut.svg' + }, + { + label: NxChartType.Doughnut + '2', + value: { + chartType: { + type: NxChartType.Pie, + variant: 'Doughnut', + chartOptions: { + seriesStyle: { + radius: ['80%', '90%'] + } + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'doughnut-2.svg' + }, + { + label: NxChartType.Nightingale, + value: { + chartType: { + type: NxChartType.Pie, + variant: 'Nightingale', + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'nightingale.svg' + }, + { + label: NxChartType.Nightingale + '2', + value: { + chartType: { + type: NxChartType.Pie, + variant: 'Nightingale', + chartOptions: { + seriesStyle: { + radius: [0] + } + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'nightingale-2.svg' + }, + { + label: NxChartType.Waterfall, + value: { + chartType: { + type: NxChartType.Waterfall, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}] + }, + icon: 'waterfall.svg', + width: '40px' + } + ] + }, + { + label: 'Trend', + charts: [ + { + label: NxChartType.Line, + icon: 'line.svg', + width: '50px', + value: { + chartType: { + type: NxChartType.Line, + orient: ChartOrient.vertical + }, + dimensions: [{}], + measures: [{}] + } + }, + { + label: NxChartType.Line + '2', + icon: 'line.svg', + width: '50px', + value: { + chartType: { + type: NxChartType.Line, + orient: ChartOrient.horizontal + }, + dimensions: [{}], + measures: [{}] + }, + rotate: true + }, + { + label: NxChartType.Area, + icon: 'area.svg', + width: '50px', + value: { + chartType: { + type: NxChartType.Line, + orient: ChartOrient.vertical, + chartOptions: { + seriesStyle: { + areaStyle: {} + } + } + }, + dimensions: [{}], + measures: [{}] + } + }, + { + label: NxChartType.AreaStacked, + icon: 'area-stacked.svg', + width: '50px', + value: { + chartType: { + type: NxChartType.Line, + orient: ChartOrient.vertical, + chartOptions: { + seriesStyle: { + areaStyle: {}, + stack: 'normal' + } + } + }, + dimensions: [{}], + measures: [{}, {}] + } + }, + { + label: NxChartType.ThemeRiver, + icon: 'theme-river.svg', + width: '50px', + value: { + chartType: { + type: NxChartType.ThemeRiver, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}] + } + } + ] + }, + { + label: 'Correlation', + charts: [ + { + label: NxChartType.Scatter, + icon: 'scatter.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Scatter, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}, {}] + } + }, + { + label: NxChartType.Bubble, + icon: 'bubble.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Scatter, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [ + {}, + {}, + { + role: ChartMeasureRoleType.Size + } + ] + } + }, + { + label: NxChartType.Heatmap, + icon: 'heatmap.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Heatmap, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}, {}], + measures: [{}] + } + } + ] + }, + { + label: 'Distribution', + charts: [ + { + label: NxChartType.Boxplot, + icon: 'boxplot.png', + width: '40px', + value: { + chartType: { + type: NxChartType.Boxplot, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}, {}], + measures: [{}] + } + }, + { + label: NxChartType.Tree, + icon: 'tree.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Tree, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [ + { + displayHierarchy: true + } + ], + measures: [{}] + } + }, + { + label: NxChartType.Treemap, + icon: 'tree-map.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Treemap, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [ + { + displayHierarchy: true + } + ], + measures: [{}] + } + }, + { + label: NxChartType.Sunburst, + icon: 'sunburst.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Sunburst, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [ + { + displayHierarchy: true + } + ], + measures: [{}] + } + }, + { + label: NxChartType.Sankey, + icon: 'sankey.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Sankey, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [ + { + displayHierarchy: true + } + ], + measures: [{}] + } + }, + { + label: NxChartType.Funnel, + icon: 'funnel.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Funnel, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [], + measures: [{}, {}, {}] + } + } + ] + }, + { + label: 'Geo', + charts: [ + { + label: NxChartType.GeoMap, + icon: 'geomap.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.GeoMap, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}] + } + } + ] + }, + { + label: '3D', + charts: [ + { + label: NxChartType.Bar3D, + icon: 'gl.svg', + width: '60px', + value: { + chartType: { + type: NxChartType.Bar3D, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}, {}, {}], + measures: [{}] + } + }, + { + label: NxChartType.Line3D, + icon: 'gl.svg', + width: '60px', + value: { + chartType: { + type: NxChartType.Line3D, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}, {}, {}], + measures: [{}] + } + }, + { + label: NxChartType.Scatter3D, + icon: 'gl.svg', + width: '60px', + value: { + chartType: { + type: NxChartType.Scatter3D, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}, {}, {}], + measures: [{}] + } + } + ] + }, + { + label: 'Custom', + charts: [ + { + label: NxChartType.Custom, + icon: 'custom.svg', + width: '40px', + value: { + chartType: { + type: NxChartType.Custom, + chartOptions: { + seriesStyle: {} + } + }, + dimensions: [{}], + measures: [{}] + } + } + ] + } +] + +export function getChartType(chartType: ChartType) { + for(const group of CHARTS) { + for(const chart of group.charts) { + if(chart.value.chartType.type === chartType?.type) { + return chart + } + } + } +} \ No newline at end of file diff --git a/libs/story-angular/src/lib/shared.module.ts b/libs/story-angular/src/lib/shared.module.ts deleted file mode 100644 index d74aca9e4..000000000 --- a/libs/story-angular/src/lib/shared.module.ts +++ /dev/null @@ -1,67 +0,0 @@ -// import { DragDropModule } from '@angular/cdk/drag-drop' -// import { LayoutModule } from '@angular/cdk/layout' -// import { PortalModule } from '@angular/cdk/portal' -// import { CommonModule } from '@angular/common' -// import { NgModule } from '@angular/core' -// import { FormsModule, ReactiveFormsModule } from '@angular/forms' -// import { MatBadgeModule } from '@angular/material/badge' -// import { MatBottomSheetModule } from '@angular/material/bottom-sheet' -// import { MatButtonModule } from '@angular/material/button' -// import { MatCardModule } from '@angular/material/card' -// import { MatCheckboxModule } from '@angular/material/checkbox' -// import { MatRippleModule } from '@angular/material/core' -// import { MatDialogModule } from '@angular/material/dialog' -// import { MatExpansionModule } from '@angular/material/expansion' -// import { MatFormFieldModule } from '@angular/material/form-field' -// import { MatIconModule } from '@angular/material/icon' -// import { MatInputModule } from '@angular/material/input' -// import { MatListModule } from '@angular/material/list' -// import { MatMenuModule } from '@angular/material/menu' -// import { MatProgressBarModule } from '@angular/material/progress-bar' -// import { MatProgressSpinnerModule } from '@angular/material/progress-spinner' -// import { MatSelectModule } from '@angular/material/select' -// import { MatSidenavModule } from '@angular/material/sidenav' -// import { MatSlideToggleModule } from '@angular/material/slide-toggle' -// import { MatStepperModule } from '@angular/material/stepper' -// import { MatTableModule } from '@angular/material/table' -// import { MatTabsModule } from '@angular/material/tabs' -// import { MatToolbarModule } from '@angular/material/toolbar' -// import { MatTooltipModule } from '@angular/material/tooltip' -// import { MatTreeModule } from '@angular/material/tree' - -// const MAT_MODULES = [ -// MatIconModule, -// MatButtonModule, -// MatListModule, -// MatTabsModule, -// MatFormFieldModule, -// MatInputModule, -// MatDialogModule, -// MatSidenavModule, -// MatTableModule, -// MatBottomSheetModule, -// MatTreeModule, -// MatExpansionModule, -// MatStepperModule, -// MatMenuModule, -// MatCheckboxModule, -// MatToolbarModule, -// MatTooltipModule, -// MatSelectModule, -// MatProgressBarModule, -// DragDropModule, -// PortalModule, -// LayoutModule, -// MatSlideToggleModule, -// MatProgressSpinnerModule, -// MatCardModule, -// MatBadgeModule, -// MatRippleModule -// ] - -// @NgModule({ -// declarations: [], -// imports: [CommonModule, FormsModule, ReactiveFormsModule, ...MAT_MODULES], -// exports: [CommonModule, FormsModule, ReactiveFormsModule, ...MAT_MODULES] -// }) -// export class NxStorySharedModule {} diff --git a/libs/story-angular/src/public-api.ts b/libs/story-angular/src/public-api.ts index 44926d5de..6c68b721d 100644 --- a/libs/story-angular/src/public-api.ts +++ b/libs/story-angular/src/public-api.ts @@ -3,3 +3,4 @@ */ export * from './lib/settings/index' +export * from './lib/explorer/index' \ No newline at end of file diff --git a/libs/story-angular/story/story-widget/story-widget.component.html b/libs/story-angular/story/story-widget/story-widget.component.html index 4e060473a..ad4eeebc3 100644 --- a/libs/story-angular/story/story-widget/story-widget.component.html +++ b/libs/story-angular/story/story-widget/story-widget.component.html @@ -101,6 +101,10 @@ refresh {{ 'Story.Widget.Refresh' | translate: {Default: "Refresh"} }} +