Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 8 additions & 3 deletions src/app/modules/inventory-detail/detail/header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MatDialog } from '@angular/material/dialog'
import { type MatSelect } from '@angular/material/select'
import { AgGridAngular } from 'ag-grid-angular'
import type { ColDef, GridApi, GridReadyEvent } from 'ag-grid-community'
import { filter, take, tap } from 'rxjs'
import type { AccessLevelInstance, Label, Organization } from '@seed/api'
import { LabelComponent } from '@seed/components'
import { MaterialImports } from '@seed/materials'
Expand Down Expand Up @@ -208,9 +209,13 @@ export class HeaderComponent implements OnInit {
},
})

dialogRef.afterClosed().subscribe((message) => {
if (message === 'refresh') this.refreshDetail.emit()
})
dialogRef.afterClosed()
.pipe(
take(1),
filter(Boolean),
tap(() => { this.refreshDetail.emit() }),
)
.subscribe()
}

trackByFn(_index: number, { id }: AccessLevelInstance) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
<div class="flex justify-between gap-4 overflow-hidden">
<span class="my-auto">{{ params.displayName }}</span>
<mat-icon class="scale-50 cursor-pointer" (click)="toggleMenu($event)" svgIcon="fa-solid:chevron-down"></mat-icon>
<!-- TRIGGER -->
<div class="flex w-full justify-between gap-4 overflow-hidden" #trigger>
<div class="flex gap-4">
<span class="my-auto">{{ params.displayName }}</span>
@if (sortIcon) {
<mat-icon class="scale-50" [svgIcon]="sortIcon"></mat-icon>
}
</div>
<div class="cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-gray-800" (click)="toggleMenu()">
<mat-icon class="scale-50" svgIcon="fa-solid:ellipsis-vertical"></mat-icon>
</div>
</div>
<!-- THIS IS IN DEVELOPMENT -->
@if (menuVisible) {
<div class="cell-menu absolute z-[1000] w-48 rounded-md border border-gray-300 bg-white p-2 shadow-lg">
<button (click)="sortAsc()">Sort Asc</button>

<!-- MENU -->
<ng-template #menu>
<div class="w-80 rounded border p-2 text-sm" [class.bg-[#2A3341]]="scheme === 'dark'" [class.bg-white]="scheme === 'light'">
<div class="menu-option" (click)="sortCol('asc')">
<mat-icon class="scale-50" svgIcon="fa-solid:arrow-up"></mat-icon>
<span>Sort Ascending</span>
</div>

<div class="menu-option" (click)="sortCol('desc')">
<mat-icon class="scale-50" svgIcon="fa-solid:arrow-down"></mat-icon>
<span>Sort Descending</span>
</div>

@if (sortIcon) {
<div class="menu-option" (click)="sortCol(null)">
<mat-icon class="scale-50" svgIcon="fa-solid:minus"></mat-icon>
<span>Clear Sort</span>
</div>
}

<mat-divider></mat-divider>

<div class="menu-option" (click)="pinCol('left')">
<mat-icon class="scale-50" [svgIcon]="'fa-solid:thumbtack'"></mat-icon>
<span>Pin Left</span>
</div>
<div class="menu-option" (click)="pinCol('right')">
<mat-icon class="scale-50" svgIcon="fa-solid:thumbtack"></mat-icon>
<span>Pin right</span>
</div>
@if (pinState) {
<div class="menu-option" (click)="pinCol(null)">
<mat-icon class="scale-50" svgIcon="fa-solid:minus"></mat-icon>
<span>Unpin</span>
</div>
}

<mat-divider></mat-divider>
<div class="menu-option" (click)="hideCol()">
<mat-icon class="scale-50" svgIcon="fa-solid:eye-slash"></mat-icon>
<span>Hide Column</span>
</div>
</div>
}
</ng-template>
147 changes: 133 additions & 14 deletions src/app/modules/inventory-list/list/grid/cell-header-menu.component.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,149 @@
import { Component } from '@angular/core'
import type { FlexibleConnectedPositionStrategyOrigin, OverlayRef } from '@angular/cdk/overlay'
import { Overlay } from '@angular/cdk/overlay'
import { TemplatePortal } from '@angular/cdk/portal'
import { CommonModule } from '@angular/common'
import type { AfterViewInit, TemplateRef } from '@angular/core'
import { Component, inject, ViewChild, ViewContainerRef } from '@angular/core'
import type { IHeaderAngularComp } from 'ag-grid-angular'
import type { IHeaderParams } from 'ag-grid-community'
import type { Column, GridApi, IHeaderParams } from 'ag-grid-community'
import { take } from 'rxjs'
import type { CurrentUser, OrganizationUserSettings } from '@seed/api'
import { OrganizationService } from '@seed/api'
import { MaterialImports } from '@seed/materials'
import { ConfigService } from '@seed/services'
import type { InventoryType } from 'app/modules/inventory/inventory.types'

@Component({
selector: 'seed-inventory-grid-cell-header-menu',
templateUrl: './cell-header-menu.component.html',
imports: [MaterialImports],
imports: [CommonModule, MaterialImports],
})
export class CellHeaderMenuComponent implements IHeaderAngularComp {
// THIS COMPONENT IS STILL IN DEVELOPMENT
public params: IHeaderParams
public menuVisible = false
export class CellHeaderMenuComponent implements IHeaderAngularComp, AfterViewInit {
@ViewChild('menu') menuTemplate!: TemplateRef<unknown>
@ViewChild('trigger') trigger!: FlexibleConnectedPositionStrategyOrigin

agInit(params: IHeaderParams): void {
private _configService = inject(ConfigService)
private _organizationService = inject(OrganizationService)
private _overlay = inject(Overlay)
private _vcr = inject(ViewContainerRef)
column: Column<unknown>
gridApi: GridApi
opened = false
orgId: number
orgUserId: number
overlayRef: OverlayRef
params: IHeaderParams
pinState: unknown
scheme: 'dark' | 'light'
sortIcon = ''
type: InventoryType
userSettings: OrganizationUserSettings

agInit(params: IHeaderParams & { currentUser: CurrentUser; type: InventoryType }): void {
this.params = params
this.column = params.column
this.gridApi = params.api
this.type = params.type
const { org_id, org_user_id, settings } = params.currentUser
this.orgId = org_id
this.orgUserId = org_user_id
this.userSettings = settings
}

ngAfterViewInit(): void {
this.getScheme()
this.setOverlay()
this.updateSortState()
this.column.addEventListener('sortChanged', () => {
this.updateSortState()
})
this.gridApi.addEventListener('columnPinned', () => {
this.pinState = this.column.isPinned()
})
}

getScheme() {
this._configService.scheme$.subscribe((scheme) => {
this.scheme = scheme
})
}

updateSortState() {
const state = this.gridApi.getColumnState().find((col) => col.colId === this.params.column.getColId())
const sortDir = state?.sort ?? null
const iconMap = {
asc: 'fa-solid:arrow-up',
desc: 'fa-solid:arrow-down',
}
this.sortIcon = iconMap[sortDir] ?? ''
}

setOverlay() {
const positionStrategy = this._overlay
.position()
.flexibleConnectedTo(this.trigger)
.withPositions([
{
originX: 'end',
originY: 'bottom',
overlayX: 'start',
overlayY: 'top',
},
])

this.overlayRef = this._overlay.create({
positionStrategy,
hasBackdrop: true,
backdropClass: 'transparent-backdrop',
})

this.overlayRef.backdropClick().subscribe(() => {
this.overlayRef.detach()
})
}

toggleMenu(): void {
// event.stopPropagation()
// this.menuVisible = !this.menuVisible
if (this.overlayRef?.hasAttached()) {
this.overlayRef.detach()
} else {
const portal = new TemplatePortal(this.menuTemplate, this._vcr)
this.overlayRef?.attach(portal)
}
}

sortCol(direction: 'asc' | 'desc' | null): void {
this.gridApi.applyColumnState({
state: [{ colId: this.params.column.getColId(), sort: direction }],
defaultState: { sort: null },
})
const dir = direction === 'desc' ? '-' : ''
const colDef = this.column.getColDef()
const sort = `${dir}${colDef.field}`
this.userSettings.sorts?.[this.type].push(sort)

this.detach()
}

pinCol(direction: 'left' | 'right' | null): void {
this.gridApi.setColumnsPinned([this.column], direction)
this.detach()
}

hideCol() {
this.gridApi.setColumnsVisible([this.column], false)
this.detach()
}

toggleMenu(event: MouseEvent): void {
event.stopPropagation()
this.menuVisible = !this.menuVisible
updateOrgUserSettings() {
return this._organizationService.updateOrganizationUser(this.orgUserId, this.orgId, this.userSettings)
.pipe(take(1))
.subscribe()
}

sortAsc(): void {
console.log('sort asc')
this.menuVisible = false
detach() {
this.overlayRef.detach()
}

refresh() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'
import type { OnChanges, SimpleChanges } from '@angular/core'
import { Component, Input } from '@angular/core'
import type { ColDef, GridApi } from 'ag-grid-community'
import type { OrganizationUserSettings } from '@seed/api'
import type { Column, OrganizationUserSettings } from '@seed/api'
import { MaterialImports } from '@seed/materials'
import type { AgFilter, FilterSortChip, FilterType, InventoryType } from '../../../inventory/inventory.types'

Expand All @@ -12,15 +12,17 @@ import type { AgFilter, FilterSortChip, FilterType, InventoryType } from '../../
imports: [CommonModule, MaterialImports],
})
export class FilterSortChipsComponent implements OnChanges {
@Input() gridApi: GridApi
@Input() columns: Column[]
@Input() columnDefs: ColDef[]
@Input() gridApi: GridApi
@Input() userSettings: OrganizationUserSettings
@Input() type: InventoryType
filterChips: FilterSortChip[] = []
sortChips: FilterSortChip[] = []

ngOnChanges({ userSettings }: SimpleChanges) {
if (userSettings?.currentValue) {
ngOnChanges(changes: SimpleChanges) {
const { userSettings, columnDefs } = changes
if (userSettings?.currentValue || columnDefs?.currentValue) {
this.getFilterChips()
this.getSortChips()
}
Expand All @@ -38,12 +40,20 @@ export class FilterSortChipsComponent implements OnChanges {
this.filterChips = []
for (const columnName of Object.keys(this.filters)) {
const colDef = this.columnDefs.find(({ field }) => field === columnName)

if (!colDef) return

const displayName = this.buildFilterDisplayName(colDef, this.filters[columnName])
const chip = { field: colDef.field, displayName, original: columnName }
this.filterChips.push(chip)
if (colDef) {
this.filterChips.push({
field: colDef.field,
displayName: this.buildFilterDisplayName(colDef, this.filters[columnName]),
original: columnName,
})
} else {
const column = this.columns.find((c) => c.name === columnName)
this.filterChips.push({
field: columnName,
displayName: column?.display_name ?? columnName,
original: columnName,
})
}
}
}

Expand All @@ -53,11 +63,21 @@ export class FilterSortChipsComponent implements OnChanges {
const direction = sort.startsWith('-') ? 'desc' : 'asc'
sort = sort.replace(/^-/, '')
const colDef = this.columnDefs.find(({ field }) => field === sort)

if (!colDef) return

const chip = { field: colDef.field, displayName: `${colDef.headerName} ${direction}`, original: sort }
this.sortChips.push(chip)
if (colDef) {
this.sortChips.push({
field: colDef.field,
displayName: `${colDef.headerName} ${direction}`,
original: sort,
})
} else {
const column = this.columns.find((c) => c.name === sort)
const displayName = column?.display_name ?? sort
this.sortChips.push({
field: sort,
displayName: `${displayName} ${direction}`,
original: sort,
})
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
<div class="mx-2 flex justify-between">
<mat-icon class="scale-75 cursor-pointer" (click)="resetGrid()" svgIcon="fa-solid:arrows-rotate" matTooltip="Reset Grid"></mat-icon>

<!-- POTENTIAL RESET MENU FOR MORE CONTROL -->

<!-- <mat-icon
class="scale-75 cursor-pointer"
[matMenuTriggerFor]="menu"
svgIcon="fa-solid:arrows-rotate"
matTooltip="Reset Grid Menu"
></mat-icon> -->

<!-- <mat-menu #menu="matMenu">
<div class="px-0">
@if (gridApi) {
<div class="menu-option" [ngClass]="{ '': scheme === 'dark' }" (click)="resetGrid()">
<mat-icon class="scale-50" svgIcon="fa-solid:rotate"></mat-icon>
<span class="w-200">Reset Grid</span>
</div>
<mat-divider class="mx-3 my-1"></mat-divider>

<div class="menu-option"
[ngClass]="{'': scheme === 'dark'}" (click)="resetColumns()">
<div class="w-6"></div>
<span>Reset Columns</span>
</div>
<div class="menu-option"
[ngClass]="{'': scheme === 'dark'}" (click)="resetFilters()">
<div class="w-6"></div>
<span>Reset Filters</span>
</div>
<div class="menu-option"
[ngClass]="{'': scheme === 'dark'}" (click)="resetSorts()">
<div class="w-6"></div>
<span>Reset Sorts</span>
</div>
<mat-divider class="mx-3 my-1"></mat-divider>
}

<div class="menu-option" [ngClass]="{ '': scheme === 'dark' }" (click)="resetUserSettings()">
<div class="w-6"></div>
<span>Reset User Settings</span>
</div>
</div>
</mat-menu> -->

<div class="my-auto flex justify-end">
@if (selectedViewIds.length > 0) {
<span class="my-auto mr-10">({{ selectedViewIds.length }} selected)</span>
Expand Down
Loading