Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
8e7f31c
an attempt to use material tabs for property/taxlot tabs in organizat…
crutan Mar 3, 2025
3379a6c
dnd to rearrange geocoding order for property columns. taxlot column…
crutan Mar 4, 2025
ea49ac8
geocoding screens
crutan Mar 4, 2025
2a579c9
use protected rather than private, fix selector
crutan Mar 5, 2025
85d8ee6
fix naming (underscore->kebab) add table paginators and filters to list
crutan Mar 6, 2025
e630916
routing changes to ensure routes remain active in sidebar
crutan Mar 7, 2025
2d6b58c
add rename button
crutan Mar 7, 2025
6a75f6b
prettier
crutan Mar 7, 2025
484d9ef
fix double submit
crutan Mar 10, 2025
696652f
move from subscribe -> pipe.map.subscribe for data loader/filter
crutan Mar 10, 2025
5af88ee
linter
crutan Mar 10, 2025
079fc71
show all columns - canonical are disabled
crutan Mar 11, 2025
ec4d408
use seed-page-table-container, add filter
crutan Mar 11, 2025
3e405f4
use page-table component in column list
crutan Mar 11, 2025
858dc7a
linter
crutan Mar 11, 2025
0b3bb4f
initial pass of work to support column mappings in agGrid
crutan Mar 31, 2025
965cf54
column mappings - both buttons functional, needs csv import
crutan Apr 7, 2025
b868444
ag-grid and file-saver
crutan Apr 7, 2025
920815a
add help for column mappings / skeletonized help for other column com…
crutan Apr 7, 2025
6c52c3b
enable creation of new profile, handle read only profile action buttons
crutan Apr 8, 2025
ff3a574
fix typing for help components
crutan Apr 8, 2025
d7a9eef
prettier
crutan Apr 8, 2025
a5aebdf
remove merge markers
crutan Apr 8, 2025
0133e5d
prettier
crutan Apr 9, 2025
6fbe76d
changes to try and prevent linter error in CI
crutan Apr 9, 2025
5f01a48
try different import method for filesaver
crutan Apr 9, 2025
6499acf
disable eslint-no-unsafe-call for the filesavver line
crutan Apr 9, 2025
bb997f2
column list help
crutan Apr 9, 2025
400ca93
help templates for column settings pages
crutan Apr 9, 2025
51756fe
add handling for comstock mappings
crutan Apr 9, 2025
87a888e
prettier
crutan Apr 9, 2025
1f79263
include help around comstock mappings
crutan Apr 9, 2025
636671c
ensure the buildingsync defaults profile cannot be renamed / deleted
crutan Apr 9, 2025
ba74d61
reorganize buttons, disable on change, move save changes to more noti…
crutan Apr 9, 2025
06b4b27
ensure org remains selected in nav
crutan Apr 9, 2025
12ee4d7
ensure taxlot is properly cased
crutan Apr 10, 2025
67d3c3c
enlarge create modal, add more fields, enable autocomplete and unit s…
crutan Apr 10, 2025
4f1203d
prettier
crutan Apr 10, 2025
1163e48
alphabetize imports
crutan Apr 10, 2025
8c4281a
Merge branch 'main' into crutan/column_mappings
crutan Apr 10, 2025
b0bec1a
fix missing cspell from merge
crutan Apr 10, 2025
2ba31a3
dark mode theme for ag grid - prevent double click to edit on read on…
crutan Apr 11, 2025
8b4a7b0
building sync profile copy -> BuildingSync Custom
crutan Apr 11, 2025
3af4b4b
max modal height w body scroll
perryr16 Apr 11, 2025
f1fa676
prettier/linter
crutan Apr 11, 2025
e063d4a
one more linter fix
crutan Apr 11, 2025
fc14442
one more linter fix
crutan Apr 11, 2025
ab88fe7
remove console
perryr16 Apr 15, 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"ag-grid-community": "^33.1.1",
"crypto-es": "^2.1.0",
"cspell": "^8.17.3",
"file-saver": "^2.0.5",
"jwt-decode": "^4.0.0",
"lodash-es": "^4.17.21",
"luxon": "^3.5.0",
Expand Down
56 changes: 32 additions & 24 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/@seed/api/column/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './column.service'
export * from './column.types'
export * from './mappable_column.service'
59 changes: 59 additions & 0 deletions src/@seed/api/column/mappable_column.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { HttpErrorResponse } from '@angular/common/http'
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 { ErrorService } from '@seed/services'
import { UserService } from '../user'
import type { Column, ColumnsResponse } from './column.types'

@Injectable({ providedIn: 'root' })
export class MappableColumnService {
private _httpClient = inject(HttpClient)
private _userService = inject(UserService)
private _propertyColumns = new ReplaySubject<Column[]>(1)
private _taxLotColumns = new ReplaySubject<Column[]>(1)
private _errorService = inject(ErrorService)
private readonly _unsubscribeAll$ = new Subject<void>()

propertyColumns$ = this._propertyColumns.asObservable()
taxLotColumns$ = this._taxLotColumns.asObservable()

constructor() {
// Fetch current org data whenever user org id changes
this._userService.currentOrganizationId$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((organizationId) => {
this.getPropertyColumns(organizationId).subscribe()
this.getTaxLotColumns(organizationId).subscribe()
})
}

getPropertyColumns(org_id: number): Observable<Column[]> {
const url = `/api/v3/columns/mappable/?inventory_type=property&organization_id=${org_id}`
return this._httpClient.get<ColumnsResponse>(url).pipe(
map((cr) => {
const cols = cr.columns.filter((c) => c.table_name === 'PropertyState')
this._propertyColumns.next(cols)
return cols
}),
catchError((error: HttpErrorResponse) => {
// TODO need to figure out error handling
return this._errorService.handleError(error, 'Error fetching columns')
}),
)
}

getTaxLotColumns(org_id: number): Observable<Column[]> {
const url = `/api/v3/columns/mappable/?inventory_type=taxlot&organization_id=${org_id}`
return this._httpClient.get<ColumnsResponse>(url).pipe(
map((cr) => {
const cols = cr.columns.filter((c) => c.table_name === 'TaxLotState')
this._taxLotColumns.next(cols)
return cols
}),
catchError((error: HttpErrorResponse) => {
// TODO need to figure out error handling
return this._errorService.handleError(error, 'Error fetching columns')
}),
)
}
}
109 changes: 109 additions & 0 deletions src/@seed/api/column_mapping_profile/column_mapping_profile.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import type { HttpErrorResponse } from '@angular/common/http'
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 { ErrorService } from '@seed/services'
import { UserService } from '../user'
import type { ColumnMapping, ColumnMappingProfile, ColumnMappingProfileDeleteResponse, ColumnMappingProfilesRequest, ColumnMappingProfileUpdateResponse, ColumnMappingSuggestionResponse } from './column_mapping_profile.types'

@Injectable({ providedIn: 'root' })
export class ColumnMappingProfileService {
private _httpClient = inject(HttpClient)
private _userService = inject(UserService)
private _profiles = new ReplaySubject<ColumnMappingProfile[]>(1)
private _errorService = inject(ErrorService)
private readonly _unsubscribeAll$ = new Subject<void>()

profiles$ = this._profiles.asObservable()

constructor() {
// Fetch current org data whenever user org id changes
this._userService.currentOrganizationId$.pipe(takeUntil(this._unsubscribeAll$)).subscribe((organizationId) => {
this.getProfiles(organizationId).subscribe()
})
}

getProfiles(org_id: number): Observable<ColumnMappingProfile[]> {
const url = `/api/v3/column_mapping_profiles/filter/?organization_id=${org_id}`
return this._httpClient.post<ColumnMappingProfilesRequest>(url, {}).pipe(
map((response) => {
this._profiles.next(response.data)
return response.data
}),
catchError((error: HttpErrorResponse) => {
// TODO need to figure out error handling
return this._errorService.handleError(error, 'Error fetching column mapping profiles')
}),
)
}

updateMappings(org_id: number, profile_id: number, mappings: ColumnMapping[]): Observable<ColumnMappingProfile> {
const url = `/api/v3/column_mapping_profiles/${profile_id}/?organization_id=${org_id}`
return this._httpClient.put<ColumnMappingProfileUpdateResponse>(url, { mappings }).pipe(
map((response) => {
return response.data
}),
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error updating profile')
}),
)
}

update(org_id: number, profile: ColumnMappingProfile): Observable<ColumnMappingProfile> {
const url = `/api/v3/column_mapping_profiles/${profile.id}/?organization_id=${org_id}`
return this._httpClient.put<ColumnMappingProfileUpdateResponse>(url, { name: profile.name }).pipe(
map((response) => {
return response.data
}),
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error updating profile')
}),
)
}

delete(org_id: number, profile_id: number): Observable<ColumnMappingProfileDeleteResponse> {
const url = `/api/v3/column_mapping_profiles/${profile_id}/?organization_id=${org_id}`
return this._httpClient.delete<ColumnMappingProfileDeleteResponse>(url).pipe(
map((response) => {
return response
}),
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error removing profile')
}),
)
}

create(org_id: number, profile: ColumnMappingProfile): Observable<ColumnMappingProfileUpdateResponse> {
const url = `/api/v3/column_mapping_profiles/?organization_id=${org_id}`
return this._httpClient.post<ColumnMappingProfileUpdateResponse>(url, { ...profile }).pipe(
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error creating profile')
}),
)
}

export(org_id: number, profile_id: number) {
const url = `/api/v3/column_mapping_profiles/${profile_id}/csv/?organization_id=${org_id}`
return this._httpClient.get(url, { responseType: 'text' }).pipe(
map((response) => {
return new Blob([response], { type: 'text/csv;charset: utf-8' })
}),
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error retrieving csv')
}),
)
}

suggestions(org_id: number, headers: string[]) {
const url = `/api/v3/column_mapping_profiles/suggestions/?organization_id=${org_id}`
return this._httpClient.post<ColumnMappingSuggestionResponse>(url, { headers }).pipe(
map((response) => {
return response.data
}),
catchError((error: HttpErrorResponse) => {
return this._errorService.handleError(error, 'Error retrieving suggestions')
}),
)
}
}
Loading
Loading