diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts
index 446bc76749..ac9d31fcdc 100644
--- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts
+++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts
@@ -27,11 +27,17 @@
//++
import {
- ChangeDetectionStrategy, Component, Injector, OnInit,
+ ChangeDetectionStrategy,
+ Component,
+ Injector,
+ OnInit,
} from '@angular/core';
import { StateService } from '@uirouter/core';
import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service';
import { States } from 'core-app/core/states/states.service';
+import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query';
+import { InAppNotificationsStore } from 'core-app/features/in-app-notifications/store/in-app-notifications.store';
+import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service';
import { FirstRouteService } from 'core-app/core/routing/first-route-service';
import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service';
import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service';
@@ -39,6 +45,7 @@ import { WorkPackageSingleViewBase } from 'core-app/features/work-packages/routi
import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service';
import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service';
import { BackRoutingService } from 'core-app/features/work-packages/components/back-routing/back-routing.service';
+import { Observable } from 'rxjs';
@Component({
templateUrl: './wp-split-view.html',
@@ -46,20 +53,28 @@ import { BackRoutingService } from 'core-app/features/work-packages/components/b
selector: 'wp-split-view-entry',
providers: [
{ provide: HalResourceNotificationService, useClass: WorkPackageNotificationService },
+ InAppNotificationsService,
+ InAppNotificationsStore,
+ InAppNotificationsQuery,
],
})
export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase implements OnInit {
/** Reference to the base route e.g., work-packages.partitioned.list or bim.partitioned.split */
private baseRoute:string = this.$state.current.data.baseRoute;
- constructor(public injector:Injector,
+ public displayNotificationsButton$:Observable = this.ianService.query.hasUnread$;
+
+ constructor(
+ public injector:Injector,
public states:States,
public firstRoute:FirstRouteService,
public keepTab:KeepTabService,
public wpTableSelection:WorkPackageViewSelectionService,
public wpTableFocus:WorkPackageViewFocusService,
readonly $state:StateService,
- readonly backRouting:BackRoutingService) {
+ readonly backRouting:BackRoutingService,
+ readonly ianService:InAppNotificationsService,
+ ) {
super(injector, $state.params.workPackageId);
}
@@ -95,17 +110,25 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp
);
}
});
+
+ if (wpId) {
+ this.ianService.setActiveFacet('unread');
+ this.ianService.setActiveFilters([
+ ['resourceId', '=', [wpId]],
+ ['resourceType', '=', ['WorkPackage']],
+ ]);
+ }
}
- public get shouldFocus() {
+ get shouldFocus():boolean {
return this.$state.params.focus === true;
}
- public showBackButton():boolean {
+ showBackButton():boolean {
return this.baseRoute.includes('bim');
}
- public backToList() {
+ backToList():void {
this.backRouting.goToBaseState();
}
}
diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html
index 610a72f625..08463a9e72 100644
--- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html
+++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html
@@ -6,9 +6,22 @@
-
+
+
-
+
+
+
+
-
+ 0 && !tab.showCountAsBubble"
data-qa-selector="tab-count"
diff --git a/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts b/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts
index be205fadd5..9e519e1a3a 100644
--- a/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts
+++ b/frontend/src/app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component.ts
@@ -6,6 +6,7 @@ import {
ElementRef,
EventEmitter,
Input,
+ Injector,
OnChanges,
Output,
SimpleChanges,
@@ -47,8 +48,10 @@ export class ScrollableTabsComponent implements AfterViewInit, OnChanges {
private pane:Element;
- constructor(private cdRef:ChangeDetectorRef) {
- }
+ constructor(
+ private cdRef:ChangeDetectorRef,
+ public injector:Injector,
+ ) { }
ngAfterViewInit():void {
this.container = this.scrollContainer.nativeElement;
diff --git a/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.html b/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.html
index c6d1e3d433..0d2d57491e 100644
--- a/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.html
+++ b/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.html
@@ -1,4 +1,4 @@
-
+ 0"
diff --git a/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts b/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts
index 0fff237a4c..d6474a204e 100644
--- a/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts
+++ b/frontend/src/app/shared/components/tabs/tab-badges/tab-count.component.ts
@@ -1,5 +1,4 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
-import { Observable } from 'rxjs';
@Component({
selector: 'op-tab-count',
@@ -8,5 +7,5 @@ import { Observable } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TabCountComponent {
- @Input('counter') counter$:Observable;
+ @Input() count:number;
}
diff --git a/frontend/src/app/shared/components/tabs/tab.interface.ts b/frontend/src/app/shared/components/tabs/tab.interface.ts
index f179673690..bc7da26d5a 100644
--- a/frontend/src/app/shared/components/tabs/tab.interface.ts
+++ b/frontend/src/app/shared/components/tabs/tab.interface.ts
@@ -1,4 +1,5 @@
import { Observable } from 'rxjs';
+import { Injector } from '@angular/core';
export interface TabDefinition {
/** Internal identifier of the tab */
@@ -12,7 +13,7 @@ export interface TabDefinition {
/** UI router params to use uiParams with */
routeParams?:unknown;
/** Show a tab count with this observable's result */
- counter?:Observable;
+ counter?:(injector?:Injector) => Observable;
/** Whether the counter should be shown as number in brackets or within a bubble */
showCountAsBubble?:boolean;
/** Disable the tab, optionally with an explanatory title */
diff --git a/frontend/src/global_styles/content/work_packages/new/_split_view.sass b/frontend/src/global_styles/content/work_packages/new/_split_view.sass
index fb725d8e76..4970cdb055 100644
--- a/frontend/src/global_styles/content/work_packages/new/_split_view.sass
+++ b/frontend/src/global_styles/content/work_packages/new/_split_view.sass
@@ -2,6 +2,8 @@
// Override the top header in create mode
&.-create-mode
top: 0
+ overflow-y: auto
+
// Details header containing switch icon and status|type row
.work-packages--new-details-header
margin: 0.375em 0
diff --git a/frontend/src/global_styles/layout/_boards.sass b/frontend/src/global_styles/layout/_boards.sass
index db573c708c..fe79cf46e5 100644
--- a/frontend/src/global_styles/layout/_boards.sass
+++ b/frontend/src/global_styles/layout/_boards.sass
@@ -1,7 +1,5 @@
// Let board list span whole screen
.router--boards-full-view
- @include extended-content--bottom
- @include extended-content--right
@include extended-content--left
#content
diff --git a/frontend/src/global_styles/layout/work_packages/_details_view.sass b/frontend/src/global_styles/layout/work_packages/_details_view.sass
index b03d4e3302..60349645bc 100644
--- a/frontend/src/global_styles/layout/work_packages/_details_view.sass
+++ b/frontend/src/global_styles/layout/work_packages/_details_view.sass
@@ -49,8 +49,8 @@ body.router--work-packages-partitioned-split-view-new
height: 100%
position: relative
width: 100%
- // Min-width is actually 645px but the border already needs 2px
- min-width: 643px
+ // Min-width is actually 530px but the border already needs 2px
+ min-width: 528px
@media only screen and (max-width: 1280px)
@at-root
@@ -76,15 +76,14 @@ body.router--work-packages-partitioned-split-view-new
margin: .5rem .5rem 0 0
.work-packages--details-content
- position: absolute
- top: 50px
- bottom: 55px
- width: 100%
- overflow-x: hidden
- overflow-y: scroll
- padding: 0 5px 0 20px
-
- @include styled-scroll-bar
+ display: flex
+ flex-direction: column
+ position: absolute
+ top: 50px
+ bottom: 55px
+ width: 100%
+ overflow: hidden
+ padding: 0 5px 10px 20px
.work-packages--details
@@ -109,6 +108,25 @@ body.router--work-packages-partitioned-split-view-new
padding: 5px 0 5px 5px
font-size: 18px
+ .work-packages--details-form
+ display: flex
+ flex-direction: column
+ overflow: hidden
+
+ .work-package-details-tab
+ overflow-y: auto
+ overflow-x: hidden
+ @include styled-scroll-bar
+
+ .work-packages--breadcrumb
+ display: grid
+ grid-template-columns: 1fr auto
+ grid-column-gap: 10px
+ align-items: center
+
+ .work-packages--details-button button
+ margin-bottom: 0
+
.work-packages--type-selector
flex-shrink: 0
diff --git a/frontend/src/global_styles/layout/work_packages/_full_view.sass b/frontend/src/global_styles/layout/work_packages/_full_view.sass
index 784061be28..2bf9cf716d 100644
--- a/frontend/src/global_styles/layout/work_packages/_full_view.sass
+++ b/frontend/src/global_styles/layout/work_packages/_full_view.sass
@@ -96,7 +96,7 @@
line-height: calc(var(--work-package-details--tab-height) - 10px)
.work-packages-full-view--split-right
- min-width: 645px
+ min-width: 530px
overflow-y: scroll
overflow-x: auto
position: relative
diff --git a/frontend/src/global_styles/layout/work_packages/_print.sass b/frontend/src/global_styles/layout/work_packages/_print.sass
index cb7f113ce7..3e46deb815 100644
--- a/frontend/src/global_styles/layout/work_packages/_print.sass
+++ b/frontend/src/global_styles/layout/work_packages/_print.sass
@@ -6,6 +6,7 @@
@media print
// -------------------- Work Package views --------------------
.router--work-packages-partitioned-split-view,
+ .router--work-packages-partitioned-split-view-details,
.router--work-packages-full-view,
.router--work-packages-full-create
#wrapper
@@ -18,6 +19,7 @@
overflow: visible !important
position: relative
grid-template-columns: auto
+ height: 100vh
#content-wrapper,
#content
diff --git a/frontend/src/global_styles/layout/work_packages/_table.sass b/frontend/src/global_styles/layout/work_packages/_table.sass
index f1bc9c6c0e..0dce47a91f 100644
--- a/frontend/src/global_styles/layout/work_packages/_table.sass
+++ b/frontend/src/global_styles/layout/work_packages/_table.sass
@@ -27,6 +27,7 @@
//++
.router--work-packages-partitioned-split-view,
+.router--work-packages-partitioned-split-view-details,
.router--work-packages-full-view,
.router--work-packages-full-create
.in_modal &
@@ -34,7 +35,8 @@
top: 12px
-.router--work-packages-partitioned-split-view:not(.router--work-packages-full-create)
+.router--work-packages-partitioned-split-view:not(.router--work-packages-full-create),
+.router--work-packages-partitioned-split-view-details:not(.router--work-packages-full-create)
@include extended-content--bottom
@include extended-content--right
diff --git a/spec/features/notifications/notification_center/notification_center_spec.rb b/spec/features/notifications/notification_center/notification_center_spec.rb
index bba40b4234..6c31b92075 100644
--- a/spec/features/notifications/notification_center/notification_center_spec.rb
+++ b/spec/features/notifications/notification_center/notification_center_spec.rb
@@ -77,12 +77,16 @@ describe "Notification center", type: :feature, js: true, with_settings: { journ
center.expect_work_package_item notification2
center.mark_all_read
- center.expect_bell_count 0
- notification.reload
- expect(notification.read_ian).to be_truthy
+ retry_block do
+ notification.reload
+ raise "Expected notification to be marked read" unless notification.read_ian
+ end
center.expect_no_item notification
center.expect_no_item notification2
+
+ center.open
+ center.expect_bell_count 0
end
it 'can open the split screen of the notification' do
diff --git a/spec/features/work_packages/tabs/activity_notifications_spec.rb b/spec/features/work_packages/tabs/activity_notifications_spec.rb
index a7f8083bb1..c31b29ddd2 100644
--- a/spec/features/work_packages/tabs/activity_notifications_spec.rb
+++ b/spec/features/work_packages/tabs/activity_notifications_spec.rb
@@ -20,14 +20,8 @@ describe 'Activity tab notifications', js: true, selenium: true do
work_package
end
shared_let(:admin) { FactoryBot.create(:admin) }
- shared_let(:full_view) { Pages::FullWorkPackage.new(work_package, project) }
- before do
- login_as(admin)
- full_view.visit!
- end
-
- context 'when there are notifications for the work package' do
+ shared_examples_for 'when there are notifications for the work package' do
shared_let(:notification) do
FactoryBot.create :notification,
recipient: admin,
@@ -35,24 +29,65 @@ describe 'Activity tab notifications', js: true, selenium: true do
resource: work_package,
journal: work_package.journals.last
end
-
- it 'Shows a notification bubble with the right number' do
+ it 'shows a notification bubble with the right number' do
expect(page).to have_selector('[data-qa-selector="tab-counter-Activity"]', text: '1')
end
- it 'Shows a notification icon next to activities that have an unread notification' do
+ it 'shows a notification icon next to activities that have an unread notification' do
expect(page).to have_selector('[data-qa-selector="user-activity-bubble"]', count: 1)
expect(page).to have_selector('[data-qa-activity-number="3"] [data-qa-selector="user-activity-bubble"]')
end
+
+ it 'shows a button to mark the notifications as read' do
+ expect(page).to have_selector('[data-qa-selector="mark-notification-read-button"]')
+
+ # A click marks the notification as read ...
+ page.find('[data-qa-selector="mark-notification-read-button"]').click
+
+ # ... and updates the view accordingly
+ expect(page).not_to have_selector('[data-qa-selector="mark-notification-read-button"]')
+ expect(page).not_to have_selector('[data-qa-selector="tab-counter-Activity"]')
+ expect(page).not_to have_selector('[data-qa-selector="user-activity-bubble"]')
+ end
end
- context 'when there are no notifications for the work package' do
- it 'Shows no notification bubble' do
+ shared_examples_for 'when there are no notifications for the work package' do
+ it 'shows no notification bubble' do
expect(page).not_to have_selector('[data-qa-selector="tab-counter-Activity"]')
end
- it 'Does not show any notification icons next to activities' do
+ it 'does not show any notification icons next to activities' do
expect(page).not_to have_selector('[data-qa-selector="user-activity-bubble"]')
end
+
+ it 'shows no button to mark the notifications as read' do
+ expect(page).not_to have_selector('[data-qa-selector="mark-notification-read-button"]')
+ end
+ end
+
+ context 'when on full view' do
+ shared_let(:full_view) { Pages::FullWorkPackage.new(work_package, project) }
+
+ before do
+ login_as(admin)
+ full_view.visit_tab! 'activity'
+ end
+
+ it_behaves_like 'when there are notifications for the work package'
+
+ it_behaves_like 'when there are no notifications for the work package'
+ end
+
+ context 'when on split view' do
+ shared_let(:split_view) { Pages::SplitWorkPackage.new(work_package, project) }
+
+ before do
+ login_as(admin)
+ split_view.visit_tab! 'activity'
+ end
+
+ it_behaves_like 'when there are notifications for the work package'
+
+ it_behaves_like 'when there are no notifications for the work package'
end
end