Skip to content

Commit 0ea02b9

Browse files
committed
Merge branch 'develop' into custom/HIO687AS
2 parents 695728c + e25c213 commit 0ea02b9

13 files changed

Lines changed: 167 additions & 44 deletions

File tree

apps/concierge/src/app/parking/parking-topbar.component.ts

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -127,33 +127,35 @@ import { ParkingStateService } from './parking-state.service';
127127
</a>
128128
</div>
129129
}
130-
<mat-form-field appearance="outline" class="no-subscript w-56">
131-
<mat-select
132-
[(ngModel)]="zones"
133-
(ngModelChange)="updateZones($event)"
134-
[placeholder]="'COMMON.LEVEL_ALL' | translate"
135-
multiple
136-
>
137-
@for (level of levels | async; track level) {
138-
<mat-option [value]="level.id">
139-
<div class="flex flex-col-reverse">
140-
@if (use_region) {
141-
<div class="text-xs opacity-30">
142-
{{
143-
(level.parent_id | building)
144-
?.display_name
145-
}}
146-
<span class="opacity-0"> - </span>
130+
@if (!is_requests_view()) {
131+
<mat-form-field appearance="outline" class="no-subscript w-56">
132+
<mat-select
133+
[(ngModel)]="zones"
134+
(ngModelChange)="updateZones($event)"
135+
[placeholder]="'COMMON.LEVEL_ALL' | translate"
136+
multiple
137+
>
138+
@for (level of levels | async; track level) {
139+
<mat-option [value]="level.id">
140+
<div class="flex flex-col-reverse">
141+
@if (use_region) {
142+
<div class="text-xs opacity-30">
143+
{{
144+
(level.parent_id | building)
145+
?.display_name
146+
}}
147+
<span class="opacity-0"> - </span>
148+
</div>
149+
}
150+
<div>
151+
{{ level.display_name || level.name }}
147152
</div>
148-
}
149-
<div>
150-
{{ level.display_name || level.name }}
151153
</div>
152-
</div>
153-
</mat-option>
154-
}
155-
</mat-select>
156-
</mat-form-field>
154+
</mat-option>
155+
}
156+
</mat-select>
157+
</mat-form-field>
158+
}
157159
<div class="w-px min-w-2 flex-1"></div>
158160
@if (section() === 'manage') {
159161
<button
@@ -261,11 +263,13 @@ export class ParkingTopbarComponent extends AsyncHandler implements OnInit {
261263
if (!this._router.url.includes('parking')) return;
262264
this._router.navigate([], {
263265
relativeTo: this._route,
264-
queryParams: { zone_ids: z.join(',') },
266+
queryParams: { zone_ids: z.length ? z.join(',') : null },
265267
queryParamsHandling: 'merge',
266268
});
267269
this._state.setOptions({ zones: z });
268270
};
271+
public readonly is_requests_view = () =>
272+
this.section() === 'events' && this.view() === 'requests';
269273

270274
public get use_region() {
271275
return !!this._settings.get('app.use_region');
@@ -288,6 +292,10 @@ export class ParkingTopbarComponent extends AsyncHandler implements OnInit {
288292
this.subscription(
289293
'route.query',
290294
this._route.queryParamMap.subscribe((params) => {
295+
if (this.is_requests_view()) {
296+
this.clearZones();
297+
return;
298+
}
291299
if (
292300
params.has('zone_ids') &&
293301
this._router.url.includes('parking')
@@ -309,6 +317,10 @@ export class ParkingTopbarComponent extends AsyncHandler implements OnInit {
309317
'levels',
310318
this._state.levels.pipe(debounceTime(100)).subscribe((levels) => {
311319
if (this.use_region) return;
320+
if (this.is_requests_view()) {
321+
this.clearZones();
322+
return;
323+
}
312324
this.zones = this.zones.filter((zone) =>
313325
levels.find((lvl) => lvl.id === zone),
314326
);
@@ -348,5 +360,33 @@ export class ParkingTopbarComponent extends AsyncHandler implements OnInit {
348360
const [section, view] = parts.slice(-2);
349361
this.section.set(section as any);
350362
this.view.set(view.split('?')[0] as any);
363+
if (this.is_requests_view()) {
364+
this.clearZones();
365+
return;
366+
}
367+
this.selectDefaultZoneForManage();
368+
}
369+
370+
private clearZones() {
371+
const has_query_param = this._route.snapshot.queryParamMap.has(
372+
'zone_ids',
373+
);
374+
if (!this.zones.length && !has_query_param) {
375+
this._state.setOptions({ zones: [] });
376+
return;
377+
}
378+
this.zones = [];
379+
this.updateZones([]);
380+
}
381+
382+
private async selectDefaultZoneForManage() {
383+
if (this.section() !== 'manage' || this.use_region || this.zones.length) {
384+
return;
385+
}
386+
const levels = await nextValueFrom(this.levels);
387+
const first_level = levels[0]?.id;
388+
if (!first_level) return;
389+
this.zones = [first_level];
390+
this.updateZones(this.zones);
351391
}
352392
}

libs/bookings/src/lib/booking-form.service.ts

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -584,13 +584,13 @@ export class BookingFormService extends AsyncHandler {
584584
details.loading(i18n('BOOKINGS.CONFIRM_LOADING'));
585585
if (options.group) {
586586
await this.postFormForGroup().catch((_) => {
587-
notifyError(JSON.stringify(_));
587+
notifyError(this._error_message(_));
588588
details.close();
589589
throw _;
590590
});
591591
} else
592592
await this.postForm().catch((_) => {
593-
notifyError(JSON.stringify(_));
593+
notifyError(this._error_message(_));
594594
details.close();
595595
throw _;
596596
});
@@ -778,7 +778,8 @@ export class BookingFormService extends AsyncHandler {
778778
if (!group) throw i18n('BOOKINGS.GROUP_NOT_SET');
779779
const rollback_on_group_error =
780780
this.setting('rollback_group_bookings') === true;
781-
const extra_members = members.filter(
781+
const member_list = members || [];
782+
const extra_members = member_list.filter(
782783
(_) => _.email !== currentUser().email,
783784
);
784785
if (extra_members.length <= 0) throw i18n('BOOKINGS.GROUP_NO_MEMBERS');
@@ -788,9 +789,12 @@ export class BookingFormService extends AsyncHandler {
788789
(_) => _.id === form.asset_id || _.map_id === form.asset_id,
789790
);
790791
if (!active_resource) {
791-
throw 'Selected resource is no longer available';
792+
throw i18n('BOOKINGS.DESK_AVAILABLE_ERROR');
792793
}
793794
const level = this._org.levelWithID([active_resource.zone?.id]);
795+
if (!level?.map_id) {
796+
throw i18n('BOOKINGS.GROUP_MAP_UNAVAILABLE');
797+
}
794798
const resources = [
795799
active_resource,
796800
...(await this._getNearbyResources(
@@ -804,18 +808,37 @@ export class BookingFormService extends AsyncHandler {
804808
[currentUser(), ...extra_members],
805809
'email',
806810
);
811+
if (resources.length < group_members.length) {
812+
throw i18n('BOOKINGS.GROUP_INSUFFICIENT_RESOURCES', {
813+
available: resources.length,
814+
members: group_members.length,
815+
});
816+
}
817+
const unavailable_errors: string[] = [];
807818
const available = await Promise.all(
808-
group_members.map((_, idx) => {
819+
group_members.map(async (member, idx) => {
809820
const resource = resources[idx];
810-
if (!resource) return false;
811-
return this._checkResourceAvailable(
812-
{
813-
...form,
814-
asset_id: resource.map_id || resource.id,
815-
user_email: _.email,
816-
},
817-
type,
818-
);
821+
if (!resource) {
822+
unavailable_errors.push(
823+
`${member.name || member.email}: ${i18n('BOOKINGS.GROUP_MEMBER_NO_RESOURCE')}`,
824+
);
825+
return false;
826+
}
827+
try {
828+
return await this._checkResourceAvailable(
829+
{
830+
...form,
831+
asset_id: resource.map_id || resource.id,
832+
user_email: member.email,
833+
},
834+
type,
835+
);
836+
} catch (error) {
837+
unavailable_errors.push(
838+
`${member.name || member.email}: ${this._error_message(error)}`,
839+
);
840+
return false;
841+
}
819842
}),
820843
);
821844
const unavailable = group_members.filter((_, idx) => !available[idx]);
@@ -858,27 +881,43 @@ export class BookingFormService extends AsyncHandler {
858881
: [this._org.organisation.id, this._org.region?.id]
859882
).filter((_) => _),
860883
});
861-
const bkn = await this.postForm(true);
884+
const bkn = await this.postForm(true).catch((error) => {
885+
throw `${user.name || user.email}: ${this._error_message(error)}`;
886+
});
862887
if (bkn?.id) booking_ids.push(bkn.id);
863888
if (bkn.id && !id) id = bkn.id;
864889
if (bkn.user_email === currentUser().email) user_booking = bkn;
865890
}
866891
if (unavailable.length) {
892+
const unavailable_error = unavailable_errors.length
893+
? unavailable_errors.join('\n')
894+
: group_error;
867895
if (rollback_on_group_error) {
868896
await this.rollbackGroupBookings(booking_ids);
869-
throw group_error;
897+
throw unavailable_error;
870898
}
871-
notifyWarn(group_error);
899+
notifyWarn(unavailable_error);
872900
}
873901
} catch (error) {
874902
if (rollback_on_group_error && booking_ids.length) {
875903
await this.rollbackGroupBookings(booking_ids);
876904
}
877-
throw error;
905+
throw this._error_message(error);
878906
}
879907
return user_booking;
880908
}
881909

910+
private _error_message(error: any) {
911+
if (typeof error === 'string') return error;
912+
if (error instanceof Error && error.message) return error.message;
913+
if (typeof error?.error === 'string') return error.error;
914+
if (typeof error?.message === 'string') return error.message;
915+
if (typeof error?.error?.message === 'string') {
916+
return error.error.message;
917+
}
918+
return i18n('BOOKINGS.ERROR_GENERIC');
919+
}
920+
882921
private async rollbackGroupBookings(booking_ids: string[]) {
883922
const rollback_errors = (
884923
await Promise.allSettled(

shared/assets/locale/ar.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@
521521
"GROUP_NOT_SET": "لا توجد مجموعة متاحة للحجز لها.",
522522
"GROUP_NO_MEMBERS": "لا يوجد أعضاء في مجموعتك للحجز لهم.",
523523
"GROUP_SOME_HAVE_BOOKINGS": "بعض أعضاء مجموعتك لديهم بالفعل حجز مكتب. [{{ members }}]",
524+
"GROUP_MAP_UNAVAILABLE": "تعذر العثور على خريطة للمورد المحدد.",
525+
"GROUP_INSUFFICIENT_RESOURCES": "يتوفر فقط {{ available }} من الموارد القريبة لـ {{ members }} من أعضاء المجموعة.",
526+
"GROUP_MEMBER_NO_RESOURCE": "لا يوجد مورد قريب متاح.",
527+
"ERROR_GENERIC": "فشل إكمال طلب الحجز.",
524528
"NO_USER": "لم يتم تحديد مستخدم للحجز له.",
525529
"VISITOR_BOOKED": "{{ name }} لديه بالفعل دعوة للوقت المحدد.",
526530
"RESOURCE_BOOKED": "{{ name }} غير متاح في الوقت المحدد",

shared/assets/locale/en-AU.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,10 @@
563563
"GROUP_NOT_SET": "No group available to book for.",
564564
"GROUP_NO_MEMBERS": "No members in your group to book for.",
565565
"GROUP_SOME_HAVE_BOOKINGS": "Some members of your group already have a desk booking. [{{ members }}]",
566+
"GROUP_MAP_UNAVAILABLE": "Unable to locate a map for the selected resource.",
567+
"GROUP_INSUFFICIENT_RESOURCES": "Only {{ available }} nearby resources are available for {{ members }} group members.",
568+
"GROUP_MEMBER_NO_RESOURCE": "No nearby resource is available.",
569+
"ERROR_GENERIC": "Failed to complete booking request.",
566570
"NO_USER": "No user was selected to book for.",
567571
"VISITOR_BOOKED": "{{ name }} already has an invite for the selected time.",
568572
"RESOURCE_BOOKED": "{{ name }} is not available at the selected time",

shared/assets/locale/en-GB.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,10 @@
520520
"GROUP_NOT_SET": "No group available to book for.",
521521
"GROUP_NO_MEMBERS": "No members in your group to book for.",
522522
"GROUP_SOME_HAVE_BOOKINGS": "Some members of your group already have a desk booking. [{{ members }}]",
523+
"GROUP_MAP_UNAVAILABLE": "Unable to locate a map for the selected resource.",
524+
"GROUP_INSUFFICIENT_RESOURCES": "Only {{ available }} nearby resources are available for {{ members }} group members.",
525+
"GROUP_MEMBER_NO_RESOURCE": "No nearby resource is available.",
526+
"ERROR_GENERIC": "Failed to complete booking request.",
523527
"NO_USER": "No user was selected to book for.",
524528
"VISITOR_BOOKED": "{{ name }} already has an invite for the selected time.",
525529
"RESOURCE_BOOKED": "{{ name }} is not available at the selected time",

shared/assets/locale/en-US.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,10 @@
520520
"GROUP_NOT_SET": "No group available to book for.",
521521
"GROUP_NO_MEMBERS": "No members in your group to book for.",
522522
"GROUP_SOME_HAVE_BOOKINGS": "Some members of your group already have a desk booking. [{{ members }}]",
523+
"GROUP_MAP_UNAVAILABLE": "Unable to locate a map for the selected resource.",
524+
"GROUP_INSUFFICIENT_RESOURCES": "Only {{ available }} nearby resources are available for {{ members }} group members.",
525+
"GROUP_MEMBER_NO_RESOURCE": "No nearby resource is available.",
526+
"ERROR_GENERIC": "Failed to complete booking request.",
523527
"NO_USER": "No user was selected to book for.",
524528
"VISITOR_BOOKED": "{{ name }} already has an invite for the selected time.",
525529
"RESOURCE_BOOKED": "{{ name }} is not available at the selected time",

shared/assets/locale/es.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@
521521
"GROUP_NOT_SET": "No hay grupo disponible para reservar.",
522522
"GROUP_NO_MEMBERS": "No hay miembros en su grupo para los que reservar.",
523523
"GROUP_SOME_HAVE_BOOKINGS": "Algunos miembros de su grupo ya tienen una reserva de escritorio. [{{ members }}]",
524+
"GROUP_MAP_UNAVAILABLE": "No se puede encontrar un mapa para el recurso seleccionado.",
525+
"GROUP_INSUFFICIENT_RESOURCES": "Solo hay {{ available }} recursos cercanos disponibles para {{ members }} miembros del grupo.",
526+
"GROUP_MEMBER_NO_RESOURCE": "No hay recursos cercanos disponibles.",
527+
"ERROR_GENERIC": "No se pudo completar la solicitud de reserva.",
524528
"NO_USER": "No se seleccionó ningún usuario para el que reservar.",
525529
"VISITOR_BOOKED": "{{ name }} ya tiene una invitación para la hora seleccionada.",
526530
"RESOURCE_BOOKED": "{{ name }} no está disponible a la hora seleccionada",

shared/assets/locale/fr-CA.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@
521521
"GROUP_NOT_SET": "Aucun groupe disponible pour la réservation.",
522522
"GROUP_NO_MEMBERS": "Aucun membre dans votre groupe pour lequel réserver.",
523523
"GROUP_SOME_HAVE_BOOKINGS": "Certains membres de votre groupe ont déjà une réservation de bureau. [{{ members }}]",
524+
"GROUP_MAP_UNAVAILABLE": "Impossible de localiser une carte pour la ressource sélectionnée.",
525+
"GROUP_INSUFFICIENT_RESOURCES": "Seulement {{ available }} ressources à proximité sont disponibles pour {{ members }} membres du groupe.",
526+
"GROUP_MEMBER_NO_RESOURCE": "Aucune ressource à proximité n'est disponible.",
527+
"ERROR_GENERIC": "Échec de la finalisation de la demande de réservation.",
524528
"NO_USER": "Aucun utilisateur n'a été sélectionné pour la réservation.",
525529
"VISITOR_BOOKED": "{{ name }} a déjà une invitation pour l'heure sélectionnée.",
526530
"RESOURCE_BOOKED": "{{ name }} n'est pas disponible à l'heure sélectionnée",

shared/assets/locale/fr.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@
521521
"GROUP_NOT_SET": "Aucun groupe disponible pour réserver.",
522522
"GROUP_NO_MEMBERS": "Aucun membre dans votre groupe pour réserver.",
523523
"GROUP_SOME_HAVE_BOOKINGS": "Certains membres de votre groupe ont déjà une réservation de bureau. [{{ members }}]",
524+
"GROUP_MAP_UNAVAILABLE": "Impossible de localiser une carte pour la ressource sélectionnée.",
525+
"GROUP_INSUFFICIENT_RESOURCES": "Seulement {{ available }} ressources à proximité sont disponibles pour {{ members }} membres du groupe.",
526+
"GROUP_MEMBER_NO_RESOURCE": "Aucune ressource à proximité n'est disponible.",
527+
"ERROR_GENERIC": "Échec de la finalisation de la demande de réservation.",
524528
"NO_USER": "Aucun utilisateur n’a été sélectionné pour la réservation.",
525529
"VISITOR_BOOKED": "{{ name }} a déjà une invitation pour la plage horaire sélectionnée.",
526530
"RESOURCE_BOOKED": "{{ name }} n’est pas disponible pour la plage horaire sélectionnée",

shared/assets/locale/it.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,10 @@
521521
"GROUP_NOT_SET": "Nessun gruppo disponibile per la prenotazione.",
522522
"GROUP_NO_MEMBERS": "Nessun membro nel tuo gruppo da prenotare.",
523523
"GROUP_SOME_HAVE_BOOKINGS": "Alcuni membri del tuo gruppo hanno già una prenotazione per la scrivania. [{{ members }}]",
524+
"GROUP_MAP_UNAVAILABLE": "Impossibile trovare una mappa per la risorsa selezionata.",
525+
"GROUP_INSUFFICIENT_RESOURCES": "Sono disponibili solo {{ available }} risorse vicine per {{ members }} membri del gruppo.",
526+
"GROUP_MEMBER_NO_RESOURCE": "Non è disponibile alcuna risorsa nelle vicinanze.",
527+
"ERROR_GENERIC": "Impossibile completare la richiesta di prenotazione.",
524528
"NO_USER": "Nessun utente è stato selezionato per la prenotazione.",
525529
"VISITOR_BOOKED": "{{ name }} ha già un invito per l'orario selezionato.",
526530
"RESOURCE_BOOKED": "{{ name }} non è disponibile per l'orario selezionato",

0 commit comments

Comments
 (0)