From 2bd1d409ea56768c1cb2093f29ab963cf744b965 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 3 Mar 2025 20:36:03 +0000 Subject: [PATCH 01/30] an attempt to use material tabs for property/taxlot tabs in organization column settings --- src/app/core/navigation/navigation.service.ts | 16 +--- .../columns/columns.component.html | 35 +++++++ .../columns/columns.component.ts | 94 +++++++++++++++++++ .../organizations/columns/columns.routes.ts | 52 ++++++++++ .../data-types-properties.component.ts | 12 +++ .../data-types-taxlots.component.ts | 12 +++ .../data_types/data-types.component.html | 1 + .../geocoding-properties.component.ts | 12 +++ .../geocoding/geocoding-taxlots.component.ts | 12 +++ .../geocoding/geocoding.component.html | 1 + .../columns/list/list-properties.component.ts | 12 +++ .../columns/list/list-taxlots.component.ts | 12 +++ .../columns/list/list.component.html | 1 + .../matching-criteria-properties.component.ts | 12 +++ .../matching-criteria-taxlots.component.ts | 12 +++ .../matching-criteria.component.html | 1 + src/app/modules/organizations/index.ts | 2 +- .../organizations/organizations.routes.ts | 6 +- 18 files changed, 288 insertions(+), 17 deletions(-) create mode 100644 src/app/modules/organizations/columns/columns.component.html create mode 100644 src/app/modules/organizations/columns/columns.component.ts create mode 100644 src/app/modules/organizations/columns/columns.routes.ts create mode 100644 src/app/modules/organizations/columns/data_types/data-types-properties.component.ts create mode 100644 src/app/modules/organizations/columns/data_types/data-types-taxlots.component.ts create mode 100644 src/app/modules/organizations/columns/data_types/data-types.component.html create mode 100644 src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts create mode 100644 src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts create mode 100644 src/app/modules/organizations/columns/geocoding/geocoding.component.html create mode 100644 src/app/modules/organizations/columns/list/list-properties.component.ts create mode 100644 src/app/modules/organizations/columns/list/list-taxlots.component.ts create mode 100644 src/app/modules/organizations/columns/list/list.component.html create mode 100644 src/app/modules/organizations/columns/matching_critieria/matching-criteria-properties.component.ts create mode 100644 src/app/modules/organizations/columns/matching_critieria/matching-criteria-taxlots.component.ts create mode 100644 src/app/modules/organizations/columns/matching_critieria/matching-criteria.component.html diff --git a/src/app/core/navigation/navigation.service.ts b/src/app/core/navigation/navigation.service.ts index 8f4fa620..3aea9423 100644 --- a/src/app/core/navigation/navigation.service.ts +++ b/src/app/core/navigation/navigation.service.ts @@ -47,20 +47,12 @@ export class NavigationService { type: 'basic', }, { - id: 'organizations/column-mappings', - link: '/organizations/column-mappings/properties', - title: 'Column mappings', - icon: 'fa-solid:right-left', - type: 'basic', - regexMatch: /^\/organizations\/column-mappings\/(properties|taxlots)/, - }, - { - id: 'organizations/column-settings', - link: '/organizations/column-settings/properties', - title: 'Column Settings', + id: 'organizations/columns', + link: '/organizations/columns/list/properties', + title: 'Columns', icon: 'fa-solid:sliders', type: 'basic', - regexMatch: /^\/organizations\/column-settings\/(properties|taxlots)/, + regexMatch: /^\/organizations\/columns\/list\/(properties|taxlots)/, }, { id: 'organizations/cycles', diff --git a/src/app/modules/organizations/columns/columns.component.html b/src/app/modules/organizations/columns/columns.component.html new file mode 100644 index 00000000..d298ef64 --- /dev/null +++ b/src/app/modules/organizations/columns/columns.component.html @@ -0,0 +1,35 @@ + + + + + +
+

Columns

+
+
+ + + +
+
+ + + + + + + + +
diff --git a/src/app/modules/organizations/columns/columns.component.ts b/src/app/modules/organizations/columns/columns.component.ts new file mode 100644 index 00000000..03cdf17d --- /dev/null +++ b/src/app/modules/organizations/columns/columns.component.ts @@ -0,0 +1,94 @@ +import { CommonModule, Location } from '@angular/common' +import { Component, inject, type OnInit } from '@angular/core' +import { MatIconModule } from '@angular/material/icon' +import { MatSidenavModule } from '@angular/material/sidenav' +import { MatTabsModule } from '@angular/material/tabs' +import { Title } from '@angular/platform-browser' +import { Router, RouterOutlet } from '@angular/router' +import { type NavigationItem, VerticalNavigationComponent } from '@seed/components' +import { PageComponent } from '@seed/components' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-columns', + templateUrl: './columns.component.html', + imports: [CommonModule, SharedImports, MatIconModule, MatSidenavModule, MatTabsModule, PageComponent, VerticalNavigationComponent, RouterOutlet], +}) +export class ColumnsComponent implements OnInit { + private _title = inject(Title) + private _router = inject(Router) + private _location = inject(Location) + tabs = [ + { + label: 'Properties', + route: 'properties', + }, + { + label: 'TaxLots', + route: 'taxlots', + }, + ] + pageTitle: string + columnsNavigationMenu: NavigationItem[] = [ + { + id: 'organizations/columns/list', + title: 'Column List', + link: '/organizations/columns/list/properties', + type: 'basic', + fn: (n: NavigationItem) => { this.setNavTitle(n) }, + }, + { + id: 'organizations/columns/geocoding', + link: '/organizations/columns/geocoding/properties', + title: 'Geocoding', + type: 'basic', + fn: (n: NavigationItem) => { this.setNavTitle(n) }, + }, + { + id: 'organization/columns/data_type', + link: '/organizations/columns/data_types/properties', + title: 'Data Types', + type: 'basic', + fn: (n: NavigationItem) => { this.setNavTitle(n) }, + }, + { + id: 'organizations/columns/matching_criteria', + link: '/organizations/columns/matching_criteria/properties', + title: 'Matching Criteria', + type: 'basic', + fn: (n: NavigationItem) => { this.setNavTitle(n) }, + }, + ] + + ngOnInit(): void { + this.setTitle() + } + + currentType(): string { + return this._location.path().split('/').pop() + } + + async navigateTo(type: string) { + const loc = this._location.path() + if (loc.includes(type)) { + return + } + const newPath = loc.replace(this.inverseType(type), type) + await this._router.navigateByUrl(newPath).then(() => { + this.setTitle() + }) + } + + inverseType(type: string): string { + return type === 'properties' ? 'taxlots' : 'properties' + } + + setNavTitle(n: NavigationItem) { + this.pageTitle = n.title + } + + setTitle() { + const basePath = `${this._location.path().split('/').slice(0, -1).join('/')}/properties` + this.pageTitle = this.columnsNavigationMenu.find((n) => n.link === basePath).title + } +} diff --git a/src/app/modules/organizations/columns/columns.routes.ts b/src/app/modules/organizations/columns/columns.routes.ts new file mode 100644 index 00000000..952f78a8 --- /dev/null +++ b/src/app/modules/organizations/columns/columns.routes.ts @@ -0,0 +1,52 @@ +import type { Routes } from '@angular/router' +import { DataTypesPropertiesComponent } from './data_types/data-types-properties.component' +import { DataTypesTaxLotsComponent } from './data_types/data-types-taxlots.component' +import { GeocodingPropertiesComponent } from './geocoding/geocoding-properties.component' +import { GeocodingTaxlotsComponent } from './geocoding/geocoding-taxlots.component' +import { ListPropertiesComponent } from './list/list-properties.component' +import { ListTaxLotComponent } from './list/list-taxlots.component' +import { MatchingCriteriaPropertiesComponent } from './matching_critieria/matching-criteria-properties.component' +import { MatchingCriteriaTaxLotsComponent } from './matching_critieria/matching-criteria-taxlots.component' + +export default [ + { + path: 'list/properties', + title: 'Column List', + component: ListPropertiesComponent, + }, + { + path: 'list/taxlots', + title: 'Column List', + component: ListTaxLotComponent, + }, + { + path: 'geocoding/properties', + title: 'Geocoding Order', + component: GeocodingPropertiesComponent, + }, + { + path: 'geocoding/taxlots', + title: 'Geocoding Order', + component: GeocodingTaxlotsComponent, + }, + { + path: 'data_types/properties', + title: 'Data Types', + component: DataTypesPropertiesComponent, + }, + { + path: 'data_types/taxlots', + title: 'Data Types', + component: DataTypesTaxLotsComponent, + }, + { + path: 'matching_criteria/properties', + title: 'Matching Criteria', + component: MatchingCriteriaPropertiesComponent, + }, + { + path: 'matching_criteria/taxlots', + title: 'Matching Criteria', + component: MatchingCriteriaTaxLotsComponent, + }, +] satisfies Routes diff --git a/src/app/modules/organizations/columns/data_types/data-types-properties.component.ts b/src/app/modules/organizations/columns/data_types/data-types-properties.component.ts new file mode 100644 index 00000000..d7c100d6 --- /dev/null +++ b/src/app/modules/organizations/columns/data_types/data-types-properties.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-column-data-types-properties', + templateUrl: './data-types.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class DataTypesPropertiesComponent { + type = 'properties' +} diff --git a/src/app/modules/organizations/columns/data_types/data-types-taxlots.component.ts b/src/app/modules/organizations/columns/data_types/data-types-taxlots.component.ts new file mode 100644 index 00000000..2f260cd5 --- /dev/null +++ b/src/app/modules/organizations/columns/data_types/data-types-taxlots.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-column-data-types-taxlots', + templateUrl: './data-types.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class DataTypesTaxLotsComponent { + type = 'taxlots' +} diff --git a/src/app/modules/organizations/columns/data_types/data-types.component.html b/src/app/modules/organizations/columns/data_types/data-types.component.html new file mode 100644 index 00000000..61dc8e90 --- /dev/null +++ b/src/app/modules/organizations/columns/data_types/data-types.component.html @@ -0,0 +1 @@ +{{ type }} \ No newline at end of file diff --git a/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts b/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts new file mode 100644 index 00000000..7e375f03 --- /dev/null +++ b/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-column-geocoding-properties', + templateUrl: './geocoding.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class GeocodingPropertiesComponent { + type = 'properties' +} diff --git a/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts b/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts new file mode 100644 index 00000000..cef3ffdb --- /dev/null +++ b/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-column-geocoding-taxlots', + templateUrl: './geocoding.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class GeocodingTaxlotsComponent { + type = 'taxlots' +} diff --git a/src/app/modules/organizations/columns/geocoding/geocoding.component.html b/src/app/modules/organizations/columns/geocoding/geocoding.component.html new file mode 100644 index 00000000..61dc8e90 --- /dev/null +++ b/src/app/modules/organizations/columns/geocoding/geocoding.component.html @@ -0,0 +1 @@ +{{ type }} \ No newline at end of file diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts new file mode 100644 index 00000000..5e329eda --- /dev/null +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-columns-list-properties', + templateUrl: './list.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class ListPropertiesComponent { + type = 'properties' +} diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts new file mode 100644 index 00000000..fafd3c33 --- /dev/null +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-columns-list-taxlots', + templateUrl: './list.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class ListTaxLotComponent { + type = 'taxlots' +} diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html new file mode 100644 index 00000000..788100cb --- /dev/null +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -0,0 +1 @@ +{{ type }} diff --git a/src/app/modules/organizations/columns/matching_critieria/matching-criteria-properties.component.ts b/src/app/modules/organizations/columns/matching_critieria/matching-criteria-properties.component.ts new file mode 100644 index 00000000..0fb7c672 --- /dev/null +++ b/src/app/modules/organizations/columns/matching_critieria/matching-criteria-properties.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-column-matching-criteria-properties', + templateUrl: './matching-criteria.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class MatchingCriteriaPropertiesComponent { + type = 'properties' +} diff --git a/src/app/modules/organizations/columns/matching_critieria/matching-criteria-taxlots.component.ts b/src/app/modules/organizations/columns/matching_critieria/matching-criteria-taxlots.component.ts new file mode 100644 index 00000000..1600e085 --- /dev/null +++ b/src/app/modules/organizations/columns/matching_critieria/matching-criteria-taxlots.component.ts @@ -0,0 +1,12 @@ +import { Component, ViewEncapsulation } from '@angular/core' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-column-matching-criteria-taxlots', + templateUrl: './matching-criteria.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class MatchingCriteriaTaxLotsComponent { + type = 'taxlots' +} diff --git a/src/app/modules/organizations/columns/matching_critieria/matching-criteria.component.html b/src/app/modules/organizations/columns/matching_critieria/matching-criteria.component.html new file mode 100644 index 00000000..61dc8e90 --- /dev/null +++ b/src/app/modules/organizations/columns/matching_critieria/matching-criteria.component.html @@ -0,0 +1 @@ +{{ type }} \ No newline at end of file diff --git a/src/app/modules/organizations/index.ts b/src/app/modules/organizations/index.ts index bfb9a120..c18a2be7 100644 --- a/src/app/modules/organizations/index.ts +++ b/src/app/modules/organizations/index.ts @@ -1,6 +1,6 @@ export * from './access-level-tree/access-level-tree.component' export * from './column-mappings/column-mappings.component' -export * from './column-settings/column-settings.component' +export * from './columns/columns.component' export * from './cycles/cycles.component' export * from './data-quality/data-quality.component' export * from './derived-columns/derived-columns.component' diff --git a/src/app/modules/organizations/organizations.routes.ts b/src/app/modules/organizations/organizations.routes.ts index ff0e24df..75ef4000 100644 --- a/src/app/modules/organizations/organizations.routes.ts +++ b/src/app/modules/organizations/organizations.routes.ts @@ -2,8 +2,7 @@ import type { UrlSegment } from '@angular/router' import type { OrganizationGenericTypeMatcher } from './organizations.types' import { AccessLevelTreeComponent, - ColumnMappingsComponent, - ColumnSettingsComponent, + ColumnsComponent, CyclesComponent, DataQualityComponent, DerivedColumnsComponent, @@ -42,8 +41,7 @@ const derivedColumnsTypeMatcher = (segments: UrlSegment[]) => { export default [ { path: 'settings', component: SettingsComponent, loadChildren: () => import('app/modules/organizations/settings/settings.routes') }, { path: 'access-level-tree', component: AccessLevelTreeComponent }, - { matcher: columnMappingTypeMatcher, component: ColumnMappingsComponent }, - { matcher: columnSettingsTypeMatcher, component: ColumnSettingsComponent }, + { path: 'columns', component: ColumnsComponent, loadChildren: () => import('app/modules/organizations/columns/columns.routes') }, { matcher: dataQualityTypeMatcher, component: DataQualityComponent }, { matcher: derivedColumnsTypeMatcher, component: DerivedColumnsComponent }, { path: 'cycles', component: CyclesComponent }, From d8421f8fd264f69b6e3825b5588b1dd3e82d385f Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 3 Mar 2025 22:13:23 +0000 Subject: [PATCH 02/30] column list, with two components sharing a template in this case --- .../columns/list/list-properties.component.ts | 41 +++++++++++++++++-- .../columns/list/list-taxlots.component.ts | 41 +++++++++++++++++-- .../columns/list/list.component.html | 40 +++++++++++++++++- 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts index 5e329eda..8b99c6d9 100644 --- a/src/app/modules/organizations/columns/list/list-properties.component.ts +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -1,12 +1,47 @@ -import { Component, ViewEncapsulation } from '@angular/core' +import { Component, inject, type OnDestroy, type OnInit, ViewEncapsulation } from '@angular/core' +import { MatIcon } from '@angular/material/icon' +import { MatTableDataSource, MatTableModule } from '@angular/material/table' +import { MatTooltip } from '@angular/material/tooltip' +import { Subject, takeUntil } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' +import { naturalSort } from '@seed/utils' @Component({ selector: 'seed-organizations-columns-list-properties', templateUrl: './list.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports], + imports: [SharedImports, MatIcon, MatTableModule, MatTooltip], }) -export class ListPropertiesComponent { +export class ListPropertiesComponent implements OnDestroy, OnInit { + private _columnService = inject(ColumnService) + private readonly _unsubscribeAll$ = new Subject() + columnTableDataSource = new MatTableDataSource([]) + columnTableColumns = [ + 'canonical', + 'display_name', + 'column_name', + 'column_description', + 'actions', + ] type = 'properties' + + ngOnInit(): void { + this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { + this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + }) + } + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } + + delete(column: Column) { + console.log('Delete called for column: ', column) + } + + rename(column: Column) { + console.log('Rename called for column: ', column) + } } diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts index fafd3c33..3d0bfd04 100644 --- a/src/app/modules/organizations/columns/list/list-taxlots.component.ts +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -1,12 +1,47 @@ -import { Component, ViewEncapsulation } from '@angular/core' +import { Component, inject, type OnDestroy, type OnInit, ViewEncapsulation } from '@angular/core' +import { MatIcon } from '@angular/material/icon' +import { MatTableDataSource, MatTableModule } from '@angular/material/table' +import { MatTooltip } from '@angular/material/tooltip' +import { Subject, takeUntil } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' +import { naturalSort } from '@seed/utils' @Component({ selector: 'seed-organizations-columns-list-taxlots', templateUrl: './list.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports], + imports: [SharedImports, MatIcon, MatTableModule, MatTooltip], }) -export class ListTaxLotComponent { +export class ListTaxLotComponent implements OnDestroy, OnInit { + private _columnService = inject(ColumnService) + private readonly _unsubscribeAll$ = new Subject() + columnTableDataSource = new MatTableDataSource([]) + columnTableColumns = [ + 'canonical', + 'display_name', + 'column_name', + 'column_description', + 'actions', + ] type = 'taxlots' + + ngOnInit(): void { + this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { + this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + }) + } + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } + + delete(column: Column) { + console.log('Delete called for column: ', column) + } + + rename(column: Column) { + console.log('Rename called for column: ', column) + } } diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html index 788100cb..d105da4d 100644 --- a/src/app/modules/organizations/columns/list/list.component.html +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -1 +1,39 @@ -{{ type }} + + + + + @if (!c.is_extra_data && !c.derived_column) { + + } + + + + Display Name + {{ c.display_name }} + + + + Column Name + {{ c.column_name }} + + + + Description + {{ c.column_description }} + + + + Actions + + + + + + + + + + + + + From dacdb9ca42c13d93b322a6a22ec7aa805cf9c92d Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 4 Mar 2025 19:00:11 +0000 Subject: [PATCH 03/30] dnd to rearrange geocoding order for property columns. taxlot columns implementation to come. --- src/@seed/api/column/column.service.ts | 13 +++ .../geocoding-properties.component.ts | 86 ++++++++++++++++++- .../geocoding/geocoding-taxlots.component.ts | 2 +- .../geocoding/geocoding.component.html | 46 +++++++++- .../geocoding/geocoding2.component.html | 0 .../columns/modal/update-modal.component.html | 12 +++ .../columns/modal/update-modal.component.ts | 71 +++++++++++++++ 7 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 src/app/modules/organizations/columns/geocoding/geocoding2.component.html create mode 100644 src/app/modules/organizations/columns/modal/update-modal.component.html create mode 100644 src/app/modules/organizations/columns/modal/update-modal.component.ts diff --git a/src/@seed/api/column/column.service.ts b/src/@seed/api/column/column.service.ts index f3e590e0..d739dcfc 100644 --- a/src/@seed/api/column/column.service.ts +++ b/src/@seed/api/column/column.service.ts @@ -4,6 +4,7 @@ import { inject, Injectable } from '@angular/core' import type { Observable } from 'rxjs' import { catchError, map, ReplaySubject, Subject, takeUntil } from 'rxjs' import { ErrorService } from '@seed/services/error/error.service' +import { type UploaderResponse } from '@seed/services/uploader/uploader.types' import { SnackbarService } from 'app/core/snackbar/snackbar.service' import { UserService } from '../user' import type { Column, ColumnsResponse } from './column.types' @@ -58,4 +59,16 @@ export class ColumnService { }), ) } + + updateMultipleColumns(organization_id: number, table_name: string, changes: object): Observable { + const url = '/api/v3/columns/update_multiple/' + return this._httpClient.post(url, { organization_id, table_name, changes }).pipe( + map((ur) => { + return ur + }), + catchError((error: HttpErrorResponse) => { + return this._errorService.handleError(error, 'Error updating columns') + }), + ) + } } diff --git a/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts b/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts index 7e375f03..9c31a38c 100644 --- a/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts +++ b/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts @@ -1,12 +1,90 @@ -import { Component, ViewEncapsulation } from '@angular/core' +import { CdkDrag, type CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop' +import { Component, inject, type OnDestroy, type OnInit, ViewEncapsulation } from '@angular/core' +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms' +import { MatButtonModule } from '@angular/material/button' +import { MatDialog } from '@angular/material/dialog' +import { MatIcon } from '@angular/material/icon' +import { MatSelectModule } from '@angular/material/select' +import { MatTooltip } from '@angular/material/tooltip' +import { Subject, takeUntil } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' +import { type UploaderResponse } from '@seed/services/uploader/uploader.types' +import { naturalSort } from '@seed/utils' +import { UpdateModalComponent } from '../modal/update-modal.component' @Component({ selector: 'seed-organizations-column-geocoding-properties', templateUrl: './geocoding.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports], + imports: [SharedImports, CdkDropList, CdkDrag, MatButtonModule, MatIcon, MatSelectModule, MatTooltip, ReactiveFormsModule], }) -export class GeocodingPropertiesComponent { - type = 'properties' +export class GeocodingPropertiesComponent implements OnDestroy, OnInit { + private _columnService = inject(ColumnService) + private readonly _unsubscribeAll$ = new Subject() + private _dialog = inject(MatDialog) + columns: Column[] + availableColumns: Column[] + removedColumns: Column[] = [] + type = 'PropertyState' + dirty = false + addForm = new FormGroup({ + addGeocoder: new FormControl(null, [Validators.required]), + }) + + ngOnInit(): void { + this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { + this.columns = columns.sort((a, b) => a.geocoding_order - b.geocoding_order).filter((c) => c.geocoding_order != 0) + this.availableColumns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.geocoding_order === 0) + }) + } + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } + + drop(event: CdkDragDrop) { + moveItemInArray(this.columns, event.previousIndex, event.currentIndex) + this.dirty = true + } + + delete(column: Column) { + this.columns = this.columns.filter((c) => c.id !== column.id) + this.availableColumns.push(column) + this.availableColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.removedColumns.push(column) + this.dirty = true + } + + add() { + const columnToAdd = this.availableColumns.find((c) => c.id === this.addForm.get('addGeocoder').value) + this.availableColumns = this.availableColumns.filter((c) => c.id !== columnToAdd.id) + columnToAdd.geocoding_order = this.columns.length + 1 + this.columns.push(columnToAdd) + this.dirty = true + } + + save() { + const changes = {} + for (const [i, column] of this.columns.entries()) { + changes[column.id] = { geocoding_order: (i + 1) } + } + for (const c of this.removedColumns) { + changes[c.id] = { geocoding_order: 0 } + } + console.log('Saving ', changes) + this._columnService.updateMultipleColumns(this.columns[0].organization_id, this.type, changes).subscribe((response: UploaderResponse) => { + const dialogRef = this._dialog.open(UpdateModalComponent, { + width: '40rem', + data: { progressResponse: response }, + }) + dialogRef + .afterClosed() + .pipe( + takeUntil(this._unsubscribeAll$), + ) + .subscribe() + }) + } } diff --git a/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts b/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts index cef3ffdb..4a8e2b26 100644 --- a/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts +++ b/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts @@ -3,7 +3,7 @@ import { SharedImports } from '@seed/directives' @Component({ selector: 'seed-organizations-column-geocoding-taxlots', - templateUrl: './geocoding.component.html', + templateUrl: './geocoding2.component.html', encapsulation: ViewEncapsulation.None, imports: [SharedImports], }) diff --git a/src/app/modules/organizations/columns/geocoding/geocoding.component.html b/src/app/modules/organizations/columns/geocoding/geocoding.component.html index 61dc8e90..df092b1c 100644 --- a/src/app/modules/organizations/columns/geocoding/geocoding.component.html +++ b/src/app/modules/organizations/columns/geocoding/geocoding.component.html @@ -1 +1,45 @@ -{{ type }} \ No newline at end of file +
+ Drag columns to reorder them. Add columns with the selector below. +
+
When all your changes have been made, make sure to save them.
+
+ +
+ +
+@for (column of columns; track column.id) { +
+
+
{{ column.geocoding_order }}
+
{{ column.display_name }}
+
+
+
+} +
+ +
Add A GeoCoding Column
+
+ + Add Column + + @for (c of availableColumns; track c.id) { + {{ c.display_name }} + } + + +
+ +
+
+ +
When all your changes have been made, make sure to save them.
+
+ +
diff --git a/src/app/modules/organizations/columns/geocoding/geocoding2.component.html b/src/app/modules/organizations/columns/geocoding/geocoding2.component.html new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/organizations/columns/modal/update-modal.component.html b/src/app/modules/organizations/columns/modal/update-modal.component.html new file mode 100644 index 00000000..b0a2006b --- /dev/null +++ b/src/app/modules/organizations/columns/modal/update-modal.component.html @@ -0,0 +1,12 @@ +

+ Update Columns +

+ +
+ @if (inProgress) { + + } + @if (errorMessage) { + {{ errorMessage }} + } +
\ No newline at end of file diff --git a/src/app/modules/organizations/columns/modal/update-modal.component.ts b/src/app/modules/organizations/columns/modal/update-modal.component.ts new file mode 100644 index 00000000..7e05d3cf --- /dev/null +++ b/src/app/modules/organizations/columns/modal/update-modal.component.ts @@ -0,0 +1,71 @@ +import { CommonModule } from '@angular/common' +import type { OnDestroy, OnInit } from '@angular/core' +import { Component, inject } from '@angular/core' +import { MatButtonModule } from '@angular/material/button' +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog' +import { MatProgressBarModule } from '@angular/material/progress-bar' +import { Subject, takeUntil } from 'rxjs' +import { AlertComponent } from '@seed/components' +import { UploaderService } from '@seed/services/uploader/uploader.service' +import type { ProgressBarObj, UploaderResponse } from '@seed/services/uploader/uploader.types' +import { SnackbarService } from 'app/core/snackbar/snackbar.service' + +@Component({ + selector: 'seed-columns-update-modal', + templateUrl: './update-modal.component.html', + imports: [AlertComponent, CommonModule, MatButtonModule, MatDialogModule, MatProgressBarModule], +}) +export class UpdateModalComponent implements OnDestroy, OnInit { + private _uploaderService = inject(UploaderService) + private _dialogRef = inject(MatDialogRef) + private _snackBar = inject(SnackbarService) + private readonly _unsubscribeAll$ = new Subject() + errorMessage: string + inProgress = false + progressBarObj: ProgressBarObj = { + message: '', + progress: 0, + complete: false, + statusMessage: '', + progressLastUpdated: null, + progressLastChecked: null, + } + + data = inject(MAT_DIALOG_DATA) as { progressResponse: UploaderResponse } + + ngOnInit(): void { + this.inProgress = true + const successFn = () => { + setTimeout(() => { + this._snackBar.success('Columns Updated') + this.close() + }, 300) + } + const failureFn = () => { + this._snackBar.alert('Failed to update columns') + this.close() + } + + this._uploaderService.checkProgressLoop({ + progressKey: this.data.progressResponse.progress_key, + offset: 0, + multiplier: 1, + successFn, + failureFn, + progressBarObj: this.progressBarObj, + }).pipe(takeUntil(this._unsubscribeAll$)).subscribe() + } + + close() { + this._dialogRef.close() + } + + dismiss() { + this._dialogRef.close() + } + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } +} From bcd1c8121be6d7119de45f4526f08e5e7804fde4 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 4 Mar 2025 22:27:04 +0000 Subject: [PATCH 04/30] geocoding screens --- .../geocoding-properties.component.ts | 81 +--------------- .../geocoding/geocoding-taxlots.component.ts | 17 +++- .../geocoding/geocoding.component.html | 6 +- .../columns/geocoding/geocoding.component.ts | 96 +++++++++++++++++++ 4 files changed, 116 insertions(+), 84 deletions(-) create mode 100644 src/app/modules/organizations/columns/geocoding/geocoding.component.ts diff --git a/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts b/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts index 9c31a38c..1221a11b 100644 --- a/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts +++ b/src/app/modules/organizations/columns/geocoding/geocoding-properties.component.ts @@ -1,17 +1,12 @@ -import { CdkDrag, type CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop' -import { Component, inject, type OnDestroy, type OnInit, ViewEncapsulation } from '@angular/core' -import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms' +import { CdkDrag, CdkDropList } from '@angular/cdk/drag-drop' +import { Component, ViewEncapsulation } from '@angular/core' +import { ReactiveFormsModule } from '@angular/forms' import { MatButtonModule } from '@angular/material/button' -import { MatDialog } from '@angular/material/dialog' import { MatIcon } from '@angular/material/icon' import { MatSelectModule } from '@angular/material/select' import { MatTooltip } from '@angular/material/tooltip' -import { Subject, takeUntil } from 'rxjs' -import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' -import { type UploaderResponse } from '@seed/services/uploader/uploader.types' -import { naturalSort } from '@seed/utils' -import { UpdateModalComponent } from '../modal/update-modal.component' +import { GeocodingComponent } from './geocoding.component' @Component({ selector: 'seed-organizations-column-geocoding-properties', @@ -19,72 +14,6 @@ import { UpdateModalComponent } from '../modal/update-modal.component' encapsulation: ViewEncapsulation.None, imports: [SharedImports, CdkDropList, CdkDrag, MatButtonModule, MatIcon, MatSelectModule, MatTooltip, ReactiveFormsModule], }) -export class GeocodingPropertiesComponent implements OnDestroy, OnInit { - private _columnService = inject(ColumnService) - private readonly _unsubscribeAll$ = new Subject() - private _dialog = inject(MatDialog) - columns: Column[] - availableColumns: Column[] - removedColumns: Column[] = [] +export class GeocodingPropertiesComponent extends GeocodingComponent { type = 'PropertyState' - dirty = false - addForm = new FormGroup({ - addGeocoder: new FormControl(null, [Validators.required]), - }) - - ngOnInit(): void { - this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { - this.columns = columns.sort((a, b) => a.geocoding_order - b.geocoding_order).filter((c) => c.geocoding_order != 0) - this.availableColumns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.geocoding_order === 0) - }) - } - - ngOnDestroy(): void { - this._unsubscribeAll$.next() - this._unsubscribeAll$.complete() - } - - drop(event: CdkDragDrop) { - moveItemInArray(this.columns, event.previousIndex, event.currentIndex) - this.dirty = true - } - - delete(column: Column) { - this.columns = this.columns.filter((c) => c.id !== column.id) - this.availableColumns.push(column) - this.availableColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) - this.removedColumns.push(column) - this.dirty = true - } - - add() { - const columnToAdd = this.availableColumns.find((c) => c.id === this.addForm.get('addGeocoder').value) - this.availableColumns = this.availableColumns.filter((c) => c.id !== columnToAdd.id) - columnToAdd.geocoding_order = this.columns.length + 1 - this.columns.push(columnToAdd) - this.dirty = true - } - - save() { - const changes = {} - for (const [i, column] of this.columns.entries()) { - changes[column.id] = { geocoding_order: (i + 1) } - } - for (const c of this.removedColumns) { - changes[c.id] = { geocoding_order: 0 } - } - console.log('Saving ', changes) - this._columnService.updateMultipleColumns(this.columns[0].organization_id, this.type, changes).subscribe((response: UploaderResponse) => { - const dialogRef = this._dialog.open(UpdateModalComponent, { - width: '40rem', - data: { progressResponse: response }, - }) - dialogRef - .afterClosed() - .pipe( - takeUntil(this._unsubscribeAll$), - ) - .subscribe() - }) - } } diff --git a/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts b/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts index 4a8e2b26..0ba7cd6c 100644 --- a/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts +++ b/src/app/modules/organizations/columns/geocoding/geocoding-taxlots.component.ts @@ -1,12 +1,19 @@ +import { CdkDrag, CdkDropList } from '@angular/cdk/drag-drop' import { Component, ViewEncapsulation } from '@angular/core' +import { ReactiveFormsModule } from '@angular/forms' +import { MatButtonModule } from '@angular/material/button' +import { MatIcon } from '@angular/material/icon' +import { MatSelectModule } from '@angular/material/select' +import { MatTooltip } from '@angular/material/tooltip' import { SharedImports } from '@seed/directives' +import { GeocodingComponent } from './geocoding.component' @Component({ - selector: 'seed-organizations-column-geocoding-taxlots', - templateUrl: './geocoding2.component.html', + selector: 'seed-organizations-column-geocoding-properties', + templateUrl: './geocoding.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports], + imports: [SharedImports, CdkDropList, CdkDrag, MatButtonModule, MatIcon, MatSelectModule, MatTooltip, ReactiveFormsModule], }) -export class GeocodingTaxlotsComponent { - type = 'taxlots' +export class GeocodingTaxlotsComponent extends GeocodingComponent { + type = 'TaxLotState' } diff --git a/src/app/modules/organizations/columns/geocoding/geocoding.component.html b/src/app/modules/organizations/columns/geocoding/geocoding.component.html index df092b1c..151191aa 100644 --- a/src/app/modules/organizations/columns/geocoding/geocoding.component.html +++ b/src/app/modules/organizations/columns/geocoding/geocoding.component.html @@ -1,7 +1,7 @@
Drag columns to reorder them. Add columns with the selector below.
-
When all your changes have been made, make sure to save them.
+
When all your changes have been made, make sure to save them.
-
When all your changes have been made, make sure to save them.
+
When all your changes have been made, make sure to save them.
+
+ + diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.ts b/src/app/modules/organizations/columns/data-types/data-types.component.ts new file mode 100644 index 00000000..753dcb8c --- /dev/null +++ b/src/app/modules/organizations/columns/data-types/data-types.component.ts @@ -0,0 +1,28 @@ +import { Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core' +import { FormGroup } from '@angular/forms' +import { Subject } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' +import { SharedImports } from '@seed/directives' +import { DataTypes } from './data-types.constants' + +@Component({ + selector: 'seed-organizations-column-data-types-properties', + template: '', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) + +export class DataTypesComponent implements OnDestroy { + protected _columnService = inject(ColumnService) + protected readonly _unsubscribeAll$ = new Subject() + columns: Column[] + type: string + dataTypesForm = new FormGroup({}) + dataTypes = DataTypes + isLoading = true + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } +} diff --git a/src/app/modules/organizations/columns/data-types/data-types.constants.ts b/src/app/modules/organizations/columns/data-types/data-types.constants.ts new file mode 100644 index 00000000..b91956bb --- /dev/null +++ b/src/app/modules/organizations/columns/data-types/data-types.constants.ts @@ -0,0 +1,17 @@ +export const DataTypes = [ + { id: 'None', label: '' }, + { id: 'number', label: 'Number' }, + { id: 'float', label: 'Float' }, + { id: 'integer', label: 'Integer' }, + { id: 'string', label: 'Text' }, + { id: 'datetime', label: 'Datetime' }, + { id: 'date', label: 'Date' }, + { id: 'boolean', label: 'Boolean' }, + { id: 'area', label: 'Area' }, + { id: 'eui', label: 'EUI' }, + { id: 'geometry', label: 'Geometry' }, + { id: 'ghg', label: 'GHG' }, + { id: 'ghg_intensity', label: 'GHG Intensity' }, + { id: 'wui', label: 'WUI' }, + { id: 'water_use', label: 'Water Use' }, +] diff --git a/src/app/modules/organizations/columns/data_types/data-types-properties.component.ts b/src/app/modules/organizations/columns/data_types/data-types-properties.component.ts deleted file mode 100644 index d7c100d6..00000000 --- a/src/app/modules/organizations/columns/data_types/data-types-properties.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component, ViewEncapsulation } from '@angular/core' -import { SharedImports } from '@seed/directives' - -@Component({ - selector: 'seed-organizations-column-data-types-properties', - templateUrl: './data-types.component.html', - encapsulation: ViewEncapsulation.None, - imports: [SharedImports], -}) -export class DataTypesPropertiesComponent { - type = 'properties' -} diff --git a/src/app/modules/organizations/columns/data_types/data-types-taxlots.component.ts b/src/app/modules/organizations/columns/data_types/data-types-taxlots.component.ts deleted file mode 100644 index 2f260cd5..00000000 --- a/src/app/modules/organizations/columns/data_types/data-types-taxlots.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component, ViewEncapsulation } from '@angular/core' -import { SharedImports } from '@seed/directives' - -@Component({ - selector: 'seed-organizations-column-data-types-taxlots', - templateUrl: './data-types.component.html', - encapsulation: ViewEncapsulation.None, - imports: [SharedImports], -}) -export class DataTypesTaxLotsComponent { - type = 'taxlots' -} diff --git a/src/app/modules/organizations/columns/data_types/data-types.component.html b/src/app/modules/organizations/columns/data_types/data-types.component.html deleted file mode 100644 index 61dc8e90..00000000 --- a/src/app/modules/organizations/columns/data_types/data-types.component.html +++ /dev/null @@ -1 +0,0 @@ -{{ type }} \ No newline at end of file diff --git a/src/app/modules/organizations/columns/geocoding/geocoding2.component.html b/src/app/modules/organizations/columns/geocoding/geocoding2.component.html deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts index 8b99c6d9..6523efbd 100644 --- a/src/app/modules/organizations/columns/list/list-properties.component.ts +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -1,5 +1,8 @@ -import { Component, inject, type OnDestroy, type OnInit, ViewEncapsulation } from '@angular/core' +import { type AfterViewInit, Component, inject, type OnDestroy, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' +import { MatFormFieldModule } from '@angular/material/form-field' import { MatIcon } from '@angular/material/icon' +import { MatInputModule } from '@angular/material/input' +import { MatPaginator } from '@angular/material/paginator' import { MatTableDataSource, MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' import { Subject, takeUntil } from 'rxjs' @@ -11,9 +14,9 @@ import { naturalSort } from '@seed/utils' selector: 'seed-organizations-columns-list-properties', templateUrl: './list.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports, MatIcon, MatTableModule, MatTooltip], + imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip], }) -export class ListPropertiesComponent implements OnDestroy, OnInit { +export class ListPropertiesComponent implements AfterViewInit, OnDestroy, OnInit { private _columnService = inject(ColumnService) private readonly _unsubscribeAll$ = new Subject() columnTableDataSource = new MatTableDataSource([]) @@ -25,6 +28,7 @@ export class ListPropertiesComponent implements OnDestroy, OnInit { 'actions', ] type = 'properties' + @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { @@ -32,6 +36,10 @@ export class ListPropertiesComponent implements OnDestroy, OnInit { }) } + ngAfterViewInit(): void { + this.columnTableDataSource.paginator = this.paginator + } + ngOnDestroy(): void { this._unsubscribeAll$.next() this._unsubscribeAll$.complete() @@ -44,4 +52,9 @@ export class ListPropertiesComponent implements OnDestroy, OnInit { rename(column: Column) { console.log('Rename called for column: ', column) } + + applyFilter(event: Event): void { + const filterValue = (event.target as HTMLInputElement).value + this.columnTableDataSource.filter = filterValue.trim().toLowerCase() + } } diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts index 3d0bfd04..cb33719d 100644 --- a/src/app/modules/organizations/columns/list/list-taxlots.component.ts +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -1,5 +1,8 @@ -import { Component, inject, type OnDestroy, type OnInit, ViewEncapsulation } from '@angular/core' +import { type AfterViewInit, Component, inject, type OnDestroy, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' +import { MatFormFieldModule } from '@angular/material/form-field' import { MatIcon } from '@angular/material/icon' +import { MatInputModule } from '@angular/material/input' +import { MatPaginator } from '@angular/material/paginator' import { MatTableDataSource, MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' import { Subject, takeUntil } from 'rxjs' @@ -11,9 +14,9 @@ import { naturalSort } from '@seed/utils' selector: 'seed-organizations-columns-list-taxlots', templateUrl: './list.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports, MatIcon, MatTableModule, MatTooltip], + imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip], }) -export class ListTaxLotComponent implements OnDestroy, OnInit { +export class ListTaxLotComponent implements AfterViewInit, OnDestroy, OnInit { private _columnService = inject(ColumnService) private readonly _unsubscribeAll$ = new Subject() columnTableDataSource = new MatTableDataSource([]) @@ -25,6 +28,7 @@ export class ListTaxLotComponent implements OnDestroy, OnInit { 'actions', ] type = 'taxlots' + @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { @@ -32,6 +36,10 @@ export class ListTaxLotComponent implements OnDestroy, OnInit { }) } + ngAfterViewInit() { + this.columnTableDataSource.paginator = this.paginator + } + ngOnDestroy(): void { this._unsubscribeAll$.next() this._unsubscribeAll$.complete() @@ -44,4 +52,9 @@ export class ListTaxLotComponent implements OnDestroy, OnInit { rename(column: Column) { console.log('Rename called for column: ', column) } + + applyFilter(event: Event): void { + const filterValue = (event.target as HTMLInputElement).value + this.columnTableDataSource.filter = filterValue.trim().toLowerCase() + } } diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html index d105da4d..38867ee4 100644 --- a/src/app/modules/organizations/columns/list/list.component.html +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -1,6 +1,11 @@ + + + Filter + + - + Canonical? @if (!c.is_extra_data && !c.derived_column) { @@ -37,3 +42,5 @@ + + From 5e69ed470b6f108895e9aa832609a4246af8fd4d Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Fri, 7 Mar 2025 19:52:31 +0000 Subject: [PATCH 08/30] add form modal for editing, abstract common code for column components to central list.component file --- .../columns/list/list-properties.component.ts | 33 +----- .../columns/list/list-taxlots.component.ts | 28 +---- .../columns/list/list.component.html | 12 +- .../columns/list/list.component.ts | 60 ++++++++++ .../list/modal/delete-modal.component.ts | 0 .../list/modal/form-modal.component.html | 33 ++++++ .../list/modal/form-modal.component.ts | 107 ++++++++++++++++++ .../list/modal/rename-modal.component.ts | 0 8 files changed, 217 insertions(+), 56 deletions(-) create mode 100644 src/app/modules/organizations/columns/list/list.component.ts create mode 100644 src/app/modules/organizations/columns/list/modal/delete-modal.component.ts create mode 100644 src/app/modules/organizations/columns/list/modal/form-modal.component.html create mode 100644 src/app/modules/organizations/columns/list/modal/form-modal.component.ts create mode 100644 src/app/modules/organizations/columns/list/modal/rename-modal.component.ts diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts index 6523efbd..4895e11e 100644 --- a/src/app/modules/organizations/columns/list/list-properties.component.ts +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -1,14 +1,14 @@ -import { type AfterViewInit, Component, inject, type OnDestroy, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' +import { type AfterViewInit, Component, type OnDestroy, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' import { MatFormFieldModule } from '@angular/material/form-field' import { MatIcon } from '@angular/material/icon' import { MatInputModule } from '@angular/material/input' import { MatPaginator } from '@angular/material/paginator' -import { MatTableDataSource, MatTableModule } from '@angular/material/table' +import { MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' -import { Subject, takeUntil } from 'rxjs' -import { type Column, ColumnService } from '@seed/api/column' +import { takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' +import { ListComponent } from './list.component' @Component({ selector: 'seed-organizations-columns-list-properties', @@ -16,17 +16,7 @@ import { naturalSort } from '@seed/utils' encapsulation: ViewEncapsulation.None, imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip], }) -export class ListPropertiesComponent implements AfterViewInit, OnDestroy, OnInit { - private _columnService = inject(ColumnService) - private readonly _unsubscribeAll$ = new Subject() - columnTableDataSource = new MatTableDataSource([]) - columnTableColumns = [ - 'canonical', - 'display_name', - 'column_name', - 'column_description', - 'actions', - ] +export class ListPropertiesComponent extends ListComponent implements AfterViewInit, OnDestroy, OnInit { type = 'properties' @ViewChild(MatPaginator) paginator: MatPaginator @@ -40,19 +30,6 @@ export class ListPropertiesComponent implements AfterViewInit, OnDestroy, OnInit this.columnTableDataSource.paginator = this.paginator } - ngOnDestroy(): void { - this._unsubscribeAll$.next() - this._unsubscribeAll$.complete() - } - - delete(column: Column) { - console.log('Delete called for column: ', column) - } - - rename(column: Column) { - console.log('Rename called for column: ', column) - } - applyFilter(event: Event): void { const filterValue = (event.target as HTMLInputElement).value this.columnTableDataSource.filter = filterValue.trim().toLowerCase() diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts index cb33719d..129bad19 100644 --- a/src/app/modules/organizations/columns/list/list-taxlots.component.ts +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -1,14 +1,14 @@ -import { type AfterViewInit, Component, inject, type OnDestroy, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' +import { type AfterViewInit, Component, type OnDestroy, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' import { MatFormFieldModule } from '@angular/material/form-field' import { MatIcon } from '@angular/material/icon' import { MatInputModule } from '@angular/material/input' import { MatPaginator } from '@angular/material/paginator' -import { MatTableDataSource, MatTableModule } from '@angular/material/table' +import { MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' -import { Subject, takeUntil } from 'rxjs' -import { type Column, ColumnService } from '@seed/api/column' +import { takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' +import { ListComponent } from './list.component' @Component({ selector: 'seed-organizations-columns-list-taxlots', @@ -16,17 +16,7 @@ import { naturalSort } from '@seed/utils' encapsulation: ViewEncapsulation.None, imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip], }) -export class ListTaxLotComponent implements AfterViewInit, OnDestroy, OnInit { - private _columnService = inject(ColumnService) - private readonly _unsubscribeAll$ = new Subject() - columnTableDataSource = new MatTableDataSource([]) - columnTableColumns = [ - 'canonical', - 'display_name', - 'column_name', - 'column_description', - 'actions', - ] +export class ListTaxLotComponent extends ListComponent implements AfterViewInit, OnDestroy, OnInit { type = 'taxlots' @ViewChild(MatPaginator) paginator: MatPaginator @@ -45,14 +35,6 @@ export class ListTaxLotComponent implements AfterViewInit, OnDestroy, OnInit { this._unsubscribeAll$.complete() } - delete(column: Column) { - console.log('Delete called for column: ', column) - } - - rename(column: Column) { - console.log('Rename called for column: ', column) - } - applyFilter(event: Event): void { const filterValue = (event.target as HTMLInputElement).value this.columnTableDataSource.filter = filterValue.trim().toLowerCase() diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html index 38867ee4..06eee018 100644 --- a/src/app/modules/organizations/columns/list/list.component.html +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -30,12 +30,14 @@ Actions - - - - - + + + @if (c.is_extra_data) { + + + + } diff --git a/src/app/modules/organizations/columns/list/list.component.ts b/src/app/modules/organizations/columns/list/list.component.ts new file mode 100644 index 00000000..ef25868e --- /dev/null +++ b/src/app/modules/organizations/columns/list/list.component.ts @@ -0,0 +1,60 @@ +import { Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core' +import { MatDialog } from '@angular/material/dialog' +import { MatTableDataSource } from '@angular/material/table' +import { Subject, takeUntil } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' +import { SharedImports } from '@seed/directives' +import { FormModalComponent } from './modal/form-modal.component' + +@Component({ + selector: 'seed-organizations-columns-list-properties', + template: '', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class ListComponent implements OnDestroy { + protected _columnService = inject(ColumnService) + protected readonly _unsubscribeAll$ = new Subject() + private _dialog = inject(MatDialog) + columnTableDataSource = new MatTableDataSource([]) + columnTableColumns = [ + 'canonical', + 'display_name', + 'column_name', + 'column_description', + 'actions', + ] + type: string + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } + + delete(column: Column) { + console.log('Delete called for column: ', column) + } + + rename(column: Column) { + console.log('Rename called for column: ', column) + } + + edit(column: Column) { + const dialogRef = this._dialog.open(FormModalComponent, { + width: '40rem', + data: { column }, + }) + + dialogRef + .afterClosed() + .pipe( + takeUntil(this._unsubscribeAll$), + ) + .subscribe() + } + + applyFilter(event: Event): void { + const filterValue = (event.target as HTMLInputElement).value + this.columnTableDataSource.filter = filterValue.trim().toLowerCase() + } +} diff --git a/src/app/modules/organizations/columns/list/modal/delete-modal.component.ts b/src/app/modules/organizations/columns/list/modal/delete-modal.component.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/organizations/columns/list/modal/form-modal.component.html b/src/app/modules/organizations/columns/list/modal/form-modal.component.html new file mode 100644 index 00000000..a9ffc04e --- /dev/null +++ b/src/app/modules/organizations/columns/list/modal/form-modal.component.html @@ -0,0 +1,33 @@ +

+ Edit Column +

+
+ + Display Name + + @if (form.controls.display_name?.hasError('required')) { + Display Name is a required field + } + + + + Column Description + + @if (form.controls.display_name?.hasError('required')) { + Column Description is a required field + } + +
+@if (inProgress) { + +} +@if (!inProgress) { +
+ + + + + + +
+} \ No newline at end of file diff --git a/src/app/modules/organizations/columns/list/modal/form-modal.component.ts b/src/app/modules/organizations/columns/list/modal/form-modal.component.ts new file mode 100644 index 00000000..b288a481 --- /dev/null +++ b/src/app/modules/organizations/columns/list/modal/form-modal.component.ts @@ -0,0 +1,107 @@ +import { CommonModule } from '@angular/common' +import type { OnDestroy, OnInit } from '@angular/core' +import { Component, inject } from '@angular/core' +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms' +import { MatButtonModule } from '@angular/material/button' +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog' +import { MatFormFieldModule } from '@angular/material/form-field' +import { MatInputModule } from '@angular/material/input' +import { MatProgressBarModule } from '@angular/material/progress-bar' +import { type Observable, Subject, switchMap, takeUntil, tap } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' +import { UploaderService } from '@seed/services/uploader/uploader.service' +import type { ProgressBarObj, UploaderResponse } from '@seed/services/uploader/uploader.types' +import { SnackbarService } from 'app/core/snackbar/snackbar.service' + +@Component({ + selector: 'seed-labels-form-modal', + templateUrl: './form-modal.component.html', + imports: [ + CommonModule, + MatButtonModule, + MatDialogModule, + MatFormFieldModule, + FormsModule, + MatInputModule, + MatProgressBarModule, + ReactiveFormsModule, + ], +}) +export class FormModalComponent implements OnDestroy, OnInit { + private _dialogRef = inject(MatDialogRef) + private _columnService = inject(ColumnService) + private _snackBar = inject(SnackbarService) + private _uploaderService = inject(UploaderService) + private readonly _unsubscribeAll$ = new Subject() + column: Column + refreshFn: (organization_id: number) => Observable + data = inject(MAT_DIALOG_DATA) as { column: Column } + inProgress = false + progressBarObj: ProgressBarObj = { + message: '', + progress: 0, + complete: false, + statusMessage: '', + progressLastUpdated: null, + progressLastChecked: null, + } + form = new FormGroup({ + display_name: new FormControl('', [Validators.required]), + column_description: new FormControl(null, [Validators.required]), + organization_id: new FormControl(null, [Validators.required]), + table_name: new FormControl('', [Validators.required]), + id: new FormControl(null), + }) + + ngOnInit(): void { + this.form.patchValue(this.data.column) + if (this.data.column.table_name === 'PropertyState') { + this.refreshFn = (org_id) => this._columnService.getPropertyColumns(org_id) + } else if (this.data.column.table_name === 'TaxLotState') { + this.refreshFn = (org_id) => this._columnService.getTaxLotColumns(org_id) + } + } + + onSubmit() { + const successFn = () => { + setTimeout(() => { + this.refreshFn(this.data.column.organization_id).subscribe() + this._snackBar.success('Column Updated') + this.close() + }, 300) + } + const failureFn = () => { + this._snackBar.alert('Failed to update column') + this.close() + } + const c = this.form.value as Column + const changes = {} + this.inProgress = true + changes[`${c.id}`] = { display_name: c.display_name, column_description: c.column_description } + this._columnService.updateMultipleColumns(c.organization_id, c.table_name, changes).pipe( + takeUntil(this._unsubscribeAll$), + tap((response: UploaderResponse) => { + this.progressBarObj.progress = response.progress + }), + switchMap(({ progress_key }) => { + return this._uploaderService.checkProgressLoop({ + progressKey: progress_key, + offset: 0, + multiplier: 1, + successFn, + failureFn, + progressBarObj: this.progressBarObj, + }) + }), + ).subscribe() + } + + close() { + this._dialogRef.close() + } + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } +} diff --git a/src/app/modules/organizations/columns/list/modal/rename-modal.component.ts b/src/app/modules/organizations/columns/list/modal/rename-modal.component.ts new file mode 100644 index 00000000..e69de29b From 64b55a93f674c395ee6f3d8b8fb003b28494bf6b Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Fri, 7 Mar 2025 21:20:23 +0000 Subject: [PATCH 09/30] column deletion modal --- .../columns/list/list.component.ts | 24 +++++- .../list/modal/delete-modal.component.html | 23 +++++ .../list/modal/delete-modal.component.ts | 83 +++++++++++++++++++ 3 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/app/modules/organizations/columns/list/modal/delete-modal.component.html diff --git a/src/app/modules/organizations/columns/list/list.component.ts b/src/app/modules/organizations/columns/list/list.component.ts index ef25868e..64b68a93 100644 --- a/src/app/modules/organizations/columns/list/list.component.ts +++ b/src/app/modules/organizations/columns/list/list.component.ts @@ -1,9 +1,10 @@ import { Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core' import { MatDialog } from '@angular/material/dialog' import { MatTableDataSource } from '@angular/material/table' -import { Subject, takeUntil } from 'rxjs' +import { Subject, takeUntil, tap } from 'rxjs' import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' +import { DeleteModalComponent } from './modal/delete-modal.component' import { FormModalComponent } from './modal/form-modal.component' @Component({ @@ -31,8 +32,25 @@ export class ListComponent implements OnDestroy { this._unsubscribeAll$.complete() } - delete(column: Column) { - console.log('Delete called for column: ', column) + delete(column: Column): void { + const dialogRef = this._dialog.open(DeleteModalComponent, { + width: '40rem', + data: { column }, + }) + + dialogRef + .afterClosed() + .pipe( + takeUntil(this._unsubscribeAll$), + tap(() => { + if (column.table_name === 'PropertyState') { + this._columnService.getPropertyColumns(column.organization_id).subscribe() + } else if (column.table_name === 'TaxLotState') { + this._columnService.getTaxLotColumns(column.organization_id).subscribe() + } + }), + ) + .subscribe() } rename(column: Column) { diff --git a/src/app/modules/organizations/columns/list/modal/delete-modal.component.html b/src/app/modules/organizations/columns/list/modal/delete-modal.component.html new file mode 100644 index 00000000..01f26a21 --- /dev/null +++ b/src/app/modules/organizations/columns/list/modal/delete-modal.component.html @@ -0,0 +1,23 @@ +

+ Delete Column {{ data.column.display_name }}? +

+ +
+ @if (inProgress) { + + } + @if (errorMessage) { + {{ errorMessage }} + } +
+ +@if (!inProgress) { +
+ + + + + + +
+} \ No newline at end of file diff --git a/src/app/modules/organizations/columns/list/modal/delete-modal.component.ts b/src/app/modules/organizations/columns/list/modal/delete-modal.component.ts index e69de29b..bcd373cf 100644 --- a/src/app/modules/organizations/columns/list/modal/delete-modal.component.ts +++ b/src/app/modules/organizations/columns/list/modal/delete-modal.component.ts @@ -0,0 +1,83 @@ +import { CommonModule } from '@angular/common' +import type { OnDestroy } from '@angular/core' +import { Component, inject } from '@angular/core' +import { MatButtonModule } from '@angular/material/button' +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog' +import { MatProgressBarModule } from '@angular/material/progress-bar' +import { Subject, switchMap, takeUntil, tap } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' +import { AlertComponent } from '@seed/components' +import { UploaderService } from '@seed/services/uploader/uploader.service' +import type { ProgressBarObj, UploaderResponse } from '@seed/services/uploader/uploader.types' +import { SnackbarService } from 'app/core/snackbar/snackbar.service' + +@Component({ + selector: 'seed-cycles-delete-modal', + templateUrl: './delete-modal.component.html', + imports: [AlertComponent, CommonModule, MatButtonModule, MatDialogModule, MatProgressBarModule], +}) +export class DeleteModalComponent implements OnDestroy { + private _columnService = inject(ColumnService) + private _uploaderService = inject(UploaderService) + private _dialogRef = inject(MatDialogRef) + private _snackBar = inject(SnackbarService) + private readonly _unsubscribeAll$ = new Subject() + errorMessage: string + inProgress = false + progressBarObj: ProgressBarObj = { + message: '', + progress: 0, + complete: false, + statusMessage: '', + progressLastUpdated: null, + progressLastChecked: null, + } + + data = inject(MAT_DIALOG_DATA) as { column: Column } + + onSubmit() { + this.inProgress = true + const successFn = () => { + setTimeout(() => { + this._snackBar.success('Column deleted') + this.close() + }, 300) + } + const failureFn = () => { + this._snackBar.alert('Failed to delete column') + this.close() + } + + // initiate delete cycle task + this._columnService.deleteColumn(this.data.column).pipe( + takeUntil(this._unsubscribeAll$), + tap((response: UploaderResponse) => { + this.progressBarObj.progress = response.progress + }), + switchMap(({ progress_key }) => { + return this._uploaderService.checkProgressLoop({ + progressKey: progress_key, + offset: 0, + multiplier: 1, + successFn, + failureFn, + progressBarObj: this.progressBarObj, + }) + }), + ) + .subscribe() + } + + close() { + this._dialogRef.close() + } + + dismiss() { + this._dialogRef.close() + } + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } +} From e77514b68c06a230341300515a242e9fd9c62928 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Fri, 7 Mar 2025 21:21:40 +0000 Subject: [PATCH 10/30] service changes for column deletion --- src/@seed/api/column/column.service.ts | 12 ++++++++++++ src/@seed/services/uploader/index.ts | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 src/@seed/services/uploader/index.ts diff --git a/src/@seed/api/column/column.service.ts b/src/@seed/api/column/column.service.ts index d739dcfc..a54e9a5c 100644 --- a/src/@seed/api/column/column.service.ts +++ b/src/@seed/api/column/column.service.ts @@ -71,4 +71,16 @@ export class ColumnService { }), ) } + + deleteColumn(column: Column): Observable { + const url = `/api/v3/columns/${column.id}/?organization_id=${column.organization_id}` + return this._httpClient.delete(url).pipe( + map((ur) => { + return ur + }), + catchError((error: HttpErrorResponse) => { + return this._errorService.handleError(error, 'Error deleting column') + }), + ) + } } diff --git a/src/@seed/services/uploader/index.ts b/src/@seed/services/uploader/index.ts new file mode 100644 index 00000000..b57e7fd5 --- /dev/null +++ b/src/@seed/services/uploader/index.ts @@ -0,0 +1,2 @@ +export * from './uploader.service' +export * from './uploader.types' From dd29e516455ceaa1a115eb30778b9eadd950a76e Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Fri, 7 Mar 2025 22:15:53 +0000 Subject: [PATCH 11/30] routing changes to ensure routes remain active in sidebar --- .../columns/columns.component.ts | 15 ++++--- .../organizations/columns/columns.routes.ts | 44 ++++++++++++++----- .../import-settings-properties.component.ts} | 6 +-- .../import-settings-taxlots.component.ts} | 6 +-- .../import-settings.component.html} | 0 5 files changed, 48 insertions(+), 23 deletions(-) rename src/app/modules/organizations/columns/{matching_critieria/matching-criteria-properties.component.ts => import-settings/import-settings-properties.component.ts} (55%) rename src/app/modules/organizations/columns/{matching_critieria/matching-criteria-taxlots.component.ts => import-settings/import-settings-taxlots.component.ts} (56%) rename src/app/modules/organizations/columns/{matching_critieria/matching-criteria.component.html => import-settings/import-settings.component.html} (100%) diff --git a/src/app/modules/organizations/columns/columns.component.ts b/src/app/modules/organizations/columns/columns.component.ts index 03cdf17d..d2501b49 100644 --- a/src/app/modules/organizations/columns/columns.component.ts +++ b/src/app/modules/organizations/columns/columns.component.ts @@ -32,29 +32,30 @@ export class ColumnsComponent implements OnInit { columnsNavigationMenu: NavigationItem[] = [ { id: 'organizations/columns/list', + exactMatch: false, title: 'Column List', - link: '/organizations/columns/list/properties', + link: '/organizations/columns/list', type: 'basic', fn: (n: NavigationItem) => { this.setNavTitle(n) }, }, { id: 'organizations/columns/geocoding', - link: '/organizations/columns/geocoding/properties', + link: '/organizations/columns/geocoding', title: 'Geocoding', type: 'basic', fn: (n: NavigationItem) => { this.setNavTitle(n) }, }, { id: 'organization/columns/data_type', - link: '/organizations/columns/data_types/properties', + link: '/organizations/columns/data-types', title: 'Data Types', type: 'basic', fn: (n: NavigationItem) => { this.setNavTitle(n) }, }, { - id: 'organizations/columns/matching_criteria', - link: '/organizations/columns/matching_criteria/properties', - title: 'Matching Criteria', + id: 'organizations/columns/import-settings', + link: '/organizations/columns/import-settings', + title: 'Import Settings', type: 'basic', fn: (n: NavigationItem) => { this.setNavTitle(n) }, }, @@ -89,6 +90,6 @@ export class ColumnsComponent implements OnInit { setTitle() { const basePath = `${this._location.path().split('/').slice(0, -1).join('/')}/properties` - this.pageTitle = this.columnsNavigationMenu.find((n) => n.link === basePath).title + this.pageTitle = this.columnsNavigationMenu.find((n) => basePath.includes(n.link)).title } } diff --git a/src/app/modules/organizations/columns/columns.routes.ts b/src/app/modules/organizations/columns/columns.routes.ts index 9ad0abb1..f4b47f9c 100644 --- a/src/app/modules/organizations/columns/columns.routes.ts +++ b/src/app/modules/organizations/columns/columns.routes.ts @@ -3,12 +3,18 @@ import { DataTypesPropertiesComponent } from './data-types/data-types-properties import { DataTypesTaxLotsComponent } from './data-types/data-types-taxlots.component' import { GeocodingPropertiesComponent } from './geocoding/geocoding-properties.component' import { GeocodingTaxlotsComponent } from './geocoding/geocoding-taxlots.component' +import { ImportSettingsPropertiesComponent } from './import-settings/import-settings-properties.component' +import { ImportSettingsTaxLotsComponent } from './import-settings/import-settings-taxlots.component' import { ListPropertiesComponent } from './list/list-properties.component' import { ListTaxLotComponent } from './list/list-taxlots.component' -import { MatchingCriteriaPropertiesComponent } from './matching_critieria/matching-criteria-properties.component' -import { MatchingCriteriaTaxLotsComponent } from './matching_critieria/matching-criteria-taxlots.component' export default [ + { + path: 'list', + title: 'Column List', + pathMatch: 'full', + redirectTo: 'list/properties', + }, { path: 'list/properties', title: 'Column List', @@ -19,6 +25,12 @@ export default [ title: 'Column List', component: ListTaxLotComponent, }, + { + path: 'geocoding', + title: 'Geocoding Order', + pathMatch: 'full', + redirectTo: 'geocoding/properties', + }, { path: 'geocoding/properties', title: 'Geocoding Order', @@ -30,23 +42,35 @@ export default [ component: GeocodingTaxlotsComponent, }, { - path: 'data_types/properties', + path: 'data-types', + title: 'Data Types', + pathMatch: 'full', + redirectTo: 'data-types/properties', + }, + { + path: 'data-types/properties', title: 'Data Types', component: DataTypesPropertiesComponent, }, { - path: 'data_types/taxlots', + path: 'data-types/taxlots', title: 'Data Types', component: DataTypesTaxLotsComponent, }, { - path: 'matching_criteria/properties', - title: 'Matching Criteria', - component: MatchingCriteriaPropertiesComponent, + path: 'import-settings', + title: 'Import Settings', + pathMatch: 'full', + redirectTo: 'import-settings/properties', + }, + { + path: 'import-settings/properties', + title: 'Import Settings', + component: ImportSettingsPropertiesComponent, }, { - path: 'matching_criteria/taxlots', - title: 'Matching Criteria', - component: MatchingCriteriaTaxLotsComponent, + path: 'import-settings/taxlots', + title: 'Import Settings', + component: ImportSettingsTaxLotsComponent, }, ] satisfies Routes diff --git a/src/app/modules/organizations/columns/matching_critieria/matching-criteria-properties.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts similarity index 55% rename from src/app/modules/organizations/columns/matching_critieria/matching-criteria-properties.component.ts rename to src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts index 0fb7c672..e3d97e47 100644 --- a/src/app/modules/organizations/columns/matching_critieria/matching-criteria-properties.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts @@ -2,11 +2,11 @@ import { Component, ViewEncapsulation } from '@angular/core' import { SharedImports } from '@seed/directives' @Component({ - selector: 'seed-organizations-column-matching-criteria-properties', - templateUrl: './matching-criteria.component.html', + selector: 'seed-organizations-column-import-settings-properties', + templateUrl: './import-settings.component.html', encapsulation: ViewEncapsulation.None, imports: [SharedImports], }) -export class MatchingCriteriaPropertiesComponent { +export class ImportSettingsPropertiesComponent { type = 'properties' } diff --git a/src/app/modules/organizations/columns/matching_critieria/matching-criteria-taxlots.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts similarity index 56% rename from src/app/modules/organizations/columns/matching_critieria/matching-criteria-taxlots.component.ts rename to src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts index 1600e085..424b4e96 100644 --- a/src/app/modules/organizations/columns/matching_critieria/matching-criteria-taxlots.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts @@ -2,11 +2,11 @@ import { Component, ViewEncapsulation } from '@angular/core' import { SharedImports } from '@seed/directives' @Component({ - selector: 'seed-organizations-column-matching-criteria-taxlots', - templateUrl: './matching-criteria.component.html', + selector: 'seed-organizations-column-import-settings-taxlots', + templateUrl: './import-settings.component.html', encapsulation: ViewEncapsulation.None, imports: [SharedImports], }) -export class MatchingCriteriaTaxLotsComponent { +export class ImportSettingsTaxLotsComponent { type = 'taxlots' } diff --git a/src/app/modules/organizations/columns/matching_critieria/matching-criteria.component.html b/src/app/modules/organizations/columns/import-settings/import-settings.component.html similarity index 100% rename from src/app/modules/organizations/columns/matching_critieria/matching-criteria.component.html rename to src/app/modules/organizations/columns/import-settings/import-settings.component.html From 47c52408ac029171548113946ead23d2c2b286c9 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Fri, 7 Mar 2025 22:16:04 +0000 Subject: [PATCH 12/30] add rename button --- src/app/modules/organizations/columns/list/list.component.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html index 06eee018..6f4df58e 100644 --- a/src/app/modules/organizations/columns/list/list.component.html +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -33,6 +33,9 @@ + + + @if (c.is_extra_data) { From 2bf6fd12e5153a17124aee3c356f7f311a342e8d Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Fri, 7 Mar 2025 22:37:59 +0000 Subject: [PATCH 13/30] prettier --- .../columns/columns.component.html | 6 ++-- .../data-types/data-types.component.html | 27 +++++++---------- .../geocoding/geocoding.component.html | 29 +++++++++++-------- .../import-settings.component.html | 2 +- .../columns/list/list.component.html | 6 ++-- .../list/modal/delete-modal.component.html | 18 ++++++------ .../list/modal/form-modal.component.html | 22 +++++++------- .../columns/modal/update-modal.component.html | 6 ++-- 8 files changed, 55 insertions(+), 61 deletions(-) diff --git a/src/app/modules/organizations/columns/columns.component.html b/src/app/modules/organizations/columns/columns.component.html index d298ef64..133e0139 100644 --- a/src/app/modules/organizations/columns/columns.component.html +++ b/src/app/modules/organizations/columns/columns.component.html @@ -23,11 +23,11 @@

Columns

- + + diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.html b/src/app/modules/organizations/columns/data-types/data-types.component.html index cc5736a5..37cbf218 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.html +++ b/src/app/modules/organizations/columns/data-types/data-types.component.html @@ -1,23 +1,18 @@ -
- This allows the user to set the type, such as Text, Number, Date for Extra Data Fields. -
+
This allows the user to set the type, such as Text, Number, Date for Extra Data Fields.
@if (columns && columns.length === 0) { -
- No extra data columns have been created yet. -
+
No extra data columns have been created yet.
}
- + Display Name {{ c.display_name }} - Data Type - + @for (type of dataTypes; track type.id) { {{ type.label }} @@ -25,16 +20,16 @@ - + - + -
- -
+
+ +
diff --git a/src/app/modules/organizations/columns/geocoding/geocoding.component.html b/src/app/modules/organizations/columns/geocoding/geocoding.component.html index 151191aa..c7a24247 100644 --- a/src/app/modules/organizations/columns/geocoding/geocoding.component.html +++ b/src/app/modules/organizations/columns/geocoding/geocoding.component.html @@ -1,6 +1,4 @@ -
- Drag columns to reorder them. Add columns with the selector below. -
+
Drag columns to reorder them. Add columns with the selector below.
When all your changes have been made, make sure to save them.
-@for (column of columns; track column.id) { -
-
-
{{ column.geocoding_order }}
-
{{ column.display_name }}
+ @for (column of columns; track column.id) { +
+
+
{{ column.geocoding_order }}
+
{{ column.display_name }}
+
+
+ +
-
-
-} + }
Add A GeoCoding Column
@@ -37,7 +42,7 @@
-
When all your changes have been made, make sure to save them.
+
When all your changes have been made, make sure to save them.
- - - - -
-} \ No newline at end of file +
+ + + + + + +
+} diff --git a/src/app/modules/organizations/columns/list/modal/form-modal.component.html b/src/app/modules/organizations/columns/list/modal/form-modal.component.html index a9ffc04e..ad0d4aab 100644 --- a/src/app/modules/organizations/columns/list/modal/form-modal.component.html +++ b/src/app/modules/organizations/columns/list/modal/form-modal.component.html @@ -1,6 +1,4 @@ -

- Edit Column -

+

Edit Column

Display Name @@ -22,12 +20,12 @@

} @if (!inProgress) { -
- - - - - - -
-} \ No newline at end of file +
+ + + + + + +
+} diff --git a/src/app/modules/organizations/columns/modal/update-modal.component.html b/src/app/modules/organizations/columns/modal/update-modal.component.html index b0a2006b..266646e5 100644 --- a/src/app/modules/organizations/columns/modal/update-modal.component.html +++ b/src/app/modules/organizations/columns/modal/update-modal.component.html @@ -1,6 +1,4 @@ -

- Update Columns -

+

Update Columns

@if (inProgress) { @@ -9,4 +7,4 @@

{{ errorMessage }} } -

\ No newline at end of file + From 5d1ddb7fedc0688cce6fc32ae88dc836ff7c140c Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Fri, 7 Mar 2025 22:41:01 +0000 Subject: [PATCH 14/30] remove column mapping/settings route matchers --- src/app/modules/organizations/organizations.routes.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/app/modules/organizations/organizations.routes.ts b/src/app/modules/organizations/organizations.routes.ts index 75ef4000..557e694b 100644 --- a/src/app/modules/organizations/organizations.routes.ts +++ b/src/app/modules/organizations/organizations.routes.ts @@ -18,21 +18,11 @@ const genericTypeMatcher = (args: OrganizationGenericTypeMatcher) => (segments: } } -const columnMappingTypeMatcher = (segments: UrlSegment[]) => { - const args = { segments, validTypes: ['goal', 'properties', 'taxlots'], validPage: 'column-mappings' } - return genericTypeMatcher(args)(segments) -} - const dataQualityTypeMatcher = (segments: UrlSegment[]) => { const args = { segments, validTypes: ['goal', 'properties', 'taxlots'], validPage: 'data-quality' } return genericTypeMatcher(args)(segments) } -const columnSettingsTypeMatcher = (segments: UrlSegment[]) => { - const args = { segments, validTypes: ['properties', 'taxlots'], validPage: 'column-settings' } - return genericTypeMatcher(args)(segments) -} - const derivedColumnsTypeMatcher = (segments: UrlSegment[]) => { const args = { segments, validTypes: ['properties', 'taxlots'], validPage: 'derived-columns' } return genericTypeMatcher(args)(segments) From 92a1e030fd423f6abc1fe20af7c0301b974846f8 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 10 Mar 2025 21:16:38 +0000 Subject: [PATCH 15/30] simplify update/delete responses - remove map --- src/@seed/api/column/column.service.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/@seed/api/column/column.service.ts b/src/@seed/api/column/column.service.ts index a54e9a5c..e4e17120 100644 --- a/src/@seed/api/column/column.service.ts +++ b/src/@seed/api/column/column.service.ts @@ -63,9 +63,6 @@ export class ColumnService { updateMultipleColumns(organization_id: number, table_name: string, changes: object): Observable { const url = '/api/v3/columns/update_multiple/' return this._httpClient.post(url, { organization_id, table_name, changes }).pipe( - map((ur) => { - return ur - }), catchError((error: HttpErrorResponse) => { return this._errorService.handleError(error, 'Error updating columns') }), @@ -75,9 +72,6 @@ export class ColumnService { deleteColumn(column: Column): Observable { const url = `/api/v3/columns/${column.id}/?organization_id=${column.organization_id}` return this._httpClient.delete(url).pipe( - map((ur) => { - return ur - }), catchError((error: HttpErrorResponse) => { return this._errorService.handleError(error, 'Error deleting column') }), From 975018b3084fb57aa6ae87988cffbc21228396e3 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 10 Mar 2025 21:26:52 +0000 Subject: [PATCH 16/30] fix underscore in id --- src/app/modules/organizations/columns/columns.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/organizations/columns/columns.component.ts b/src/app/modules/organizations/columns/columns.component.ts index d2501b49..3fb775b8 100644 --- a/src/app/modules/organizations/columns/columns.component.ts +++ b/src/app/modules/organizations/columns/columns.component.ts @@ -46,7 +46,7 @@ export class ColumnsComponent implements OnInit { fn: (n: NavigationItem) => { this.setNavTitle(n) }, }, { - id: 'organization/columns/data_type', + id: 'organization/columns/data-type', link: '/organizations/columns/data-types', title: 'Data Types', type: 'basic', From 61003ecbb3a3217af7ab345d44190441c86d38a1 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 10 Mar 2025 21:27:15 +0000 Subject: [PATCH 17/30] fix double submit --- .../organizations/columns/data-types/data-types.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.html b/src/app/modules/organizations/columns/data-types/data-types.component.html index 37cbf218..0a70fdc6 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.html +++ b/src/app/modules/organizations/columns/data-types/data-types.component.html @@ -27,7 +27,7 @@
-
From 94fe8e510b0d0cba0e96cf8ef274e1f4a6e931c1 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 10 Mar 2025 21:27:41 +0000 Subject: [PATCH 18/30] move from subscribe -> pipe.map.subscribe for data loader/filter --- .../data-types-properties.component.ts | 49 ++++++------------- .../data-types-taxlots.component.ts | 45 ++++------------- .../data-types/data-types.component.ts | 30 +++++++++++- .../columns/list/list-properties.component.ts | 15 +++--- .../columns/list/list-taxlots.component.ts | 20 +++----- 5 files changed, 65 insertions(+), 94 deletions(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts index a2360dff..c5ced6a6 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts @@ -7,7 +7,7 @@ import { MatFormFieldModule } from '@angular/material/form-field' import { MatPaginator } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' import { MatTableDataSource, MatTableModule } from '@angular/material/table' -import { takeUntil } from 'rxjs' +import { map, takeUntil } from 'rxjs' import { type Column } from '@seed/api/column' import { SharedImports } from '@seed/directives' import { type UploaderResponse } from '@seed/services/uploader/uploader.types' @@ -23,7 +23,6 @@ import { DataTypesComponent } from './data-types.component' }) export class DataTypesPropertiesComponent extends DataTypesComponent implements AfterViewInit, OnInit { type = 'PropertyState' - private _dialog = inject(MatDialog) columnTableDataSource = new MatTableDataSource([]) columnTableColumns = [ 'display_name', @@ -32,43 +31,23 @@ export class DataTypesPropertiesComponent extends DataTypesComponent implements @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { - this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { - this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.is_extra_data) - for (const c of this.columns) { - this.dataTypesForm.addControl(`${c.id}`, new FormControl((c.data_type), [Validators.required])) - } - if (this.columns.length > 0) { - this.isLoading = false - } - this.columnTableDataSource.data = this.columns - }) + this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( + map((columns) => { + this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.is_extra_data) + for (const c of this.columns) { + this.dataTypesForm.addControl(`${c.id}`, new FormControl((c.data_type), [Validators.required])) + } + if (this.columns.length > 0) { + this.isLoading = false + } + this.columnTableDataSource.data = this.columns + }) + ).subscribe() } ngAfterViewInit(): void { this.columnTableDataSource.paginator = this.paginator } - save(): void { - const changes = {} - for (const column of this.columns) { - const setting = this.dataTypesForm.get(`${column.id}`).value - if (setting !== column.data_type) { - changes[column.id] = { data_type: setting } - } - } - if (Object.keys(changes).length > 0) { - this._columnService.updateMultipleColumns(this.columns[0].organization_id, this.type, changes).subscribe((response: UploaderResponse) => { - const dialogRef = this._dialog.open(UpdateModalComponent, { - width: '40rem', - data: { progressResponse: response }, - }) - dialogRef - .afterClosed() - .pipe( - takeUntil(this._unsubscribeAll$), - ) - .subscribe() - }) - } - } + } diff --git a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts index 4ed27491..b9d67f65 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts @@ -2,17 +2,15 @@ import { CommonModule } from '@angular/common' import { type AfterViewInit, Component, inject, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms' import { MatButtonModule } from '@angular/material/button' -import { MatDialog } from '@angular/material/dialog' import { MatFormFieldModule } from '@angular/material/form-field' import { MatPaginator } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' import { MatTableDataSource, MatTableModule } from '@angular/material/table' -import { takeUntil } from 'rxjs' +import { map, takeUntil } from 'rxjs' import { type Column } from '@seed/api/column' import { SharedImports } from '@seed/directives' import { type UploaderResponse } from '@seed/services/uploader/uploader.types' import { naturalSort } from '@seed/utils' -import { UpdateModalComponent } from '../modal/update-modal.component' import { DataTypesComponent } from './data-types.component' @Component({ @@ -23,7 +21,6 @@ import { DataTypesComponent } from './data-types.component' }) export class DataTypesTaxLotsComponent extends DataTypesComponent implements AfterViewInit, OnInit { type = 'TaxLotState' - private _dialog = inject(MatDialog) columnTableDataSource = new MatTableDataSource([]) columnTableColumns = [ 'display_name', @@ -32,40 +29,18 @@ export class DataTypesTaxLotsComponent extends DataTypesComponent implements Aft @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { - this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { - this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.is_extra_data) - for (const c of this.columns) { - this.dataTypesForm.addControl(`${c.id}`, new FormControl((c.data_type), [Validators.required])) - } - this.columnTableDataSource.data = this.columns - }) + this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( + map((columns) => { + this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.is_extra_data) + for (const c of this.columns) { + this.dataTypesForm.addControl(`${c.id}`, new FormControl((c.data_type), [Validators.required])) + } + this.columnTableDataSource.data = this.columns + }) + ).subscribe() } ngAfterViewInit() { this.columnTableDataSource.paginator = this.paginator } - - save(): void { - const changes = {} - for (const column of this.columns) { - const setting = this.dataTypesForm.get(`${column.id}`).value - if (setting !== column.data_type) { - changes[column.id] = { data_type: setting } - } - } - if (Object.keys(changes).length > 0) { - this._columnService.updateMultipleColumns(this.columns[0].organization_id, this.type, changes).subscribe((response: UploaderResponse) => { - const dialogRef = this._dialog.open(UpdateModalComponent, { - width: '40rem', - data: { progressResponse: response }, - }) - dialogRef - .afterClosed() - .pipe( - takeUntil(this._unsubscribeAll$), - ) - .subscribe() - }) - } - } } diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.ts b/src/app/modules/organizations/columns/data-types/data-types.component.ts index 753dcb8c..970f477d 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types.component.ts @@ -1,8 +1,11 @@ import { Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core' import { FormGroup } from '@angular/forms' -import { Subject } from 'rxjs' +import { MatDialog } from '@angular/material/dialog' +import { Subject, takeUntil } from 'rxjs' import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' +import { UpdateModalComponent } from '../modal/update-modal.component' +import { type UploaderResponse } from '@seed/services/uploader' import { DataTypes } from './data-types.constants' @Component({ @@ -15,6 +18,7 @@ import { DataTypes } from './data-types.constants' export class DataTypesComponent implements OnDestroy { protected _columnService = inject(ColumnService) protected readonly _unsubscribeAll$ = new Subject() + private _dialog = inject(MatDialog) columns: Column[] type: string dataTypesForm = new FormGroup({}) @@ -25,4 +29,28 @@ export class DataTypesComponent implements OnDestroy { this._unsubscribeAll$.next() this._unsubscribeAll$.complete() } + + save(): void { + const changes = {} + for (const column of this.columns) { + const setting = this.dataTypesForm.get(`${column.id}`).value + if (setting !== column.data_type) { + changes[column.id] = { data_type: setting } + } + } + if (Object.keys(changes).length > 0) { + this._columnService.updateMultipleColumns(this.columns[0].organization_id, this.type, changes).subscribe((response: UploaderResponse) => { + const dialogRef = this._dialog.open(UpdateModalComponent, { + width: '40rem', + data: { progressResponse: response }, + }) + dialogRef + .afterClosed() + .pipe( + takeUntil(this._unsubscribeAll$), + ) + .subscribe() + }) + } + } } diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts index 4895e11e..000020fa 100644 --- a/src/app/modules/organizations/columns/list/list-properties.component.ts +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -5,7 +5,7 @@ import { MatInputModule } from '@angular/material/input' import { MatPaginator } from '@angular/material/paginator' import { MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' -import { takeUntil } from 'rxjs' +import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' import { ListComponent } from './list.component' @@ -21,17 +21,14 @@ export class ListPropertiesComponent extends ListComponent implements AfterViewI @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { - this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { - this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) - }) + this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( + map((columns) => { + this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + }) + ).subscribe() } ngAfterViewInit(): void { this.columnTableDataSource.paginator = this.paginator } - - applyFilter(event: Event): void { - const filterValue = (event.target as HTMLInputElement).value - this.columnTableDataSource.filter = filterValue.trim().toLowerCase() - } } diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts index 129bad19..fbd30e85 100644 --- a/src/app/modules/organizations/columns/list/list-taxlots.component.ts +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -5,7 +5,7 @@ import { MatInputModule } from '@angular/material/input' import { MatPaginator } from '@angular/material/paginator' import { MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' -import { takeUntil } from 'rxjs' +import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' import { ListComponent } from './list.component' @@ -21,22 +21,14 @@ export class ListTaxLotComponent extends ListComponent implements AfterViewInit, @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { - this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((columns) => { - this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) - }) + this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( + map((columns) => { + this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + }) + ).subscribe() } ngAfterViewInit() { this.columnTableDataSource.paginator = this.paginator } - - ngOnDestroy(): void { - this._unsubscribeAll$.next() - this._unsubscribeAll$.complete() - } - - applyFilter(event: Event): void { - const filterValue = (event.target as HTMLInputElement).value - this.columnTableDataSource.filter = filterValue.trim().toLowerCase() - } } From f6a74493e020c5512f04684581d07e0b02d1976e Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 10 Mar 2025 21:40:46 +0000 Subject: [PATCH 19/30] linter --- .../data-types/data-types-properties.component.ts | 9 ++------- .../columns/data-types/data-types-taxlots.component.ts | 5 ++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts index c5ced6a6..f41ae6a4 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts @@ -1,8 +1,7 @@ import { CommonModule } from '@angular/common' -import { type AfterViewInit, Component, inject, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' +import { type AfterViewInit, Component, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms' import { MatButtonModule } from '@angular/material/button' -import { MatDialog } from '@angular/material/dialog' import { MatFormFieldModule } from '@angular/material/form-field' import { MatPaginator } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' @@ -10,9 +9,7 @@ import { MatTableDataSource, MatTableModule } from '@angular/material/table' import { map, takeUntil } from 'rxjs' import { type Column } from '@seed/api/column' import { SharedImports } from '@seed/directives' -import { type UploaderResponse } from '@seed/services/uploader/uploader.types' import { naturalSort } from '@seed/utils' -import { UpdateModalComponent } from '../modal/update-modal.component' import { DataTypesComponent } from './data-types.component' @Component({ @@ -41,13 +38,11 @@ export class DataTypesPropertiesComponent extends DataTypesComponent implements this.isLoading = false } this.columnTableDataSource.data = this.columns - }) + }), ).subscribe() } ngAfterViewInit(): void { this.columnTableDataSource.paginator = this.paginator } - - } diff --git a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts index b9d67f65..b0a1b46e 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common' -import { type AfterViewInit, Component, inject, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' +import { type AfterViewInit, Component, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms' import { MatButtonModule } from '@angular/material/button' import { MatFormFieldModule } from '@angular/material/form-field' @@ -9,7 +9,6 @@ import { MatTableDataSource, MatTableModule } from '@angular/material/table' import { map, takeUntil } from 'rxjs' import { type Column } from '@seed/api/column' import { SharedImports } from '@seed/directives' -import { type UploaderResponse } from '@seed/services/uploader/uploader.types' import { naturalSort } from '@seed/utils' import { DataTypesComponent } from './data-types.component' @@ -36,7 +35,7 @@ export class DataTypesTaxLotsComponent extends DataTypesComponent implements Aft this.dataTypesForm.addControl(`${c.id}`, new FormControl((c.data_type), [Validators.required])) } this.columnTableDataSource.data = this.columns - }) + }), ).subscribe() } From 951afbaaaf3028b792212ddb33508c17967ee7c7 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Mon, 10 Mar 2025 21:42:56 +0000 Subject: [PATCH 20/30] more linter --- .../organizations/columns/data-types/data-types.component.ts | 2 +- .../organizations/columns/list/list-properties.component.ts | 2 +- .../organizations/columns/list/list-taxlots.component.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.ts b/src/app/modules/organizations/columns/data-types/data-types.component.ts index 970f477d..ab7140e9 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types.component.ts @@ -4,8 +4,8 @@ import { MatDialog } from '@angular/material/dialog' import { Subject, takeUntil } from 'rxjs' import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' -import { UpdateModalComponent } from '../modal/update-modal.component' import { type UploaderResponse } from '@seed/services/uploader' +import { UpdateModalComponent } from '../modal/update-modal.component' import { DataTypes } from './data-types.constants' @Component({ diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts index 000020fa..460c3bc6 100644 --- a/src/app/modules/organizations/columns/list/list-properties.component.ts +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -24,7 +24,7 @@ export class ListPropertiesComponent extends ListComponent implements AfterViewI this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( map((columns) => { this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) - }) + }), ).subscribe() } diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts index fbd30e85..70a65771 100644 --- a/src/app/modules/organizations/columns/list/list-taxlots.component.ts +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -24,7 +24,7 @@ export class ListTaxLotComponent extends ListComponent implements AfterViewInit, this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( map((columns) => { this.columnTableDataSource.data = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) - }) + }), ).subscribe() } From 63aed75071fd570a76460a10480ee2447b52bfae Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 15:15:53 +0000 Subject: [PATCH 21/30] remove red boxes on data type --- .../columns/data-types/data-types.component.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.html b/src/app/modules/organizations/columns/data-types/data-types.component.html index 0a70fdc6..304d1089 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.html +++ b/src/app/modules/organizations/columns/data-types/data-types.component.html @@ -4,15 +4,15 @@
No extra data columns have been created yet.
}
- + - Display Name - {{ c.display_name }} + Display Name + {{ c.display_name }} - Data Type - + Data Type + @for (type of dataTypes; track type.id) { {{ type.label }} From b0b94cd4c90b0b4ee10055e883cb4b831cbad20c Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 15:25:41 +0000 Subject: [PATCH 22/30] show all columns - canonical are disabled --- .../data-types/data-types-properties.component.ts | 8 +++----- .../data-types/data-types-taxlots.component.ts | 6 ++---- .../columns/data-types/data-types.component.html | 12 ++++++------ .../columns/data-types/data-types.component.ts | 14 ++++++++++++-- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts index f41ae6a4..baa31f21 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common' import { type AfterViewInit, Component, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' -import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms' +import { ReactiveFormsModule } from '@angular/forms' import { MatButtonModule } from '@angular/material/button' import { MatFormFieldModule } from '@angular/material/form-field' import { MatPaginator } from '@angular/material/paginator' @@ -30,10 +30,8 @@ export class DataTypesPropertiesComponent extends DataTypesComponent implements ngOnInit(): void { this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( map((columns) => { - this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.is_extra_data) - for (const c of this.columns) { - this.dataTypesForm.addControl(`${c.id}`, new FormControl((c.data_type), [Validators.required])) - } + this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.initializeFormControls() if (this.columns.length > 0) { this.isLoading = false } diff --git a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts index b0a1b46e..d068e9f6 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts @@ -30,10 +30,8 @@ export class DataTypesTaxLotsComponent extends DataTypesComponent implements Aft ngOnInit(): void { this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( map((columns) => { - this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => c.is_extra_data) - for (const c of this.columns) { - this.dataTypesForm.addControl(`${c.id}`, new FormControl((c.data_type), [Validators.required])) - } + this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.initializeFormControls() this.columnTableDataSource.data = this.columns }), ).subscribe() diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.html b/src/app/modules/organizations/columns/data-types/data-types.component.html index 304d1089..10c14202 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.html +++ b/src/app/modules/organizations/columns/data-types/data-types.component.html @@ -4,15 +4,15 @@
No extra data columns have been created yet.
}
- + - - + + - - + - +
Display Name{{ c.display_name }}Display Name{{ c.display_name }} Data Type + Data Type @for (type of dataTypes; track type.id) { {{ type.label }} @@ -23,7 +23,7 @@
diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.ts b/src/app/modules/organizations/columns/data-types/data-types.component.ts index ab7140e9..5020e342 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types.component.ts @@ -1,5 +1,5 @@ import { Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core' -import { FormGroup } from '@angular/forms' +import { FormControl, FormGroup, Validators } from '@angular/forms' import { MatDialog } from '@angular/material/dialog' import { Subject, takeUntil } from 'rxjs' import { type Column, ColumnService } from '@seed/api/column' @@ -32,7 +32,7 @@ export class DataTypesComponent implements OnDestroy { save(): void { const changes = {} - for (const column of this.columns) { + for (const column of this.columns.filter((c) => c.is_extra_data)) { const setting = this.dataTypesForm.get(`${column.id}`).value if (setting !== column.data_type) { changes[column.id] = { data_type: setting } @@ -53,4 +53,14 @@ export class DataTypesComponent implements OnDestroy { }) } } + + initializeFormControls() { + for (const c of this.columns) { + const stringId = `${c.id}` + this.dataTypesForm.addControl(stringId, new FormControl((c.data_type), [Validators.required])) + if (!c.is_extra_data) { + this.dataTypesForm.controls[stringId].disable() + } + } + } } From a0f6201e6880a3e6fa3145156a0dd209b9d715a6 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 16:19:48 +0000 Subject: [PATCH 23/30] use seed-page-table-container, add filter --- .../data-types-properties.component.ts | 13 ++--- .../data-types-taxlots.component.ts | 14 ++--- .../data-types/data-types.component.html | 51 +++++++++++-------- .../data-types/data-types.component.ts | 11 ++++ 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts index baa31f21..b8779fe4 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts @@ -3,28 +3,25 @@ import { type AfterViewInit, Component, type OnInit, ViewChild, ViewEncapsulatio import { ReactiveFormsModule } from '@angular/forms' import { MatButtonModule } from '@angular/material/button' import { MatFormFieldModule } from '@angular/material/form-field' +import { MatInputModule } from '@angular/material/input' import { MatPaginator } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' -import { MatTableDataSource, MatTableModule } from '@angular/material/table' +import { MatTableModule } from '@angular/material/table' import { map, takeUntil } from 'rxjs' -import { type Column } from '@seed/api/column' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' +import { TableContainerComponent } from '@seed/components' import { DataTypesComponent } from './data-types.component' @Component({ selector: 'seed-organizations-column-data-types-properties', templateUrl: './data-types.component.html', encapsulation: ViewEncapsulation.None, - imports: [CommonModule, SharedImports, MatButtonModule, MatFormFieldModule, MatPaginator, MatSelectModule, MatTableModule, ReactiveFormsModule], + imports: [CommonModule, SharedImports, TableContainerComponent, MatButtonModule, MatFormFieldModule, MatInputModule, MatPaginator, MatSelectModule, MatTableModule, ReactiveFormsModule], }) export class DataTypesPropertiesComponent extends DataTypesComponent implements AfterViewInit, OnInit { type = 'PropertyState' - columnTableDataSource = new MatTableDataSource([]) - columnTableColumns = [ - 'display_name', - 'data_type', - ] + @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { diff --git a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts index d068e9f6..25053eb8 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts @@ -1,30 +1,26 @@ import { CommonModule } from '@angular/common' import { type AfterViewInit, Component, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core' -import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms' +import { ReactiveFormsModule } from '@angular/forms' import { MatButtonModule } from '@angular/material/button' import { MatFormFieldModule } from '@angular/material/form-field' +import { MatInputModule } from '@angular/material/input' import { MatPaginator } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' -import { MatTableDataSource, MatTableModule } from '@angular/material/table' +import { MatTableModule } from '@angular/material/table' import { map, takeUntil } from 'rxjs' -import { type Column } from '@seed/api/column' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' +import { TableContainerComponent } from '@seed/components' import { DataTypesComponent } from './data-types.component' @Component({ selector: 'seed-organizations-column-data-types-taxlots', templateUrl: './data-types.component.html', encapsulation: ViewEncapsulation.None, - imports: [CommonModule, SharedImports, MatButtonModule, MatFormFieldModule, MatPaginator, MatSelectModule, MatTableModule, ReactiveFormsModule], + imports: [CommonModule, SharedImports, TableContainerComponent, MatButtonModule, MatFormFieldModule, MatInputModule, MatPaginator, MatSelectModule, MatTableModule, ReactiveFormsModule], }) export class DataTypesTaxLotsComponent extends DataTypesComponent implements AfterViewInit, OnInit { type = 'TaxLotState' - columnTableDataSource = new MatTableDataSource([]) - columnTableColumns = [ - 'display_name', - 'data_type', - ] @ViewChild(MatPaginator) paginator: MatPaginator ngOnInit(): void { diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.html b/src/app/modules/organizations/columns/data-types/data-types.component.html index 10c14202..ab22d9a1 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.html +++ b/src/app/modules/organizations/columns/data-types/data-types.component.html @@ -1,35 +1,44 @@
This allows the user to set the type, such as Text, Number, Date for Extra Data Fields.
- @if (columns && columns.length === 0) {
No extra data columns have been created yet.
}
- - - - - + + + Filter + + + - - - - +
Display Name{{ c.display_name }}Data Type - - @for (type of dataTypes; track type.id) { - {{ type.label }} - } - -
+ + + + - - -
Display Name{{ c.display_name }}
- + + Data Type + + + @for (type of dataTypes; track type.id) { + {{ type.label }} + } + + + + + + + +
+ + + +
- diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.ts b/src/app/modules/organizations/columns/data-types/data-types.component.ts index 5020e342..8cce9cd6 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types.component.ts @@ -1,6 +1,7 @@ import { Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core' import { FormControl, FormGroup, Validators } from '@angular/forms' import { MatDialog } from '@angular/material/dialog' +import { MatTableDataSource } from '@angular/material/table' import { Subject, takeUntil } from 'rxjs' import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' @@ -20,6 +21,11 @@ export class DataTypesComponent implements OnDestroy { protected readonly _unsubscribeAll$ = new Subject() private _dialog = inject(MatDialog) columns: Column[] + columnTableDataSource = new MatTableDataSource([]) + columnTableColumns = [ + 'display_name', + 'data_type', + ] type: string dataTypesForm = new FormGroup({}) dataTypes = DataTypes @@ -30,6 +36,11 @@ export class DataTypesComponent implements OnDestroy { this._unsubscribeAll$.complete() } + applyFilter(event: Event): void { + const filterValue = (event.target as HTMLInputElement).value + this.columnTableDataSource.filter = filterValue.trim().toLowerCase() + } + save(): void { const changes = {} for (const column of this.columns.filter((c) => c.is_extra_data)) { From 6d194cd6948e7cdc4663f5cd8eabd7574fe38eed Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 16:24:41 +0000 Subject: [PATCH 24/30] use page-table component in column list --- .../organizations/columns/list/list-properties.component.ts | 3 ++- .../organizations/columns/list/list-taxlots.component.ts | 3 ++- .../modules/organizations/columns/list/list.component.html | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts index 460c3bc6..07ea2d6f 100644 --- a/src/app/modules/organizations/columns/list/list-properties.component.ts +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -8,13 +8,14 @@ import { MatTooltip } from '@angular/material/tooltip' import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' +import { TableContainerComponent } from '@seed/components' import { ListComponent } from './list.component' @Component({ selector: 'seed-organizations-columns-list-properties', templateUrl: './list.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip], + imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip, TableContainerComponent], }) export class ListPropertiesComponent extends ListComponent implements AfterViewInit, OnDestroy, OnInit { type = 'properties' diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts index 70a65771..33089095 100644 --- a/src/app/modules/organizations/columns/list/list-taxlots.component.ts +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -8,13 +8,14 @@ import { MatTooltip } from '@angular/material/tooltip' import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' +import { TableContainerComponent } from '@seed/components' import { ListComponent } from './list.component' @Component({ selector: 'seed-organizations-columns-list-taxlots', templateUrl: './list.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip], + imports: [SharedImports, MatFormFieldModule, MatIcon, MatInputModule, MatPaginator, MatTableModule, MatTooltip, TableContainerComponent], }) export class ListTaxLotComponent extends ListComponent implements AfterViewInit, OnDestroy, OnInit { type = 'taxlots' diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html index 2d475b0f..78f713b8 100644 --- a/src/app/modules/organizations/columns/list/list.component.html +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -2,7 +2,8 @@ Filter - + + - +
Canonical? @@ -45,5 +46,6 @@
+
From 687955639c018c74b7a57b688caf3facf472acf3 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 16:25:40 +0000 Subject: [PATCH 25/30] move filter into table-container --- .../organizations/columns/list/list.component.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html index 78f713b8..99b644c9 100644 --- a/src/app/modules/organizations/columns/list/list.component.html +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -1,9 +1,9 @@ - - Filter - - - + + Filter + + +
+ +
Canonical? From b0261e711097cb34559e869833f7cb117f2ecde3 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 16:26:55 +0000 Subject: [PATCH 26/30] prettier --- .../data-types/data-types.component.html | 37 ++++----- .../import-settings.component.ts | 26 +++++++ .../columns/list/list.component.html | 78 +++++++++---------- 3 files changed, 82 insertions(+), 59 deletions(-) create mode 100644 src/app/modules/organizations/columns/import-settings/import-settings.component.ts diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.html b/src/app/modules/organizations/columns/data-types/data-types.component.html index ab22d9a1..cbe6350d 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.html +++ b/src/app/modules/organizations/columns/data-types/data-types.component.html @@ -1,15 +1,14 @@
This allows the user to set the type, such as Text, Number, Date for Extra Data Fields.
- @if (columns && columns.length === 0) { -
No extra data columns have been created yet.
- } -
- - - Filter - - -
- +@if (columns && columns.length === 0) { +
No extra data columns have been created yet.
+} +
+ + + Filter + + + @@ -32,13 +31,11 @@
Display Name
-
- -
- - +
+ +
+
- -
+
diff --git a/src/app/modules/organizations/columns/import-settings/import-settings.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts new file mode 100644 index 00000000..1b9d57d4 --- /dev/null +++ b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts @@ -0,0 +1,26 @@ +import { Component, type OnDestroy, ViewEncapsulation } from '@angular/core' +import { Subject } from 'rxjs' +import { type Column, type ColumnService } from '@seed/api/column' +import { SharedImports } from '@seed/directives' + +@Component({ + selector: 'seed-organizations-column-import-settings', + templateUrl: './import-settings.component.html', + encapsulation: ViewEncapsulation.None, + imports: [SharedImports], +}) +export class ImportSettingsComponent implements OnDestroy{ + protected _columnService: ColumnService + protected readonly _unsubscribeAll$ = new Subject() + columns: Column[] + + ngOnDestroy(): void { + this._unsubscribeAll$.next() + this._unsubscribeAll$.complete() + } + + save(): void { + + } + +} diff --git a/src/app/modules/organizations/columns/list/list.component.html b/src/app/modules/organizations/columns/list/list.component.html index 99b644c9..491ca05f 100644 --- a/src/app/modules/organizations/columns/list/list.component.html +++ b/src/app/modules/organizations/columns/list/list.component.html @@ -2,50 +2,50 @@ Filter - + - - - - - - - - + + + + + + + + - - - - + + + + - - - - + + + + - - - + - + + + + @if (c.is_extra_data) { + + + + } + + - - -
Canonical? - @if (!c.is_extra_data && !c.derived_column) { - - } - Display Name{{ c.display_name }}Canonical? + @if (!c.is_extra_data && !c.derived_column) { + + } + Display Name{{ c.display_name }}Column Name{{ c.column_name }}Column Name{{ c.column_name }}Description{{ c.column_description }}Description{{ c.column_description }}Actions - - - - - - - @if (c.is_extra_data) { - - + + Actions + + - } -
+
From ce2e47ae5403a0cd2a55055870e32cf47133c65d Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 16:35:18 +0000 Subject: [PATCH 27/30] import settings skeleton components --- .../import-settings-properties.component.ts | 15 +++++++++++++-- .../import-settings-taxlots.component.ts | 14 ++++++++++++-- .../import-settings/import-settings.component.ts | 11 +++++++---- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts index e3d97e47..5ff32778 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts @@ -1,5 +1,7 @@ -import { Component, ViewEncapsulation } from '@angular/core' +import { Component, type OnInit, ViewEncapsulation } from '@angular/core' +import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' +import { ImportSettingsComponent } from './import-settings.component' @Component({ selector: 'seed-organizations-column-import-settings-properties', @@ -7,6 +9,15 @@ import { SharedImports } from '@seed/directives' encapsulation: ViewEncapsulation.None, imports: [SharedImports], }) -export class ImportSettingsPropertiesComponent { + +export class ImportSettingsPropertiesComponent extends ImportSettingsComponent implements OnInit{ type = 'properties' + + ngOnInit(): void { + this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( + map((columns) => { + this.filterColumns(columns) + }), + ) + } } diff --git a/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts index 424b4e96..8c668d87 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts @@ -1,5 +1,7 @@ -import { Component, ViewEncapsulation } from '@angular/core' +import { Component, type OnInit, ViewEncapsulation } from '@angular/core' +import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' +import { ImportSettingsComponent } from './import-settings.component' @Component({ selector: 'seed-organizations-column-import-settings-taxlots', @@ -7,6 +9,14 @@ import { SharedImports } from '@seed/directives' encapsulation: ViewEncapsulation.None, imports: [SharedImports], }) -export class ImportSettingsTaxLotsComponent { +export class ImportSettingsTaxLotsComponent extends ImportSettingsComponent implements OnInit { type = 'taxlots' + + ngOnInit(): void { + this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( + map((columns) => { + this.filterColumns(columns) + }), + ) + } } diff --git a/src/app/modules/organizations/columns/import-settings/import-settings.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts index 1b9d57d4..392631c1 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts @@ -2,6 +2,7 @@ import { Component, type OnDestroy, ViewEncapsulation } from '@angular/core' import { Subject } from 'rxjs' import { type Column, type ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' +import { naturalSort } from '@seed/utils' @Component({ selector: 'seed-organizations-column-import-settings', @@ -9,18 +10,20 @@ import { SharedImports } from '@seed/directives' encapsulation: ViewEncapsulation.None, imports: [SharedImports], }) -export class ImportSettingsComponent implements OnDestroy{ +export class ImportSettingsComponent implements OnDestroy { protected _columnService: ColumnService protected readonly _unsubscribeAll$ = new Subject() columns: Column[] + availableColumns: Column[] + type: string ngOnDestroy(): void { this._unsubscribeAll$.next() this._unsubscribeAll$.complete() } - save(): void { - + filterColumns(columns: Column[]) { + this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => { c.is_excluded_from_hash || c.merge_protection || c.recognize_empty}) + this.availableColumns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => !this.columns.includes(c)) } - } From b491abb065cbf542d39b3ec0f19921ddd9d6dcd6 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 16:50:43 +0000 Subject: [PATCH 28/30] linter --- .../data-types-properties.component.ts | 2 +- .../columns/data-types/data-types.component.ts | 2 +- .../import-settings-properties.component.ts | 2 +- .../import-settings.component.ts | 17 +++++++++++++++-- .../columns/list/list-properties.component.ts | 2 +- .../columns/list/list-taxlots.component.ts | 2 +- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts index b8779fe4..e5dd2987 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-properties.component.ts @@ -8,9 +8,9 @@ import { MatPaginator } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' import { MatTableModule } from '@angular/material/table' import { map, takeUntil } from 'rxjs' +import { TableContainerComponent } from '@seed/components' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' -import { TableContainerComponent } from '@seed/components' import { DataTypesComponent } from './data-types.component' @Component({ diff --git a/src/app/modules/organizations/columns/data-types/data-types.component.ts b/src/app/modules/organizations/columns/data-types/data-types.component.ts index 8cce9cd6..522f3f8d 100644 --- a/src/app/modules/organizations/columns/data-types/data-types.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types.component.ts @@ -70,7 +70,7 @@ export class DataTypesComponent implements OnDestroy { const stringId = `${c.id}` this.dataTypesForm.addControl(stringId, new FormControl((c.data_type), [Validators.required])) if (!c.is_extra_data) { - this.dataTypesForm.controls[stringId].disable() + this.dataTypesForm.get(stringId)?.disable() } } } diff --git a/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts index 5ff32778..8cd0bd4b 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts @@ -10,7 +10,7 @@ import { ImportSettingsComponent } from './import-settings.component' imports: [SharedImports], }) -export class ImportSettingsPropertiesComponent extends ImportSettingsComponent implements OnInit{ +export class ImportSettingsPropertiesComponent extends ImportSettingsComponent implements OnInit { type = 'properties' ngOnInit(): void { diff --git a/src/app/modules/organizations/columns/import-settings/import-settings.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts index 392631c1..2503acc0 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts @@ -23,7 +23,20 @@ export class ImportSettingsComponent implements OnDestroy { } filterColumns(columns: Column[]) { - this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => { c.is_excluded_from_hash || c.merge_protection || c.recognize_empty}) - this.availableColumns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)).filter((c) => !this.columns.includes(c)) + this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + .filter((c) => { this.includeColumn(c) }) + this.availableColumns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + .filter((c) => !this.columns.includes(c)) + } + + includeColumn(column: Column): boolean { + if (column.is_excluded_from_hash) { + return true + } else if (column.recognize_empty) { + return true + } else if (column.merge_protection === 'Favor Existing') { + return true + } + return false } } diff --git a/src/app/modules/organizations/columns/list/list-properties.component.ts b/src/app/modules/organizations/columns/list/list-properties.component.ts index 07ea2d6f..8b944d9d 100644 --- a/src/app/modules/organizations/columns/list/list-properties.component.ts +++ b/src/app/modules/organizations/columns/list/list-properties.component.ts @@ -6,9 +6,9 @@ import { MatPaginator } from '@angular/material/paginator' import { MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' import { map, takeUntil } from 'rxjs' +import { TableContainerComponent } from '@seed/components' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' -import { TableContainerComponent } from '@seed/components' import { ListComponent } from './list.component' @Component({ diff --git a/src/app/modules/organizations/columns/list/list-taxlots.component.ts b/src/app/modules/organizations/columns/list/list-taxlots.component.ts index 33089095..05c14f7f 100644 --- a/src/app/modules/organizations/columns/list/list-taxlots.component.ts +++ b/src/app/modules/organizations/columns/list/list-taxlots.component.ts @@ -6,9 +6,9 @@ import { MatPaginator } from '@angular/material/paginator' import { MatTableModule } from '@angular/material/table' import { MatTooltip } from '@angular/material/tooltip' import { map, takeUntil } from 'rxjs' +import { TableContainerComponent } from '@seed/components' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' -import { TableContainerComponent } from '@seed/components' import { ListComponent } from './list.component' @Component({ From 59c5d36b3c391b2b4127ea5b61b42dc0c0e411f9 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Tue, 11 Mar 2025 16:56:58 +0000 Subject: [PATCH 29/30] missed one linter issue --- .../columns/data-types/data-types-taxlots.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts index 25053eb8..915aa152 100644 --- a/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts +++ b/src/app/modules/organizations/columns/data-types/data-types-taxlots.component.ts @@ -8,9 +8,9 @@ import { MatPaginator } from '@angular/material/paginator' import { MatSelectModule } from '@angular/material/select' import { MatTableModule } from '@angular/material/table' import { map, takeUntil } from 'rxjs' +import { TableContainerComponent } from '@seed/components' import { SharedImports } from '@seed/directives' import { naturalSort } from '@seed/utils' -import { TableContainerComponent } from '@seed/components' import { DataTypesComponent } from './data-types.component' @Component({ From e4a3dcd8c55afcaffdebd477ca0b1867cda97446 Mon Sep 17 00:00:00 2001 From: Caleb Rutan Date: Wed, 12 Mar 2025 22:17:21 +0000 Subject: [PATCH 30/30] import settings page for organization->columns --- .../import-settings-properties.component.ts | 12 +- .../import-settings-taxlots.component.ts | 12 +- .../import-settings.component.html | 124 ++++++++++++- .../import-settings.component.ts | 170 ++++++++++++++++-- 4 files changed, 299 insertions(+), 19 deletions(-) diff --git a/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts index 8cd0bd4b..28d02bc2 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings-properties.component.ts @@ -1,4 +1,8 @@ import { Component, type OnInit, ViewEncapsulation } from '@angular/core' +import { ReactiveFormsModule } from '@angular/forms' +import { MatButtonModule } from '@angular/material/button' +import { MatIconModule } from '@angular/material/icon' +import { MatSelectModule } from '@angular/material/select' import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { ImportSettingsComponent } from './import-settings.component' @@ -7,17 +11,17 @@ import { ImportSettingsComponent } from './import-settings.component' selector: 'seed-organizations-column-import-settings-properties', templateUrl: './import-settings.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports], + imports: [SharedImports, MatButtonModule, MatIconModule, MatSelectModule, ReactiveFormsModule], }) export class ImportSettingsPropertiesComponent extends ImportSettingsComponent implements OnInit { - type = 'properties' + type = 'PropertyState' ngOnInit(): void { this._columnService.propertyColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( map((columns) => { - this.filterColumns(columns) + this.prepareColumns(columns) }), - ) + ).subscribe() } } diff --git a/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts index 8c668d87..05a878fa 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings-taxlots.component.ts @@ -1,4 +1,8 @@ import { Component, type OnInit, ViewEncapsulation } from '@angular/core' +import { ReactiveFormsModule } from '@angular/forms' +import { MatButtonModule } from '@angular/material/button' +import { MatIconModule } from '@angular/material/icon' +import { MatSelectModule } from '@angular/material/select' import { map, takeUntil } from 'rxjs' import { SharedImports } from '@seed/directives' import { ImportSettingsComponent } from './import-settings.component' @@ -7,16 +11,16 @@ import { ImportSettingsComponent } from './import-settings.component' selector: 'seed-organizations-column-import-settings-taxlots', templateUrl: './import-settings.component.html', encapsulation: ViewEncapsulation.None, - imports: [SharedImports], + imports: [SharedImports, MatButtonModule, MatIconModule, MatSelectModule, ReactiveFormsModule], }) export class ImportSettingsTaxLotsComponent extends ImportSettingsComponent implements OnInit { - type = 'taxlots' + type = 'TaxLotState' ngOnInit(): void { this._columnService.taxLotColumns$.pipe(takeUntil(this._unsubscribeAll$)).pipe( map((columns) => { - this.filterColumns(columns) + this.prepareColumns(columns) }), - ) + ).subscribe() } } diff --git a/src/app/modules/organizations/columns/import-settings/import-settings.component.html b/src/app/modules/organizations/columns/import-settings/import-settings.component.html index 788100cb..935f18b3 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings.component.html +++ b/src/app/modules/organizations/columns/import-settings/import-settings.component.html @@ -1 +1,123 @@ -{{ type }} +
+
+
Exclude Columns From Uniqueness
+
+ Adding a field here will remove the field from the hash that uniquely represents each record. Incoming data will not be imported into + SEED if the only fields changed are marked as excluded from the uniqueness calculations. The incoming data will instead be calculated + to be a duplicate of the existing data and will therefore be ignored. +
+
    + @for (c of excludedColumns; track c.id) { +
  • +
    {{ c.display_name }}
    +
    + + + +
    +
  • + } +
+ @if (columns && availableExcludedColumns) { +
+ + Add Column + + @for (c of availableExcludedColumns(); track c.id) { + {{ c.display_name }} + } + + +
+ +
+
+ } +
+ +
+
Recognize Empty
+
+ Adding a column here will affect how empty or blank values are treated during merges. Specifically, empty values will be able to + replace non-empty values per the "Merge Protection" setting. +
+
    + @for (c of emptyColumns; track c.id) { +
  • +
    {{ c.display_name }}
    +
    + + + +
    +
  • + } +
+ @if (columns && availableEmptyColumns) { +
+ + Add Column + + @for (c of availableEmptyColumns(); track c.id) { + {{ c.display_name }} + } + + +
+ +
+
+ } +
+ +
+
Merge Protection
+
+ Normally when an imported record is merged into another record the newest value overwrites an older one. Merge protection prevents + this, and is particularly useful for columns where you have manually edited values that you want to persist even after importing and + merging new data. +
+
    + @for (c of mergeProtectedColumns; track c.id) { +
  • +
    {{ c.display_name }}
    +
    + + + +
    +
  • + } +
+ @if (columns && removeMergeProtected) { +
+ + Add Column + + @for (c of availableMergeProtectionColumns(); track c.id) { + {{ c.display_name }} + } + + +
+ +
+
+ } +
+
+ +
+ + +
diff --git a/src/app/modules/organizations/columns/import-settings/import-settings.component.ts b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts index 2503acc0..03640757 100644 --- a/src/app/modules/organizations/columns/import-settings/import-settings.component.ts +++ b/src/app/modules/organizations/columns/import-settings/import-settings.component.ts @@ -1,32 +1,182 @@ -import { Component, type OnDestroy, ViewEncapsulation } from '@angular/core' -import { Subject } from 'rxjs' -import { type Column, type ColumnService } from '@seed/api/column' +import { Component, inject, type OnDestroy, ViewEncapsulation } from '@angular/core' +import { FormControl, FormGroup, Validators } from '@angular/forms' +import { MatDialog } from '@angular/material/dialog' +import { Router } from '@angular/router' +import { Subject, takeUntil } from 'rxjs' +import { type Column, ColumnService } from '@seed/api/column' import { SharedImports } from '@seed/directives' +import { type UploaderResponse } from '@seed/services/uploader' import { naturalSort } from '@seed/utils' +import { UpdateModalComponent } from '../modal/update-modal.component' +type ColumnChangeSet = { + is_excluded_from_hash: boolean; + recognize_empty: boolean; + merge_protection?: 0 | 1; +} @Component({ selector: 'seed-organizations-column-import-settings', - templateUrl: './import-settings.component.html', + template: '', encapsulation: ViewEncapsulation.None, imports: [SharedImports], }) export class ImportSettingsComponent implements OnDestroy { - protected _columnService: ColumnService + protected _columnService = inject(ColumnService) protected readonly _unsubscribeAll$ = new Subject() + private _dialog = inject(MatDialog) + private _router = inject(Router) columns: Column[] - availableColumns: Column[] + excludedColumns: Column[] + emptyColumns: Column[] + mergeProtectedColumns: Column[] + removedExcludedColumns: Column[] = [] + removedEmptyColumns: Column[] = [] + removedMergeProtectedColumns: Column[] = [] + dirty = false type: string + addEmptyForm = new FormGroup({ + column: new FormControl(null, [Validators.required]), + }) + addExcludeForm = new FormGroup({ + column: new FormControl(null, [Validators.required]), + }) + addMergeProtectedForm = new FormGroup({ + column: new FormControl(null, [Validators.required]), + }) ngOnDestroy(): void { + if (this.dirty) { + this.save() + } this._unsubscribeAll$.next() this._unsubscribeAll$.complete() } - filterColumns(columns: Column[]) { + async cancel() { + this.dirty = false + await this._router.navigate(['/organizations/columns/list']) + } + + save(): void { + const changes = {} + for (const column of this.columns.filter((c) => this.columnChanged(c))) { + changes[column.id] = this.columnChangeset(column) + } + if (Object.keys(changes).length > 0) { + this._columnService.updateMultipleColumns(this.columns[0].organization_id, this.type, changes).subscribe((response: UploaderResponse) => { + const dialogRef = this._dialog.open(UpdateModalComponent, { + width: '40rem', + data: { progressResponse: response }, + }) + dialogRef + .afterClosed() + .pipe( + takeUntil(this._unsubscribeAll$), + ) + .subscribe() + }) + } + } + + columnChanged(column: Column): boolean { + if (this.removedEmptyColumns.includes(column) || this.removedExcludedColumns.includes(column) || this.removedMergeProtectedColumns.includes(column)) { + return true + } + if (this.excludedColumns.includes(column) && !column.is_excluded_from_hash) { + return true + } + if (this.emptyColumns.includes(column) && !column.recognize_empty) { + return true + } + if (this.mergeProtectedColumns.includes(column) && column.merge_protection === 'Favor New') { + return true + } + return false + } + + columnChangeset(column: Column): ColumnChangeSet { + const changeset: ColumnChangeSet = { + is_excluded_from_hash: column.is_excluded_from_hash, + recognize_empty: column.recognize_empty, + } + if (this.removedEmptyColumns.includes(column)) { + changeset.recognize_empty = false + } else if (this.emptyColumns.includes(column)) { + changeset.recognize_empty = true + } + if (this.removedExcludedColumns.includes(column)) { + changeset.is_excluded_from_hash = false + } else if (this.excludedColumns.includes(column)) { + changeset.is_excluded_from_hash = true + } + if (this.removedMergeProtectedColumns.includes(column)) { + changeset.merge_protection = 0 // 'Favor New' + } else if (this.mergeProtectedColumns.includes(column)) { + changeset.merge_protection = 1 // 'Favor Existing' + } + + return changeset + } + + prepareColumns(columns: Column[]) { this.columns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) - .filter((c) => { this.includeColumn(c) }) - this.availableColumns = columns.sort((a, b) => naturalSort(a.display_name, b.display_name)) - .filter((c) => !this.columns.includes(c)) + this.excludedColumns = columns.filter((c) => c.is_excluded_from_hash) + this.emptyColumns = columns.filter((c) => c.recognize_empty) + this.mergeProtectedColumns = columns.filter((c) => c.merge_protection === 'Favor Existing') + } + + availableExcludedColumns(): Column[] { + return this.columns.filter((c) => !c.is_excluded_from_hash) + } + + availableEmptyColumns(): Column[] { + return this.columns.filter((c) => !c.recognize_empty) + } + + availableMergeProtectionColumns(): Column[] { + return this.columns.filter((c) => c.merge_protection !== 'Favor Existing') + } + + addExcluded() { + this.excludedColumns.push(this.columns.find((c) => c.id === this.addExcludeForm.get('column').value)) + this.excludedColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.dirty = true + this.addExcludeForm.reset() + } + + addEmpty() { + this.emptyColumns.push(this.columns.find((c) => c.id === this.addEmptyForm.get('column').value)) + this.emptyColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.dirty = true + this.addEmptyForm.reset() + } + + addMerge() { + this.mergeProtectedColumns.push(this.columns.find((c) => c.id === this.addMergeProtectedForm.get('column').value)) + this.mergeProtectedColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.dirty = true + this.addMergeProtectedForm.reset() + } + + removeExcluded(column: Column) { + this.excludedColumns = this.excludedColumns.filter((c) => c.id !== column.id) + this.removedExcludedColumns.push(column) + this.excludedColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.dirty = true + } + + removeEmpty(column: Column) { + this.emptyColumns = this.emptyColumns.filter((c) => c.id !== column.id) + this.removedEmptyColumns.push(column) + this.emptyColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.dirty = true + } + + removeMergeProtected(column: Column) { + this.mergeProtectedColumns = this.mergeProtectedColumns.filter((c) => c.id !== column.id) + this.removedMergeProtectedColumns.push(column) + this.mergeProtectedColumns.sort((a, b) => naturalSort(a.display_name, b.display_name)) + this.dirty = true } includeColumn(column: Column): boolean {