Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions frontend/src/app/components/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,13 @@ export class DashboardComponent implements OnInit, OnDestroy {
}

get defaultTableToOpen () {
console.log('dashboard component get defaultTableToOpen');
console.log(this._connections.defaultTableToOpen);
return this._connections.defaultTableToOpen;
}

ngOnInit() {
this.connectionID = this._connections.currentConnectionID;
// this.isTestConnection = this._connections.currentConnection.isTestConnection;
this.dataSource = new TablesDataSource(this._tables, this._connections, this._uiSettings);
this.dataSource = new TablesDataSource(this._tables, this._connections, this._uiSettings, this._tableRow);

this._tableState.cast.subscribe(row => {
this.selectedRow = row;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,75 @@
width: 100%;
margin-top: 8px;
margin-bottom: 16px;
}

.related-records__title {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px 0 16px;
}

.related-records__title h3 {
margin: 0 !important;
}

.related-records__toggle_open {
transform: rotate(180deg);
transition: transform 200ms ease;
}

.related-records__accordion {
display: block;
margin-left: 8px;
margin-bottom: 16px;
width: calc(100% - 16px);
}

.related-records__header {
padding: 0 8px;
}

.related-records__table-name {
flex: 1 0 auto;
}

.related-records__actions {
flex-grow: 0;
justify-content: flex-end;
}

.related-record {
--mdc-list-list-item-two-line-container-height: 60px;

padding-left: 8px;
padding-right: 8px;
}

.related-record ::ng-deep .mdc-list-item__primary-text::before {
height: 24px;
}

.related-records__panel ::ng-deep .mat-expansion-panel-body {
padding: 0 8px;
}

.loading-related-records {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px 0 16px;
}

.loading-related-records__title {
mix-blend-mode: normal !important;
height: 28px;
width: 120px;
}

.loading-related-records__button {
mix-blend-mode: normal !important;
border-radius: 50%;
height: 36px;
width: 36px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,60 @@ <h2 class="mat-heading-2 row-preview-sidebar__title">Preview</h2>
</div>

<br />

<div *ngIf="selectedRow?.relatedRecords?.referenced_by.length">
<div class="related-records__title">
<h3 class="mat-heading-3">Related records</h3>
<button mat-icon-button type="button"
(click)="toggleReferencedRecords()">
<mat-icon [ngClass]="{'related-records__toggle_open': referencedRecordsShown}">keyboard_arrow_down</mat-icon>
</button>
</div>
<mat-accordion multi="true" *ngIf="referencedRecordsShown" class="related-records__accordion">
<mat-expansion-panel *ngFor="let referencedTable of referencedTables; let i = index" class="related-records__panel">
<mat-expansion-panel-header class="related-records__header">
<mat-panel-title class="related-records__table-name"> {{referencedTable.displayTableName}} </mat-panel-title>
<mat-panel-description class="related-records__actions">
<mat-spinner *ngIf="!referencedRecords[referencedTable.table_name]" diameter="20"></mat-spinner>
<span *ngIf="referencedRecords[referencedTable.table_name] && !referencedRecords[referencedTable.table_name].rows.length">Absent</span>
<a mat-icon-button *ngIf="referencedRecords[referencedTable.table_name] && referencedRecords[referencedTable.table_name].rows.length"
[routerLink]="['/dashboard', selectedRow.connectionID, referencedTable.table_name, 'settings']"
matTooltip="Set up records view">
<mat-icon>settings</mat-icon>
</a>
<a mat-icon-button *ngIf="referencedRecords[referencedTable.table_name]"
routerLink="/dashboard/{{selectedRow.connectionID}}/{{referencedTable.table_name}}"
[queryParams]="referencedTablesURLParams[i]"
matTooltip="Open records in table view">
<mat-icon>open_in_new</mat-icon>
</a>
</mat-panel-description>
</mat-expansion-panel-header>

<mat-nav-list>
<a mat-list-item *ngFor="let row of referencedRecords[referencedTable.table_name]?.rows; let i = index" class="related-record"
[routerLink]="['/dashboard', selectedRow.connectionID, referencedTable.table_name, 'entry']"
[queryParams]="referencedRecords[referencedTable.table_name]?.links[i]">
<span matListItemTitle>{{row[referencedRecords[referencedTable.table_name].identityColumn]}}</span>
<span matListItemLine>
<span *ngFor="let field_name of referencedRecords[referencedTable.table_name].fieldsOrder">
<strong>{{field_name}}:</strong>
{{ row[field_name]}}
</span>
</span>
</a>
</mat-nav-list>
</mat-expansion-panel>
</mat-accordion>
</div>

<div *ngIf="!selectedRow?.relatedRecords?.referenced_by">
<div class="skeleton loading-related-records">
<div class="bone loading-related-records__title"></div>
<div class="bone loading-related-records__button"></div>
</div>
</div>

<ng-container *ngIf="selectedRow && selectedRow.record; else loadingContent">
<div *ngFor="let column of columns" class="row-preview-sidebar__field">
<strong>{{column.normalizedTitle}}</strong>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DbTableRowViewComponent } from './db-table-row-view.component';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { ActivatedRoute } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { Angulartics2Module } from 'angulartics2';

describe('DbTableRowViewComponent', () => {
let component: DbTableRowViewComponent;
Expand All @@ -12,9 +14,11 @@ describe('DbTableRowViewComponent', () => {
TestBed.configureTestingModule({
imports: [
MatSnackBarModule,
DbTableRowViewComponent
DbTableRowViewComponent,
Angulartics2Module.forRoot()
],
providers: [
provideHttpClient(),
{ provide: ActivatedRoute, useValue: {} }
]
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { ActivatedRoute, RouterModule } from '@angular/router';
import { Component, Input, OnInit } from '@angular/core';
import { TableRow, Widget } from 'src/app/models/table';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { TableField, TableRow, Widget } from 'src/app/models/table';
import { normalizeFieldName, normalizeTableName } from 'src/app/lib/normalize';

import { ClipboardModule } from '@angular/cdk/clipboard';
import { CommonModule } from '@angular/common';
import { ConnectionsService } from 'src/app/services/connections.service';
import JsonURL from "@jsonurl/jsonurl";
import { MatButtonModule } from '@angular/material/button';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTooltipModule } from '@angular/material/tooltip';
import { NotificationsService } from 'src/app/services/notifications.service';
import { PlaceholderRecordViewComponent } from '../../skeletons/placeholder-record-view/placeholder-record-view.component';
import { TableStateService } from 'src/app/services/table-state.service';
import { normalizeFieldName } from 'src/app/lib/normalize';
import { TablesService } from 'src/app/services/tables.service';
import { formatFieldValue } from 'src/app/lib/format-field-value';

@Component({
selector: 'app-db-table-row-view',
Expand All @@ -21,41 +28,151 @@ import { normalizeFieldName } from 'src/app/lib/normalize';
MatButtonModule,
ClipboardModule,
MatTooltipModule,
MatExpansionModule,
MatProgressSpinnerModule,
MatListModule,
RouterModule,
CommonModule,
PlaceholderRecordViewComponent
]
})
export class DbTableRowViewComponent implements OnInit {
export class DbTableRowViewComponent implements OnInit, OnDestroy {
@Input() activeFilters: object;

public selectedRowCast: any;
public selectedRow: TableRow;
public columns: {
title: string;
normalizedTitle: string;
}[] = [];
public referencedTables: { table_name: string; displayTableName: string; columns: string[] }[] = [];
public referencedTablesURLParams: any;
public referencedRecords: {} = {};
public referencedRecordsShown: boolean = false;

constructor(
private _tables: TablesService,
private _tableState: TableStateService,
private _notifications: NotificationsService,
private route: ActivatedRoute,
) { }

ngOnInit(): void {
this._tableState.cast.subscribe((row) => {
// this.connectionID = this._connections.connectionID;

this.selectedRowCast = this._tableState.cast.subscribe((row) => {
row && console.log('Row selected:', row.primaryKeys);
this.selectedRow = row;
if (row && row.columnsOrder) {
const columnsOrder = this.selectedRow.columnsOrder.length ? this.selectedRow.columnsOrder : Object.keys(this.selectedRow.record);

this.columns = columnsOrder.map(column => {
return {
title: column,
normalizedTitle: normalizeFieldName(column)
}
})

if (row.relatedRecords?.referenced_by.length) {
this.referencedRecords = {};

this.referencedTables = row.relatedRecords.referenced_by
.map((table: any) => { return {...table, displayTableName: table.display_name || normalizeTableName(table.table_name)}});

this.referencedTablesURLParams = row.relatedRecords.referenced_by
.map((table: any) => {
const params = {[table.column_name]: {
eq: row.record[row.relatedRecords.referenced_on_column_name]
}};
return {
filters: JsonURL.stringify(params),
page_index: 0
}});

row.relatedRecords.referenced_by.forEach((table: any) => {
const filters = {[table.column_name]: {
eq: row.record[row.relatedRecords.referenced_on_column_name]
}};

this._tables.fetchTable({
connectionID: row.connectionID,
tableName: table.table_name,
requstedPage: 1,
chunkSize: 30,
filters
}).subscribe((res) => {
let identityColumn = res.identity_column;
let fieldsOrder = [];

const foreignKeyMap = {};
for (const fk of res.foreignKeys) {
foreignKeyMap[fk.column_name] = fk.referenced_column_name;
}

// Format each row
const formattedRows = res.rows.map(row => {
const formattedRow = {};

for (const key in row) {
if (foreignKeyMap[key] && typeof row[key] === 'object' && row[key] !== null) {
const preferredKey = Object.keys(row[key]).find(k => k !== foreignKeyMap[key]);
formattedRow[key] = preferredKey ? row[key][preferredKey] : row[key][foreignKeyMap[key]];
} else {
formattedRow[key] = formatFieldValue(row[key], res.structure.find((field: TableField) => field.column_name === key)?.data_type || 'text');
}
}
return formattedRow;
})

if (res.identity_column && res.list_fields.length) {
identityColumn = res.identity_column;
fieldsOrder = res.list_fields.filter((field: string) => field !== res.identity_column).slice(0, 3);
}

if (res.identity_column && !res.list_fields.length) {
identityColumn = res.identity_column;
fieldsOrder = res.structure.filter((field: TableField) => field.column_name !== res.identity_column).map((field: TableField) => field.column_name).slice(0, 3);
}

if (!res.identity_column && res.list_fields.length) {
identityColumn = res.list_fields[0];
fieldsOrder = res.list_fields.slice(1, 4);
}

if (!res.identity_column && !res.list_fields.length) {
identityColumn = res.structure[0].column_name;
console.log(identityColumn);
fieldsOrder = res.structure.slice(1, 4).map((field: TableField) => field.column_name);
}

const tableRecords = {
rows: formattedRows,
links: res.rows.map(row => {
let params = {};
Object.keys(res.primaryColumns).forEach((key) => {
params[res.primaryColumns[key].column_name] = row[res.primaryColumns[key].column_name];
});
return params;
}),
identityColumn,
fieldsOrder
}
this.referencedRecords[table.table_name] = tableRecords;
});
});
}
}
});
}

ngOnDestroy() {
this.selectedRowCast.unsubscribe();
}

toggleReferencedRecords() {
this.referencedRecordsShown = !this.referencedRecordsShown;
}

isForeignKey(columnName: string) {
return this.selectedRow.foreignKeysList.includes(columnName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,14 +535,22 @@ tr.mat-row:hover {

.foreign-key-button {
background: transparent;
border: none;
border: 1px solid transparent;
border-radius: 12px;
color: var(--color-accentedPalette-500);
cursor: pointer;
font-size: inherit;
padding: 2px 8px;
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
width: 100%;
max-width: 100%;
transition: background 100ms, border-color 100ms;
}

.foreign-key-button:hover{
background-color: var(--color-accentedPalette-100);
border: 1px solid var(--color-accentedPalette-300);
}

.foreign-key-button_selected {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('DbTableComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(DbTableComponent);
component = fixture.componentInstance;
component.table = new TablesDataSource({} as any, {} as any, {} as any);
component.table = new TablesDataSource({} as any, {} as any, {} as any, {} as any);
component.selection = new SelectionModel<any>(true, []);
component.filterComparators = mockFilterComparators;
fixture.autoDetectChanges();
Expand Down
Loading