Skip to content

Commit 14bc361

Browse files
committed
#986 - Grouping frozen beans in active/frozen/archive state.
1 parent 864d7a0 commit 14bc361

File tree

6 files changed

+616
-451
lines changed

6 files changed

+616
-451
lines changed

src/app/beans/beans.page.html

Lines changed: 121 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -22,150 +22,167 @@
2222
</ion-header>
2323
<ion-content #beanContent>
2424

25-
<ion-segment (ionChange)="segmentChanged()" [(ngModel)]="bean_segment" class="ion-padding-vertical tabs" >
25+
<ion-segment (ionChange)="segmentChanged()" [(ngModel)]="bean_segment" class="ion-padding-vertical tabs">
2626
<ion-segment-button value="open">
2727
<ion-label>
2828
{{"PAGE_BEANS_LIST_OBTAINABLE" | translate}}
2929
<br />
3030
@if (openBeans?.length !== openBeansLength) {
31-
<span class="value">
32-
( {{ openBeans?.length }} / {{ openBeansLength }} )
33-
</span>
31+
<span class="value">
32+
( {{ openBeans?.length }} / {{ openBeansLength }} )
33+
</span>
3434
} @else {
35-
<span class="value">( {{ openBeansLength }} )</span>
35+
<span class="value">( {{ openBeansLength }} )</span>
3636
}
3737
</ion-label>
3838
</ion-segment-button>
3939
@if (settings?.freeze_coffee_beans) {
40-
<ion-segment-button value="frozen">
41-
<ion-label>
42-
{{"FROZEN_BEANS" | translate}}
43-
<br />
44-
@if (frozenBeans?.length !== frozenBeansLength) {
45-
<span class="value">
46-
( {{ frozenBeans?.length }} / {{ frozenBeansLength }} )
47-
</span>
48-
} @else {
49-
<span class="value">( {{ frozenBeansLength }} )</span>
50-
}
51-
</ion-label>
52-
</ion-segment-button>
40+
<ion-segment-button value="frozen">
41+
<ion-label>
42+
{{"FROZEN_BEANS" | translate}}
43+
<br />
44+
@if (frozenBeans?.length !== frozenBeansLength) {
45+
<span class="value">
46+
( {{ frozenBeans?.length }} / {{ frozenBeansLength }} )
47+
</span>
48+
} @else {
49+
<span class="value">( {{ frozenBeansLength }} )</span>
50+
}
51+
</ion-label>
52+
</ion-segment-button>
5353
}
5454
@if (settings?.show_archived_beans) {
55-
<ion-segment-button value="archive">
56-
<ion-label>
57-
{{"TAB_ARCHIVE" | translate}}
58-
<br />
59-
@if (finishedBeans?.length !== finishedBeansLength) {
60-
<span class="value">
61-
( {{ finishedBeans?.length }} / {{ finishedBeansLength }} )
62-
</span>
63-
} @else {
64-
<span class="value">( {{ finishedBeansLength }} )</span>
65-
}
66-
</ion-label>
67-
</ion-segment-button>
55+
<ion-segment-button value="archive">
56+
<ion-label>
57+
{{"TAB_ARCHIVE" | translate}}
58+
<br />
59+
@if (finishedBeans?.length !== finishedBeansLength) {
60+
<span class="value">
61+
( {{ finishedBeans?.length }} / {{ finishedBeansLength }} )
62+
</span>
63+
} @else {
64+
<span class="value">( {{ finishedBeansLength }} )</span>
65+
}
66+
</ion-label>
67+
</ion-segment-button>
6868
}
6969
</ion-segment>
70-
@switch (bean_segment) {
70+
@switch (bean_segment) {
7171
@case ('open') {
72-
@if (openBeans?.length > 0 || uiShallBarBeDisplayed || uiIsTextSearchActive || uiIsFilterActive || uiIsSortActive) {
73-
<div class="ion-justify-content-end container" style="margin-left:10px;margin-right:10px;margin-bottom:20px;">
74-
<ng-container [ngTemplateOutlet]="beanSearchToolbar"></ng-container>
75-
</div>
72+
@if (openBeans?.length > 0 || uiShallBarBeDisplayed || uiIsTextSearchActive || uiIsFilterActive || uiIsSortActive) {
73+
<div class="ion-justify-content-end container" style="margin-left:10px;margin-right:10px;margin-bottom:20px;">
74+
<ng-container [ngTemplateOutlet]="beanSearchToolbar"></ng-container>
75+
</div>
76+
}
77+
@if (openBeans.length == 0) {
78+
<div class="ion-padding ion-text-center">
79+
<div>
80+
<ion-icon color="inactive" name="beanconqueror-beans" size="large"></ion-icon>
81+
</div>
82+
<div class="text-color-inactive ion-margin-top">
83+
{{"PAGE_BEANS_LIST_YOU_GOT_NO_FRESH_BEANS" | translate}}
84+
</div>
85+
</div>
86+
}
87+
<ag-virtual-scroll #openScroll [items]="openBeans" [height]="'100vh'" [min-row-height]="uiIsCollapseActive?60:210">
88+
@for (item of openScroll.items; track item) {
89+
@if (item['beans']) {
90+
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()" [bean]="item['beans'][0]"
91+
[beanGroup]="item"></bean-information>
92+
} @else {
93+
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()" [bean]="item"></bean-information>
7694
}
77-
@if (openBeans.length == 0) {
78-
<div class="ion-padding ion-text-center">
79-
<div>
80-
<ion-icon color="inactive" name="beanconqueror-beans" size="large"></ion-icon>
81-
</div>
82-
<div class="text-color-inactive ion-margin-top">
83-
{{"PAGE_BEANS_LIST_YOU_GOT_NO_FRESH_BEANS" | translate}}
84-
</div>
85-
</div>
8695
}
87-
<ag-virtual-scroll #openScroll [items]="openBeans" [height]="'100vh'" [min-row-height]="uiIsCollapseActive?60:210">
88-
@for (bean of openScroll.items; track bean) {
89-
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()"
90-
[bean]="bean"></bean-information>
91-
}
92-
</ag-virtual-scroll>
96+
</ag-virtual-scroll>
9397
}
9498
@case ('frozen') {
95-
@if (settings?.freeze_coffee_beans) {
96-
@if (frozenBeans?.length > 0 || uiShallBarBeDisplayed || uiIsTextSearchActive || uiIsFilterActive || uiIsSortActive) {
97-
<div class="ion-justify-content-end container" style="margin-left:10px;margin-right:10px;margin-bottom:20px;">
98-
<ng-container [ngTemplateOutlet]="beanSearchToolbar"></ng-container>
99-
</div>
100-
}
101-
@if (frozenBeans.length == 0) {
102-
<div class="ion-padding ion-text-center">
103-
<div>
104-
<ion-icon color="inactive" name="beanconqueror-beans" size="large"></ion-icon>
105-
</div>
106-
<div class="text-color-inactive ion-margin-top">
107-
{{"PAGE_BEANS_LIST_YOU_GOT_NO_FROZEN_BEANS" | translate}}
108-
</div>
109-
</div>
110-
}
111-
<ag-virtual-scroll #frozenScroll [items]="frozenBeans" [height]="'100vh'" [min-row-height]="uiIsCollapseActive?60:210">
112-
@for (bean of frozenScroll.items; track bean) {
113-
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()"
114-
[bean]="bean"></bean-information>
115-
}
116-
</ag-virtual-scroll>
99+
@if (settings?.freeze_coffee_beans) {
100+
@if (frozenBeans?.length > 0 || uiShallBarBeDisplayed || uiIsTextSearchActive || uiIsFilterActive || uiIsSortActive) {
101+
<div class="ion-justify-content-end container" style="margin-left:10px;margin-right:10px;margin-bottom:20px;">
102+
<ng-container [ngTemplateOutlet]="beanSearchToolbar"></ng-container>
103+
</div>
104+
}
105+
@if (frozenBeans.length == 0) {
106+
<div class="ion-padding ion-text-center">
107+
<div>
108+
<ion-icon color="inactive" name="beanconqueror-beans" size="large"></ion-icon>
109+
</div>
110+
<div class="text-color-inactive ion-margin-top">
111+
{{"PAGE_BEANS_LIST_YOU_GOT_NO_FROZEN_BEANS" | translate}}
112+
</div>
113+
</div>
114+
}
115+
<ag-virtual-scroll #frozenScroll [items]="frozenBeans" [height]="'100vh'"
116+
[min-row-height]="uiIsCollapseActive?60:210">
117+
@for (item of frozenScroll.items; track item) {
118+
@if (item['beans']) {
119+
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()" [bean]="item['beans'][0]"
120+
[beanGroup]="item"></bean-information>
121+
} @else {
122+
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()" [bean]="item"></bean-information>
123+
}
117124
}
125+
</ag-virtual-scroll>
126+
}
118127
}
119128
@case ('archive') {
120-
@if (settings?.show_archived_beans) {
121-
@if (finishedBeans?.length > 0 || uiShallBarBeDisplayed || uiIsTextSearchActive || uiIsFilterActive || uiIsSortActive) {
122-
<div class="ion-justify-content-end container" style="margin-left:10px;margin-right:10px;margin-bottom:20px;">
123-
<ng-container [ngTemplateOutlet]="beanSearchToolbar"></ng-container>
124-
</div>
125-
}
126-
@if (finishedBeans.length == 0) {
127-
<div class="ion-padding ion-text-center">
128-
<div>
129-
<ion-icon color="inactive" name="beanconqueror-beans" size="large"></ion-icon>
130-
</div>
131-
<div class="text-color-inactive ion-margin-top">
132-
{{"PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS" | translate}}
133-
</div>
134-
</div>
135-
}
136-
<ag-virtual-scroll #archivedScroll [items]="finishedBeans" [height]="'100vh'" [min-row-height]="uiIsCollapseActive?60:210">
137-
@for (bean of archivedScroll.items; track bean) {
138-
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()"
139-
[bean]="bean"></bean-information>
140-
}
141-
</ag-virtual-scroll>
129+
@if (settings?.show_archived_beans) {
130+
@if (finishedBeans?.length > 0 || uiShallBarBeDisplayed || uiIsTextSearchActive || uiIsFilterActive || uiIsSortActive)
131+
{
132+
<div class="ion-justify-content-end container" style="margin-left:10px;margin-right:10px;margin-bottom:20px;">
133+
<ng-container [ngTemplateOutlet]="beanSearchToolbar"></ng-container>
134+
</div>
135+
}
136+
@if (finishedBeans.length == 0) {
137+
<div class="ion-padding ion-text-center">
138+
<div>
139+
<ion-icon color="inactive" name="beanconqueror-beans" size="large"></ion-icon>
140+
</div>
141+
<div class="text-color-inactive ion-margin-top">
142+
{{"PAGE_BEANS_LIST_YOU_GOT_NO_FINISHED_BEANS" | translate}}
143+
</div>
144+
</div>
145+
}
146+
<ag-virtual-scroll #archivedScroll [items]="finishedBeans" [height]="'100vh'"
147+
[min-row-height]="uiIsCollapseActive?60:210">
148+
@for (item of archivedScroll.items; track item) {
149+
@if (item['beans']) {
150+
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()" [bean]="item['beans'][0]"
151+
[beanGroup]="item"></bean-information>
152+
} @else {
153+
<bean-information [collapsed]='uiIsCollapseActive' (beanAction)="beanAction()" [bean]="item"></bean-information>
142154
}
155+
}
156+
</ag-virtual-scroll>
157+
}
158+
}
143159
}
144-
}
145160

146161
</ion-content>
147162

148163
<ng-template #beanSearchToolbar>
149-
<ion-searchbar (ionChange)="research()" (ionInput)='research()' [(ngModel)]="uiSearchText" class="ion-no-padding ion-no-margin" debounce="750" placeholder="{{'SEARCH' | translate}}" search-icon="beanconqueror-detail" showCancelButton="never"></ion-searchbar>
164+
<ion-searchbar (ionChange)="research()" (ionInput)='research()' [(ngModel)]="uiSearchText"
165+
class="ion-no-padding ion-no-margin" debounce="750" placeholder="{{'SEARCH' | translate}}"
166+
search-icon="beanconqueror-detail" showCancelButton="never"></ion-searchbar>
150167
<ion-button (click)="showFilter()" fill="clear" size="small" tappable>
151168
@if (uiIsFilterActive) {
152-
<ion-icon name="beanconqueror-filter-active" slot="icon-only"></ion-icon>
169+
<ion-icon name="beanconqueror-filter-active" slot="icon-only"></ion-icon>
153170
} @else {
154-
<ion-icon name="beanconqueror-filter-inactive" slot="icon-only"></ion-icon>
171+
<ion-icon name="beanconqueror-filter-inactive" slot="icon-only"></ion-icon>
155172
}
156173
</ion-button>
157174
<ion-button (click)="showSort()" fill="clear" size="small" tappable>
158175
@if (uiIsSortActive) {
159-
<ion-icon name="beanconqueror-sort-active" slot="icon-only"></ion-icon>
176+
<ion-icon name="beanconqueror-sort-active" slot="icon-only"></ion-icon>
160177
} @else {
161-
<ion-icon name="beanconqueror-sort-inactive" slot="icon-only"></ion-icon>
178+
<ion-icon name="beanconqueror-sort-inactive" slot="icon-only"></ion-icon>
162179
}
163180
</ion-button>
164181
<ion-button (click)="toggleCollapseBeans()" fill="clear" size="small" tappable>
165182
@if (uiIsCollapseActive) {
166-
<ion-icon name="beanconqueror-expand-active" slot="icon-only"></ion-icon>
183+
<ion-icon name="beanconqueror-expand-active" slot="icon-only"></ion-icon>
167184
} @else {
168-
<ion-icon name="beanconqueror-expand-inactive" slot="icon-only"></ion-icon>
185+
<ion-icon name="beanconqueror-expand-inactive" slot="icon-only"></ion-icon>
169186
}
170187
</ion-button>
171-
</ng-template>
188+
</ng-template>

src/app/beans/beans.page.ts

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { BeanSortFilterHelperService } from '../../services/beanSortFilterHelper
3030
import { NfcService } from '../../services/nfcService/nfc-service.service';
3131

3232
import { UIImage } from '../../services/uiImage';
33+
import { BeanGroup } from '../../interfaces/bean/beanGroup';
3334
@Component({
3435
selector: 'beans',
3536
templateUrl: './beans.page.html',
@@ -41,9 +42,9 @@ export class BeansPage implements OnDestroy {
4142

4243
public settings: Settings;
4344

44-
public openBeans: Array<Bean> = [];
45-
public finishedBeans: Array<Bean> = [];
46-
public frozenBeans: Array<Bean> = [];
45+
public openBeans: Array<Bean | BeanGroup> = [];
46+
public finishedBeans: Array<Bean | BeanGroup> = [];
47+
public frozenBeans: Array<Bean | BeanGroup> = [];
4748
public finishedBeansLength: number = 0;
4849
public openBeansLength: number = 0;
4950
public frozenBeansLength: number = 0;
@@ -447,12 +448,45 @@ export class BeansPage implements OnDestroy {
447448
sort,
448449
filters,
449450
);
451+
452+
const groupedBeans: Array<Bean | BeanGroup> = [];
453+
const processedUuids = new Set<string>();
454+
455+
for (const bean of filteredBeans) {
456+
if (processedUuids.has(bean.config.uuid)) {
457+
continue;
458+
}
459+
460+
if (bean.frozenGroupId) {
461+
// Find all beans with this frozenGroupId in the filtered list
462+
// We only look in the filtered list to respect current filters
463+
const groupMembers = filteredBeans.filter(
464+
(b) => b.frozenGroupId === bean.frozenGroupId,
465+
);
466+
467+
if (groupMembers.length > 1) {
468+
groupedBeans.push({
469+
frozenGroupId: bean.frozenGroupId,
470+
beans: groupMembers,
471+
expanded: false,
472+
});
473+
groupMembers.forEach((b) => processedUuids.add(b.config.uuid));
474+
} else {
475+
groupedBeans.push(bean);
476+
processedUuids.add(bean.config.uuid);
477+
}
478+
} else {
479+
groupedBeans.push(bean);
480+
processedUuids.add(bean.config.uuid);
481+
}
482+
}
483+
450484
if (_type === 'open') {
451-
this.openBeans = filteredBeans;
485+
this.openBeans = groupedBeans;
452486
} else if (_type === 'archive') {
453-
this.finishedBeans = filteredBeans;
487+
this.finishedBeans = groupedBeans;
454488
} else if (_type === 'frozen') {
455-
this.frozenBeans = filteredBeans;
489+
this.frozenBeans = groupedBeans;
456490
}
457491
this.retriggerScroll();
458492
}

0 commit comments

Comments
 (0)