From ca7abb4902cf1e0f55eba9a494905ec058b040a0 Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Wed, 24 Jun 2026 20:25:58 +1000 Subject: [PATCH 01/62] fix(bookings): combine booked with clashes for availability --- libs/bookings/src/lib/booking-form.service.ts | 33 +++++---- .../src/test/booking-form.service.spec.ts | 68 +++++++++---------- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/libs/bookings/src/lib/booking-form.service.ts b/libs/bookings/src/lib/booking-form.service.ts index 01de81051c..4e3f644ff8 100644 --- a/libs/bookings/src/lib/booking-form.service.ts +++ b/libs/bookings/src/lib/booking-form.service.ts @@ -562,22 +562,29 @@ export class BookingFormService extends AsyncHandler { this._org.organisation.id; let booked_ids: string[] = []; if (!isMock()) { - booked_ids = + // Always exclude resources booked in the first-instance window. + booked_ids = await this._bookedResourceList( + { + period_start: getUnixTime(date), + period_end: getUnixTime(addMinutes(date, duration)), + type: options.type, + zones, + }, + resources.length, + ); + // For recurring desk bookings, also exclude any resource that + // clashes with a later instance in the series. Union with (not + // replace) the base booked list so enabling recurrence can only + // remove availability, never add it back. + if ( options.type === 'desk' && raw.recurrence_type && raw.recurrence_type !== 'none' - ? await this._recurringBookedResourceList(resources, zones) - : await this._bookedResourceList( - { - period_start: getUnixTime(date), - period_end: getUnixTime( - addMinutes(date, duration), - ), - type: options.type, - zones, - }, - resources.length, - ); + ) { + const recurring_clashes = + await this._recurringBookedResourceList(resources, zones); + booked_ids = unique([...booked_ids, ...recurring_clashes]); + } } this._resource_use = {}; for (const id of booked_ids) { diff --git a/libs/bookings/src/test/booking-form.service.spec.ts b/libs/bookings/src/test/booking-form.service.spec.ts index 6e81f2b66c..46064399d0 100644 --- a/libs/bookings/src/test/booking-form.service.spec.ts +++ b/libs/bookings/src/test/booking-form.service.spec.ts @@ -305,38 +305,25 @@ describe('BookingFormService', () => { it.todo('should list available assets'); - it('should exclude desks that clash with any recurring instance', async () => { - jest.mocked(ts_client.listChildMetadata).mockResolvedValue([ - { - metadata: { - desks: { - details: [ - { - id: 'desk-1', - name: 'Desk 1', - features: [], - }, - { - id: 'desk-2', - name: 'Desk 2', - features: [], - }, - ], - }, - }, - zone: { id: 'lvl-1', parent_id: 'bld-1' }, - }, - ] as any); - jest.mocked(ts_client.showMetadata).mockResolvedValue({ - id: 'bld-1', - details: [], - } as any); + it('should exclude window-booked AND recurring-clash desks', async () => { + // desk-1 is booked in the first-instance window, desk-2 clashes with a + // later recurrence instance. Enabling recurrence must exclude both, not + // replace the window-booked query and let desk-1 re-appear. (booking_mod as any).bookedResourceList = jest.fn(() => - Promise.resolve([]), + Promise.resolve(['desk-1']), ); (booking_mod as any).findBookingClashes = jest.fn(() => Promise.resolve(['desk-2']), ); + const desks = ['desk-1', 'desk-2', 'desk-3'].map((id) => ({ + id, + name: id, + bookable: true, + features: [], + zone: { id: 'lvl-1', parent_id: 'bld-1' }, + })); + // The recurring clash query reads the live form model (== `raw` in + // production via the debounced value), so seed the recurrence there too. spectator.service.model.update((m) => ({ ...m, date: new Date(2028, 5, 15, 15, 0, 0).valueOf(), @@ -348,22 +335,29 @@ describe('BookingFormService', () => { ), })); - const clashes = await ( + const available = await ( spectator.service as any - )._recurringBookedResourceList( - [ - { id: 'desk-1', zone: { id: 'lvl-1' } }, - { id: 'desk-2', zone: { id: 'lvl-1' } }, - ], - 'bld-1', + )._computeAvailableResources( + { type: 'desk' }, + desks, + {}, + { + date: new Date(2028, 5, 15, 15, 0, 0).valueOf(), + duration: 60, + recurrence_type: 'daily', + recurrence_interval: 1, + recurrence_end: Math.floor( + new Date(2028, 5, 18, 23, 59, 59).valueOf() / 1000, + ), + }, ); - expect(clashes).toEqual(['desk-2']); + expect(booking_mod.bookedResourceList).toHaveBeenCalled(); const clash_booking = (booking_mod.findBookingClashes as jest.Mock).mock .calls[0][0]; - expect(clash_booking.asset_ids).toEqual(['desk-1', 'desk-2']); + expect(clash_booking.asset_ids).toEqual(['desk-1', 'desk-2', 'desk-3']); expect(clash_booking.recurrence_type).toBe('daily'); - expect(booking_mod.bookedResourceList).not.toHaveBeenCalled(); + expect(available.map((_: any) => _.id)).toEqual(['desk-3']); }); it.todo('should allow filtering of available assets'); From 663571cd2528c700423463659c00cda07f0e6cc3 Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Wed, 24 Jun 2026 23:07:21 +1000 Subject: [PATCH 02/62] fix(workplace): tweak level selection when on map view --- .../desk-flow-select.component.ts | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/apps/workplace/src/app/book/desk-flow-new/desk-flow-select.component.ts b/apps/workplace/src/app/book/desk-flow-new/desk-flow-select.component.ts index 7c37255320..869df5212c 100644 --- a/apps/workplace/src/app/book/desk-flow-new/desk-flow-select.component.ts +++ b/apps/workplace/src/app/book/desk-flow-new/desk-flow-select.component.ts @@ -803,30 +803,24 @@ export class DeskFlowSelectComponent extends AsyncHandler implements OnInit { .map((resource) => resource.zone?.id) .filter((_) => _), ); - const viewable_levels = level_list.filter( - (lvl) => !lvl.tags.includes('parking') && level_ids.has(lvl.id), - ); - if (viewable_levels.length) { - const current_zones = this.options_value()?.zones || []; - const current_zone_valid = current_zones.some((zone) => - viewable_levels.some((lvl) => lvl.id === zone), + const viewable_levels = level_list + .filter( + (lvl) => !lvl.tags.includes('parking') && level_ids.has(lvl.id), + ) + .sort( + (a, b) => + a.parent_id.localeCompare(b.parent_id) || + (a.display_name || '').localeCompare(b.display_name || ''), ); - if (current_zone_valid) { - const valid_zone = current_zones.find((zone) => - viewable_levels.some((lvl) => lvl.id === zone), - ); - this.setOptions({ zones: [valid_zone] }); - } else { - const first_level = viewable_levels.sort( - (a, b) => - a.parent_id.localeCompare(b.parent_id) || - (a.display_name || '').localeCompare( - b.display_name || '', - ), - )[0]; - this.setOptions({ zones: [first_level.id] }); - } - } + if (!viewable_levels.length) return; + // The zone filters are hidden on the map, so apply the user's selected + // level as the map's active level. Prefer a level they already picked in + // the list filters, otherwise default to the first viewable level. + const current_zones = this.options_value()?.zones || []; + const active_level = + viewable_levels.find((lvl) => current_zones.includes(lvl.id)) || + viewable_levels[0]; + this.setOptions({ zones: [active_level.id], zone_id: active_level.id }); } public toggleDesk(space: Space) { From 2d7d7b43193117a08ccd3f23b903ae1236fde3f8 Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Wed, 24 Jun 2026 23:35:04 +1000 Subject: [PATCH 03/62] fix(workplace): fix z-indexing of layout items on mobile --- .../src/app/components/footer-menu.component.ts | 17 ++++++++--------- .../virtual-concierge-button.component.ts | 2 +- .../app/landing-new/landing-new.component.ts | 2 +- .../team-schedule/team-schedule.component.ts | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/workplace/src/app/components/footer-menu.component.ts b/apps/workplace/src/app/components/footer-menu.component.ts index 4c1253d514..e05c933468 100644 --- a/apps/workplace/src/app/components/footer-menu.component.ts +++ b/apps/workplace/src/app/components/footer-menu.component.ts @@ -168,7 +168,7 @@ const FEATURE_MENU_ITEMS: FooterMenuItem[] = [ } @if (footer_item_count() > 1) {
}
From ca32cf7e29d7505f49702434010497a5171e2a4c Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Thu, 25 Jun 2026 10:36:12 +1000 Subject: [PATCH 05/62] fix(concierge): fix bookings modals not have the model passed to the form --- apps/concierge/src/app/desks/desk-book-modal.component.ts | 2 +- .../src/app/parking/parking-request-modal.component.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/concierge/src/app/desks/desk-book-modal.component.ts b/apps/concierge/src/app/desks/desk-book-modal.component.ts index 58c4d4e190..bc9ae773b8 100644 --- a/apps/concierge/src/app/desks/desk-book-modal.component.ts +++ b/apps/concierge/src/app/desks/desk-book-modal.component.ts @@ -32,7 +32,7 @@ import { NewDeskFormDetailsComponent } from 'apps/workplace/src/app/book/desk-fl " (confirm)="save()" > - + `, styles: [``], diff --git a/apps/concierge/src/app/parking/parking-request-modal.component.ts b/apps/concierge/src/app/parking/parking-request-modal.component.ts index 2ae64360dd..10f94a087c 100644 --- a/apps/concierge/src/app/parking/parking-request-modal.component.ts +++ b/apps/concierge/src/app/parking/parking-request-modal.component.ts @@ -68,6 +68,7 @@ import { ParkingRequestFormDetailsComponent } from '../../../../workplace/src/ap Date: Thu, 25 Jun 2026 10:41:47 +1000 Subject: [PATCH 06/62] chore(workplace): tweak styles for parking request form on mobile --- .../parking-request-form.component.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/workplace/src/app/book/parking-request-flow/parking-request-form.component.ts b/apps/workplace/src/app/book/parking-request-flow/parking-request-form.component.ts index ff03509e3f..69517dedec 100644 --- a/apps/workplace/src/app/book/parking-request-flow/parking-request-form.component.ts +++ b/apps/workplace/src/app/book/parking-request-flow/parking-request-form.component.ts @@ -28,7 +28,7 @@ import { ParkingRequestFormDetailsComponent } from './parking-request-form-detai class="bg-base-200 relative z-0 flex h-full w-full flex-col overflow-auto" >
+
+ @for (option of view_options; track option.mode) { + + } +
-
+
-
+
( + 'grid', + ); public readonly managed_group_id = signal(''); // Switching managed group refetches its users and zones; debounce so quick // re-selection doesn't fire a pair of queries per change. diff --git a/shared/assets/locale/ar.json b/shared/assets/locale/ar.json index 42d2dcd52d..eff9b39354 100644 --- a/shared/assets/locale/ar.json +++ b/shared/assets/locale/ar.json @@ -2275,10 +2275,11 @@ "MEDIA_PREVIEW": "معاينة الوسائط", "MEDIA_SAVE_ERROR": "فشل حفظ عنصر الوسائط. الخطأ: {{ error }}", "MEDIA_SAVE_SUCCESS": "تم حفظ عنصر الوسائط.", - "MEDIA_SEARCH": "بحث", "MEDIA_SAVING": "جار حفظ عنصر الوسائط...", + "MEDIA_SEARCH": "بحث", "MEDIA_TITLE": "وسائط اللافتات", "MEDIA_URL_ARIA": "رابط الوسائط", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "الرسالة", "MINUTES_BETWEEN_PLAYS": "الدقائق بين مرات التشغيل", "MONTHLY": "شهري", @@ -2536,6 +2537,7 @@ "UNDOING_CHANGES": "جار التراجع عن التغييرات...", "UNDO_CHANGES": "التراجع عن التغييرات", "UNNAMED_GROUP": "مجموعة بلا اسم", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "أوقات التشغيل القادمة", "UPLOAD_MEDIA": "تحميل وسائط", "UPLOAD_MEDIA_ARIA": "تحميل ملف وسائط", @@ -2547,6 +2549,9 @@ "USER_PERMISSIONS": "أذونات المستخدم", "VALID_FROM": "صالح من", "VERSION_TO_APPROVE": "الإصدار المطلوب اعتماده", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "رابط صفحة الويب", "WEEKDAYS": "أيام العمل", "WEEKLY": "أسبوعي", diff --git a/shared/assets/locale/en-AU.json b/shared/assets/locale/en-AU.json index 5833d98dba..ffbe9c15b9 100644 --- a/shared/assets/locale/en-AU.json +++ b/shared/assets/locale/en-AU.json @@ -145,6 +145,7 @@ "MEDIA_SEARCH": "Search", "MEDIA_TITLE": "Signage Media", "MEDIA_URL_ARIA": "Media URL", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Message", "MINUTES_BETWEEN_PLAYS": "Minutes between plays", "MONTHLY": "Monthly", @@ -405,6 +406,7 @@ "UNDOING_CHANGES": "Undoing changes...", "UNDO_CHANGES": "Undo Changes", "UNNAMED_GROUP": "Unnamed group", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Upcoming Play Times", "UPLOAD_MEDIA": "Upload media", "UPLOAD_MEDIA_ARIA": "Upload media file", @@ -416,6 +418,9 @@ "USER_PERMISSIONS": "User permissions", "VALID_FROM": "Valid From", "VERSION_TO_APPROVE": "Version to approve", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "Webpage URL", "WEEKDAYS": "Weekdays", "WEEKLY": "Weekly", diff --git a/shared/assets/locale/en-GB.json b/shared/assets/locale/en-GB.json index ad6f86efa0..6524dd7ed7 100644 --- a/shared/assets/locale/en-GB.json +++ b/shared/assets/locale/en-GB.json @@ -2337,6 +2337,7 @@ "MEDIA_SEARCH": "Search", "MEDIA_TITLE": "Signage Media", "MEDIA_URL_ARIA": "Media URL", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Message", "MINUTES_BETWEEN_PLAYS": "Minutes between plays", "MONTHLY": "Monthly", @@ -2594,6 +2595,7 @@ "UNDOING_CHANGES": "Undoing changes...", "UNDO_CHANGES": "Undo Changes", "UNNAMED_GROUP": "Unnamed group", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Upcoming Play Times", "UPLOAD_MEDIA": "Upload media", "UPLOAD_MEDIA_ARIA": "Upload media file", @@ -2605,6 +2607,9 @@ "USER_PERMISSIONS": "User permissions", "VALID_FROM": "Valid From", "VERSION_TO_APPROVE": "Version to approve", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "Webpage URL", "WEEKDAYS": "Weekdays", "WEEKLY": "Weekly", diff --git a/shared/assets/locale/en-US.json b/shared/assets/locale/en-US.json index 91a2cb152e..3a18466cf8 100644 --- a/shared/assets/locale/en-US.json +++ b/shared/assets/locale/en-US.json @@ -2337,6 +2337,7 @@ "MEDIA_SEARCH": "Search", "MEDIA_TITLE": "Signage Media", "MEDIA_URL_ARIA": "Media URL", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Message", "MINUTES_BETWEEN_PLAYS": "Minutes between plays", "MONTHLY": "Monthly", @@ -2594,6 +2595,7 @@ "UNDOING_CHANGES": "Undoing changes...", "UNDO_CHANGES": "Undo Changes", "UNNAMED_GROUP": "Unnamed group", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Upcoming Play Times", "UPLOAD_MEDIA": "Upload media", "UPLOAD_MEDIA_ARIA": "Upload media file", @@ -2605,6 +2607,9 @@ "USER_PERMISSIONS": "User permissions", "VALID_FROM": "Valid From", "VERSION_TO_APPROVE": "Version to approve", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "Webpage URL", "WEEKDAYS": "Weekdays", "WEEKLY": "Weekly", diff --git a/shared/assets/locale/es.json b/shared/assets/locale/es.json index 6dcacfc237..543ecf24f8 100644 --- a/shared/assets/locale/es.json +++ b/shared/assets/locale/es.json @@ -2281,10 +2281,11 @@ "MEDIA_PREVIEW": "Vista previa del medio", "MEDIA_SAVE_ERROR": "No se pudo guardar el elemento multimedia. Error: {{ error }}", "MEDIA_SAVE_SUCCESS": "Elemento multimedia guardado correctamente.", - "MEDIA_SEARCH": "Buscar", "MEDIA_SAVING": "Guardando elemento multimedia...", + "MEDIA_SEARCH": "Buscar", "MEDIA_TITLE": "Medios de señalización", "MEDIA_URL_ARIA": "URL del medio", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Mensaje", "MINUTES_BETWEEN_PLAYS": "Minutos entre reproducciones", "MONTHLY": "Mensual", @@ -2542,6 +2543,7 @@ "UNDOING_CHANGES": "Deshaciendo cambios...", "UNDO_CHANGES": "Deshacer cambios", "UNNAMED_GROUP": "Grupo sin nombre", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Próximas reproducciones", "UPLOAD_MEDIA": "Subir medio", "UPLOAD_MEDIA_ARIA": "Subir archivo multimedia", @@ -2553,6 +2555,9 @@ "USER_PERMISSIONS": "Permisos de usuario", "VALID_FROM": "Válido desde", "VERSION_TO_APPROVE": "Versión para aprobar", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "URL de la página web", "WEEKDAYS": "Días laborables", "WEEKLY": "Semanal", diff --git a/shared/assets/locale/fr-CA.json b/shared/assets/locale/fr-CA.json index d36020bfe1..9d4a2a4637 100644 --- a/shared/assets/locale/fr-CA.json +++ b/shared/assets/locale/fr-CA.json @@ -2275,10 +2275,11 @@ "MEDIA_PREVIEW": "Aperçu du média", "MEDIA_SAVE_ERROR": "Impossible d'enregistrer l'élément média. Erreur : {{ error }}", "MEDIA_SAVE_SUCCESS": "Élément média enregistré.", - "MEDIA_SEARCH": "Rechercher", "MEDIA_SAVING": "Enregistrement de l'élément média...", + "MEDIA_SEARCH": "Rechercher", "MEDIA_TITLE": "Médias d'affichage", "MEDIA_URL_ARIA": "URL du média", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Message", "MINUTES_BETWEEN_PLAYS": "Minutes entre les lectures", "MONTHLY": "Mensuel", @@ -2536,6 +2537,7 @@ "UNDOING_CHANGES": "Annulation des changements...", "UNDO_CHANGES": "Annuler les changements", "UNNAMED_GROUP": "Groupe sans nom", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Lectures à venir", "UPLOAD_MEDIA": "Téléverser un média", "UPLOAD_MEDIA_ARIA": "Téléverser un fichier média", @@ -2547,6 +2549,9 @@ "USER_PERMISSIONS": "Autorisations de l'utilisateur", "VALID_FROM": "Valide à partir du", "VERSION_TO_APPROVE": "Version à approuver", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "URL de la page web", "WEEKDAYS": "Jours de semaine", "WEEKLY": "Hebdomadaire", diff --git a/shared/assets/locale/fr.json b/shared/assets/locale/fr.json index 4631021cba..e4e6b99660 100644 --- a/shared/assets/locale/fr.json +++ b/shared/assets/locale/fr.json @@ -2275,10 +2275,11 @@ "MEDIA_PREVIEW": "Aperçu du média", "MEDIA_SAVE_ERROR": "Impossible d'enregistrer l'élément média. Erreur : {{ error }}", "MEDIA_SAVE_SUCCESS": "Élément média enregistré.", - "MEDIA_SEARCH": "Rechercher", "MEDIA_SAVING": "Enregistrement de l'élément média...", + "MEDIA_SEARCH": "Rechercher", "MEDIA_TITLE": "Médias d'affichage", "MEDIA_URL_ARIA": "URL du média", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Message", "MINUTES_BETWEEN_PLAYS": "Minutes entre les lectures", "MONTHLY": "Mensuel", @@ -2536,6 +2537,7 @@ "UNDOING_CHANGES": "Annulation des changements...", "UNDO_CHANGES": "Annuler les changements", "UNNAMED_GROUP": "Groupe sans nom", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Lectures à venir", "UPLOAD_MEDIA": "Téléverser un média", "UPLOAD_MEDIA_ARIA": "Téléverser un fichier média", @@ -2547,6 +2549,9 @@ "USER_PERMISSIONS": "Autorisations de l'utilisateur", "VALID_FROM": "Valide à partir du", "VERSION_TO_APPROVE": "Version à approuver", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "URL de la page web", "WEEKDAYS": "Jours de semaine", "WEEKLY": "Hebdomadaire", diff --git a/shared/assets/locale/it.json b/shared/assets/locale/it.json index bc671124e3..72ca60f038 100644 --- a/shared/assets/locale/it.json +++ b/shared/assets/locale/it.json @@ -2275,10 +2275,11 @@ "MEDIA_PREVIEW": "Anteprima media", "MEDIA_SAVE_ERROR": "Impossibile salvare l'elemento multimediale. Errore: {{ error }}", "MEDIA_SAVE_SUCCESS": "Elemento multimediale salvato.", - "MEDIA_SEARCH": "Cerca", "MEDIA_SAVING": "Salvataggio elemento multimediale...", + "MEDIA_SEARCH": "Cerca", "MEDIA_TITLE": "Media di segnaletica", "MEDIA_URL_ARIA": "URL media", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Messaggio", "MINUTES_BETWEEN_PLAYS": "Minuti tra le riproduzioni", "MONTHLY": "Mensile", @@ -2536,6 +2537,7 @@ "UNDOING_CHANGES": "Annullamento modifiche...", "UNDO_CHANGES": "Annulla modifiche", "UNNAMED_GROUP": "Gruppo senza nome", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Riproduzioni imminenti", "UPLOAD_MEDIA": "Carica media", "UPLOAD_MEDIA_ARIA": "Carica file multimediale", @@ -2547,6 +2549,9 @@ "USER_PERMISSIONS": "Autorizzazioni utente", "VALID_FROM": "Valido dal", "VERSION_TO_APPROVE": "Versione da approvare", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "URL pagina web", "WEEKDAYS": "Giorni feriali", "WEEKLY": "Settimanale", diff --git a/shared/assets/locale/ja-JP.json b/shared/assets/locale/ja-JP.json index 0bdd8df947..8d1ab05750 100644 --- a/shared/assets/locale/ja-JP.json +++ b/shared/assets/locale/ja-JP.json @@ -2274,10 +2274,11 @@ "MEDIA_PREVIEW": "メディアプレビュー", "MEDIA_SAVE_ERROR": "メディア項目の保存に失敗しました。エラー: {{ error }}", "MEDIA_SAVE_SUCCESS": "メディア項目を保存しました。", - "MEDIA_SEARCH": "検索", "MEDIA_SAVING": "メディア項目を保存中...", + "MEDIA_SEARCH": "検索", "MEDIA_TITLE": "サイネージメディア", "MEDIA_URL_ARIA": "メディア URL", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "メッセージ", "MINUTES_BETWEEN_PLAYS": "再生間隔(分)", "MONTHLY": "毎月", @@ -2535,6 +2536,7 @@ "UNDOING_CHANGES": "変更を元に戻しています...", "UNDO_CHANGES": "変更を元に戻す", "UNNAMED_GROUP": "名前のないグループ", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "今後の再生時間", "UPLOAD_MEDIA": "メディアをアップロード", "UPLOAD_MEDIA_ARIA": "メディアファイルをアップロード", @@ -2546,6 +2548,9 @@ "USER_PERMISSIONS": "ユーザー権限", "VALID_FROM": "有効開始日", "VERSION_TO_APPROVE": "承認するバージョン", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "Web ページ URL", "WEEKDAYS": "平日", "WEEKLY": "毎週", diff --git a/shared/assets/locale/pt.json b/shared/assets/locale/pt.json index 18355d0196..d5af7c1614 100644 --- a/shared/assets/locale/pt.json +++ b/shared/assets/locale/pt.json @@ -2275,10 +2275,11 @@ "MEDIA_PREVIEW": "Pré-visualização de multimédia", "MEDIA_SAVE_ERROR": "Não foi possível guardar o item multimédia. Erro: {{ error }}", "MEDIA_SAVE_SUCCESS": "Item multimédia guardado.", - "MEDIA_SEARCH": "Pesquisar", "MEDIA_SAVING": "A guardar item multimédia...", + "MEDIA_SEARCH": "Pesquisar", "MEDIA_TITLE": "Multimédia de sinalização", "MEDIA_URL_ARIA": "URL da multimédia", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "Mensagem", "MINUTES_BETWEEN_PLAYS": "Minutos entre reproduções", "MONTHLY": "Mensal", @@ -2536,6 +2537,7 @@ "UNDOING_CHANGES": "A anular alterações...", "UNDO_CHANGES": "Anular alterações", "UNNAMED_GROUP": "Grupo sem nome", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "Próximas reproduções", "UPLOAD_MEDIA": "Carregar multimédia", "UPLOAD_MEDIA_ARIA": "Carregar ficheiro multimédia", @@ -2547,6 +2549,9 @@ "USER_PERMISSIONS": "Permissões do utilizador", "VALID_FROM": "Válido desde", "VERSION_TO_APPROVE": "Versão a aprovar", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "URL da página web", "WEEKDAYS": "Dias úteis", "WEEKLY": "Semanal", diff --git a/shared/assets/locale/zh.json b/shared/assets/locale/zh.json index 06cfcdf808..74f6daceb8 100644 --- a/shared/assets/locale/zh.json +++ b/shared/assets/locale/zh.json @@ -2276,10 +2276,11 @@ "MEDIA_PREVIEW": "媒体预览", "MEDIA_SAVE_ERROR": "保存媒体项目失败。错误:{{ error }}", "MEDIA_SAVE_SUCCESS": "媒体项目已保存。", - "MEDIA_SEARCH": "搜索", "MEDIA_SAVING": "正在保存媒体项目...", + "MEDIA_SEARCH": "搜索", "MEDIA_TITLE": "数字标牌媒体", "MEDIA_URL_ARIA": "媒体 URL", + "MEDIA_VIEW_ARIA": "Media view mode", "MESSAGE": "消息", "MINUTES_BETWEEN_PLAYS": "播放间隔分钟数", "MONTHLY": "每月", @@ -2537,6 +2538,7 @@ "UNDOING_CHANGES": "正在撤销变更...", "UNDO_CHANGES": "撤销变更", "UNNAMED_GROUP": "未命名组", + "UNTAGGED": "Untagged", "UPCOMING_PLAY_TIMES": "即将播放时间", "UPLOAD_MEDIA": "上传媒体", "UPLOAD_MEDIA_ARIA": "上传媒体文件", @@ -2548,6 +2550,9 @@ "USER_PERMISSIONS": "用户权限", "VALID_FROM": "有效起始日期", "VERSION_TO_APPROVE": "待批准版本", + "VIEW_FOLDER": "Folders", + "VIEW_GRID": "Grid view", + "VIEW_LIST": "List view", "WEBPAGE_URL_ARIA": "网页 URL", "WEEKDAYS": "工作日", "WEEKLY": "每周", From 4f8992a99cc7bac557af504389663a49ddd03fc8 Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Thu, 25 Jun 2026 13:27:28 +1000 Subject: [PATCH 11/62] feat(signage-manager): add ability to add new playlists from media view --- .../app/media/playlist-sidebar.component.ts | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/apps/signage-manager/src/app/media/playlist-sidebar.component.ts b/apps/signage-manager/src/app/media/playlist-sidebar.component.ts index 22e1088305..9c5b58cbf4 100644 --- a/apps/signage-manager/src/app/media/playlist-sidebar.component.ts +++ b/apps/signage-manager/src/app/media/playlist-sidebar.component.ts @@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms'; import { MatRippleModule } from '@angular/material/core'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterLink } from '@angular/router'; import { AuthenticatedImageDirective, @@ -26,16 +27,39 @@ type PlaylistStatus =