Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Organization Columns - Part 1 #26

Merged
merged 31 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2bd1d40
an attempt to use material tabs for property/taxlot tabs in organizat…
crutan Mar 3, 2025
d8421f8
column list, with two components sharing a template in this case
crutan Mar 3, 2025
dacdb9c
dnd to rearrange geocoding order for property columns. taxlot column…
crutan Mar 4, 2025
bcd1c81
geocoding screens
crutan Mar 4, 2025
be1bfc3
move service call to individual components
crutan Mar 5, 2025
59b0d9c
use protected rather than private, fix selector
crutan Mar 5, 2025
964fe92
fix naming (underscore->kebab) add table paginators and filters to list
crutan Mar 6, 2025
5e69ed4
add form modal for editing, abstract common code for column component…
crutan Mar 7, 2025
64b55a9
column deletion modal
crutan Mar 7, 2025
e77514b
service changes for column deletion
crutan Mar 7, 2025
dd29e51
routing changes to ensure routes remain active in sidebar
crutan Mar 7, 2025
47c5240
add rename button
crutan Mar 7, 2025
2bf6fd1
prettier
crutan Mar 7, 2025
5d1ddb7
remove column mapping/settings route matchers
crutan Mar 7, 2025
92a1e03
simplify update/delete responses - remove map
crutan Mar 10, 2025
975018b
fix underscore in id
crutan Mar 10, 2025
61003ec
fix double submit
crutan Mar 10, 2025
94fe8e5
move from subscribe -> pipe.map.subscribe for data loader/filter
crutan Mar 10, 2025
f6a7449
linter
crutan Mar 10, 2025
951afba
more linter
crutan Mar 10, 2025
63aed75
remove red boxes on data type
crutan Mar 11, 2025
b0b94cd
show all columns - canonical are disabled
crutan Mar 11, 2025
a0f6201
use seed-page-table-container, add filter
crutan Mar 11, 2025
6d194cd
use page-table component in column list
crutan Mar 11, 2025
6879556
move filter into table-container
crutan Mar 11, 2025
b0261e7
prettier
crutan Mar 11, 2025
ce2e47a
import settings skeleton components
crutan Mar 11, 2025
b491abb
linter
crutan Mar 11, 2025
59c5d36
missed one linter issue
crutan Mar 11, 2025
e4a3dcd
import settings page for organization->columns
crutan Mar 12, 2025
4172d98
Merge branch 'main' into crutan/different_tabs_approach
axelstudios Mar 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/@seed/api/column/column.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http'
import { inject, Injectable } from '@angular/core'
import type { Observable } from 'rxjs'
import { catchError, map, ReplaySubject, Subject, takeUntil } from 'rxjs'
import type { ProgressResponse } from '@seed/api/progress'
import { ErrorService } from '@seed/services/error/error.service'
import { UserService } from '../user'
import type { Column, ColumnsResponse } from './column.types'
Expand Down Expand Up @@ -57,4 +58,22 @@ export class ColumnService {
}),
)
}

updateMultipleColumns(organization_id: number, table_name: string, changes: object): Observable<ProgressResponse> {
const url = '/api/v3/columns/update_multiple/'
return this._httpClient.post<ProgressResponse>(url, { organization_id, table_name, changes }).pipe(
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error updating columns')
}),
)
}

deleteColumn(column: Column): Observable<ProgressResponse> {
const url = `/api/v3/columns/${column.id}/?organization_id=${column.organization_id}`
return this._httpClient.delete<ProgressResponse>(url).pipe(
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error deleting column')
}),
)
}
}
2 changes: 2 additions & 0 deletions src/@seed/services/uploader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './uploader.service'
export * from './uploader.types'
16 changes: 4 additions & 12 deletions src/app/core/navigation/navigation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
35 changes: 35 additions & 0 deletions src/app/modules/organizations/columns/columns.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<mat-drawer-container class="static h-full">
<mat-drawer class="fixed top-16 h-full" mode="side" opened>
<seed-vertical-navigation
class="mt-4 bg-gray-100 dark:bg-gray-700 print:hidden"
[appearance]="'default'"
[mode]="'side'"
[navigation]="columnsNavigationMenu"
[opened]="true"
[autoCollapse]="true"
name="columnssNavigation"
>
<!-- Navigation header hook -->
<ng-container verticalNavigationContentHeader>
<div class="static flex items-center justify-center p-4">
<h3 class="font-xl font-bold">Columns</h3>
</div>
</ng-container>

<!-- Navigation footer hook -->
<ng-container verticalNavigationFooter> </ng-container>
</seed-vertical-navigation>
</mat-drawer>
<mat-drawer-content class="h-full bg-white p-4">
<seed-page [config]="{ title: pageTitle }">
<nav [tabPanel]="tabPanel" mat-tab-nav-bar>
@for (tab of tabs; track tab.label) {
<a [active]="tab.route === currentType()" (click)="navigateTo(tab.route)" mat-tab-link>{{ tab.label }}</a>
}
</nav>
<mat-tab-nav-panel #tabPanel>
<router-outlet></router-outlet>
</mat-tab-nav-panel>
</seed-page>
</mat-drawer-content>
</mat-drawer-container>
112 changes: 112 additions & 0 deletions src/app/modules/organizations/columns/columns.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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',
exactMatch: false,
title: 'Column List',
link: '/organizations/columns/list',
type: 'basic',
fn: (n: NavigationItem) => {
this.setNavTitle(n)
},
},
{
id: 'organizations/columns/geocoding',
link: '/organizations/columns/geocoding',
title: 'Geocoding',
type: 'basic',
fn: (n: NavigationItem) => {
this.setNavTitle(n)
},
},
{
id: 'organization/columns/data-type',
link: '/organizations/columns/data-types',
title: 'Data Types',
type: 'basic',
fn: (n: NavigationItem) => {
this.setNavTitle(n)
},
},
{
id: 'organizations/columns/import-settings',
link: '/organizations/columns/import-settings',
title: 'Import Settings',
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) => basePath.includes(n.link)).title
}
}
72 changes: 72 additions & 0 deletions src/app/modules/organizations/columns/columns.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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 { 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'

export default [
{
path: 'list',
pathMatch: 'full',
redirectTo: 'list/properties',
},
{
path: 'list/properties',
title: 'Column List',
component: ListPropertiesComponent,
},
{
path: 'list/taxlots',
title: 'Column List',
component: ListTaxLotComponent,
},
{
path: 'geocoding',
pathMatch: 'full',
redirectTo: 'geocoding/properties',
},
{
path: 'geocoding/properties',
title: 'Geocoding Order',
component: GeocodingPropertiesComponent,
},
{
path: 'geocoding/taxlots',
title: 'Geocoding Order',
component: GeocodingTaxlotsComponent,
},
{
path: 'data-types',
pathMatch: 'full',
redirectTo: 'data-types/properties',
},
{
path: 'data-types/properties',
title: 'Data Types',
component: DataTypesPropertiesComponent,
},
{
path: 'data-types/taxlots',
title: 'Data Types',
component: DataTypesTaxLotsComponent,
},
{
path: 'import-settings',
pathMatch: 'full',
redirectTo: 'import-settings/properties',
},
{
path: 'import-settings/properties',
title: 'Import Settings',
component: ImportSettingsPropertiesComponent,
},
{
path: 'import-settings/taxlots',
title: 'Import Settings',
component: ImportSettingsTaxLotsComponent,
},
] satisfies Routes
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { CommonModule } from '@angular/common'
import { type AfterViewInit, Component, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core'
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 { 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 { DataTypesComponent } from './data-types.component'

@Component({
selector: 'seed-organizations-column-data-types-properties',
templateUrl: './data-types.component.html',
encapsulation: ViewEncapsulation.None,
imports: [
CommonModule,
SharedImports,
TableContainerComponent,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
MatPaginator,
MatSelectModule,
MatTableModule,
ReactiveFormsModule,
],
})
export class DataTypesPropertiesComponent extends DataTypesComponent implements AfterViewInit, OnInit {
type = 'PropertyState'

@ViewChild(MatPaginator) paginator: MatPaginator

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))
this.initializeFormControls()
if (this.columns.length > 0) {
this.isLoading = false
}
this.columnTableDataSource.data = this.columns
}),
)
.subscribe()
}

ngAfterViewInit(): void {
this.columnTableDataSource.paginator = this.paginator
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { CommonModule } from '@angular/common'
import { type AfterViewInit, Component, type OnInit, ViewChild, ViewEncapsulation } from '@angular/core'
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 { 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 { DataTypesComponent } from './data-types.component'

@Component({
selector: 'seed-organizations-column-data-types-taxlots',
templateUrl: './data-types.component.html',
encapsulation: ViewEncapsulation.None,
imports: [
CommonModule,
SharedImports,
TableContainerComponent,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
MatPaginator,
MatSelectModule,
MatTableModule,
ReactiveFormsModule,
],
})
export class DataTypesTaxLotsComponent extends DataTypesComponent implements AfterViewInit, OnInit {
type = 'TaxLotState'
@ViewChild(MatPaginator) paginator: MatPaginator

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))
this.initializeFormControls()
this.columnTableDataSource.data = this.columns
}),
)
.subscribe()
}

ngAfterViewInit() {
this.columnTableDataSource.paginator = this.paginator
}
}
Loading