diff --git a/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.css b/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.css index f4f887777..aa75def10 100644 --- a/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.css +++ b/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.css @@ -67,50 +67,6 @@ } } -.row-preview-sidebar__field-value { - word-wrap: anywhere; - word-break: keep-all; - white-space: break-spaces; - max-width: 100%; -} - -@media (prefers-color-scheme: light) { - .row-preview-sidebar__field-value { - color: rgba(0, 0, 0, 0.64); - } -} - -@media (prefers-color-scheme: dark) { - .row-preview-sidebar__field-value { - color: rgba(255, 255, 255, 0.64); - } -} - -.row-preview-sidebar__field-value_foreign-key { - display: flex; - align-items: center; - text-decoration: none; -} - -@media (prefers-color-scheme: light) { - .row-preview-sidebar__field-value_foreign-key { - border-bottom: 1px solid rgba(0, 0, 0, 0.64); - } -} - -@media (prefers-color-scheme: dark) { - .row-preview-sidebar__field-value_foreign-key { - border-bottom: 1px solid rgba(255, 255, 255, 0.64); - } -} - -.row-preview-sidebar__field-value-icon { - font-size: 16px; - height: 16px; - margin-left: 8px; - width: 16px; -} - .row-preview-sidebar__image { width: 100%; margin-top: 8px; diff --git a/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.html b/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.html index 6cd5b71f9..459d95da8 100644 --- a/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.html +++ b/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.html @@ -80,26 +80,35 @@

Preview

-
+
{{column.normalizedTitle}} - - {{getForeignKeyValue(column.title)}} - edit - + + + + -
-
- Image -
- {{selectedRow.record[column.title] || '—'}} -
- - {{selectedRow.record[column.title] || '—'}} + + + + +
diff --git a/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.ts b/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.ts index 940cc649f..cc96f5e29 100644 --- a/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.ts +++ b/frontend/src/app/components/dashboard/db-table-row-view/db-table-row-view.component.ts @@ -18,6 +18,9 @@ import { PlaceholderRecordViewComponent } from '../../skeletons/placeholder-reco import { TableStateService } from 'src/app/services/table-state.service'; import { TablesService } from 'src/app/services/tables.service'; import { formatFieldValue } from 'src/app/lib/format-field-value'; +import { ForeignKeyRecordViewComponent } from '../../ui-components/record-view-fields/foreign-key/foreign-key.component'; +import { UIwidgets, recordViewFieldTypes } from 'src/app/consts/record-view-types'; +import { DynamicModule } from 'ng-dynamic-component'; @Component({ selector: 'app-db-table-row-view', @@ -33,7 +36,9 @@ import { formatFieldValue } from 'src/app/lib/format-field-value'; MatListModule, RouterModule, CommonModule, - PlaceholderRecordViewComponent + PlaceholderRecordViewComponent, + ForeignKeyRecordViewComponent, + DynamicModule ] }) export class DbTableRowViewComponent implements OnInit, OnDestroy { @@ -48,19 +53,29 @@ export class DbTableRowViewComponent implements OnInit, OnDestroy { public referencedTables: { table_name: string; displayTableName: string; columns: string[] }[] = []; public referencedTablesURLParams: any; public referencedRecords: {} = {}; + public recordViewComponents; + public widgetsMap: { [key: string]: any } = {}; + public UIwidgets = UIwidgets; constructor( private _tables: TablesService, private _tableState: TableStateService, private _notifications: NotificationsService, + private _connections: ConnectionsService, private route: ActivatedRoute, ) { } ngOnInit(): void { // this.connectionID = this._connections.connectionID; + const connectionType = this._connections.currentConnection.type; + this.recordViewComponents = recordViewFieldTypes[connectionType]; + this.selectedRowCast = this._tableState.cast.subscribe((row) => { this.selectedRow = row; + + console.log('Selected row:', this.selectedRow); + if (row && row.columnsOrder) { const columnsOrder = this.selectedRow.columnsOrder.length ? this.selectedRow.columnsOrder : Object.keys(this.selectedRow.record); diff --git a/frontend/src/app/components/dashboard/db-table/db-table.component.ts b/frontend/src/app/components/dashboard/db-table/db-table.component.ts index b4a586bb5..1610f42ab 100644 --- a/frontend/src/app/components/dashboard/db-table/db-table.component.ts +++ b/frontend/src/app/components/dashboard/db-table/db-table.component.ts @@ -451,6 +451,7 @@ export class DbTableComponent implements OnInit { foreignKeysList: this.tableData.foreignKeysList, widgets: this.tableData.widgets, widgetsList: this.tableData.widgetsList, + fieldsTypes: this.tableData.tableTypes, relatedRecords: this.tableData.relatedRecords || null, link: `/dashboard/${this.connectionID}/${this.name}/entry` }); @@ -469,16 +470,15 @@ export class DbTableComponent implements OnInit { foreignKeysList: null, widgets: null, widgetsList: null, + fieldsTypes: null, relatedRecords: null, link: null }) this._tableRow.fetchTableRow(this.connectionID, foreignKeys.referenced_table_name, {[foreignKeys.referenced_column_name]: row[foreignKeys.referenced_column_name]}) .subscribe(res => { - const filedsTypes = res.structure.reduce((acc, field) => { - acc[field.column_name ] = field.data_type; - return acc; - }, {}); + const foreignKeysList = res.foreignKeys.map((foreignKey: TableForeignKey) => foreignKey.column_name); + const filedsTypes = getTableTypes(res.structure, foreignKeysList); const formattedRecord = Object.entries(res.row).reduce((acc, [key, value]) => { acc[key] = formatFieldValue(value, filedsTypes[key]); @@ -494,7 +494,7 @@ export class DbTableComponent implements OnInit { [foreignKeys.referenced_column_name]: res.row[foreignKeys.referenced_column_name] }, foreignKeys: Object.assign({}, ...res.foreignKeys.map((foreignKey: TableForeignKey) => ({[foreignKey.column_name]: foreignKey}))), - foreignKeysList: res.foreignKeys.map(fk => fk.column_name), + foreignKeysList, widgets: Object.assign({}, ...res.table_widgets.map((widget: Widget) => { let parsedParams; @@ -513,6 +513,7 @@ export class DbTableComponent implements OnInit { }) ), widgetsList: res.table_widgets.map(widget => widget.field_name), + fieldsTypes: filedsTypes, relatedRecords: res.referenced_table_names_and_columns[0], link: `/dashboard/${this.connectionID}/${foreignKeys.referenced_table_name}/entry` }); diff --git a/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts b/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts index 366a08b38..5f71c3e27 100644 --- a/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts +++ b/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts @@ -270,7 +270,7 @@ export class DbTableRowEditComponent implements OnInit { chunkSize: 30, filters }).subscribe((res) => { - + const foreignKeysList = res.foreignKeys.map((foreignKey: TableForeignKey) => foreignKey.column_name); this.relatedRecordsProperties = Object.assign({}, this.relatedRecordsProperties, { [table.table_name]: { connectionID: this.connectionID, @@ -278,7 +278,7 @@ export class DbTableRowEditComponent implements OnInit { columnsOrder: res.list_fields, primaryColumns: res.primaryColumns, foreignKeys: Object.assign({}, ...res.foreignKeys.map((foreignKey: TableForeignKey) => ({[foreignKey.column_name]: foreignKey}))), - foreignKeysList: res.foreignKeys.map(fk => fk.column_name), + foreignKeysList, widgets: Object.assign({}, ...res.widgets.map((widget: Widget) => { let parsedParams; @@ -297,6 +297,7 @@ export class DbTableRowEditComponent implements OnInit { }) ), widgetsList: res.widgets.map(widget => widget.field_name), + fieldsTypes: getTableTypes(res.structure, foreignKeysList), relatedRecords: [], link: `/dashboard/${this.connectionID}/${table.table_name}/entry` } diff --git a/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.css b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.css new file mode 100644 index 000000000..967fc930d --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.css @@ -0,0 +1,25 @@ +.field-view-value { + word-wrap: anywhere; + word-break: keep-all; + white-space: break-spaces; + max-width: 100%; +} + +@media (prefers-color-scheme: light) { + .field-view-value { + color: rgba(0, 0, 0, 0.64); + } +} + +@media (prefers-color-scheme: dark) { + .field-view-value { + color: rgba(255, 255, 255, 0.64); + } +} + +.field-view-icon { + font-size: 16px; + height: 16px; + margin-left: 8px; + width: 16px; +} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.html b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.html new file mode 100644 index 000000000..67e1f2808 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.html @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.ts b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.ts new file mode 100644 index 000000000..fe8eb408e --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.ts @@ -0,0 +1,20 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { TableField, WidgetStructure } from 'src/app/models/table'; + +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-base-record-view-field', + templateUrl: './base-record-view-field.component.html', + styleUrls: ['./base-record-view-field.component.css'], + imports: [CommonModule] +}) +export class BaseRecordViewFieldComponent { + @Input() key: string; + @Input() value: any; + @Input() structure: TableField; + @Input() widgetStructure: WidgetStructure; + // @Input() relations: TableForeignKey; + + @Output() onCopyToClipboard = new EventEmitter(); +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.html b/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.html new file mode 100644 index 000000000..ea5a639c0 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.html @@ -0,0 +1,3 @@ +
+ {{value || '—'}} +
diff --git a/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.ts b/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.ts new file mode 100644 index 000000000..c377e06d0 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.ts @@ -0,0 +1,16 @@ +import { CommonModule } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-binary-data-caption-record-view', + templateUrl: './binary-data-caption.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './binary-data-caption.component.css'], + imports: [CommonModule, MatIconModule, MatButtonModule, MatTooltipModule] +}) +export class BinaryDataCaptionRecordViewComponent extends BaseRecordViewFieldComponent { +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.css b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.css new file mode 100644 index 000000000..603046a74 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.css @@ -0,0 +1,27 @@ +.boolean-icon { + transform: scale(0.85); +} + +@media (prefers-color-scheme: light) { + .boolean-icon-true { + color: #1B5E20; + } + + .boolean-icon-false { + color: #B71C1C; + } +} + +@media (prefers-color-scheme: dark) { + .boolean-icon-true { + color: #4CAF50; + } + + .boolean-icon-false { + color: #E53935; + } +} + + + + diff --git a/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.html b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.html new file mode 100644 index 000000000..6afe2967d --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.html @@ -0,0 +1,3 @@ +check_small +close_small + diff --git a/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.ts b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.ts new file mode 100644 index 000000000..0947ac496 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.ts @@ -0,0 +1,16 @@ +import { Component, Injectable } from '@angular/core'; + + +import { CommonModule } from '@angular/common'; +import { MatIconModule } from '@angular/material/icon'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-boolean-record-view', + templateUrl: './boolean.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './boolean.component.css'], + imports: [MatIconModule, CommonModule] +}) +export class BooleanRecordViewComponent extends BaseRecordViewFieldComponent { +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/code/code.component.css b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.css new file mode 100644 index 000000000..103d0c4a3 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.css @@ -0,0 +1,20 @@ +:host { + width: 100%; +} + +.code-editor-box { + display: block; + border: 1px solid rgba(0, 0, 0, 0.38); + border-radius: 0; + margin-top: 4px; + margin-bottom: 20px; + /* overflow-y: auto; + resize: vertical; */ + overflow: auto; + resize: both; +} + +.code-editor-box ::ng-deep .ngs-code-editor { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/code/code.component.html b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.html new file mode 100644 index 000000000..bc49d5236 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.html @@ -0,0 +1,9 @@ +
+ + +
+ diff --git a/frontend/src/app/components/ui-components/record-view-fields/code/code.component.ts b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.ts new file mode 100644 index 000000000..03ddbcbcd --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.ts @@ -0,0 +1,40 @@ +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { Component, Injectable } from '@angular/core'; +import { CodeEditorModule } from '@ngstack/code-editor'; +import { UiSettingsService } from 'src/app/services/ui-settings.service'; + +@Injectable() +@Component({ + selector: 'app-code-record-view', + templateUrl: './code.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './code.component.css'], + imports: [CodeEditorModule] +}) +export class CodeRecordViewComponent extends BaseRecordViewFieldComponent { + + public codeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + lineNumbers: 'off' + }; + public codeEditorTheme = 'vs-dark'; + + constructor( + private _uiSettings: UiSettingsService, + ) { + super(); + } + + ngOnInit(): void { + this.codeModel = { + language: `${this.widgetStructure.widget_params.language}`, + uri: `${this.key}.json`, + value: this.value, + } + + this.codeEditorTheme = this._uiSettings.editorTheme; + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/color/color.component.css b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.css new file mode 100644 index 000000000..273272d64 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.css @@ -0,0 +1,17 @@ +.color-display { + display: flex; + align-items: center; + gap: 8px; +} + +.color-swatch { + width: 20px; + height: 20px; + border-radius: 4px; + /* border: 1px solid #ccc; */ + flex-shrink: 0; +} + +.field-value { + font-family: monospace; +} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/color/color.component.html b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.html new file mode 100644 index 000000000..6e2da6998 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.html @@ -0,0 +1,8 @@ +
+
+ {{value || '—'}} +
\ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/color/color.component.ts b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.ts new file mode 100644 index 000000000..581351e80 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.ts @@ -0,0 +1,49 @@ +import { Component, Injectable } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import colorString from 'color-string'; +import { CommonModule } from '@angular/common'; + +@Injectable() +@Component({ + selector: 'app-color-record-view', + templateUrl: './color.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './color.component.css'], + imports: [CommonModule] +}) +export class ColorRecordViewComponent extends BaseRecordViewFieldComponent { + get isValidColor(): boolean { + if (!this.value) return false; + return this.parseColor(this.value) !== null; + } + + get normalizedColorForDisplay(): string { + const parsed = this.parseColor(this.value); + if (parsed) { + const [r, g, b] = parsed.value; + return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; + } + return '#000000'; + } + + + private parseColor(color: string): any { + if (!color) return null; + + // Try parsing with color-string + const parsed = colorString.get(color); + if (parsed) return parsed; + + // Try hex without hash + if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { + return colorString.get('#' + color); + } + + return null; + } + + private toHex(n: number): string { + const hex = n.toString(16); + return hex.length === 1 ? '0' + hex : hex; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/country/country.component.css b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.css new file mode 100644 index 000000000..cc2400ea0 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.css @@ -0,0 +1,4 @@ +.country-flag { + margin-right: 6px; + font-size: 1.2em; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/country/country.component.html b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.html new file mode 100644 index 000000000..27895c83a --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.html @@ -0,0 +1,4 @@ + + {{ countryFlag }} + {{ countryName }} + diff --git a/frontend/src/app/components/ui-components/record-view-fields/country/country.component.ts b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.ts new file mode 100644 index 000000000..4b841e4e0 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.ts @@ -0,0 +1,49 @@ +import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; +import { Component, Injectable, OnChanges, OnInit, SimpleChanges } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { CommonModule } from '@angular/common'; + +@Injectable() +@Component({ + selector: 'app-country-record-view', + templateUrl: './country.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './country.component.css'], + imports: [CommonModule] +}) +export class CountryRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + static type = 'country'; + + public countryName: string = ''; + public countryFlag: string = ''; + public showFlag: boolean = true; + + ngOnInit(): void { + this.parseWidgetParams(); + + if (this.value) { + const country = COUNTRIES.find(c => c.code === this.value); + this.countryName = country ? country.name : this.value; + this.countryFlag = getCountryFlag(this.value); + } else { + this.countryName = '—'; + this.countryFlag = ''; + } + } + + private parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; + + if (params.show_flag !== undefined) { + this.showFlag = params.show_flag; + } + } catch (e) { + console.error('Error parsing country widget params:', e); + } + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.css b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.html b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.html new file mode 100644 index 000000000..c6ed53b32 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.html @@ -0,0 +1 @@ +{{formattedDateTime || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.ts b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.ts new file mode 100644 index 000000000..1eb0ff15a --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.ts @@ -0,0 +1,32 @@ +import { Component, Injectable, OnInit } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { format } from 'date-fns'; + +@Injectable() +@Component({ + selector: 'app-date-time-record-view', + templateUrl: './date-time.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './date-time.component.css'], + imports: [] +}) +export class DateTimeRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + static type = 'datetime'; + + public formattedDateTime: string; + + ngOnInit(): void { + if (this.value) { + try { + const date = new Date(this.value); + if (!isNaN(date.getTime())) { + this.formattedDateTime = format(date, "P p"); + } else { + this.formattedDateTime = this.value; + } + } catch (error) { + this.formattedDateTime = this.value; + } + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/date/date.component.css b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/date/date.component.html b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.html new file mode 100644 index 000000000..5edf46f97 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.html @@ -0,0 +1 @@ +{{formattedDate || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/date/date.component.ts b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.ts new file mode 100644 index 000000000..846ccd903 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.ts @@ -0,0 +1,32 @@ +import { Component, Injectable, OnInit } from '@angular/core'; +import { format, parseISO } from 'date-fns'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-date-record-view', + templateUrl: './date.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './date.component.css'], + imports: [] +}) +export class DateRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + static type = 'date'; + + public formattedDate: string; + + ngOnInit(): void { + if (this.value) { + try { + const date = new Date(this.value); + if (!isNaN(date.getTime())) { + this.formattedDate = format(date, "P"); + } else { + this.formattedDate = this.value; + } + } catch (error) { + this.formattedDate = this.value; + } + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/file/file.component.css b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.css new file mode 100644 index 000000000..7f667e698 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.css @@ -0,0 +1,4 @@ +.field-value { + font-style: italic; + color: #666; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/file/file.component.html b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.html new file mode 100644 index 000000000..b05b4f798 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.html @@ -0,0 +1 @@ +{{displayText}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/file/file.component.ts b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.ts new file mode 100644 index 000000000..77dfad7d5 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.ts @@ -0,0 +1,29 @@ +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { Component, Injectable } from '@angular/core'; + +interface Blob { + type: string; + data: any[]; +} + +@Injectable() +@Component({ + selector: 'app-file-record-view', + templateUrl: './file.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './file.component.css'], + imports: [] +}) +export class FileRecordViewComponent extends BaseRecordViewFieldComponent { + get isBlob(): boolean { + return typeof this.value === 'object' && this.value !== null && 'type' in this.value && 'data' in this.value; + } + + get displayText(): string { + if (this.isBlob) { + return 'Binary Data'; + } else if (typeof this.value === 'string' && this.value.length > 20) { + return 'Binary Data'; + } + return this.value ? String(this.value) : '—'; + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.css b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.css new file mode 100644 index 000000000..700cad576 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.css @@ -0,0 +1,17 @@ +.foreign-key-link { + display: flex; + align-items: center; + text-decoration: none; +} + +@media (prefers-color-scheme: light) { + .foreign-key-link { + border-bottom: 1px solid rgba(0, 0, 0, 0.64); + } +} + +@media (prefers-color-scheme: dark) { + .foreign-key-link { + border-bottom: 1px solid rgba(255, 255, 255, 0.64); + } +} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.html b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.html new file mode 100644 index 000000000..9eaaa8314 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.html @@ -0,0 +1,8 @@ + + {{displayValue}} + edit + diff --git a/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.ts b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.ts new file mode 100644 index 000000000..569dff91e --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.ts @@ -0,0 +1,28 @@ +import { Component, EventEmitter, Injectable, Input, Output } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { ClipboardModule } from '@angular/cdk/clipboard'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterModule } from '@angular/router'; + +@Injectable() +@Component({ + selector: 'app-foreign-key-record-view', + templateUrl: './foreign-key.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './foreign-key.component.css'], + imports: [MatIconModule, RouterModule, CommonModule] +}) +export class ForeignKeyRecordViewComponent extends BaseRecordViewFieldComponent { + @Input() link: string; + @Input() primaryKeysParams: any; + @Input() displayValue: string; + + @Output() onForeignKeyClick = new EventEmitter<{foreignKey: any, value: string}>(); + + constructor() { + super(); + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/id/id.component.css b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.css new file mode 100644 index 000000000..277f14993 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.css @@ -0,0 +1,9 @@ +.field-display-id { + display: flex; + align-items: center; +} + +.field-value-id { + font-weight: 500; + flex-grow: 1; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/id/id.component.html b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.html new file mode 100644 index 000000000..a7259d1fb --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.html @@ -0,0 +1 @@ +{{value || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/id/id.component.ts b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.ts new file mode 100644 index 000000000..0289f4b78 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.ts @@ -0,0 +1,13 @@ +import { Component, Injectable } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-id-record-view', + templateUrl: './id.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './id.component.css'], + imports: [] +}) +export class IdRecordViewComponent extends BaseRecordViewFieldComponent { +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/image/image.component.css b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.css new file mode 100644 index 000000000..5decf8e34 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.css @@ -0,0 +1,4 @@ +.image-preview { + width: 100%; + height: auto; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/image/image.component.html b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.html new file mode 100644 index 000000000..f4f494885 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.html @@ -0,0 +1,5 @@ +[Image URL] +Image +{{value || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/image/image.component.ts b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.ts new file mode 100644 index 000000000..e2d9f7df4 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.ts @@ -0,0 +1,22 @@ +import { CommonModule } from '@angular/common'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { Component, Injectable } from '@angular/core'; + +@Injectable() +@Component({ + selector: 'app-image-record-view', + templateUrl: './image.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './image.component.css'], + imports: [CommonModule] +}) +export class ImageRecordViewComponent extends BaseRecordViewFieldComponent { + get isUrl(): boolean { + if (!this.value) return false; + try { + new URL(this.value); + return true; + } catch { + return false; + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.css b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.css new file mode 100644 index 000000000..103d0c4a3 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.css @@ -0,0 +1,20 @@ +:host { + width: 100%; +} + +.code-editor-box { + display: block; + border: 1px solid rgba(0, 0, 0, 0.38); + border-radius: 0; + margin-top: 4px; + margin-bottom: 20px; + /* overflow-y: auto; + resize: vertical; */ + overflow: auto; + resize: both; +} + +.code-editor-box ::ng-deep .ngs-code-editor { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.html b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.html new file mode 100644 index 000000000..d89f394c2 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.html @@ -0,0 +1,8 @@ +
+ + +
diff --git a/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.ts b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.ts new file mode 100644 index 000000000..e6fb6fa3c --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.ts @@ -0,0 +1,39 @@ +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { Component, Injectable } from '@angular/core'; +import { CodeEditorModule } from '@ngstack/code-editor'; +import { UiSettingsService } from 'src/app/services/ui-settings.service'; + +@Injectable() +@Component({ + selector: 'app-json-editor-record-view', + templateUrl: './json-editor.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './json-editor.component.css'], + imports: [CodeEditorModule] +}) +export class JsonEditorRecordViewComponent extends BaseRecordViewFieldComponent { + public codeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + lineNumbers: 'off' + }; + public codeEditorTheme = 'vs-dark'; + + constructor( + private _uiSettings: UiSettingsService, + ) { + super(); + } + + ngOnInit(): void { + this.codeModel = { + language: 'json', + uri: `${this.key}.json`, + value: this.value + } + + this.codeEditorTheme = this._uiSettings.editorTheme; + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.css b/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.html b/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.html new file mode 100644 index 000000000..7dfcb3f4b --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.html @@ -0,0 +1 @@ +{{value || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.ts b/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.ts new file mode 100644 index 000000000..84c4f8733 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.ts @@ -0,0 +1,14 @@ +import { Component, Injectable } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-long-text-record-view', + templateUrl: './long-text.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './long-text.component.css'], + imports: [] +}) +export class LongTextRecordViewComponent extends BaseRecordViewFieldComponent { + +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/money/money.component.css b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/money/money.component.html b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.html new file mode 100644 index 000000000..cba669daf --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.html @@ -0,0 +1,3 @@ + + {{formattedValue || '—'}} + diff --git a/frontend/src/app/components/ui-components/record-view-fields/money/money.component.ts b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.ts new file mode 100644 index 000000000..f0154db66 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.ts @@ -0,0 +1,57 @@ +import { Component, Injectable, OnInit } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { getCurrencyByCode } from 'src/app/consts/currencies'; + +@Injectable() +@Component({ + selector: 'app-money-record-view', + templateUrl: './money.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './money.component.css'], + imports: [] +}) +export class MoneyRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + public displayCurrency: string = ''; + public currencySymbol: string = ''; + + ngOnInit(): void { + // Get currency from widget params + this.displayCurrency = ''; + if (this.widgetStructure && this.widgetStructure.widget_params && this.widgetStructure.widget_params.default_currency) { + this.displayCurrency = this.widgetStructure.widget_params.default_currency; + const currency = getCurrencyByCode(this.displayCurrency); + this.currencySymbol = currency ? currency.symbol : ''; + } + } + + get formattedValue(): string { + if (!this.value) { + return ''; + } + + let amount: number | string; + let currency: string = this.displayCurrency; + + if (typeof this.value === 'object' && this.value.amount !== undefined) { + amount = this.value.amount; + if (this.value.currency) { + currency = this.value.currency; + const currencyObj = getCurrencyByCode(currency); + this.currencySymbol = currencyObj ? currencyObj.symbol : ''; + } + } else { + amount = this.value; + } + + if (typeof amount === 'string') { + amount = parseFloat(amount); + } + + if (isNaN(amount as number)) { + return ''; + } + + const decimalPlaces = this.widgetStructure?.widget_params?.decimal_places ?? 2; + return `${this.currencySymbol}${(amount as number).toFixed(decimalPlaces)}`; + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/number/number.component.css b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/number/number.component.html b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.html new file mode 100644 index 000000000..8f3bd4c32 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.html @@ -0,0 +1 @@ +{{displayValue}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/number/number.component.ts b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.ts new file mode 100644 index 000000000..bb3773ddf --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.ts @@ -0,0 +1,35 @@ +import { Component, Injectable } from '@angular/core'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import convert from 'convert'; + +@Injectable() +@Component({ + selector: 'app-number-record-view', + templateUrl: './number.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './number.component.css'], + imports: [] +}) +export class NumberRecordViewComponent extends BaseRecordViewFieldComponent { + + get displayValue(): string { + if (this.value == null || this.value === '') { + return '—'; + } + + const unit = this.widgetStructure?.widget_params?.unit; + + if (!unit) { + return this.value.toString(); + } + + try { + const convertedValue = convert(parseFloat(this.value), unit).to('best'); + // Format number to max 2 decimal places without trailing zeros + const formattedQuantity = parseFloat(convertedValue.quantity.toFixed(2)).toString(); + return `${formattedQuantity} ${convertedValue.unit}`; + } catch (error) { + console.warn('Unit conversion failed:', error); + return this.value.toString(); + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/password/password.component.css b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/password/password.component.html b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.html new file mode 100644 index 000000000..a1e3ab35b --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.html @@ -0,0 +1 @@ +•••••••• diff --git a/frontend/src/app/components/ui-components/record-view-fields/password/password.component.ts b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.ts new file mode 100644 index 000000000..a61eac033 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.ts @@ -0,0 +1,13 @@ +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { Component, Injectable } from '@angular/core'; + + +@Injectable() +@Component({ + selector: 'app-password-record-view', + templateUrl: './password.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './password.component.css'], + imports: [] +}) +export class PasswordRecordViewComponent extends BaseRecordViewFieldComponent { +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.css b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.css new file mode 100644 index 000000000..094c8843b --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.css @@ -0,0 +1,15 @@ +.phone-content { + display: flex; + align-items: center; + gap: 8px; +} + +.country-flag { + font-size: 16px; + cursor: help; + user-select: none; +} + +.phone-link { + color: var(--primary-color); +} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.html b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.html new file mode 100644 index 000000000..c7aad71e8 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.html @@ -0,0 +1,4 @@ + + {{ countryFlag }} + {{ formattedNumber || value }} + \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.ts b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.ts new file mode 100644 index 000000000..133a663d9 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.ts @@ -0,0 +1,58 @@ +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { Component, Injectable, OnInit } from '@angular/core'; +import { parsePhoneNumber } from 'libphonenumber-js'; +import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { CommonModule } from '@angular/common'; + +@Injectable() +@Component({ + selector: 'app-phone-record-view', + templateUrl: './phone.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './phone.component.css'], + imports: [MatTooltipModule, CommonModule] +}) +export class PhoneRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + public countryFlag: string = ''; + public countryName: string = ''; + public formattedNumber: string = ''; + + ngOnInit(): void { + this.parsePhoneNumber(); + } + + private parsePhoneNumber(): void { + if (!this.value || typeof this.value !== 'string') { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = ''; + return; + } + + try { + const phoneNumber = parsePhoneNumber(this.value); + + if (phoneNumber && phoneNumber.country) { + const country = COUNTRIES.find(c => c.code === phoneNumber.country); + + if (country) { + this.countryFlag = getCountryFlag(country.code); + this.countryName = country.name; + this.formattedNumber = phoneNumber.formatInternational(); + } else { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } else { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } catch (error) { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/point/point.component.css b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/point/point.component.html b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.html new file mode 100644 index 000000000..5ea1fcb37 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.html @@ -0,0 +1,5 @@ + + {{ formattedPoint }} + diff --git a/frontend/src/app/components/ui-components/record-view-fields/point/point.component.ts b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.ts new file mode 100644 index 000000000..28aa20ea1 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.ts @@ -0,0 +1,44 @@ +import { Component, Injectable, OnInit } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +@Injectable() +@Component({ + selector: 'app-point-record-view', + templateUrl: './point.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './point.component.css'], + imports: [MatTooltipModule] +}) +export class PointRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + static type = 'point'; + + formattedPoint: string; + + ngOnInit() { + this.formatPoint(); + } + + private formatPoint() { + if (!this.value) { + this.formattedPoint = ''; + return; + } + + try { + if (typeof this.value === 'string') { + // Handle string format like "(x,y)" or "x,y" + const pointStr = this.value.trim().replace(/[()]/g, ''); + const [x, y] = pointStr.split(',').map(coord => parseFloat(coord.trim())); + this.formattedPoint = `(${x}, ${y})`; + } else if (typeof this.value === 'object') { + // Handle object format like {x: 1, y: 2} + const x = this.value.x || this.value[0]; + const y = this.value.y || this.value[1]; + this.formattedPoint = `(${x}, ${y})`; + } + } catch (e) { + this.formattedPoint = String(this.value); + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/select/select.component.css b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/select/select.component.html b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.html new file mode 100644 index 000000000..8f3bd4c32 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.html @@ -0,0 +1 @@ +{{displayValue}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/select/select.component.ts b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.ts new file mode 100644 index 000000000..7b196d05f --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.ts @@ -0,0 +1,38 @@ +import { Component, Injectable, OnInit } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-select-record-view', + templateUrl: './select.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './select.component.css'], + imports: [] +}) +export class SelectRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + public displayValue: string; + + ngOnInit(): void { + this.setDisplayValue(); + } + + private setDisplayValue(): void { + if (!this.value) { + this.displayValue = '—'; + return; + } + + if (this.widgetStructure?.widget_params?.options) { + // Find the matching option based on value and use its label + const option = this.widgetStructure.widget_params.options.find( + (opt: { value: any, label: string }) => opt.value === this.value + ); + this.displayValue = option ? option.label : this.value; + } else if (this.structure?.data_type_params) { + // If no widget structure but we have data_type_params, just use the value + this.displayValue = this.value; + } else { + this.displayValue = this.value; + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.css b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.html b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.html new file mode 100644 index 000000000..7dfcb3f4b --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.html @@ -0,0 +1 @@ +{{value || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.ts b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.ts new file mode 100644 index 000000000..6af39d952 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.ts @@ -0,0 +1,12 @@ +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { Component, Injectable } from '@angular/core'; + +@Injectable() +@Component({ + selector: 'app-static-text-record-view', + templateUrl: './static-text.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './static-text.component.css'], + imports: [] +}) +export class StaticTextRecordViewComponent extends BaseRecordViewFieldComponent { +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/text/text.component.css b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/components/ui-components/record-view-fields/text/text.component.html b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.html new file mode 100644 index 000000000..7dfcb3f4b --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.html @@ -0,0 +1 @@ +{{value || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/text/text.component.ts b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.ts new file mode 100644 index 000000000..788c2c8fb --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.ts @@ -0,0 +1,13 @@ +import { Component, Injectable } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-text-record-view', + templateUrl: './text.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './text.component.css'], + imports: [] +}) +export class TextRecordViewComponent extends BaseRecordViewFieldComponent { +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.css b/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.css new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.html b/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.html new file mode 100644 index 000000000..0f4b46264 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.html @@ -0,0 +1 @@ +{{ formattedInterval }} diff --git a/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.ts b/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.ts new file mode 100644 index 000000000..278f1c60f --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.ts @@ -0,0 +1,37 @@ +import { Component, Injectable, OnInit } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-time-interval-record-view', + templateUrl: './time-interval.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './time-interval.component.css'], + imports: [] +}) +export class TimeIntervalRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + + formattedInterval: string; + + ngOnInit() { + if (!this.value) { + this.formattedInterval = '—'; + return; + } + + try { + const interval = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; + let parts = []; + + if (interval.days) parts.push(`${interval.days}d`); + if (interval.hours) parts.push(`${interval.hours}h`); + if (interval.minutes) parts.push(`${interval.minutes}m`); + if (interval.seconds) parts.push(`${interval.seconds}s`); + if (interval.milliseconds) parts.push(`${interval.milliseconds}ms`); + + this.formattedInterval = parts.length > 0 ? parts.join(' ') : '0'; + } catch (e) { + this.formattedInterval = String(this.value); + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/time/time.component.css b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.css new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.css @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/ui-components/record-view-fields/time/time.component.html b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.html new file mode 100644 index 000000000..5016beffb --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.html @@ -0,0 +1 @@ +{{formattedTime || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/time/time.component.ts b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.ts new file mode 100644 index 000000000..ac284dbd9 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.ts @@ -0,0 +1,37 @@ +import { Component, Injectable, OnInit } from '@angular/core'; + +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { format } from 'date-fns'; + +@Injectable() +@Component({ + selector: 'app-time-record-view', + templateUrl: './time.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './time.component.css'], + imports: [] +}) +export class TimeRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + static type = 'time'; + + public formattedTime: string; + + ngOnInit(): void { + if (this.value) { + try { + if (this.value.includes(':')) { + // Handle time string format + this.formattedTime = this.value; + } else { + const date = new Date(this.value); + if (!isNaN(date.getTime())) { + this.formattedTime = format(date, 'HH:mm:ss'); + } else { + this.formattedTime = this.value; + } + } + } catch (error) { + this.formattedTime = this.value; + } + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/url/url.component.css b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.css new file mode 100644 index 000000000..d8c64e6f6 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.css @@ -0,0 +1,15 @@ +.url-link { + display: flex; + align-items: center; + gap: 4px; + color: var(--color-primaryPalette-500); + word-break: break-all; +} + +.url-link__icon { + flex-shrink: 0; + margin-bottom: -2px; + width: 16px; + height: 16px; + font-size: 16px; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/url/url.component.html b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.html new file mode 100644 index 000000000..51fae391e --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.html @@ -0,0 +1,8 @@ + + link + {{value || '—'}} + +{{value || '—'}} diff --git a/frontend/src/app/components/ui-components/record-view-fields/url/url.component.ts b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.ts new file mode 100644 index 000000000..df4d3187f --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.ts @@ -0,0 +1,25 @@ +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { CommonModule } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; +import { MatIconModule } from '@angular/material/icon'; + +@Injectable() +@Component({ + selector: 'app-url-record-view', + templateUrl: './url.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './url.component.css'], + imports: [CommonModule, MatIconModule] +}) +export class UrlRecordViewComponent extends BaseRecordViewFieldComponent { + static type = 'url'; + + get isValidUrl(): boolean { + if (!this.value) return false; + try { + new URL(this.value); + return true; + } catch { + return false; + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.css b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.css new file mode 100644 index 000000000..50c4ef509 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.css @@ -0,0 +1,7 @@ +.uuid-text { + font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif; + font-stretch: condensed; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.html b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.html new file mode 100644 index 000000000..2034cc2c5 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.html @@ -0,0 +1 @@ +{{ value || 'Null' }} \ No newline at end of file diff --git a/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.ts b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.ts new file mode 100644 index 000000000..226fcadf2 --- /dev/null +++ b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.ts @@ -0,0 +1,12 @@ +import { Component, Injectable } from '@angular/core'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; + +@Injectable() +@Component({ + selector: 'app-uuid-record-view', + templateUrl: './uuid.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './uuid.component.css'], + imports: [] +}) +export class UuidRecordViewComponent extends BaseRecordViewFieldComponent { +} \ No newline at end of file diff --git a/frontend/src/app/consts/record-view-types.ts b/frontend/src/app/consts/record-view-types.ts new file mode 100644 index 000000000..e0598c62d --- /dev/null +++ b/frontend/src/app/consts/record-view-types.ts @@ -0,0 +1,299 @@ +import { BooleanRecordViewComponent } from 'src/app/components/ui-components/record-view-fields/boolean/boolean.component'; +import { CodeRecordViewComponent } from '../components/ui-components/record-view-fields/code/code.component'; +import { ColorRecordViewComponent } from '../components/ui-components/record-view-fields/color/color.component'; +import { CountryRecordViewComponent } from '../components/ui-components/record-view-fields/country/country.component'; +import { DateRecordViewComponent } from '../components/ui-components/record-view-fields/date/date.component'; +import { DateTimeRecordViewComponent } from '../components/ui-components/record-view-fields/date-time/date-time.component'; +import { FileRecordViewComponent } from '../components/ui-components/record-view-fields/file/file.component'; +import { ForeignKeyRecordViewComponent } from '../components/ui-components/record-view-fields/foreign-key/foreign-key.component'; +import { IdRecordViewComponent } from '../components/ui-components/record-view-fields/id/id.component'; +import { ImageRecordViewComponent } from '../components/ui-components/record-view-fields/image/image.component'; +import { JsonEditorRecordViewComponent } from '../components/ui-components/record-view-fields/json-editor/json-editor.component'; +import { LongTextRecordViewComponent } from 'src/app/components/ui-components/record-view-fields/long-text/long-text.component'; +import { MoneyRecordViewComponent } from '../components/ui-components/record-view-fields/money/money.component'; +import { NumberRecordViewComponent } from '../components/ui-components/record-view-fields/number/number.component'; +import { PasswordRecordViewComponent } from '../components/ui-components/record-view-fields/password/password.component'; +import { PhoneRecordViewComponent } from '../components/ui-components/record-view-fields/phone/phone.component'; +import { PointRecordViewComponent } from '../components/ui-components/record-view-fields/point/point.component'; +import { SelectRecordViewComponent } from '../components/ui-components/record-view-fields/select/select.component'; +import { StaticTextRecordViewComponent } from '../components/ui-components/record-view-fields/static-text/static-text.component'; +import { TextRecordViewComponent } from 'src/app/components/ui-components/record-view-fields/text/text.component'; +import { TimeRecordViewComponent } from '../components/ui-components/record-view-fields/time/time.component'; +import { TimeIntervalRecordViewComponent } from '../components/ui-components/record-view-fields/time-interval/time-interval.component'; +import { UrlRecordViewComponent } from '../components/ui-components/record-view-fields/url/url.component'; +import { UuidRecordViewComponent } from '../components/ui-components/record-view-fields/uuid/uuid.component'; + +export const UIwidgets = { + Default: '', + Boolean: BooleanRecordViewComponent, + Date: DateRecordViewComponent, + Time: TimeRecordViewComponent, + DateTime: DateTimeRecordViewComponent, + JSON: JsonEditorRecordViewComponent, + Textarea: LongTextRecordViewComponent, + String: TextRecordViewComponent, + Readonly: StaticTextRecordViewComponent, + Number: NumberRecordViewComponent, + Select: SelectRecordViewComponent, + Password: PasswordRecordViewComponent, + File: FileRecordViewComponent, + Code: CodeRecordViewComponent, + Image: ImageRecordViewComponent, + URL: UrlRecordViewComponent, + Country: CountryRecordViewComponent, + Phone: PhoneRecordViewComponent, + Money: MoneyRecordViewComponent, + Foreign_key: ForeignKeyRecordViewComponent, + Color: ColorRecordViewComponent, + UUID: UuidRecordViewComponent, +} + +export const recordViewFieldTypes = { + postgres: { + // numbers (number) + real: NumberRecordViewComponent, + "double precision": NumberRecordViewComponent, + smallint: NumberRecordViewComponent, + integer: NumberRecordViewComponent, + bigint: NumberRecordViewComponent, + numeric: NumberRecordViewComponent, + + //boolean (checkbox) + boolean: BooleanRecordViewComponent, + + //datetime (datepicker) + "timestamp without time zone": DateTimeRecordViewComponent, + "timestamp with time zone": DateTimeRecordViewComponent, + "time without time zone": TimeRecordViewComponent, + "time with time zone": TimeRecordViewComponent, + date: DateRecordViewComponent, + abstime: DateTimeRecordViewComponent, + realtime: DateTimeRecordViewComponent, + interval: TimeIntervalRecordViewComponent, + + // short text (text) + "character varying": TextRecordViewComponent, + macaddr: TextRecordViewComponent, + macaddr8: TextRecordViewComponent, + cidr: TextRecordViewComponent, + inet: TextRecordViewComponent, + uuid: UuidRecordViewComponent, + + //long text (textarea) + text: LongTextRecordViewComponent, + xml: LongTextRecordViewComponent, + + //select (select) + enum: SelectRecordViewComponent, + + // json-editor + json: JsonEditorRecordViewComponent, + jsonb: JsonEditorRecordViewComponent, + ARRAY: JsonEditorRecordViewComponent, + + //file + bytea: FileRecordViewComponent, + + //etc + money: MoneyRecordViewComponent, + + //mess (math) + point: PointRecordViewComponent, + line: TextRecordViewComponent, + circle: TextRecordViewComponent, + path: TextRecordViewComponent, + box: TextRecordViewComponent, + lseg: TextRecordViewComponent, + + "foreign key": ForeignKeyRecordViewComponent + }, + + mysql: { + // numbers (number) + tinyint: NumberRecordViewComponent, + smallint: NumberRecordViewComponent, + mediumint: NumberRecordViewComponent, + int: NumberRecordViewComponent, + bigint: NumberRecordViewComponent, + decimal: NumberRecordViewComponent, + float: NumberRecordViewComponent, + double: NumberRecordViewComponent, + year: NumberRecordViewComponent, + + //boolean (radiogroup) + boolean: BooleanRecordViewComponent, + + //datetime (datepicker) + date: DateRecordViewComponent, + time: TimeRecordViewComponent, + datetime: DateTimeRecordViewComponent, + timestamp: DateTimeRecordViewComponent, + + // short text (text) + char: TextRecordViewComponent, + varchar: TextRecordViewComponent, + + //long text (textarea) + text: LongTextRecordViewComponent, + tinytext: LongTextRecordViewComponent, + mediumtext: LongTextRecordViewComponent, + longtext: LongTextRecordViewComponent, + + json: JsonEditorRecordViewComponent, //json-editor + + //select (select) + enum: SelectRecordViewComponent, + + //file + binary: FileRecordViewComponent, + varbinary: FileRecordViewComponent, + blob: FileRecordViewComponent, + tinyblob: FileRecordViewComponent, + mediumblob: FileRecordViewComponent, + longblob: FileRecordViewComponent, + + //etc + set: TextRecordViewComponent, + + "foreign key": ForeignKeyRecordViewComponent + }, + + oracledb: { + // numbers (number) + NUMBER: NumberRecordViewComponent, + FLOAT: NumberRecordViewComponent, + BINARY_FLOAT: NumberRecordViewComponent, + BINARY_DOUBLE: NumberRecordViewComponent, + "INTERVAL YEAR": NumberRecordViewComponent, + "INTERVAL DAY": NumberRecordViewComponent, + + //datetime (datepicker) + DATE: DateRecordViewComponent, + TIMESTAMP: DateTimeRecordViewComponent, + + // short text (text) + CHAR: TextRecordViewComponent, + NCHAR: TextRecordViewComponent, + CLOB: TextRecordViewComponent, + NCLOB: TextRecordViewComponent, + VARCHAR2: TextRecordViewComponent, + VARCHAR: TextRecordViewComponent, + NVARCHAR2: TextRecordViewComponent, + + //file + BLOB: FileRecordViewComponent, + BFILE: FileRecordViewComponent, + RAW: FileRecordViewComponent, + "LONG RAW": FileRecordViewComponent, + LONG: FileRecordViewComponent, + + "foreign key": ForeignKeyRecordViewComponent + }, + + mssql: { + // numbers (number) + bigint: NumberRecordViewComponent, + int: NumberRecordViewComponent, + smallint: NumberRecordViewComponent, + tinyint: NumberRecordViewComponent, + decimal: NumberRecordViewComponent, + bitdecimal: NumberRecordViewComponent, + numeric: NumberRecordViewComponent, + real: NumberRecordViewComponent, + + // short text (text) + uniqueidentifier: UuidRecordViewComponent, + char: TextRecordViewComponent, + varchar: TextRecordViewComponent, + + //long text (textarea) + text: LongTextRecordViewComponent, + nchar: LongTextRecordViewComponent, + nvarchar: LongTextRecordViewComponent, + ntext: LongTextRecordViewComponent, + + //datetime (datepicker) + date: DateRecordViewComponent, + datetime: DateTimeRecordViewComponent, + smalldatetime: DateTimeRecordViewComponent, + timestamp: DateTimeRecordViewComponent, + + //file + binary: FileRecordViewComponent, + varbinary: FileRecordViewComponent, + image: ImageRecordViewComponent, + + // etc + money: MoneyRecordViewComponent, + smallmoney: MoneyRecordViewComponent, + + "foreign key": ForeignKeyRecordViewComponent + }, + mongodb: { + // numbers (number) + number: NumberRecordViewComponent, + double: NumberRecordViewComponent, + int32: NumberRecordViewComponent, + long: NumberRecordViewComponent, + decimal128: NumberRecordViewComponent, + + //boolean (radiogroup) + boolean: BooleanRecordViewComponent, + + //datetime (datepicker) + date: DateRecordViewComponent, + timestamp: DateTimeRecordViewComponent, + + // short text (text) + string: TextRecordViewComponent, + regexp: TextRecordViewComponent, + objectid: TextRecordViewComponent, + + //file + binary: FileRecordViewComponent, + + //json + object: JsonEditorRecordViewComponent, + array: JsonEditorRecordViewComponent, + + //etc + unknown: TextRecordViewComponent, + + "foreign key": ForeignKeyRecordViewComponent + }, + dynamodb: { + string: TextRecordViewComponent, + number: NumberRecordViewComponent, + boolean: BooleanRecordViewComponent, + null: StaticTextRecordViewComponent, + array: JsonEditorRecordViewComponent, + json: JsonEditorRecordViewComponent, + binary: FileRecordViewComponent, + }, + cassandra: { + int: NumberRecordViewComponent, + bigint: NumberRecordViewComponent, + varint: NumberRecordViewComponent, + decimal: NumberRecordViewComponent, + float: NumberRecordViewComponent, + double: NumberRecordViewComponent, + + boolean: BooleanRecordViewComponent, + + timeuuid: IdRecordViewComponent, + + timestamp: DateTimeRecordViewComponent, + date: DateRecordViewComponent, + time: TimeRecordViewComponent, + + uuid: UuidRecordViewComponent, + varchar: TextRecordViewComponent, + inet: TextRecordViewComponent, + ascii: TextRecordViewComponent, + text: LongTextRecordViewComponent, + + list: JsonEditorRecordViewComponent, + map: JsonEditorRecordViewComponent, + set: JsonEditorRecordViewComponent, + }, +} diff --git a/frontend/src/app/models/table.ts b/frontend/src/app/models/table.ts index 8abf1eef1..43fe6504d 100644 --- a/frontend/src/app/models/table.ts +++ b/frontend/src/app/models/table.ts @@ -48,6 +48,7 @@ export interface TableRow { foreignKeysList: string[], widgets: Widget[], widgetsList: string[], + fieldsTypes: { [key: string]: string }, relatedRecords: { referenced_on_column_name: string, referenced_by: []