1
+ import { AfterViewInit , Component , Input , OnChanges , ViewChild } from '@angular/core' ;
2
+ import { MatPaginator } from '@angular/material/paginator' ;
3
+ import { MatTableDataSource } from '@angular/material/table' ;
4
+ import { MatSort } from '@angular/material/sort' ;
5
+ import { CustomUsageReportLine , UsageReportService } from 'src/app/usage-report.service' ;
6
+
7
+ interface UsageColumn {
8
+ columnDef : string ;
9
+ header : string ;
10
+ cell : ( element : any ) => any ;
11
+ footer ?: ( ) => any ;
12
+ sticky ?: boolean ;
13
+ }
14
+
15
+ interface CodespacesUsageItem {
16
+ runs : number ;
17
+ total : number ;
18
+ cost : number ;
19
+ pricePerUnit : number ;
20
+ owner : string ;
21
+ username : string ;
22
+ sku : string ;
23
+ unitType : string ;
24
+ repositorySlug : string ;
25
+ sticky ?: boolean ;
26
+ }
27
+
28
+ @Component ( {
29
+ selector : 'app-table-codespaces-usage' ,
30
+ templateUrl : './table-codespaces-usage.component.html' ,
31
+ styleUrl : './table-codespaces-usage.component.scss'
32
+ } )
33
+ export class TableCodespacesUsageComponent implements OnChanges , AfterViewInit {
34
+ columns = [ ] as UsageColumn [ ] ;
35
+ displayedColumns = this . columns . map ( c => c . columnDef ) ;
36
+ @Input ( ) data ! : CustomUsageReportLine [ ] ;
37
+ @Input ( ) currency ! : string ;
38
+ dataSource : MatTableDataSource < CodespacesUsageItem > = new MatTableDataSource < any > ( ) ; // Initialize the dataSource property
39
+ tableType : 'sku' | 'repo' | 'user' = 'sku' ;
40
+
41
+ @ViewChild ( MatPaginator ) paginator ! : MatPaginator ;
42
+ @ViewChild ( MatSort ) sort ! : MatSort ;
43
+
44
+ constructor (
45
+ private usageReportService : UsageReportService ,
46
+ ) { }
47
+
48
+ ngOnChanges ( ) {
49
+ this . initializeColumns ( ) ;
50
+ let usage : CodespacesUsageItem [ ] = [ ] ;
51
+ let usageItems : CodespacesUsageItem [ ] = ( usage as CodespacesUsageItem [ ] ) ;
52
+ usageItems = this . data . reduce ( ( acc , line ) => {
53
+ const item = acc . find ( a => {
54
+ if ( this . tableType === 'sku' ) {
55
+ return a . sku === line . sku ;
56
+ } else if ( this . tableType === 'repo' ) {
57
+ return a . repositorySlug === line . repositorySlug ;
58
+ } else if ( this . tableType === 'user' ) {
59
+ return a . username === line . username ;
60
+ }
61
+ return false ;
62
+ } ) ;
63
+ const month : string = line . date . toLocaleString ( 'default' , { month : 'short' } ) ;
64
+ if ( item ) {
65
+ if ( ( item as any ) [ month ] ) {
66
+ ( item as any ) [ month ] += line . value ;
67
+ } else {
68
+ ( item as any ) [ month ] = line . value || 0 ;
69
+ }
70
+ item . total += line . value ;
71
+ if ( ! this . columns . find ( c => c . columnDef === month ) ) {
72
+ this . columns . push ( {
73
+ columnDef : month ,
74
+ header : month ,
75
+ cell : ( workflowItem : any ) => this . currency === 'cost' ? currencyPipe . transform ( workflowItem [ month ] ) : decimalPipe . transform ( workflowItem [ month ] ) ,
76
+ footer : ( ) => {
77
+ const total = this . dataSource . data . reduce ( ( acc , item ) => acc + ( item as any ) [ month ] , 0 ) ;
78
+ return this . currency === 'cost' ? currencyPipe . transform ( total ) : decimalPipe . transform ( total ) ;
79
+ }
80
+ } ) ;
81
+ }
82
+ item . cost += line . quantity * line . pricePerUnit ;
83
+ item . total += line . quantity ;
84
+ item . runs ++ ;
85
+ } else {
86
+ acc . push ( {
87
+ owner : line . owner ,
88
+ total : line . quantity ,
89
+ cost : line . quantity * line . pricePerUnit ,
90
+ runs : 1 ,
91
+ pricePerUnit : line . pricePerUnit || 0 ,
92
+ [ month ] : line . value ,
93
+ sku : line . sku ,
94
+ unitType : line . unitType ,
95
+ repositorySlug : line . repositorySlug ,
96
+ username : line . username
97
+ } ) ;
98
+ }
99
+ return acc ;
100
+ } , [ ] as CodespacesUsageItem [ ] ) ;
101
+
102
+ usageItems . forEach ( ( item ) => {
103
+ this . columns . forEach ( ( column : any ) => {
104
+ if ( ! ( item as any ) [ column . columnDef ] ) {
105
+ ( item as any ) [ column . columnDef ] = 0 ;
106
+ }
107
+ } ) ;
108
+ } ) ;
109
+ usage = usageItems
110
+ this . displayedColumns = this . columns . map ( c => c . columnDef ) ;
111
+ this . dataSource . data = usage ;
112
+ }
113
+
114
+ ngAfterViewInit ( ) {
115
+ this . dataSource . paginator = this . paginator ;
116
+ this . dataSource . sort = this . sort ;
117
+ }
118
+
119
+ applyFilter ( event : Event ) {
120
+ const filterValue = ( event . target as HTMLInputElement ) . value ;
121
+ this . dataSource . filter = filterValue . trim ( ) . toLowerCase ( ) ;
122
+
123
+ if ( this . dataSource . paginator ) {
124
+ this . dataSource . paginator . firstPage ( ) ;
125
+ }
126
+ }
127
+
128
+ initializeColumns ( ) {
129
+ let columns : UsageColumn [ ] = [ ] ;
130
+ if ( this . tableType === 'sku' ) {
131
+ columns = [
132
+ {
133
+ columnDef : 'sku' ,
134
+ header : 'Product' ,
135
+ cell : ( workflowItem : CodespacesUsageItem ) => `${ workflowItem . sku } ` ,
136
+ sticky : true
137
+ }
138
+ ] ;
139
+ } else if ( this . tableType === 'repo' ) {
140
+ columns = [
141
+ {
142
+ columnDef : 'repositorySlug' ,
143
+ header : 'Repository' ,
144
+ cell : ( workflowItem : CodespacesUsageItem ) => `${ workflowItem . repositorySlug } ` ,
145
+ sticky : true
146
+ }
147
+ ] ;
148
+ } else if ( this . tableType === 'user' ) {
149
+ columns = [
150
+ {
151
+ columnDef : 'username' ,
152
+ header : 'User' ,
153
+ cell : ( workflowItem : CodespacesUsageItem ) => `${ workflowItem . username } ` ,
154
+ sticky : true
155
+ }
156
+ ] ;
157
+ }
158
+ if ( this . currency === 'minutes' ) {
159
+ columns . push ( {
160
+ columnDef : 'total' ,
161
+ header : 'Total seats' ,
162
+ cell : ( workflowItem : CodespacesUsageItem ) => decimalPipe . transform ( Math . floor ( workflowItem . total ) ) ,
163
+ footer : ( ) => decimalPipe . transform ( this . data . reduce ( ( acc , line ) => acc += line . value , 0 ) )
164
+ } ) ;
165
+ } else if ( this . currency === 'cost' ) {
166
+ columns . push ( {
167
+ columnDef : 'cost' ,
168
+ header : 'Total cost' ,
169
+ cell : ( workflowItem : CodespacesUsageItem ) => currencyPipe . transform ( workflowItem . cost ) ,
170
+ footer : ( ) => currencyPipe . transform ( this . data . reduce ( ( acc , line ) => acc += line . value , 0 ) )
171
+ } ) ;
172
+ }
173
+ this . columns = columns ;
174
+ this . displayedColumns = this . columns . map ( c => c . columnDef ) ;
175
+ }
176
+ }
177
+
178
+ import { Pipe , PipeTransform } from '@angular/core' ;
179
+ import { CurrencyPipe , DecimalPipe } from '@angular/common' ;
180
+
181
+ @Pipe ( {
182
+ name : 'duration'
183
+ } )
184
+ export class DurationPipe implements PipeTransform {
185
+ transform ( minutes : number ) : string {
186
+ const seconds = minutes * 60 ;
187
+ if ( seconds < 60 ) {
188
+ return `${ seconds } sec` ;
189
+ } else if ( seconds < 3600 ) {
190
+ return `${ Math . round ( seconds / 60 ) } min` ;
191
+ } else {
192
+ return `${ Math . round ( seconds / 3600 ) } hr` ;
193
+ }
194
+ }
195
+
196
+ }
197
+
198
+ const decimalPipe = new DecimalPipe ( 'en-US' ) ;
199
+ const currencyPipe = new CurrencyPipe ( 'en-US' ) ;
0 commit comments