Skip to content

Commit 08909c9

Browse files
record view:
- unify component api for any record; - show foreign key in record view; - highlight displayed foreign key.
1 parent 5ce6e52 commit 08909c9

7 files changed

Lines changed: 158 additions & 41 deletions

File tree

frontend/src/app/components/dashboard/dashboard.component.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,6 @@ <h3 class='mat-subheading-2'>Rocketadmin can not find any tables</h3>
9393
</app-db-table>
9494
</div>
9595
<app-db-table-row-view *ngIf="selectedRow"
96-
[columns]="dataSource.columns"
97-
[foreignKeys]="dataSource.foreignKeys"
98-
[foreignKeysList]="dataSource.foreignKeysList"
99-
[widgets]="dataSource.widgets"
100-
[widgetsList]="dataSource.widgetsList"
10196
[activeFilters]="filters"
10297
></app-db-table-row-view>
10398
<app-db-table-ai-panel *ngIf="isAIpanelOpened"

frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.html

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
<div class="row-preview-sidebar__header">
55
<h2 class="mat-heading-2 row-preview-sidebar__title">Preview</h2>
66
<div class="row-preview-sidebar__actions">
7-
<button mat-icon-button
7+
<button mat-icon-button *ngIf="selectedRow.link"
88
matTooltip="Copy link to this record"
99
[cdkCopyToClipboard]="getDedicatedPageLink()"
1010
(cdkCopyToClipboardCopied)="showCopyNotification('Link to this record was copied to clipboard.')">
1111
<mat-icon>link</mat-icon>
1212
</button>
13-
<a mat-icon-button
13+
<a mat-icon-button *ngIf="selectedRow.link"
1414
[routerLink]="selectedRow.link"
1515
[queryParams]="selectedRow.primaryKeys"
1616
matTooltip="Open the record"
@@ -24,22 +24,29 @@ <h2 class="mat-heading-2 row-preview-sidebar__title">Preview</h2>
2424
</div>
2525

2626
<br />
27-
<div *ngFor="let column of columns" class="row-preview-sidebar__field">
28-
<strong>{{column.normalizedTitle}}</strong>
29-
<span *ngIf="isForeignKey(column.title); else recordContent" class="row-preview-sidebar__field-value">
30-
{{getForeignKeyValue(column.title)}}
31-
</span>
32-
<ng-template #recordContent>
33-
<div *ngIf="isWidget(column.title); else stringValue">
34-
<div *ngIf="widgets[column.title].widget_type === 'Image'">
35-
<img [src]="selectedRow.record[column.title]" alt="Image" class="row-preview-sidebar__image">
27+
<ng-container *ngIf="selectedRow && selectedRow.record; else loadingContent">
28+
<div *ngFor="let column of columns" class="row-preview-sidebar__field">
29+
<strong>{{column.normalizedTitle}}</strong>
30+
<span *ngIf="isForeignKey(column.title); else recordContent" class="row-preview-sidebar__field-value">
31+
{{getForeignKeyValue(column.title)}}
32+
</span>
33+
<ng-template #recordContent>
34+
<div *ngIf="isWidget(column.title); else stringValue">
35+
<div *ngIf="selectedRow.widgets[column.title].widget_type === 'Image'">
36+
<img [src]="selectedRow.record[column.title]" alt="Image" class="row-preview-sidebar__image">
37+
</div>
38+
<span class="row-preview-sidebar__field-value">{{selectedRow.record[column.title] || '—'}}</span>
3639
</div>
37-
<span class="row-preview-sidebar__field-value">{{selectedRow.record[column.title] || '—'}}</span>
38-
</div>
39-
<ng-template #stringValue>
40-
<span class="row-preview-sidebar__field-value">{{selectedRow.record[column.title] || '—'}}</span>
40+
<ng-template #stringValue>
41+
<span class="row-preview-sidebar__field-value">{{selectedRow.record[column.title] || '—'}}</span>
42+
</ng-template>
4143
</ng-template>
42-
</ng-template>
44+
</div>
45+
</ng-container>
4346

44-
</div>
47+
<ng-template #loadingContent>
48+
<div class="row-preview-sidebar__loading">
49+
<span>Loading...</span>
50+
</div>
51+
</ng-template>
4552
</div>

frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import { ClipboardModule } from '@angular/cdk/clipboard';
66
import { CommonModule } from '@angular/common';
77
import { MatButtonModule } from '@angular/material/button';
88
import { MatIconModule } from '@angular/material/icon';
9+
import { MatTooltipModule } from '@angular/material/tooltip';
910
import { NotificationsService } from 'src/app/services/notifications.service';
1011
import { TableStateService } from 'src/app/services/table-state.service';
11-
import { MatTooltipModule } from '@angular/material/tooltip';
12+
import { normalizeFieldName } from 'src/app/lib/normalize';
1213

1314
@Component({
1415
selector: 'app-db-table-row-view',
@@ -24,14 +25,18 @@ import { MatTooltipModule } from '@angular/material/tooltip';
2425
]
2526
})
2627
export class DbTableRowViewComponent implements OnInit {
27-
@Input() columns: object[];
28-
@Input() foreignKeys: object;
29-
@Input() foreignKeysList: string[];
30-
@Input() widgets: { string: Widget };
31-
@Input() widgetsList: string[];
28+
// @Input() structure: object[];
29+
// @Input() foreignKeys: object;
30+
// @Input() foreignKeysList: string[];
31+
// @Input() widgets: { string: Widget };
32+
// @Input() widgetsList: string[];
3233
@Input() activeFilters: object;
3334

3435
public selectedRow: TableRow;
36+
public columns: {
37+
title: string;
38+
normalizedTitle: string;
39+
}[] = [];
3540

3641
constructor(
3742
private _tableState: TableStateService,
@@ -41,29 +46,39 @@ export class DbTableRowViewComponent implements OnInit {
4146

4247
ngOnInit(): void {
4348
this._tableState.cast.subscribe((row) => {
49+
console.log('Row selected:', row);
4450
this.selectedRow = row;
51+
if (row.columnsOrder) {
52+
const columnsOrder = this.selectedRow.columnsOrder.length ? this.selectedRow.columnsOrder : Object.keys(this.selectedRow.record);
53+
this.columns = columnsOrder.map(column => {
54+
return {
55+
title: column,
56+
normalizedTitle: normalizeFieldName(column)
57+
}
58+
})
59+
}
4560
});
4661
}
4762

4863
isForeignKey(columnName: string) {
49-
return this.foreignKeysList.includes(columnName);
64+
return this.selectedRow.foreignKeysList.includes(columnName);
5065
}
5166

5267
getForeignKeyValue(field: string) {
5368
if (this.selectedRow) {
54-
const identityColumnName = Object.keys(this.selectedRow.record[field]).find(key => key !== this.foreignKeys[field].referenced_column_name);
69+
const identityColumnName = Object.keys(this.selectedRow.record[field]).find(key => key !== this.selectedRow.foreignKeys[field].referenced_column_name);
5570
if (identityColumnName) {
5671
return this.selectedRow.record[field][identityColumnName];
5772
} else {
58-
const referencedColumnName = this.foreignKeys[field].referenced_column_name;
59-
return this.selectedRow.record[field][referencedColumnName];
73+
// const referencedColumnName = this.selectedRow.foreignKeys[field].referenced_column_name;
74+
return this.selectedRow.record[field];
6075
}
6176
};
6277
return '';
6378
}
6479

6580
isWidget(columnName: string) {
66-
return this.widgetsList.includes(columnName);
81+
return this.selectedRow.widgetsList.includes(columnName);
6782
}
6883

6984
getDedicatedPageLink() {

frontend/src/app/components/dashboard/db-table/db-table.component.css

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,17 @@ tr.mat-row:hover {
533533
top: 40px;
534534
}
535535

536-
.foreign-key-link {
536+
.foreign-key-button {
537+
background: transparent;
538+
border: none;
537539
color: var(--color-accentedPalette-500);
540+
text-decoration: underline;
541+
font-size: inherit;
542+
}
543+
544+
.foreign-key-button_selected {
545+
color: var(--color-accentedPalette-700);
546+
font-weight: 600;
538547
}
539548

540549
.empty-table {

frontend/src/app/components/dashboard/db-table/db-table.component.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,16 @@ <h2 class="mat-h2 table-name">{{ displayName }}</h2>
217217
<mat-cell *matCellDef="let element; let i = index" [attr.data-label]="tableData.dataNormalizedColumns[column]" class="db-table-cell" data-hj-suppress>
218218
<div class="table-cell-content">
219219
<span *ngIf="isForeignKey(column); else contentCell" class="field-value">
220-
<a routerLink="/dashboard/{{connectionID}}/{{tableData.foreignKeys[column].referenced_table_name}}/entry"
220+
<!--<a routerLink="/dashboard/{{connectionID}}/{{tableData.foreignKeys[column].referenced_table_name}}/entry"
221221
class="foreign-key-link"
222222
[queryParams]="getForeignKeyQueryParams(tableData.foreignKeys[column], element[column])">
223223
{{ getCellValue(tableData.foreignKeys[column], element[column]) }}
224-
</a>
224+
</a>-->
225+
<button class="foreign-key-button" type="button"
226+
[ngClass]="{'foreign-key-button_selected': isForeignKeySelected(element[column], tableData.foreignKeys[column])}"
227+
(click)="handleForeignKeyView($event, tableData.foreignKeys[column], element[column])">
228+
{{ getCellValue(tableData.foreignKeys[column], element[column]) }}
229+
</button>
225230
<button type="button" mat-icon-button
226231
class="field-value-copy-button"
227232
matTooltip="Copy"
@@ -347,7 +352,7 @@ <h2 class="mat-h2 table-name">{{ displayName }}</h2>
347352
<mat-row *matRowDef="let row; columns: tableData.actionsColumnWidth === '0' ? tableData.displayedDataColumns : tableData.displayedColumns;"
348353
class="db-table-row"
349354
[ngClass]="{'db-table-row_selected': isRowSelected(tableData.getQueryParams(row))}"
350-
(click)="handleViewRow({record: row, primaryKeys: tableData.getQueryParams(row)})">
355+
(click)="handleViewRow(row)">
351356
</mat-row>
352357
</mat-table>
353358
</div>

frontend/src/app/components/dashboard/db-table/db-table.component.ts

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import * as JSON5 from 'json5';
2+
13
import { ActivatedRoute, Router } from '@angular/router';
24
import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
3-
import { CustomAction, TableForeignKey, TablePermissions, TableProperties, TableRow } from 'src/app/models/table';
5+
import { CustomAction, TableForeignKey, TablePermissions, TableProperties, TableRow, Widget } from 'src/app/models/table';
46
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
57
import { Observable, merge, of } from 'rxjs';
68
import { map, startWith, tap } from 'rxjs/operators';
@@ -33,6 +35,7 @@ import { NotificationsService } from 'src/app/services/notifications.service';
3335
import { PlaceholderTableDataComponent } from '../../skeletons/placeholder-table-data/placeholder-table-data.component';
3436
import { RouterModule } from '@angular/router';
3537
import { SelectionModel } from '@angular/cdk/collections';
38+
import { TableRowService } from 'src/app/services/table-row.service';
3639
import { TableStateService } from 'src/app/services/table-state.service';
3740
import { normalizeTableName } from '../../../lib/normalize'
3841

@@ -112,6 +115,7 @@ export class DbTableComponent implements OnInit {
112115
lte: "<="
113116
}
114117
public selectedRow: TableRow = null;
118+
public selectedRowType: 'record' | 'foreignKey';
115119

116120
@Input() set table(value){
117121
if (value) this.tableData = value;
@@ -123,6 +127,7 @@ export class DbTableComponent implements OnInit {
123127
constructor(
124128
private _tableState: TableStateService,
125129
private _notifications: NotificationsService,
130+
private _tableRow: TableRowService,
126131
private route: ActivatedRoute,
127132
public router: Router,
128133
public dialog: MatDialog,
@@ -383,15 +388,90 @@ export class DbTableComponent implements OnInit {
383388
}
384389

385390
handleViewRow(row: TableRow) {
386-
this._tableState.selectRow({...row, link: `/dashboard/${this.connectionID}/${this.name}/entry`});
391+
this.selectedRowType = 'record';
392+
this._tableState.selectRow({
393+
tableName: this.name,
394+
record: row,
395+
columnsOrder: this.tableData.dataColumns,
396+
primaryKeys: this.tableData.getQueryParams(row),
397+
foreignKeys: this.tableData.foreignKeys,
398+
foreignKeysList: this.tableData.foreignKeysList,
399+
widgets: this.tableData.widgets,
400+
widgetsList: this.tableData.widgetsList,
401+
link: `/dashboard/${this.connectionID}/${this.name}/entry`
402+
});
403+
}
404+
405+
handleForeignKeyView(event, foreignKeys, row) {
406+
event.stopPropagation();
407+
this.selectedRowType = 'foreignKey';
408+
409+
console.log('handleForeignKeyView', foreignKeys, row);
410+
411+
this._tableState.selectRow({
412+
tableName: null,
413+
record: null,
414+
columnsOrder: null,
415+
primaryKeys: null,
416+
foreignKeys: null,
417+
foreignKeysList: null,
418+
widgets: null,
419+
widgetsList: null,
420+
link: null
421+
})
422+
423+
this._tableRow.fetchTableRow(this.connectionID, foreignKeys.referenced_table_name, {[foreignKeys.referenced_column_name]: row[foreignKeys.referenced_column_name]})
424+
.subscribe(res => {
425+
console.log('Fetched foreign key row:', res);
426+
this._tableState.selectRow({
427+
tableName: foreignKeys.referenced_table_name,
428+
record: res.row,
429+
columnsOrder: res.list_fields,
430+
primaryKeys: {
431+
[foreignKeys.referenced_column_name]: res.row[foreignKeys.referenced_column_name]
432+
},
433+
foreignKeys: Object.assign({}, ...res.foreignKeys.map((foreignKey: TableForeignKey) => ({[foreignKey.column_name]: foreignKey}))),
434+
foreignKeysList: res.foreignKeys.map(fk => fk.column_name),
435+
widgets: Object.assign({}, ...res.table_widgets.map((widget: Widget) => {
436+
let parsedParams;
437+
438+
try {
439+
parsedParams = JSON5.parse(widget.widget_params);
440+
} catch {
441+
parsedParams = {};
442+
}
443+
444+
return {
445+
[widget.field_name]: {
446+
...widget,
447+
widget_params: parsedParams,
448+
},
449+
};
450+
})
451+
),
452+
widgetsList: res.table_widgets.map(widget => widget.field_name),
453+
link: `/dashboard/${this.connectionID}/${foreignKeys.referenced_table_name}/entry`
454+
});
455+
})
387456
}
388457

389458
handleViewAIpanel() {
390459
this._tableState.handleViewAIpanel();
391460
}
392461

393462
isRowSelected(primaryKeys) {
394-
return this.selectedRow && Object.keys(this.selectedRow.primaryKeys).length && JSON.stringify(this.selectedRow.primaryKeys) === JSON.stringify(primaryKeys);
463+
if (this.selectedRowType === 'record' && this.selectedRow.primaryKeys !== null) return this.selectedRow && Object.keys(this.selectedRow.primaryKeys).length && JSON.stringify(this.selectedRow.primaryKeys) === JSON.stringify(primaryKeys);
464+
return false;
465+
}
466+
467+
isForeignKeySelected(record, foreignKey: TableForeignKey) {
468+
if (this.selectedRow) console.log('isForeignKeySelected', this.selectedRow.primaryKeys, foreignKey);
469+
const primaryKeyValue = record[foreignKey.referenced_column_name];
470+
471+
if (this.selectedRowType === 'foreignKey' && this.selectedRow && this.selectedRow.record !== null) {
472+
return Object.values(this.selectedRow.primaryKeys)[0] === primaryKeyValue && this.selectedRow.tableName === foreignKey.referenced_table_name;
473+
}
474+
return false;
395475
}
396476

397477
showCopyNotification(message: string) {

frontend/src/app/models/table.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,14 @@ export interface TableSettings {
3939
}
4040

4141
export interface TableRow {
42+
tableName: string,
4243
record: object,
43-
primaryKeys: object
44+
columnsOrder: string[],
45+
primaryKeys: object,
46+
foreignKeys: TableForeignKey[],
47+
foreignKeysList: string[],
48+
widgets: Widget[],
49+
widgetsList: string[],
4450
link?: string
4551
}
4652

0 commit comments

Comments
 (0)