diff --git a/projects/common/src/color/color.ts b/projects/common/src/color/color.ts index d5d8b3afd..3e5b44eab 100644 --- a/projects/common/src/color/color.ts +++ b/projects/common/src/color/color.ts @@ -58,7 +58,9 @@ export const enum Color { Yellow6 = '#facf00', Yellow7 = '#bd9d00', Yellow8 = '#6d5b00', - Yellow9 = '#181400' + Yellow9 = '#181400', + Transparent = 'transparent', + OffWhite = '#f6f6f64d' } export interface ColorCombination { diff --git a/projects/components/src/download-file/download-file-metadata.ts b/projects/components/src/download-file/download-file-metadata.ts new file mode 100644 index 000000000..b7891cc63 --- /dev/null +++ b/projects/components/src/download-file/download-file-metadata.ts @@ -0,0 +1,6 @@ +import { Observable } from 'rxjs'; + +export interface DownloadFileMetadata { + dataSource: Observable; // This should be a stringified data for any file + fileName: string; +} diff --git a/projects/components/src/download-json/download-json.component.scss b/projects/components/src/download-file/download-file.component.scss similarity index 88% rename from projects/components/src/download-json/download-json.component.scss rename to projects/components/src/download-file/download-file.component.scss index 652ed3456..738d2a9ba 100644 --- a/projects/components/src/download-json/download-json.component.scss +++ b/projects/components/src/download-file/download-file.component.scss @@ -1,6 +1,6 @@ @import 'color-palette'; -.download-json { +.download-file { width: 40px; height: 40px; display: flex; diff --git a/projects/components/src/download-json/download-json.component.test.ts b/projects/components/src/download-file/download-file.component.test.ts similarity index 61% rename from projects/components/src/download-json/download-json.component.test.ts rename to projects/components/src/download-file/download-file.component.test.ts index c5f7fd56c..7cf721373 100644 --- a/projects/components/src/download-json/download-json.component.test.ts +++ b/projects/components/src/download-file/download-file.component.test.ts @@ -3,20 +3,21 @@ import { fakeAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { createHostFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; import { MockComponent } from 'ng-mocks'; -import { Observable, of } from 'rxjs'; +import { of } from 'rxjs'; import { ButtonComponent } from '../button/button.component'; import { IconComponent } from '../icon/icon.component'; -import { DownloadJsonComponent } from './download-json.component'; -import { DownloadJsonModule } from './download-json.module'; +import { DownloadFileMetadata } from './download-file-metadata'; +import { DownloadFileComponent } from './download-file.component'; +import { DownloadFileModule } from './download-file.module'; -describe('Download Json Component', () => { - let spectator: Spectator; +describe('Download File Component', () => { + let spectator: Spectator; const mockElement = document.createElement('a'); const createElementSpy = jest.fn().mockReturnValue(mockElement); const createHost = createHostFactory({ - component: DownloadJsonComponent, - imports: [DownloadJsonModule, RouterTestingModule], + component: DownloadFileComponent, + imports: [DownloadFileModule, RouterTestingModule], declarations: [MockComponent(ButtonComponent), MockComponent(IconComponent)], providers: [ mockProvider(Document, { @@ -29,32 +30,32 @@ describe('Download Json Component', () => { shallow: true }); - const dataSource$: Observable = of({ - spans: [] - }); + const metadata: DownloadFileMetadata = { + dataSource: of(''), + fileName: 'download.txt' + }; test('should have only download button, when data is not loading', () => { - spectator = createHost(``, { + spectator = createHost(``, { hostProps: { - dataSource: dataSource$ + metadata: metadata } }); expect(spectator.query(ButtonComponent)).toExist(); }); - test('should download json file', fakeAsync(() => { - spectator = createHost(``, { + test('should download file', fakeAsync(() => { + spectator = createHost(``, { hostProps: { - dataSource: dataSource$ + metadata: metadata } }); spyOn(spectator.component, 'triggerDownload'); expect(spectator.component.dataLoading).toBe(false); - expect(spectator.component.fileName).toBe('download.json'); - const element = spectator.query('.download-json'); + const element = spectator.query('.download-file'); expect(element).toExist(); spectator.click(element!); diff --git a/projects/components/src/download-json/download-json.component.ts b/projects/components/src/download-file/download-file.component.ts similarity index 63% rename from projects/components/src/download-json/download-json.component.ts rename to projects/components/src/download-file/download-file.component.ts index 42120fd19..2cbba774c 100644 --- a/projects/components/src/download-json/download-json.component.ts +++ b/projects/components/src/download-file/download-file.component.ts @@ -1,18 +1,18 @@ import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, Renderer2 } from '@angular/core'; import { IconType } from '@hypertrace/assets-library'; -import { IconSize } from '@hypertrace/components'; -import { Observable } from 'rxjs'; import { catchError, finalize, take } from 'rxjs/operators'; import { ButtonSize, ButtonStyle } from '../button/button'; +import { IconSize } from '../icon/icon-size'; import { NotificationService } from '../notification/notification.service'; +import { DownloadFileMetadata } from './download-file-metadata'; @Component({ - selector: 'ht-download-json', + selector: 'ht-download-file', changeDetection: ChangeDetectionStrategy.OnPush, - styleUrls: ['./download-json.component.scss'], + styleUrls: ['./download-file.component.scss'], template: ` -
+
` }) -export class DownloadJsonComponent { +export class DownloadFileComponent { @Input() - public dataSource!: Observable; - - @Input() - public fileName: string = 'download.json'; + public metadata?: DownloadFileMetadata; public dataLoading: boolean = false; private readonly dlJsonAnchorElement: HTMLAnchorElement; @@ -45,31 +42,25 @@ export class DownloadJsonComponent { public triggerDownload(): void { this.dataLoading = true; - this.dataSource - .pipe( - take(1), - catchError(() => this.notificationService.createFailureToast('Download failed')), - finalize(() => { - this.dataLoading = false; - this.changeDetector.detectChanges(); - }) - ) - .subscribe((data: unknown) => { - if (typeof data === 'string') { - this.downloadData(data); - } else { - this.downloadData(JSON.stringify(data)); - } - }); + this.metadata!.dataSource.pipe( + take(1), + catchError(() => this.notificationService.createFailureToast('Download failed')), + finalize(() => { + this.dataLoading = false; + this.changeDetector.detectChanges(); + }) + ).subscribe((data: string) => { + this.downloadData(data); + }); } private downloadData(data: string): void { this.renderer.setAttribute( this.dlJsonAnchorElement, 'href', - `data:text/json;charset=utf-8,${encodeURIComponent(data)}` + `data:text/plain;charset=utf-8,${encodeURIComponent(data)}` ); - this.renderer.setAttribute(this.dlJsonAnchorElement, 'download', this.fileName); + this.renderer.setAttribute(this.dlJsonAnchorElement, 'download', this.metadata!.fileName); this.renderer.setAttribute(this.dlJsonAnchorElement, 'display', 'none'); this.dlJsonAnchorElement.click(); } diff --git a/projects/components/src/download-json/download-json.module.ts b/projects/components/src/download-file/download-file.module.ts similarity index 67% rename from projects/components/src/download-json/download-json.module.ts rename to projects/components/src/download-file/download-file.module.ts index f731e0897..8660ff652 100644 --- a/projects/components/src/download-json/download-json.module.ts +++ b/projects/components/src/download-file/download-file.module.ts @@ -3,11 +3,11 @@ import { NgModule } from '@angular/core'; import { ButtonModule } from '../button/button.module'; import { IconModule } from '../icon/icon.module'; import { NotificationModule } from '../notification/notification.module'; -import { DownloadJsonComponent } from './download-json.component'; +import { DownloadFileComponent } from './download-file.component'; @NgModule({ - declarations: [DownloadJsonComponent], + declarations: [DownloadFileComponent], imports: [CommonModule, ButtonModule, NotificationModule, IconModule], - exports: [DownloadJsonComponent] + exports: [DownloadFileComponent] }) -export class DownloadJsonModule {} +export class DownloadFileModule {} diff --git a/projects/components/src/public-api.ts b/projects/components/src/public-api.ts index 8bfd68434..fc183c97c 100644 --- a/projects/components/src/public-api.ts +++ b/projects/components/src/public-api.ts @@ -24,6 +24,10 @@ export * from './checkbox/checkbox.module'; export * from './collapsible-sidebar/collapsible-sidebar.component'; export * from './collapsible-sidebar/collapsible-sidebar.module'; +// Collapsible sidebar +export * from './viewer/code-viewer/code-viewer.component'; +export * from './viewer/code-viewer/code-viewer.module'; + // Combo Box export * from './combo-box/combo-box.module'; export * from './combo-box/combo-box.component'; @@ -75,9 +79,10 @@ export { MenuDropdownComponent } from './menu-dropdown/menu-dropdown.component'; export { MenuItemComponent } from './menu-dropdown/menu-item/menu-item.component'; export { MenuDropdownModule } from './menu-dropdown/menu-dropdown.module'; -// Download JSON -export * from './download-json/download-json.component'; -export * from './download-json/download-json.module'; +// Download File +export * from './download-file/download-file.component'; +export * from './download-file/download-file-metadata'; +export * from './download-file/download-file.module'; // Dynamic label export * from './highlighted-label/highlighted-label.component'; @@ -272,10 +277,6 @@ export * from './overlay/overlay'; export * from './overlay/overlay.module'; export * from './overlay/sheet/sheet'; -// Snippet -export { SnippetViewerComponent } from './viewer/snippet-viewer/snippet-viewer.component'; -export { SnippetViewerModule } from './viewer/snippet-viewer/snippet-viewer.module'; - // Spinner export * from './spinner/spinner.component'; export * from './spinner/spinner.module'; diff --git a/projects/components/src/viewer/code-viewer/code-viewer.component.scss b/projects/components/src/viewer/code-viewer/code-viewer.component.scss new file mode 100644 index 000000000..60eec5c78 --- /dev/null +++ b/projects/components/src/viewer/code-viewer/code-viewer.component.scss @@ -0,0 +1,87 @@ +@import 'mixins'; + +@mixin line-base { + display: flex; + align-items: center; + width: 100%; + height: 20px; + + &.line-highlight { + background-color: $blue-2; + } +} + +.code-viewer { + @include fill-container; + display: grid; + grid-template-rows: 54px auto; + + .header { + height: 100%; + display: flex; + align-items: center; + padding: 0 12px; + border-bottom: 1px solid $gray-2; + + .title { + @include overline; + } + + .header-content { + min-width: 0; + flex: 1 1 auto; + display: flex; + justify-content: flex-end; + + .search-box { + width: 140px; + background-color: white; + } + } + } + + .content { + @include code; + height: 100%; + overflow-y: auto; + display: grid; + grid-template-columns: 40px auto; + position: relative; + + .line-numbers { + width: 100%; + display: flex; + flex-direction: column; + + .line-number { + @include line-base; + padding-left: 8px; + } + } + + .code-lines { + width: 100%; + display: flex; + flex-direction: column; + overflow-x: auto; + + .code-line { + @include line-base; + white-space: break-spaces; + + ::ng-deep { + mark { + background-color: $yellow-4; + } + } + } + } + + .copy-to-clipboard { + position: absolute; + color: $gray-9; + right: 0; + top: 0; + } + } +} diff --git a/projects/components/src/viewer/code-viewer/code-viewer.component.test.ts b/projects/components/src/viewer/code-viewer/code-viewer.component.test.ts new file mode 100644 index 000000000..48b1a0c99 --- /dev/null +++ b/projects/components/src/viewer/code-viewer/code-viewer.component.test.ts @@ -0,0 +1,73 @@ +import { FormattingModule } from '@hypertrace/common'; +import { createComponentFactory } from '@ngneat/spectator/jest'; +import { MockComponent } from 'ng-mocks'; +import { CopyToClipboardComponent } from '../../copy-to-clipboard/copy-to-clipboard.component'; +import { DownloadFileComponent } from '../../download-file/download-file.component'; +import { SearchBoxComponent } from '../../search-box/search-box.component'; +import { CodeViewerComponent } from './code-viewer.component'; + +describe('Code Viewer Component', () => { + const createComponent = createComponentFactory({ + component: CodeViewerComponent, + declarations: [ + MockComponent(SearchBoxComponent), + MockComponent(DownloadFileComponent), + MockComponent(CopyToClipboardComponent) + ], + imports: [FormattingModule], + shallow: true + }); + const code = `{\n "key": "value" \n }`; + const downloadFileName = 'code.json'; + + test('should render everything correctly', () => { + const spectator = createComponent({ + props: { + code: '' + } + }); + expect(spectator.query('.code-viewer')).not.toExist(); + + // Set code + spectator.setInput({ + code: code + }); + + expect(spectator.query('.code-viewer')).toExist(); + expect(spectator.query(SearchBoxComponent)).toExist(); + expect(spectator.query(DownloadFileComponent)).not.toExist(); + expect(spectator.query(CopyToClipboardComponent)).not.toExist(); + expect(spectator.query('.title')).toHaveText('Code Viewer'); + expect(spectator.queryAll('.line-number').length).toBe(3); + expect(spectator.queryAll('.code-line').length).toBe(3); + expect(spectator.queryAll('.line-number.line-highlight').length).toBe(0); + expect(spectator.queryAll('.code-line.line-highlight').length).toBe(0); + + // Highlight text + spectator.setInput({ + highlightText: 'key' + }); + + expect(spectator.queryAll('.code-line.line-highlight').length).toBe(1); + expect(spectator.queryAll('.code-line.line-highlight').length).toBe(1); + + // Download + spectator.setInput({ + downloadFileName: downloadFileName + }); + + expect(spectator.query(DownloadFileComponent)).toExist(); + expect(spectator.query(DownloadFileComponent)?.metadata?.fileName).toBe(downloadFileName); + + // Copy to clipboard + spectator.setInput({ + enableCopy: true + }); + + expect(spectator.query(CopyToClipboardComponent)).toExist(); + + // Search + spectator.triggerEventHandler(SearchBoxComponent, 'valueChange', 'e'); + expect(spectator.queryAll('mark').length).toBe(2); + }); +}); diff --git a/projects/components/src/viewer/code-viewer/code-viewer.component.ts b/projects/components/src/viewer/code-viewer/code-viewer.component.ts new file mode 100644 index 000000000..b37b7d525 --- /dev/null +++ b/projects/components/src/viewer/code-viewer/code-viewer.component.ts @@ -0,0 +1,129 @@ +import { ChangeDetectionStrategy, Component, Input, OnChanges, TemplateRef } from '@angular/core'; +import { Color, TypedSimpleChanges } from '@hypertrace/common'; +import { isEmpty } from 'lodash-es'; +import { of } from 'rxjs'; +import { DownloadFileMetadata } from '../../download-file/download-file-metadata'; + +@Component({ + selector: 'ht-code-viewer', + styleUrls: ['./code-viewer.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` +
+
+
{{ this.titleText }}
+
+ + + + +
+
+
+
+
+ {{ lineNumber }} +
+
+
+
+

+          
+
+ +
+
+ ` +}) +export class CodeViewerComponent implements OnChanges { + @Input() + public code: string = ''; // Pre-formatted code string + + @Input() + public highlightText: string = ''; // To highlight the entire line + + @Input() + public titleText: string = 'Code Viewer'; + + @Input() + public backgroundColor: string = Color.OffWhite; + + @Input() + public enableCopy: boolean = false; + + @Input() + public copyLabel: string = ''; + + @Input() + public copyTooltip: string = 'Copy to Clipboard'; + + @Input() + public enableSearch: boolean = true; + + @Input() + public showHeader: boolean = true; + + @Input() + public downloadFileName: string = ''; + + @Input() + public additionalHeaderContent?: TemplateRef; + + @Input() + public lineSplitter: RegExp = new RegExp('\r\n|\r|\n'); + + public codeLines: string[] = []; + public downloadCodeMetadata?: DownloadFileMetadata; + public lineNumbers: number[] = []; + public searchText: string = ''; + + public ngOnChanges(changes: TypedSimpleChanges): void { + if (changes.code) { + this.codeLines = isEmpty(this.code) ? [] : this.code.split(this.lineSplitter); + this.lineNumbers = new Array(this.codeLines.length).fill(0).map((_, index) => index + 1); + } + + if (changes.code || changes.downloadFileName) { + this.downloadCodeMetadata = { + dataSource: of(this.code), + fileName: this.downloadFileName + }; + } + } + + public isLineHighlighted(lineNum: number): boolean { + return ( + !isEmpty(this.highlightText) && this.codeLines[lineNum].toLowerCase().includes(this.highlightText.toLowerCase()) + ); + } + + public onSearch(searchText: string): void { + this.searchText = searchText; + } +} diff --git a/projects/components/src/viewer/code-viewer/code-viewer.module.ts b/projects/components/src/viewer/code-viewer/code-viewer.module.ts new file mode 100644 index 000000000..3a6075598 --- /dev/null +++ b/projects/components/src/viewer/code-viewer/code-viewer.module.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormattingModule } from '@hypertrace/common'; +import { CopyToClipboardModule } from '../../copy-to-clipboard/copy-to-clipboard.module'; +import { DownloadFileModule } from '../../download-file/download-file.module'; +import { TraceSearchBoxModule } from '../../search-box/search-box.module'; +import { CodeViewerComponent } from './code-viewer.component'; + +@NgModule({ + imports: [CommonModule, CopyToClipboardModule, DownloadFileModule, FormattingModule, TraceSearchBoxModule], + declarations: [CodeViewerComponent], + exports: [CodeViewerComponent] +}) +export class CodeViewerModule {} diff --git a/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.scss b/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.scss deleted file mode 100644 index 648b17f39..000000000 --- a/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.scss +++ /dev/null @@ -1,41 +0,0 @@ -@import 'color-palette'; -@import 'font'; - -.snippet-viewer { - @include code($gray-5); - position: relative; - background: $gray-1; - border-radius: 12px; - padding: 12px; - display: grid; - grid-template-columns: 40px minmax(0, 1fr); - grid-column-gap: 16px; - grid-row-gap: 2px; - - &:hover { - .copy-to-clipboard { - display: inherit; - } - } - - .copy-to-clipboard { - position: absolute; - right: 0; - top: 0; - user-select: none; - display: none; - } - - .line-number { - display: flex; - justify-content: center; - border-right: 1px solid white; - user-select: none; - } - - .content { - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - } -} diff --git a/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.test.ts b/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.test.ts deleted file mode 100644 index 2c5f070ac..000000000 --- a/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { createHostFactory, Spectator } from '@ngneat/spectator/jest'; -import { SnippetViewerComponent } from './snippet-viewer.component'; - -describe('Snippet Viewer Component', () => { - let spectator: Spectator; - const createHost = createHostFactory({ - component: SnippetViewerComponent, - shallow: true, - imports: [] - }); - - test('should display snippet in multi lines with line numbers', () => { - const snippet = - 'This is a text snippet line 1\n\ - This is a text snippet line 2\n\ - This is a text snippet line 3\n\ - This is a text snippet line 4\n\ - This is a text snippet line 5\n\ - This is a text snippet line 6\n\ - This is a text snippet line 7\n\ - This is a text snippet line 8\n\ - This is a text snippet line 9'; - - spectator = createHost(``, { - hostProps: { - snippet: snippet - } - }); - - const lineCountElements = spectator.queryAll('.line-number'); - expect(lineCountElements.length).toEqual(9); - - lineCountElements.forEach((lineCountElement, index) => { - const lineNumber = index + 1; - expect(lineCountElement).toHaveText(`${lineNumber}`); - }); - - const linesContentElements = spectator.queryAll('.content'); - expect(linesContentElements.length).toEqual(9); - - linesContentElements.forEach((linesContentElement, index) => { - const lineNumber = index + 1; - expect(linesContentElement).toHaveText(`This is a text snippet line ${lineNumber}`); - }); - }); -}); diff --git a/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.ts b/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.ts deleted file mode 100644 index 84e4ac71e..000000000 --- a/projects/components/src/viewer/snippet-viewer/snippet-viewer.component.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; -import { TypedSimpleChanges } from '@hypertrace/common'; - -@Component({ - selector: 'ht-snippet-viewer', - styleUrls: ['./snippet-viewer.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - template: ` -
- - -
- {{ lineNumber + 1 }} -
-
- {{ line }} -
-
-
- ` -}) -export class SnippetViewerComponent implements OnChanges { - @Input() - public snippet?: string; - - @Input() - public enableCopy?: boolean = true; - - @Input() - public copyLabel?: string = 'Copy to Clipboard'; - - @Input() - public copyTooltip?: string = 'Copy to Clipboard'; - - public lines: string[] = []; - - private readonly lineSplitter: RegExp = new RegExp('\r\n|\r|\n'); - - public ngOnChanges(changes: TypedSimpleChanges): void { - if (changes.snippet) { - this.lines = this.snippet !== undefined ? this.snippet.split(this.lineSplitter) : []; - } - } -} diff --git a/projects/components/src/viewer/snippet-viewer/snippet-viewer.module.ts b/projects/components/src/viewer/snippet-viewer/snippet-viewer.module.ts deleted file mode 100644 index 39e01779f..000000000 --- a/projects/components/src/viewer/snippet-viewer/snippet-viewer.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { CopyToClipboardModule } from '../../copy-to-clipboard/copy-to-clipboard.module'; -import { SnippetViewerComponent } from './snippet-viewer.component'; - -@NgModule({ - declarations: [SnippetViewerComponent], - imports: [CommonModule, CopyToClipboardModule], - exports: [SnippetViewerComponent] -}) -export class SnippetViewerModule {} diff --git a/projects/observability/src/pages/trace-detail/trace-detail.page.component.ts b/projects/observability/src/pages/trace-detail/trace-detail.page.component.ts index f2410a681..a3ce09a6e 100644 --- a/projects/observability/src/pages/trace-detail/trace-detail.page.component.ts +++ b/projects/observability/src/pages/trace-detail/trace-detail.page.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { IconType } from '@hypertrace/assets-library'; import { NavigationParams, NavigationService, SubscriptionLifecycle } from '@hypertrace/common'; -import { FilterOperator, IconSize } from '@hypertrace/components'; +import { DownloadFileMetadata, FilterOperator, IconSize } from '@hypertrace/components'; import { Observable } from 'rxjs'; import { LogEvent } from '../../shared/dashboard/widgets/waterfall/waterfall/waterfall-chart'; import { ApiTraceDetails } from '../api-trace-detail/api-trace-detail.service'; @@ -53,12 +53,11 @@ import { TraceDetails, TraceDetailService } from './trace-detail.service'; - + >
@@ -92,6 +91,11 @@ export class TraceDetailPageComponent { this.logEvents$ = this.traceDetailService.fetchLogEvents(); } + public getDownloadTraceDetailsJsonMetadata = (traceId: string): DownloadFileMetadata => ({ + dataSource: this.exportSpans$, + fileName: `${traceId}.json` + }); + public onClickBack(): void { this.navigationService.navigateBack(); } diff --git a/projects/observability/src/pages/trace-detail/trace-detail.page.module.ts b/projects/observability/src/pages/trace-detail/trace-detail.page.module.ts index 6d03e54a3..a2c3b8511 100644 --- a/projects/observability/src/pages/trace-detail/trace-detail.page.module.ts +++ b/projects/observability/src/pages/trace-detail/trace-detail.page.module.ts @@ -4,7 +4,7 @@ import { RouterModule } from '@angular/router'; import { FormattingModule, HtRoute, MemoizeModule } from '@hypertrace/common'; import { CopyShareableLinkToClipboardModule, - DownloadJsonModule, + DownloadFileModule, IconModule, LabelModule, LoadAsyncModule, @@ -58,7 +58,7 @@ const ROUTE_CONFIG: HtRoute[] = [ LoadAsyncModule, FormattingModule, CopyShareableLinkToClipboardModule, - DownloadJsonModule, + DownloadFileModule, NavigableDashboardModule.withDefaultDashboards(traceSequenceDashboard), NavigableTabModule, LogEventsTableModule