+
+
+
+
+
+
+
@if (selectedViewIds.length > 0) {
({{ selectedViewIds.length }} selected)
diff --git a/src/app/modules/inventory-list/list/grid/grid-controls.component.ts b/src/app/modules/inventory-list/list/grid/grid-controls.component.ts
index dbf98659..729de61b 100644
--- a/src/app/modules/inventory-list/list/grid/grid-controls.component.ts
+++ b/src/app/modules/inventory-list/list/grid/grid-controls.component.ts
@@ -1,17 +1,41 @@
-import { Component, EventEmitter, Input, Output } from '@angular/core'
+import { CommonModule } from '@angular/common'
+import type { OnChanges, OnInit, SimpleChanges } from '@angular/core'
+import { Component, EventEmitter, inject, Input, Output } from '@angular/core'
+import type { GridApi } from 'ag-grid-community'
+import { take, tap } from 'rxjs'
+import type { CurrentUser, OrganizationUserSettings } from '@seed/api'
+import { OrganizationService } from '@seed/api'
import { MaterialImports } from '@seed/materials'
-import type { Pagination } from 'app/modules/inventory'
+import { ConfigService } from '@seed/services'
+import type { Pagination } from '../../../inventory/inventory.types'
@Component({
selector: 'seed-inventory-grid-controls',
templateUrl: './grid-controls.component.html',
- imports: [MaterialImports],
+ imports: [CommonModule, MaterialImports],
})
-export class InventoryGridControlsComponent {
+export class InventoryGridControlsComponent implements OnChanges, OnInit {
+ @Input() currentUser!: CurrentUser
+ @Input() gridApi: GridApi
@Input() pagination!: Pagination
- @Input() resetGrid: () => void
@Input() selectedViewIds: number[]
@Output() pageChange = new EventEmitter()
+ private _configService = inject(ConfigService)
+ private _organizationService = inject(OrganizationService)
+ scheme: 'dark' | 'light'
+ userSettings: OrganizationUserSettings
+
+ ngOnInit(): void {
+ this._configService.scheme$.subscribe((scheme) => {
+ this.scheme = scheme
+ })
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.currentUser?.currentValue) {
+ this.userSettings = this.currentUser.settings
+ }
+ }
onPageChange = (direction: 'first' | 'previous' | 'next' | 'last') => {
const { page, num_pages } = this.pagination
@@ -22,4 +46,45 @@ export class InventoryGridControlsComponent {
this.pageChange.emit(newPage)
}
+
+ resetGrid() {
+ this.resetColumns()
+ this.resetFilters()
+ this.resetSorts()
+ }
+
+ resetColumns() {
+ this.gridApi.autoSizeAllColumns()
+ }
+
+ resetFilters() {
+ this.gridApi.setFilterModel(null)
+ this.userSettings.filters = this.currentUser.settings.filters ?? {}
+ this.userSettings.filters.properties = {}
+ this.userSettings.filters.taxlots = {}
+ this.updateOrgUser()
+ }
+
+ resetSorts() {
+ this.gridApi.applyColumnState({ state: [], applyOrder: true })
+ this.gridApi.resetColumnState()
+ this.userSettings.sorts = this.currentUser.settings?.sorts ?? {}
+ this.userSettings.sorts.properties = []
+ this.userSettings.sorts.taxlots = []
+ this.updateOrgUser()
+ }
+
+ updateOrgUser() {
+ const { org_id, org_user_id } = this.currentUser
+ this._organizationService.updateOrganizationUser(org_user_id, org_id, this.userSettings)
+ .pipe(
+ take(1),
+ tap(() => {
+ this.gridApi.refreshClientSideRowModel()
+ this.gridApi.refreshCells({ force: true })
+ this.gridApi.onSortChanged()
+ }),
+ )
+ .subscribe()
+ }
}
diff --git a/src/app/modules/inventory-list/list/grid/grid.component.html b/src/app/modules/inventory-list/list/grid/grid.component.html
index 69a8adad..8fbe0557 100644
--- a/src/app/modules/inventory-list/list/grid/grid.component.html
+++ b/src/app/modules/inventory-list/list/grid/grid.component.html
@@ -1,6 +1,7 @@
diff --git a/src/app/modules/inventory-list/list/grid/grid.component.ts b/src/app/modules/inventory-list/list/grid/grid.component.ts
index c4675347..e119b380 100644
--- a/src/app/modules/inventory-list/list/grid/grid.component.ts
+++ b/src/app/modules/inventory-list/list/grid/grid.component.ts
@@ -4,9 +4,11 @@ import { Component, EventEmitter, inject, Input, Output } from '@angular/core'
import { Router } from '@angular/router'
import { AgGridAngular } from 'ag-grid-angular'
import type { CellClickedEvent, ColDef, ColGroupDef, GridApi, GridOptions, GridReadyEvent } from 'ag-grid-community'
-import type { Label } from '@seed/api'
+import type { CurrentUser, Label, OrganizationUserSettings } from '@seed/api'
+import { OrganizationService } from '@seed/api'
import { ConfigService } from '@seed/services'
-import type { FiltersSorts, InventoryType, Pagination } from 'app/modules/inventory'
+import type { FiltersSorts, InventoryType, Pagination } from '../../../inventory/inventory.types'
+import { CellHeaderMenuComponent } from './cell-header-menu.component'
import { InventoryGridControlsComponent } from './grid-controls.component'
@Component({
@@ -14,25 +16,30 @@ import { InventoryGridControlsComponent } from './grid-controls.component'
templateUrl: './grid.component.html',
imports: [
AgGridAngular,
- // CellHeaderMenuComponent,
+ CellHeaderMenuComponent,
CommonModule,
InventoryGridControlsComponent,
],
})
export class InventoryGridComponent implements OnChanges {
@Input() columnDefs!: ColDef[]
+ @Input() currentUser: CurrentUser
@Input() inventoryType: string
@Input() labelMap: Record
+ @Input() orgId: number
+ @Input() orgUserId: number
@Input() pagination!: Pagination
@Input() rowData!: Record[]
@Input() selectedViewIds: number[]
@Input() type: InventoryType
+ @Input() userSettings: OrganizationUserSettings
@Output() pageChange = new EventEmitter()
@Output() filterSortChange = new EventEmitter()
@Output() gridReady = new EventEmitter()
@Output() selectionChanged = new EventEmitter()
@Output() gridReset = new EventEmitter()
private _configService = inject(ConfigService)
+ private _organizationService = inject(OrganizationService)
private _router = inject(Router)
agPageSize = 100
@@ -74,6 +81,7 @@ export class InventoryGridComponent implements OnChanges {
onGridReady(params: GridReadyEvent) {
this.gridApi = params.api
this.gridReady.emit(this.gridApi)
+ this.gridApi.autoSizeAllColumns()
this.gridApi.addEventListener('cellClicked', this.onCellClicked.bind(this) as (event: CellClickedEvent) => void)
}
@@ -101,31 +109,48 @@ export class InventoryGridComponent implements OnChanges {
}
getColumnDefs() {
+ const stateColumns = this.addHeaderMenu()
+
this.columnDefs = [
{ headerName: 'Shortcuts', children: this.getShortcutColumns() } as ColGroupDef,
- { headerName: 'Details', children: this.columnDefs } as ColGroupDef,
+ { headerName: 'Details', children: stateColumns } as ColGroupDef,
]
}
getShortcutColumns(): ColDef[] {
const shortcutColumns = [
this.buildInfoCell(),
- this.buildShortcutColumn('merged_indicator', 'Merged', 85, 'share'),
- this.buildShortcutColumn('meters_exist_indicator', 'Meters', 80, 'bolt', 'meters'),
- this.buildShortcutColumn('notes_count', 'Notes', 80, 'mode_comment', 'notes'),
- this.buildShortcutColumn('groups_indicator', 'Groups', 80, 'G'),
+ this.buildShortcutColumn('merged_indicator', 'Merged', 82, 'share'),
+ this.buildShortcutColumn('meters_exist_indicator', 'Meters', 78, 'bolt', 'meters'),
+ this.buildShortcutColumn('notes_count', 'Notes', 71, 'mode_comment', 'notes'),
+ this.buildShortcutColumn('groups_indicator', 'Groups', 79, 'G'),
this.buildLabelsCell(),
]
return shortcutColumns
}
- buildShortcutColumn(field: string, headerName: string, width: number, icon: string, action: string = null): ColDef {
+ addHeaderMenu() {
+ const stateColumns = this.columnDefs.map((c) => ({
+ ...c,
+ headerComponent: CellHeaderMenuComponent,
+ headerComponentParams: {
+ currentUser: this.currentUser,
+ type: this.type,
+ },
+ }))
+ return stateColumns
+ }
+
+ buildShortcutColumn(field: string, headerName: string, maxWidth: number, icon: string, action: string = null): ColDef {
return {
field,
headerName,
- width,
+ maxWidth,
filter: false,
sortable: false,
+ suppressMovable: true,
+ headerClass: 'white-space-normal',
+ cellClass: 'overflow-hidden',
cellRenderer: ({ value }) => this.actionRenderer(value, icon, action),
}
}
@@ -137,7 +162,9 @@ export class InventoryGridComponent implements OnChanges {
headerName: 'Info',
filter: false,
sortable: false,
- width: 60,
+ resizable: false,
+ suppressMovable: true,
+ maxWidth: 60,
cellRenderer: ({ value }) => this.actionRenderer(value, 'info', 'detail'),
}
}
@@ -164,6 +191,7 @@ export class InventoryGridComponent implements OnChanges {
width: 80,
filter: false,
sortable: false,
+ suppressMovable: true,
// labels come in as an array of ids [1,2,3]. Ag grid needs them formatted as a string
valueFormatter: ({ value }: { value: number[] }) => {
const labels = value
@@ -187,17 +215,6 @@ export class InventoryGridComponent implements OnChanges {
}
}
- resetGrid = () => {
- if (!this.gridApi) return
-
- this.gridApi.setFilterModel(null)
- this.gridApi.applyColumnState({ state: [], applyOrder: true })
- this.gridApi.resetColumnState()
- this.gridApi.refreshClientSideRowModel()
- this.gridApi.refreshCells({ force: true })
- this.gridReset.emit()
- }
-
/*
* ascending sorts formatted as 'column_id'
* descending sorts formatted as '-column_id'
diff --git a/src/app/modules/inventory-list/list/inventory.component.html b/src/app/modules/inventory-list/list/inventory.component.html
index 62137c08..a3a02815 100644
--- a/src/app/modules/inventory-list/list/inventory.component.html
+++ b/src/app/modules/inventory-list/list/inventory.component.html
@@ -43,8 +43,9 @@
@if (gridApi && columnDefs) {
@@ -54,17 +55,20 @@
diff --git a/src/app/modules/inventory-list/list/inventory.component.ts b/src/app/modules/inventory-list/list/inventory.component.ts
index 72380b6c..81d1e512 100644
--- a/src/app/modules/inventory-list/list/inventory.component.ts
+++ b/src/app/modules/inventory-list/list/inventory.component.ts
@@ -5,8 +5,8 @@ import { ActivatedRoute } from '@angular/router'
import type { ColDef, GridApi } from 'ag-grid-community'
import type { Observable } from 'rxjs'
import { BehaviorSubject, catchError, combineLatest, filter, map, of, Subject, switchMap, takeUntil, tap } from 'rxjs'
-import type { CurrentUser, Cycle, Label, OrganizationUserResponse, OrganizationUserSettings } from '@seed/api'
-import { CycleService, InventoryService, LabelService, OrganizationService, UserService } from '@seed/api'
+import type { Column, CurrentUser, Cycle, Label, OrganizationUserResponse, OrganizationUserSettings } from '@seed/api'
+import { ColumnService, CycleService, InventoryService, LabelService, OrganizationService, UserService } from '@seed/api'
import { InventoryTabComponent, PageComponent } from '@seed/components'
import { SharedImports } from '@seed/directives'
import { MaterialImports } from '@seed/materials'
@@ -20,7 +20,6 @@ import type {
Profile,
} from 'app/modules/inventory'
import { ActionsComponent, ConfigSelectorComponent, FilterSortChipsComponent, InventoryGridComponent } from './grid'
-// import { CellHeaderMenuComponent } from './grid/cell-header-menu.component'
@Component({
selector: 'seed-inventory',
@@ -40,6 +39,7 @@ import { ActionsComponent, ConfigSelectorComponent, FilterSortChipsComponent, In
})
export class InventoryComponent implements OnDestroy, OnInit {
private _activatedRoute = inject(ActivatedRoute)
+ private _columnService = inject(ColumnService)
private _cycleService = inject(CycleService)
private _inventoryService = inject(InventoryService)
private _organizationService = inject(OrganizationService)
@@ -49,6 +49,7 @@ export class InventoryComponent implements OnDestroy, OnInit {
readonly tabs: InventoryType[] = ['properties', 'taxlots']
readonly type = this._activatedRoute.snapshot.paramMap.get('type') as InventoryType
chunk = 100
+ columns: Column[] = []
columnDefs: ColDef[] = []
currentUser: CurrentUser
cycle: Cycle
@@ -135,8 +136,10 @@ export class InventoryComponent implements OnDestroy, OnInit {
getDependencies(org_id: number) {
this.orgId = org_id
this._cycleService.getCycles(this.orgId)
+ const columns$ = this.type === 'taxlots' ? this._columnService.taxLotColumns$ : this._columnService.propertyColumns$
return combineLatest([
+ columns$,
this._userService.currentUser$,
this._cycleService.cycles$,
this._labelService.labels$,
@@ -147,10 +150,11 @@ export class InventoryComponent implements OnDestroy, OnInit {
/*
* set class variables: cycles, profiles, inventory. returns profile id
*/
- setDependencies([currentUser, cycles, labels, profiles]: InventoryDependencies) {
+ setDependencies([columns, currentUser, cycles, labels, profiles]: InventoryDependencies) {
if (!cycles) {
return null
}
+ this.columns = columns
const { org_user_id, settings } = currentUser
this.currentUser = currentUser
@@ -272,12 +276,6 @@ export class InventoryComponent implements OnDestroy, OnInit {
return this._organizationService.updateOrganizationUser(this.orgUserId, this.orgId, this.userSettings)
}
- onGridReset() {
- this.userSettings.filters = {}
- this.userSettings.sorts = {}
- this.refreshInventory$.next()
- }
-
onPageChange(page: number) {
this.page = page
this.refreshInventory$.next()
diff --git a/src/app/modules/inventory/inventory.types.ts b/src/app/modules/inventory/inventory.types.ts
index 7c08ce37..e6396557 100644
--- a/src/app/modules/inventory/inventory.types.ts
+++ b/src/app/modules/inventory/inventory.types.ts
@@ -119,7 +119,7 @@ export type FilterSortChip = {
original: string;
}
-export type InventoryDependencies = [CurrentUser, Cycle[], Label[], Profile[]]
+export type InventoryDependencies = [Column[], CurrentUser, Cycle[], Label[], Profile[]]
type AccessLevelInstance = {
id: number;
diff --git a/src/styles/styles.scss b/src/styles/styles.scss
index 5cf5ee5b..c3b177b8 100644
--- a/src/styles/styles.scss
+++ b/src/styles/styles.scss
@@ -134,6 +134,10 @@
overflow: visible !important
}
}
+
+ .ng-star-inserted {
+ width: 100%;
+ }
}
.seed-dialog-panel {
@@ -214,6 +218,10 @@
}
}
+.menu-option {
+ @apply w-full p-2 flex items-center gap-4 rounded hover:bg-[#F4F5F4] hover:dark:bg-[#2E3B4A] text-sm cursor-pointer
+}
+
.vertical-divider {
@apply border
}