fix: dish prices sometimes go missing

This commit is contained in:
2024-02-08 14:25:54 +01:00
committed by Rainer Killinger
parent 8667603bd6
commit 0858a26dc1
12 changed files with 154 additions and 154 deletions

View File

@@ -13,20 +13,19 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, Input} from '@angular/core';
import {SCAcademicPriceGroup, SCThingThatCanBeOfferedOffer} from '@openstapps/core';
import {
SCAcademicPriceGroup,
SCThingThatCanBeOfferedAvailability,
SCThingThatCanBeOfferedOffer,
} from '@openstapps/core';
import {SettingsProvider} from '../../settings/settings.provider';
/**
* TODO
*/
@Component({
selector: 'stapps-offers-in-list',
templateUrl: 'offers-in-list.html',
styleUrls: ['offers-in-list.scss'],
})
export class OffersInListComponent {
/**
* TODO
*/
@Input() set offers(it: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>) {
this._offers = it;
this.price = it[0].prices?.default;
@@ -34,13 +33,14 @@ export class OffersInListComponent {
this.price = it[0].prices?.[(group.value as string).replace(/s$/, '') as never];
});
const availabilities = new Set(it.map(offer => offer.availability));
this.soldOut = availabilities.has('out of stock') && availabilities.size === 1;
if (it.length === 1) {
this.availability = it[0].availability;
}
}
price?: number;
soldOut: boolean;
availability: SCThingThatCanBeOfferedAvailability;
_offers: Array<SCThingThatCanBeOfferedOffer<SCAcademicPriceGroup>>;

View File

@@ -13,16 +13,10 @@
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<div>
<ion-text *ngIf="price && !soldOut" style="white-space: nowrap">
<h2>{{ price | currency : 'EUR' : 'symbol' : undefined : 'de' }}</h2>
</ion-text>
<ion-text *ngIf="soldOut" color="danger" class="sold-out" style="white-space: nowrap">
<h2>{{ 'data.detail.offers.sold_out' | translate }}</h2>
</ion-text>
<p *ngIf="_offers[0].inPlace && !soldOut" class="place" style="white-space: nowrap">
<ion-icon name="pin_drop"></ion-icon>{{ _offers[0].inPlace.name }}<span *ngIf="_offers.length > 1"
>...</span
>
</p>
</div>
<ion-badge
*ngIf="price"
[color]="availability === 'out of stock' ? 'danger' : availability === 'limited availability' ? 'warning' : 'primary'"
>
{{ availability === 'out of stock' ? ('data.detail.offers.sold_out' | translate) : (price | currency : 'EUR'
: 'symbol' : undefined : 'de') }}
</ion-badge>

View File

@@ -0,0 +1,11 @@
:host {
display: contents;
}
ion-badge {
display: inline-block;
font-size: 0.8em;
margin-top: -0.25em;
translate: 0 0.25em;
padding: 0.25em;
}

View File

@@ -20,33 +20,25 @@
detail="false"
(click)="notifySelect()"
>
<div class="item-height-placeholder"></div>
<ion-thumbnail slot="start" *ngIf="!hideThumbnail" class="ion-margin-end">
<ion-icon color="dark" [name]="item.type | dataIcon" [size]="36"></ion-icon>
</ion-thumbnail>
<ng-container *ngIf="contentTemplateRef; else defaultContent">
<ion-label class="ion-text-wrap" [ngSwitch]="true">
<div>
<ng-container *ngTemplateOutlet="contentTemplateRef; context: {$implicit: item}"></ng-container>
</div>
</ion-label>
<ng-container *ngTemplateOutlet="contentTemplateRef; context: {$implicit: item}"></ng-container>
</ng-container>
<ng-container *ngIf="listItemEndInteraction" [ngSwitch]="item.type">
<stapps-rating *ngSwitchCase="'dish'" [item]="$any(item)"></stapps-rating>
<stapps-favorite-button *ngSwitchDefault [item]="$any(item)"></stapps-favorite-button>
<stapps-rating slot="end" *ngSwitchCase="'dish'" [item]="$any(item)"></stapps-rating>
<stapps-favorite-button slot="end" *ngSwitchDefault [item]="$any(item)"></stapps-favorite-button>
</ng-container>
</ion-item>
<ng-template #defaultContent>
<ion-label class="ion-text-wrap">
<div>
<ng-template [dataListItemHost]="item"></ng-template>
<stapps-action-chip-list
*ngIf="listItemChipInteraction && appearance !== 'square'"
slot="end"
[item]="item"
></stapps-action-chip-list>
</div>
</ion-label>
<div>
<ng-template [dataListItemHost]="item"></ng-template>
<stapps-action-chip-list
*ngIf="listItemChipInteraction && appearance !== 'square'"
[item]="item"
></stapps-action-chip-list>
</div>
</ng-template>

View File

@@ -20,34 +20,25 @@
ion-item::part(native) {
height: 100%;
padding: var(--spacing-sm);
}
ion-item {
--border-color: transparent;
--inner-padding-end: 0;
--padding-start: 0;
@include border-radius-in-parallax(var(--border-radius-default));
overflow: hidden;
margin: var(--spacing-sm);
height: 100%;
ion-thumbnail {
--ion-margin: var(--spacing-xs);
margin-inline-start: var(--spacing-md);
margin-inline: var(--spacing-md);
margin-block: auto;
padding: 0;
}
ion-label {
width: 100%;
margin-right: 0;
padding-left: var(--spacing-sm);
div {
display: flex;
flex-direction: column;
}
}
}
.ion-text-wrap ::ng-deep ion-label {
@@ -58,9 +49,44 @@ ion-item {
-webkit-line-clamp: 3;
}
[slot='end'] {
margin-inline-start: 0;
margin-block: auto;
}
stapps-action-chip-list {
float: bottom;
}
ion-item ::ng-deep {
stapps-long-inline-text,
.title {
overflow: hidden;
display: -webkit-box;
white-space: break-spaces;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.title-sub {
overflow: hidden;
display: -webkit-box;
white-space: break-spaces;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
margin-block: var(--spacing-xs);
}
}
:host.square ::ng-deep {
ion-item {
margin: 0;
align-items: flex-start;
}
ion-row {
@@ -74,19 +100,12 @@ ion-item {
flex-grow: 0;
}
stapps-long-inline-text,
.title {
overflow: hidden;
display: -webkit-box;
white-space: break-spaces;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
height: 2.5em;
}
.title-sub {
display: none;
.title ~ :last-child {
margin-right: 42px;
}
stapps-rating,

View File

@@ -13,19 +13,7 @@
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ion-note>
<ng-container *ngIf="item.certifications">
<ng-container *ngFor="let cert of item.certifications">
<abbr [title]="'description' | thingTranslate: cert">
<img
*ngIf="cert.compactImage"
[src]="'compactImage' | thingTranslate: cert"
height="16"
[alt]="'name' | thingTranslate: cert"
/>
</abbr>
</ng-container>
</ng-container>
<div class="sep"></div>
<ion-label> {{ 'categories' | thingTranslate: item | join: ', ' | titlecase }} </ion-label>
<ng-container *ngIf="item.characteristics">
<ng-container *ngFor="let characteristic of 'characteristics' | thingTranslate: item">
<!-- Abbr tag shows the actual name on hover -->
@@ -36,6 +24,15 @@
></abbr>
</ng-container>
</ng-container>
<div class="sep"></div>
<ion-label> {{ 'categories' | thingTranslate: item | join: ', ' | titlecase }} </ion-label>
<ng-container *ngIf="item.certifications">
<ng-container *ngFor="let cert of item.certifications">
<abbr *ngIf="cert.compactImage" [title]="'description' | thingTranslate: cert">
<img
[src]="'compactImage' | thingTranslate: cert"
height="16"
[alt]="'name' | thingTranslate: cert"
/>
</abbr>
</ng-container>
</ng-container>
</ion-note>

View File

@@ -12,13 +12,21 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
:host {
display: contents;
}
$size: 16px;
ion-note {
margin: inherit;
margin-right: inherit;
margin-block-end: inherit;
display: flex;
flex-direction: row-reverse;
align-items: center;
justify-content: flex-end;
align-items: flex-start;
justify-content: flex-start;
gap: var(--spacing-xs) var(--spacing-sm);
flex-flow: row wrap;
list-style: none;
}
@@ -41,15 +49,5 @@ abbr {
}
img {
height: 100%;
background: var(--background-url);
}
.sep {
display: none;
margin-inline: var(--spacing-xs);
}
abbr + .sep {
display: revert;
}

View File

@@ -13,7 +13,7 @@
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<stapps-dish-characteristics *ngIf="item.characteristics" [item]="item"></stapps-dish-characteristics>
<stapps-dish-characteristics [item]="item"></stapps-dish-characteristics>
<stapps-offers-detail *ngIf="item.offers" [offers]="item.offers"></stapps-offers-detail>
<stapps-certifications-in-detail
*ngIf="item.certifications"

View File

@@ -13,19 +13,12 @@
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ion-grid>
<ion-row class="ion-justify-content-between">
<ion-col size="11" size-sm="10">
<div class="ion-text-wrap">
<ion-label class="title">{{ 'name' | thingTranslate: item }}</ion-label>
<p class="title-sub ion-hide-sm-down">{{ 'description' | thingTranslate: item }}</p>
<stapps-dish-characteristics [item]="item"></stapps-dish-characteristics>
</div>
</ion-col>
<ion-col>
<div class="ion-text-end">
<stapps-offers-in-list *ngIf="item.offers" [offers]="item.offers"></stapps-offers-in-list>
</div>
</ion-col>
</ion-row>
</ion-grid>
<ion-label class="title">
<stapps-offers-in-list
*ngIf="item.offers && item.offers.length > 0"
[offers]="item.offers"
></stapps-offers-in-list>
{{ 'name' | thingTranslate: item }}
</ion-label>
<p class="title-sub">{{ 'description' | thingTranslate: item | sentencecase }}</p>
<stapps-dish-characteristics [item]="item"></stapps-dish-characteristics>

View File

@@ -14,9 +14,11 @@
*/
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
import {PositionService} from '../../../map/position.service';
import {filter, Observable, timer} from 'rxjs';
import {filter, Observable} from 'rxjs';
import {hasValidLocation, isSCFloor, PlaceTypes, PlaceTypesWithDistance} from './place-types';
import {map} from 'rxjs/operators';
import {LatLng, geoJSON} from 'leaflet';
import {trigger, transition, style, animate} from '@angular/animations';
/**
* Shows a place as a list item
@@ -26,6 +28,7 @@ import {map} from 'rxjs/operators';
templateUrl: 'place-list-item.html',
styleUrls: ['place-list-item.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [trigger('fade', [transition(':enter', [style({opacity: 0}), animate(100)])])],
})
export class PlaceListItemComponent {
_item: PlaceTypesWithDistance;
@@ -36,8 +39,13 @@ export class PlaceListItemComponent {
@Input() set item(item: PlaceTypes) {
this._item = item;
if (!isSCFloor(item) && hasValidLocation(item)) {
this.distance = timer(0, 10_000).pipe(
map(() => this.positionService.getDistance(item.geo.point)),
this.distance = this.positionService.watchCurrentLocation().pipe(
map(position =>
new LatLng(position.latitude, position.longitude).distanceTo(
geoJSON(item.geo.point).getBounds().getCenter(),
),
),
filter(it => it !== undefined),
);
}

View File

@@ -12,45 +12,29 @@
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ion-grid>
<ion-row>
<ion-col>
<div class="ion-text-wrap">
<ion-label class="title">{{ 'name' | thingTranslate: _item }}</ion-label>
<ng-container *ngIf="_item.type !== 'floor'">
<p class="title-sub" *ngIf="_item.openingHours">
<span>
<stapps-opening-hours [openingHours]="_item.openingHours"></stapps-opening-hours>
</span>
</p>
<p>
<ion-note *ngIf="_item.categories && _item.type !== 'building'; else onlyType">
<ion-label> {{ 'categories' | thingTranslate: _item | join: ', ' | titlecase }} </ion-label>
<ion-label *ngIf="distance | async as distance" class="distance">
<ion-icon name="directions_walk"></ion-icon>
{{ distance | metersLocalized }}
</ion-label>
</ion-note>
</p>
<ng-template #onlyType>
<ion-note>
<ion-label> {{ 'type' | thingTranslate: _item | titlecase }} </ion-label>
<ion-label *ngIf="distance | async as distance" class="distance">
<ion-icon name="directions_walk"></ion-icon>
{{ distance | metersLocalized }}
</ion-label>
</ion-note>
</ng-template>
</ng-container>
<p *ngIf="_item.description">{{ 'description' | thingTranslate: _item }}</p>
</div>
</ion-col>
<ng-container *ngIf="_item.type !== 'building'">
<ion-col size="auto" class="in-place" *ngIf="_item.inPlace">
<ion-icon name="pin_drop"></ion-icon
><ion-label>{{ 'name' | thingTranslate: _item.inPlace }}</ion-label>
</ion-col>
</ng-container>
</ion-row>
</ion-grid>
<ng-template #distanceView>
<ion-label class="distance" *ngIf="distance | async as distance;" @fade>
<ion-icon name="directions_walk"></ion-icon>
{{ distance | metersLocalized }}
</ion-label>
</ng-template>
<ion-label class="title">{{ 'name' | thingTranslate: _item }}</ion-label>
<ng-container *ngIf="_item.type !== 'floor'">
<stapps-opening-hours [openingHours]="_item.openingHours"></stapps-opening-hours>
<ion-note *ngIf="_item.categories && _item.type !== 'building'; else onlyType">
<ion-label> {{ 'categories' | thingTranslate: _item | join: ', ' | titlecase }} </ion-label>
<ng-container *ngTemplateOutlet="distanceView"></ng-container>
</ion-note>
<ng-template #onlyType>
<ion-note>
<ion-label> {{ 'type' | thingTranslate: _item | titlecase }} </ion-label>
<ng-container *ngTemplateOutlet="distanceView"></ng-container>
</ion-note>
</ng-template>
</ng-container>
<p *ngIf="_item.description">{{ 'description' | thingTranslate: _item }}</p>
<ng-container *ngIf="_item.type !== 'building'">
<ion-col size="auto" class="in-place" *ngIf="_item.inPlace">
<ion-icon name="pin_drop"></ion-icon><ion-label>{{ 'name' | thingTranslate: _item.inPlace }}</ion-label>
</ion-col>
</ng-container>

View File

@@ -33,3 +33,7 @@ ion-label + ion-label.distance::before {
align-items: center;
justify-content: flex-end;
}
stapps-opening-hours ::ng-deep div {
padding-block: var(--spacing-xs);
}