diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css index 4a467359..5b5bf7c6 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css @@ -427,6 +427,95 @@ th.mat-header-cell, td.mat-cell { text-align: left; } +/* Sortable header styles */ +.sortable-header__content { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + gap: 4px; +} + +.sortable-header__text { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sortable-header__button { + opacity: 0; + width: 24px; + height: 24px; + line-height: 24px; + flex-shrink: 0; + transition: opacity 0.2s ease; +} + +.sortable-header:hover .sortable-header__button, +.sortable-header_active .sortable-header__button { + opacity: 1; +} + +.sortable-header__icon { + font-size: 18px; + width: 18px; + height: 18px; + color: rgba(0, 0, 0, 0.54); +} + +.sortable-header_active .sortable-header__icon { + color: var(--color-primaryPalette-500); +} + +@media (prefers-color-scheme: dark) { + .sortable-header__icon { + color: rgba(255, 255, 255, 0.54); + } + + .sortable-header_active .sortable-header__icon { + color: var(--color-primaryPalette-300); + } +} + +/* Sort menu styles */ +.sort-menu__item { + display: flex; + align-items: center; + gap: 8px; +} + +.sort-menu__item mat-icon { + font-size: 18px; + width: 18px; + height: 18px; +} + +.sort-menu__item-content { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 10px; + border-radius: 4px; + margin: -4px -10px; +} + +.sort-menu__item-content_active { + background-color: rgba(0, 0, 0, 0.04); +} + +@media (prefers-color-scheme: dark) { + .sort-menu__item-content_active { + background-color: rgba(255, 255, 255, 0.06); + } +} + +.sort-menu__lock-icon { + font-size: 16px; + width: 16px; + height: 16px; +} + .db-table-cell-checkbox { display: flex; align-items: center; diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html index 48798d9b..589112f4 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html @@ -235,7 +235,44 @@

{{ displayName }}

- {{ tableData.dataNormalizedColumns[column] }} + +
+ {{ tableData.dataNormalizedColumns[column] }} + +
+ + + + + + +
diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts index fef902f3..281c8878 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts @@ -17,6 +17,7 @@ import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatSelectModule } from '@angular/material/select'; import { MatSort, MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MatDividerModule } from '@angular/material/divider'; import { MatTooltipModule } from '@angular/material/tooltip'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; import JsonURL from '@jsonurl/jsonurl'; @@ -40,6 +41,7 @@ import { ConnectionsService } from 'src/app/services/connections.service'; import { NotificationsService } from 'src/app/services/notifications.service'; import { TableRowService } from 'src/app/services/table-row.service'; import { TableStateService } from 'src/app/services/table-state.service'; +import { UiSettingsService } from 'src/app/services/ui-settings.service'; import { tableDisplayTypes, UIwidgets } from '../../../consts/table-display-types'; import { normalizeTableName } from '../../../lib/normalize'; import { PlaceholderTableDataComponent } from '../../skeletons/placeholder-table-data/placeholder-table-data.component'; @@ -81,6 +83,7 @@ export interface Folder { MatAutocompleteModule, MatSelectModule, MatMenuModule, + MatDividerModule, MatTooltipModule, ClipboardModule, DragDropModule, @@ -150,11 +153,14 @@ export class DbTableViewComponent implements OnInit { @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; + public defaultSort: { column: string; direction: 'asc' | 'desc' } | null = null; + constructor( private _tableState: TableStateService, private _notifications: NotificationsService, private _tableRow: TableRowService, private _connections: ConnectionsService, + private _uiSettings: UiSettingsService, private route: ActivatedRoute, public router: Router, public dialog: MatDialog, @@ -164,7 +170,15 @@ export class DbTableViewComponent implements OnInit { this.tableData.paginator = this.paginator; this.tableData.sort = this.sort; - // this.sort.sortChange.subscribe(() => { this.paginator.pageIndex = 0 }); + + // Load default sort from settings + this.loadDefaultSort(); + + // Initialize sort - default sort takes priority + if (this.defaultSort) { + this.sort.active = this.defaultSort.column; + this.sort.direction = this.defaultSort.direction; + } merge(this.sort.sortChange, this.paginator.page) .pipe( @@ -246,6 +260,49 @@ export class DbTableViewComponent implements OnInit { return this.tableData.sortByColumns.includes(column) || !this.tableData.sortByColumns.length; } + applySort(column: string, direction: 'asc' | 'desc') { + // If clicking on already selected sort - clear it + if (this.sort.active === column && this.sort.direction === direction) { + // If this column was the default, remove the default too + if (this.defaultSort?.column === column) { + this.defaultSort = null; + this._uiSettings.updateTableSetting(this.connectionID, this.name, 'defaultSort', null); + } + // Clear sort + this.sort.active = ''; + this.sort.direction = ''; + this.sort.sortChange.emit({ active: '', direction: '' }); + } else { + this.sort.active = column; + this.sort.direction = direction; + this.sort.sortChange.emit({ active: column, direction: direction }); + } + } + + loadDefaultSort() { + const tableSettings = this._uiSettings.settings?.connections?.[this.connectionID]?.tables?.[this.name]; + if (tableSettings?.defaultSort) { + this.defaultSort = tableSettings.defaultSort; + } + } + + toggleDefaultSort(column: string) { + if (this.isDefaultSort(column)) { + // Remove default sort + this.defaultSort = null; + this._uiSettings.updateTableSetting(this.connectionID, this.name, 'defaultSort', null); + } else { + // Set current sort as default + const direction = this.sort.active === column ? this.sort.direction : 'asc'; + this.defaultSort = { column, direction: direction as 'asc' | 'desc' }; + this._uiSettings.updateTableSetting(this.connectionID, this.name, 'defaultSort', this.defaultSort); + } + } + + isDefaultSort(column: string): boolean { + return this.defaultSort?.column === column; + } + isForeignKey(column: string) { return this.tableData.foreignKeysList.includes(column); } diff --git a/frontend/src/app/models/ui-settings.ts b/frontend/src/app/models/ui-settings.ts index 90c10337..4fd8a9c4 100644 --- a/frontend/src/app/models/ui-settings.ts +++ b/frontend/src/app/models/ui-settings.ts @@ -5,6 +5,10 @@ export interface GlobalSettingsUI { export interface TableSettingsUI { shownColumns: string[]; + defaultSort?: { + column: string; + direction: 'asc' | 'desc'; + }; } export interface ConnectionSettingsUI {