diff --git a/dmp-frontend/angular.json b/dmp-frontend/angular.json
index e3a5fc57b..dbc2640b5 100644
--- a/dmp-frontend/angular.json
+++ b/dmp-frontend/angular.json
@@ -27,7 +27,8 @@
"src/styles.scss",
"src/assets/scss/material-dashboard.scss",
"src/assets/css/demo.css",
- "node_modules/cookieconsent/build/cookieconsent.min.css"
+ "node_modules/cookieconsent/build/cookieconsent.min.css",
+ "src/app/library/guided-tour/guided-tour-base-theme.scss"
],
"scripts": [
"node_modules/cookieconsent/build/cookieconsent.min.js",
diff --git a/dmp-frontend/package.json b/dmp-frontend/package.json
index 38701dc4c..07983c8b1 100644
--- a/dmp-frontend/package.json
+++ b/dmp-frontend/package.json
@@ -32,6 +32,7 @@
"moment-timezone": "^0.5.26",
"ngx-cookie-service": "^2.2.0",
"ngx-cookieconsent": "^2.2.3",
+ "ngx-guided-tour": "^1.1.10",
"rxjs": "^6.3.2",
"tinymce": "^5.1.6",
"tslib": "^1.10.0",
diff --git a/dmp-frontend/src/app/app.component.html b/dmp-frontend/src/app/app.component.html
index 6243a45e8..3e7595fa7 100644
--- a/dmp-frontend/src/app/app.component.html
+++ b/dmp-frontend/src/app/app.component.html
@@ -24,3 +24,5 @@
+
+
\ No newline at end of file
diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts
index f8575729f..a460a2e19 100644
--- a/dmp-frontend/src/app/app.module.ts
+++ b/dmp-frontend/src/app/app.module.ts
@@ -32,6 +32,7 @@ import { BaseHttpService } from './core/services/http/base-http.service';
import { ConfigurationService } from './core/services/configuration/configuration.service';
import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldDefaultOptions } from '@angular/material';
+import { GuidedTourModule } from './library/guided-tour/guided-tour.module';
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient, appConfig: ConfigurationService) {
@@ -105,7 +106,8 @@ const appearance: MatFormFieldDefaultOptions = {
NavbarModule,
SidebarModule,
NgcCookieConsentModule.forRoot(cookieConfig),
- Oauth2DialogModule
+ Oauth2DialogModule,
+ GuidedTourModule.forRoot()
],
declarations: [
AppComponent,
diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour-base-theme.scss b/dmp-frontend/src/app/library/guided-tour/guided-tour-base-theme.scss
new file mode 100644
index 000000000..bed340850
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/guided-tour-base-theme.scss
@@ -0,0 +1,132 @@
+$tour-zindex: 1081 !default;
+$tour-step-color: #ffffff !default;
+$tour-text-color: #231f1f !default;
+$tour-next-button-color: #007bff !default;
+$tour-next-button-hover: #0069d9 !default;
+$tour-back-button-color: #007bff !default;
+$tour-next-text-color: #ffffff !default;
+$tour-next-text-hover: #ffffff !default;
+$tour-skip-link-color: #5e5e5e !default;
+$tour-orb-color: #625aff !default;
+$tour-shadow-color: #4c4c4c !default;
+
+body.tour-open {
+ overflow: hidden;
+}
+
+@mixin tour-triangle($direction, $color: currentColor, $size: 1rem) {
+
+ @if not index(top right bottom left, $direction) {
+ @error 'Direction must be either `top`, `right`, `bottom` or `left`.';
+ }
+
+ $opposite-direction: top;
+
+ @if $direction==top {
+ $opposite-direction: bottom;
+ }
+
+ @if $direction==bottom {
+ $opposite-direction: top;
+ }
+
+ @if $direction==right {
+ $opposite-direction: left;
+ }
+
+ @if $direction==left {
+ $opposite-direction: right;
+ }
+
+ width: 0;
+ height: 0;
+ content: '';
+ z-index: 2;
+ border-#{$opposite-direction}: $size solid $color;
+ $perpendicular-borders: $size solid transparent;
+ @if $direction==top or $direction==bottom {
+ border-left: $perpendicular-borders;
+ border-right: $perpendicular-borders;
+ }
+ @else if $direction==right or $direction==left {
+ border-bottom: $perpendicular-borders;
+ border-top: $perpendicular-borders;
+ }
+}
+
+ngx-guided-tour {
+ .guided-tour-user-input-mask {
+ z-index: $tour-zindex;
+ }
+
+ .guided-tour-spotlight-overlay {
+ z-index: $tour-zindex + 1;
+ }
+
+ .tour-orb {
+ z-index: $tour-zindex - 2;
+ background-color: $tour-orb-color;
+ box-shadow: 0 0 0.3rem 0.1rem $tour-orb-color;
+
+ .tour-orb-ring {
+ &::after {
+ border: 1rem solid $tour-orb-color;
+ box-shadow: 0 0 0.1rem 0.1rem $tour-orb-color;
+ }
+ }
+ }
+
+ .tour-step {
+ z-index: $tour-zindex + 2;
+
+ &.tour-bottom, &.tour-bottom-right, &.tour-bottom-left {
+ .tour-arrow::before {
+ @include tour-triangle(top, $tour-step-color);
+ }
+ }
+
+ &.tour-top, &.tour-top-right, &.tour-top-left {
+ .tour-arrow::before {
+ @include tour-triangle(bottom, $tour-step-color);
+ }
+ }
+
+ &.tour-left {
+ .tour-arrow::before {
+ @include tour-triangle(right, $tour-step-color);
+ }
+ }
+
+ &.tour-right {
+ .tour-arrow::before {
+ @include tour-triangle(left, $tour-step-color);
+ }
+ }
+
+ .tour-block {
+ color: $tour-text-color;
+ background-color: $tour-step-color;
+ box-shadow: 0 0.4rem 0.6rem $tour-shadow-color;
+ }
+
+ .tour-buttons {
+ button.skip-button {
+ color: $tour-skip-link-color;
+ }
+
+ .back-button {
+ color: $tour-back-button-color;
+ }
+
+ .next-button {
+ background-color: $tour-next-button-color;
+ color: $tour-next-text-color;
+ &:hover {
+ background-color: $tour-next-button-hover;
+ color: $tour-next-text-hover;
+ }
+ }
+ }
+ }
+
+}
diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.component.html b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.html
new file mode 100644
index 000000000..e0c5e0174
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.html
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+ {{currentTourStep.title}}
+
+
+ {{ currentTourStep.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.component.scss b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.scss
new file mode 100644
index 000000000..fa2c91615
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.scss
@@ -0,0 +1,235 @@
+ngx-guided-tour {
+ .guided-tour-user-input-mask {
+ position: fixed;
+ top: 0;
+ left: 0;
+ display: block;
+ height: 100%;
+ width: 100%;
+ max-height: 100vh;
+ text-align: center;
+ opacity: 0;
+ }
+
+ .guided-tour-spotlight-overlay {
+ position: fixed;
+ box-shadow: 0 0 0 9999px rgba(0,0,0,.7), 0 0 1.5rem rgba(0,0,0,.5);
+ border-radius: 44px;
+ }
+
+ .tour-orb {
+ position: fixed;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+
+ .tour-orb-ring {
+ width: 35px;
+ height: 35px;
+ position: relative;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ animation: pulse 2s linear infinite;
+
+ &:after {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ width: 100%;
+ border-radius: 50%;
+ }
+ }
+
+ @keyframes pulse {
+ from {
+ transform: translate(-50%, -50%) scale(0.45);
+ opacity: 1.0;
+ }
+ to {
+ transform: translate(-50%, -50%) scale(1);
+ opacity: 0.0;
+ }
+ }
+ }
+
+ .tour-step {
+ position: fixed;
+ &.page-tour-step {
+ max-width: 1043px;
+ width: 50%;
+ left: 50%;
+ top: 50%;
+ border-radius: 5px;
+ transform: translate(-50%, -50%)
+ }
+ &.tour-bottom, &.tour-bottom-right, &.tour-bottom-left {
+ .tour-arrow::before {
+ position: absolute;
+ }
+ .tour-block {
+ margin-top: 10px;
+ }
+ }
+
+ &.tour-top, &.tour-top-right, &.tour-top-left {
+ margin-bottom: 10px;
+
+ .tour-arrow::before {
+ position: absolute;
+ bottom: 0;
+ }
+ .tour-block {
+ margin-bottom: 10px;
+ }
+ }
+
+ &.tour-bottom , &.tour-top {
+ .tour-arrow::before {
+ transform: translateX(-50%);
+ left: 50%;
+ }
+ }
+
+ &.tour-bottom-right, &.tour-top-right {
+ .tour-arrow::before {
+ transform: translateX(-100%);
+ left: calc(100% - 5px);
+ }
+ }
+
+ &.tour-bottom-left, &.tour-top-left {
+ .tour-arrow::before {
+ left: 5px;
+ }
+ }
+
+ &.tour-left {
+ .tour-arrow::before {
+ position: absolute;
+ left: 100%;
+ transform: translateX(-100%);
+ top: 5px;
+ }
+ .tour-block {
+ margin-right: 10px;
+ }
+ }
+
+ &.tour-right {
+ .tour-arrow::before {
+ position: absolute;
+ left: 0;
+ top: 5px;
+ }
+ .tour-block {
+ margin-left: 10px;
+ }
+ }
+
+ .tour-block {
+ padding: 15px 25px;
+ height: 348px;
+ border-radius: 5px;
+ }
+
+ .tour-progress-indicator {
+ padding-bottom: 15px;
+ }
+
+ .tour-title {
+ font-weight: lighter !important;
+ font-size: 16px !important;
+ padding-left: 65px;
+ padding-top: 28px;
+ padding-right: 156px;
+ // padding-bottom: 20px;
+ text-align: left;
+ color: #212121;
+ line-height: 26px;
+ white-space:pre-line;
+ height: 182px;
+ }
+
+ h3.tour-title {
+ font-size: 20px;
+ }
+ h2.tour-title {
+ font-size: 30px;
+ }
+
+ .tour-content {
+ min-height: 80px;
+ padding-bottom: 30px;
+ font-size: 15px;
+ }
+
+ .tour-buttons {
+ overflow: hidden; // clearfix
+ padding: 40px 156px 25px 65px;
+
+ button.link-button {
+ padding-left: 0;
+ font-size: 15px;
+ font-weight: bold;
+ max-width: none !important;
+ cursor: pointer;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ border: 1px solid transparent;
+ line-height: 1.5;
+ background-color: transparent;
+ position: relative;
+ outline: none;
+ padding: 0 15px;
+ -webkit-appearance: button;
+ }
+
+ button.skip-button.link-button {
+ padding: 0;
+ border-left: 0;
+ float: right;
+ width: 133px;
+ height: 40px;
+ border: 1px solid #129D99;
+ background: #FFFFFF 0% 0% no-repeat padding-box;
+ color: #129D99;
+ // text-align: center;
+ }
+
+ .back-button {
+ float: right;
+ }
+
+ .next-button {
+ cursor: pointer;
+ float: left;
+ border: none;
+ outline: none;
+ padding: 10px 0px;;
+ width: 101px;
+ background: #129D99 0% 0% no-repeat padding-box;
+ }
+
+ button.skip-button.link-button, .next-button {
+ font-size: 14px;
+ font-weight: bold;
+ letter-spacing: 0.35px;
+ height: 40px;
+ box-shadow: 0px 3px 6px #1E202029;
+ border-radius: 30px;
+ }
+ }
+
+ .argos-present-img {
+ background: url("../../../assets/splash/assets/img/argos\ present.png") no-repeat;
+ width: 176px;
+ height: 220px;
+ position: relative;
+ top: -205px;
+ left: 820px;
+ border-top: none;
+ }
+ }
+}
diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.component.ts b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.ts
new file mode 100644
index 000000000..2fb3ed1bc
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.ts
@@ -0,0 +1,392 @@
+import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild, ViewEncapsulation, TemplateRef, Inject } from '@angular/core';
+import { fromEvent, Subscription } from 'rxjs';
+import { DOCUMENT } from '@angular/common';
+import { Orientation, TourStep, ProgressIndicatorLocation } from './guided-tour.constants';
+import { GuidedTourService } from './guided-tour.service';
+import { WindowRefService } from "./windowref.service";
+
+@Component({
+ selector: 'ngx-guided-tour',
+ templateUrl: './guided-tour.component.html',
+ styleUrls: ['./guided-tour.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class GuidedTourComponent implements AfterViewInit, OnDestroy {
+ @Input() public topOfPageAdjustment ?= 0;
+ @Input() public tourStepWidth ?= 1043;
+ @Input() public minimalTourStepWidth ?= 900;
+ @Input() public skipText ?= 'Leave Tour';
+ @Input() public nextText ?= 'Got it!';
+ @Input() public doneText ?= 'Done';
+ @Input() public closeText ?= 'Close';
+ @Input() public backText ?= 'Back';
+ @Input() public progressIndicatorLocation?: ProgressIndicatorLocation = ProgressIndicatorLocation.InsideNextButton;
+ @Input() public progressIndicator?: TemplateRef = undefined;
+ @ViewChild('tourStep', { static: false }) public tourStep: ElementRef;
+ public highlightPadding = 4;
+ public currentTourStep: TourStep = null;
+ public selectedElementRect: DOMRect = null;
+ public isOrbShowing = false;
+ public progressIndicatorLocations = ProgressIndicatorLocation;
+
+ private resizeSubscription: Subscription;
+ private scrollSubscription: Subscription;
+
+ constructor(
+ public guidedTourService: GuidedTourService,
+ private windowRef: WindowRefService,
+ @Inject(DOCUMENT) private dom: any
+ ) { }
+
+ private get maxWidthAdjustmentForTourStep(): number {
+ return this.tourStepWidth - this.minimalTourStepWidth;
+ }
+
+ private get widthAdjustmentForScreenBound(): number {
+ if (!this.tourStep) {
+ return 0;
+ }
+ let adjustment = 0;
+ if (this.calculatedLeftPosition < 0) {
+ adjustment = -this.calculatedLeftPosition;
+ }
+ if (this.calculatedLeftPosition > this.windowRef.nativeWindow.innerWidth - this.tourStepWidth) {
+ adjustment = this.calculatedLeftPosition - (this.windowRef.nativeWindow.innerWidth - this.tourStepWidth);
+ }
+
+ return Math.min(this.maxWidthAdjustmentForTourStep, adjustment);
+ }
+
+ public get calculatedTourStepWidth() {
+ return this.tourStepWidth - this.widthAdjustmentForScreenBound;
+ }
+
+ public ngAfterViewInit(): void {
+ this.guidedTourService.guidedTourCurrentStepStream.subscribe((step: TourStep) => {
+ this.currentTourStep = step;
+ if (step && step.selector) {
+ const selectedElement = this.dom.querySelector(step.selector);
+ if (selectedElement) {
+ this.handleOrb();
+ this.scrollToAndSetElement();
+ } else {
+ this.selectedElementRect = null;
+ }
+ } else {
+ this.selectedElementRect = null;
+ }
+ });
+
+ this.guidedTourService.guidedTourOrbShowingStream.subscribe((value: boolean) => {
+ this.isOrbShowing = value;
+ });
+
+ this.resizeSubscription = fromEvent(this.windowRef.nativeWindow, 'resize').subscribe(() => {
+ this.updateStepLocation();
+ });
+
+ this.scrollSubscription = fromEvent(this.windowRef.nativeWindow, 'scroll').subscribe(() => {
+ this.updateStepLocation();
+ });
+ }
+
+ public ngOnDestroy(): void {
+ this.resizeSubscription.unsubscribe();
+ this.scrollSubscription.unsubscribe();
+ }
+
+ public scrollToAndSetElement(): void {
+ this.updateStepLocation();
+ // Allow things to render to scroll to the correct location
+ setTimeout(() => {
+ if (!this.isOrbShowing && !this.isTourOnScreen()) {
+ if (this.selectedElementRect && this.isBottom()) {
+ // Scroll so the element is on the top of the screen.
+ const topPos = ((this.windowRef.nativeWindow.scrollY + this.selectedElementRect.top) - this.topOfPageAdjustment)
+ - (this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0)
+ + this.getStepScreenAdjustment();
+ try {
+ this.windowRef.nativeWindow.scrollTo({
+ left: null,
+ top: topPos,
+ behavior: 'smooth'
+ });
+ } catch (err) {
+ if (err instanceof TypeError) {
+ this.windowRef.nativeWindow.scroll(0, topPos);
+ } else {
+ throw err;
+ }
+ }
+ } else {
+ // Scroll so the element is on the bottom of the screen.
+ const topPos = (this.windowRef.nativeWindow.scrollY + this.selectedElementRect.top + this.selectedElementRect.height)
+ - this.windowRef.nativeWindow.innerHeight
+ + (this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0)
+ - this.getStepScreenAdjustment();
+ try {
+ this.windowRef.nativeWindow.scrollTo({
+ left: null,
+ top: topPos,
+ behavior: 'smooth'
+ });
+ } catch (err) {
+ if (err instanceof TypeError) {
+ this.windowRef.nativeWindow.scroll(0, topPos);
+ } else {
+ throw err;
+ }
+ }
+ }
+ }
+ });
+ }
+
+ public handleOrb(): void {
+ this.guidedTourService.activateOrb();
+ if (this.currentTourStep && this.currentTourStep.selector) {
+ this.scrollToAndSetElement();
+ }
+ }
+
+ private isTourOnScreen(): boolean {
+ return this.tourStep
+ && this.elementInViewport(this.dom.querySelector(this.currentTourStep.selector))
+ && this.elementInViewport(this.tourStep.nativeElement);
+ }
+
+ private elementInViewport(element: HTMLElement): boolean {
+ let top = element.offsetTop;
+ const height = element.offsetHeight;
+
+ while (element.offsetParent) {
+ element = (element.offsetParent as HTMLElement);
+ top += element.offsetTop;
+ }
+ if (this.isBottom()) {
+ return (
+ top >= (this.windowRef.nativeWindow.pageYOffset
+ + this.topOfPageAdjustment
+ + (this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0)
+ + this.getStepScreenAdjustment())
+ && (top + height) <= (this.windowRef.nativeWindow.pageYOffset + this.windowRef.nativeWindow.innerHeight)
+ );
+ } else {
+ return (
+ top >= (this.windowRef.nativeWindow.pageYOffset + this.topOfPageAdjustment - this.getStepScreenAdjustment())
+ && (top + height + (this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0)) <= (this.windowRef.nativeWindow.pageYOffset + this.windowRef.nativeWindow.innerHeight)
+ );
+ }
+ }
+
+ public backdropClick(event: Event): void {
+ if (this.guidedTourService.preventBackdropFromAdvancing) {
+ event.stopPropagation();
+ } else {
+ this.guidedTourService.nextStep();
+ }
+ }
+
+ public updateStepLocation(): void {
+ if (this.currentTourStep && this.currentTourStep.selector) {
+ const selectedElement = this.dom.querySelector(this.currentTourStep.selector);
+ if (selectedElement && typeof selectedElement.getBoundingClientRect === 'function') {
+ this.selectedElementRect = (selectedElement.getBoundingClientRect() as DOMRect);
+ } else {
+ this.selectedElementRect = null;
+ }
+ } else {
+ this.selectedElementRect = null;
+ }
+ }
+
+ private isBottom(): boolean {
+ return this.currentTourStep.orientation
+ && (this.currentTourStep.orientation === Orientation.Bottom
+ || this.currentTourStep.orientation === Orientation.BottomLeft
+ || this.currentTourStep.orientation === Orientation.BottomRight);
+ }
+
+ public get topPosition(): number {
+ const paddingAdjustment = this.getHighlightPadding();
+
+ if (this.isBottom()) {
+ return this.selectedElementRect.top + this.selectedElementRect.height + paddingAdjustment;
+ }
+
+ return this.selectedElementRect.top - this.getHighlightPadding();
+ }
+
+ public get orbTopPosition(): number {
+ if (this.isBottom()) {
+ return this.selectedElementRect.top + this.selectedElementRect.height;
+ }
+
+ if (
+ this.currentTourStep.orientation === Orientation.Right
+ || this.currentTourStep.orientation === Orientation.Left
+ ) {
+ return (this.selectedElementRect.top + (this.selectedElementRect.height / 2));
+ }
+
+ return this.selectedElementRect.top;
+ }
+
+ private get calculatedLeftPosition(): number {
+ const paddingAdjustment = this.getHighlightPadding();
+
+ if (
+ this.currentTourStep.orientation === Orientation.TopRight
+ || this.currentTourStep.orientation === Orientation.BottomRight
+ ) {
+ return (this.selectedElementRect.right - this.tourStepWidth);
+ }
+
+ if (
+ this.currentTourStep.orientation === Orientation.TopLeft
+ || this.currentTourStep.orientation === Orientation.BottomLeft
+ ) {
+ return (this.selectedElementRect.left);
+ }
+
+ if (this.currentTourStep.orientation === Orientation.Left) {
+ return this.selectedElementRect.left - this.tourStepWidth - paddingAdjustment;
+ }
+
+ if (this.currentTourStep.orientation === Orientation.Right) {
+ return (this.selectedElementRect.left + this.selectedElementRect.width + paddingAdjustment);
+ }
+
+ return (this.selectedElementRect.right - (this.selectedElementRect.width / 2) - (this.tourStepWidth / 2));
+ }
+
+ public get leftPosition(): number {
+ if (this.calculatedLeftPosition >= 0) {
+ return this.calculatedLeftPosition;
+ }
+ const adjustment = Math.max(0, -this.calculatedLeftPosition)
+ const maxAdjustment = Math.min(this.maxWidthAdjustmentForTourStep, adjustment);
+ return this.calculatedLeftPosition + maxAdjustment;
+ }
+
+ public get orbLeftPosition(): number {
+ if (
+ this.currentTourStep.orientation === Orientation.TopRight
+ || this.currentTourStep.orientation === Orientation.BottomRight
+ ) {
+ return this.selectedElementRect.right;
+ }
+
+ if (
+ this.currentTourStep.orientation === Orientation.TopLeft
+ || this.currentTourStep.orientation === Orientation.BottomLeft
+ ) {
+ return this.selectedElementRect.left;
+ }
+
+ if (this.currentTourStep.orientation === Orientation.Left) {
+ return this.selectedElementRect.left;
+ }
+
+ if (this.currentTourStep.orientation === Orientation.Right) {
+ return (this.selectedElementRect.left + this.selectedElementRect.width);
+ }
+
+ return (this.selectedElementRect.right - (this.selectedElementRect.width / 2));
+ }
+
+ public get transform(): string {
+ if (
+ !this.currentTourStep.orientation
+ || this.currentTourStep.orientation === Orientation.Top
+ || this.currentTourStep.orientation === Orientation.TopRight
+ || this.currentTourStep.orientation === Orientation.TopLeft
+ ) {
+ return 'translateY(-100%)';
+ }
+ return null;
+ }
+
+ public get orbTransform(): string {
+ if (
+ !this.currentTourStep.orientation
+ || this.currentTourStep.orientation === Orientation.Top
+ || this.currentTourStep.orientation === Orientation.Bottom
+ || this.currentTourStep.orientation === Orientation.TopLeft
+ || this.currentTourStep.orientation === Orientation.BottomLeft
+ ) {
+ return 'translateY(-50%)';
+ }
+
+ if (
+ this.currentTourStep.orientation === Orientation.TopRight
+ || this.currentTourStep.orientation === Orientation.BottomRight
+ ) {
+ return 'translate(-100%, -50%)';
+ }
+
+ if (
+ this.currentTourStep.orientation === Orientation.Right
+ || this.currentTourStep.orientation === Orientation.Left
+ ) {
+ return 'translate(-50%, -50%)';
+ }
+
+ return null;
+ }
+
+ public get overlayTop(): number {
+ if (this.selectedElementRect) {
+ return this.selectedElementRect.top - this.getHighlightPadding();
+ }
+ return 0;
+ }
+
+ public get overlayLeft(): number {
+ if (this.selectedElementRect) {
+ return this.selectedElementRect.left - this.getHighlightPadding();
+ }
+ return 0;
+ }
+
+ public get overlayHeight(): number {
+ if (this.selectedElementRect) {
+ return this.selectedElementRect.height + (this.getHighlightPadding() * 2);
+ }
+ return 0;
+ }
+
+ public get overlayWidth(): number {
+ if (this.selectedElementRect) {
+ return (this.selectedElementRect.width + (this.getHighlightPadding() * 2)) * 0.95;
+ }
+ return 0;
+ }
+
+ private getHighlightPadding(): number {
+ let paddingAdjustment = this.currentTourStep.useHighlightPadding ? this.highlightPadding : 0;
+ if (this.currentTourStep.highlightPadding) {
+ paddingAdjustment = this.currentTourStep.highlightPadding;
+ }
+ return paddingAdjustment;
+ }
+
+ // This calculates a value to add or subtract so the step should not be off screen.
+ private getStepScreenAdjustment(): number {
+ if (
+ this.currentTourStep.orientation === Orientation.Left
+ || this.currentTourStep.orientation === Orientation.Right
+ ) {
+ return 0;
+ }
+
+ const scrollAdjustment = this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0;
+ const tourStepHeight = typeof this.tourStep.nativeElement.getBoundingClientRect === 'function' ? this.tourStep.nativeElement.getBoundingClientRect().height : 0;
+ const elementHeight = this.selectedElementRect.height + scrollAdjustment + tourStepHeight;
+
+ if ((this.windowRef.nativeWindow.innerHeight - this.topOfPageAdjustment) < elementHeight) {
+ return elementHeight - (this.windowRef.nativeWindow.innerHeight - this.topOfPageAdjustment);
+ }
+ return 0;
+ }
+}
diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.constants.ts b/dmp-frontend/src/app/library/guided-tour/guided-tour.constants.ts
new file mode 100644
index 000000000..6053c2583
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.constants.ts
@@ -0,0 +1,75 @@
+
+export interface TourStep {
+ /** Selector for element that will be highlighted */
+ selector?: string;
+ /** Tour title text */
+ title?: string;
+ /** Tour step text */
+ content: string;
+ /** Where the tour step will appear next to the selected element */
+ orientation?: Orientation | OrientationConfiguration[];
+ /** Action that happens when the step is opened */
+ action?: () => void;
+ /** Action that happens when the step is closed */
+ closeAction?: () => void;
+ /** Skips this step, this is so you do not have create multiple tour configurations based on user settings/configuration */
+ skipStep?: boolean;
+ /** Adds some padding for things like sticky headers when scrolling to an element */
+ scrollAdjustment?: number;
+ /** Adds default padding around tour highlighting. Does not need to be true for highlightPadding to work */
+ useHighlightPadding?: boolean;
+ /** Adds padding around tour highlighting in pixels, this overwrites the default for this step. Is not dependent on useHighlightPadding being true */
+ highlightPadding?: number;
+}
+
+export interface GuidedTour {
+ /** Identifier for tour */
+ tourId: string;
+ /** Use orb to start tour */
+ useOrb?: boolean;
+ /** Steps fo the tour */
+ steps: TourStep[];
+ /** Function will be called when tour is skipped */
+ skipCallback?: (stepSkippedOn: number) => void;
+ /** Function will be called when tour is completed */
+ completeCallback?: () => void;
+ /** Minimum size of screen in pixels before the tour is run, if the tour is resized below this value the user will be told to resize */
+ minimumScreenSize?: number;
+ /** Dialog shown if the window width is smaller than the defined minimum screen size. */
+ resizeDialog?: {
+ /** Resize dialog title text */
+ title?: string;
+ /** Resize dialog text */
+ content: string;
+ }
+ /**
+ * Prevents the tour from advancing by clicking the backdrop.
+ * This should only be set if you are completely sure your tour is displaying correctly on all screen sizes otherwise a user can get stuck.
+ */
+ preventBackdropFromAdvancing?: boolean;
+}
+
+export interface OrientationConfiguration {
+ /** Where the tour step will appear next to the selected element */
+ orientationDirection: Orientation;
+ /** When this orientation configuration starts in pixels */
+ maximumSize?: number;
+}
+
+export class Orientation {
+ public static readonly Bottom = 'bottom';
+ public static readonly BottomLeft = 'bottom-left';
+ public static readonly BottomRight = 'bottom-right';
+ public static readonly Center = 'center';
+ public static readonly Left = 'left';
+ public static readonly Right = 'right';
+ public static readonly Top = 'top';
+ public static readonly TopLeft = 'top-left';
+ public static readonly TopRight = 'top-right';
+}
+
+export enum ProgressIndicatorLocation {
+ InsideNextButton = 'inside-next-button',
+ TopOfTourBlock = 'top-of-tour-block',
+ None = 'none',
+}
diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.module.ts b/dmp-frontend/src/app/library/guided-tour/guided-tour.module.ts
new file mode 100644
index 000000000..d51266431
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.module.ts
@@ -0,0 +1,21 @@
+import { GuidedTourService } from './guided-tour.service';
+import { GuidedTourComponent } from './guided-tour.component';
+import { NgModule, ErrorHandler, ModuleWithProviders } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { WindowRefService } from './windowref.service';
+
+@NgModule({
+ declarations: [GuidedTourComponent],
+ imports: [CommonModule],
+ providers: [WindowRefService],
+ exports: [GuidedTourComponent],
+ entryComponents: [GuidedTourComponent],
+})
+export class GuidedTourModule {
+ public static forRoot(): ModuleWithProviders {
+ return {
+ ngModule: GuidedTourModule,
+ providers: [ErrorHandler, GuidedTourService],
+ };
+ }
+}
diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.service.ts b/dmp-frontend/src/app/library/guided-tour/guided-tour.service.ts
new file mode 100644
index 000000000..2823f24d0
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.service.ts
@@ -0,0 +1,229 @@
+import { debounceTime } from 'rxjs/operators';
+import { ErrorHandler, Inject, Injectable } from '@angular/core';
+import { Observable, Subject, fromEvent } from 'rxjs';
+import { GuidedTour, TourStep, Orientation, OrientationConfiguration } from './guided-tour.constants';
+import { cloneDeep } from 'lodash';
+import { DOCUMENT } from "@angular/common";
+import { WindowRefService } from "./windowref.service";
+
+@Injectable()
+export class GuidedTourService {
+ public guidedTourCurrentStepStream: Observable;
+ public guidedTourOrbShowingStream: Observable;
+
+ private _guidedTourCurrentStepSubject = new Subject();
+ private _guidedTourOrbShowingSubject = new Subject();
+ private _currentTourStepIndex = 0;
+ private _currentTour: GuidedTour = null;
+ private _onFirstStep = true;
+ private _onLastStep = true;
+ private _onResizeMessage = false;
+
+ constructor(
+ public errorHandler: ErrorHandler,
+ private windowRef: WindowRefService,
+ @Inject(DOCUMENT) private dom
+ ) {
+ this.guidedTourCurrentStepStream = this._guidedTourCurrentStepSubject.asObservable();
+ this.guidedTourOrbShowingStream = this._guidedTourOrbShowingSubject.asObservable();
+
+ fromEvent(this.windowRef.nativeWindow, 'resize').pipe(debounceTime(200)).subscribe(() => {
+ if (this._currentTour && this._currentTourStepIndex > -1) {
+ if (this._currentTour.minimumScreenSize && this._currentTour.minimumScreenSize >= this.windowRef.nativeWindow.innerWidth) {
+ this._onResizeMessage = true;
+ const dialog = this._currentTour.resizeDialog || {
+ title: 'Please resize',
+ content: 'You have resized the tour to a size that is too small to continue. Please resize the browser to a larger size to continue the tour or close the tour.'
+ };
+
+ this._guidedTourCurrentStepSubject.next(dialog);
+ } else {
+ this._onResizeMessage = false;
+ this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex));
+ }
+ }
+ });
+ }
+
+ public nextStep(): void {
+ if (this._currentTour.steps[this._currentTourStepIndex].closeAction) {
+ this._currentTour.steps[this._currentTourStepIndex].closeAction();
+ }
+ if (this._currentTour.steps[this._currentTourStepIndex + 1]) {
+ this._currentTourStepIndex++;
+ this._setFirstAndLast();
+ if (this._currentTour.steps[this._currentTourStepIndex].action) {
+ this._currentTour.steps[this._currentTourStepIndex].action();
+ // Usually an action is opening something so we need to give it time to render.
+ setTimeout(() => {
+ if (this._checkSelectorValidity()) {
+ this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex));
+ } else {
+ this.nextStep();
+ }
+ });
+ } else {
+ if (this._checkSelectorValidity()) {
+ this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex));
+ } else {
+ this.nextStep();
+ }
+ }
+ } else {
+ if (this._currentTour.completeCallback) {
+ this._currentTour.completeCallback();
+ }
+ this.resetTour();
+ }
+ }
+
+ public backStep(): void {
+ if (this._currentTour.steps[this._currentTourStepIndex].closeAction) {
+ this._currentTour.steps[this._currentTourStepIndex].closeAction();
+ }
+ if (this._currentTour.steps[this._currentTourStepIndex - 1]) {
+ this._currentTourStepIndex--;
+ this._setFirstAndLast();
+ if (this._currentTour.steps[this._currentTourStepIndex].action) {
+ this._currentTour.steps[this._currentTourStepIndex].action();
+ setTimeout(() => {
+ if (this._checkSelectorValidity()) {
+ this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex));
+ } else {
+ this.backStep();
+ }
+ });
+ } else {
+ if (this._checkSelectorValidity()) {
+ this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex));
+ } else {
+ this.backStep();
+ }
+ }
+ } else {
+ this.resetTour();
+ }
+ }
+
+ public skipTour(): void {
+ if (this._currentTour.skipCallback) {
+ this._currentTour.skipCallback(this._currentTourStepIndex);
+ }
+ this.resetTour();
+ }
+
+ public resetTour(): void {
+ this.dom.body.classList.remove('tour-open');
+ this._currentTour = null;
+ this._currentTourStepIndex = 0;
+ this._guidedTourCurrentStepSubject.next(null);
+ }
+
+ public startTour(tour: GuidedTour): void {
+ this._currentTour = cloneDeep(tour);
+ this._currentTour.steps = this._currentTour.steps.filter(step => !step.skipStep);
+ this._currentTourStepIndex = 0;
+ this._setFirstAndLast();
+ this._guidedTourOrbShowingSubject.next(this._currentTour.useOrb);
+ if (
+ this._currentTour.steps.length > 0
+ && (!this._currentTour.minimumScreenSize
+ || (this.windowRef.nativeWindow.innerWidth >= this._currentTour.minimumScreenSize))
+ ) {
+ if (!this._currentTour.useOrb) {
+ this.dom.body.classList.add('tour-open');
+ }
+ if (this._currentTour.steps[this._currentTourStepIndex].action) {
+ this._currentTour.steps[this._currentTourStepIndex].action();
+ }
+ if (this._checkSelectorValidity()) {
+ this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex));
+ } else {
+ this.nextStep();
+ }
+ }
+ }
+
+ public activateOrb(): void {
+ this._guidedTourOrbShowingSubject.next(false);
+ this.dom.body.classList.add('tour-open');
+ }
+
+ private _setFirstAndLast(): void {
+ this._onLastStep = (this._currentTour.steps.length - 1) === this._currentTourStepIndex;
+ this._onFirstStep = this._currentTourStepIndex === 0;
+ }
+
+ private _checkSelectorValidity(): boolean {
+ if (this._currentTour.steps[this._currentTourStepIndex].selector) {
+ const selectedElement = this.dom.querySelector(this._currentTour.steps[this._currentTourStepIndex].selector);
+ if (!selectedElement) {
+ this.errorHandler.handleError(
+ // If error handler is configured this should not block the browser.
+ new Error(`Error finding selector ${this._currentTour.steps[this._currentTourStepIndex].selector} on step ${this._currentTourStepIndex + 1} during guided tour: ${this._currentTour.tourId}`)
+ );
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public get onLastStep(): boolean {
+ return this._onLastStep;
+ }
+
+ public get onFirstStep(): boolean {
+ return this._onFirstStep;
+ }
+
+ public get onResizeMessage(): boolean {
+ return this._onResizeMessage;
+ }
+
+ public get currentTourStepDisplay(): number {
+ return this._currentTourStepIndex + 1;
+ }
+
+ public get currentTourStepCount(): number {
+ return this._currentTour && this._currentTour.steps ? this._currentTour.steps.length : 0;
+ }
+
+ public get preventBackdropFromAdvancing(): boolean {
+ return this._currentTour && this._currentTour.preventBackdropFromAdvancing;
+ }
+
+ private getPreparedTourStep(index: number): TourStep {
+ return this.setTourOrientation(this._currentTour.steps[index]);
+ }
+
+ private setTourOrientation(step: TourStep): TourStep {
+ const convertedStep = cloneDeep(step);
+ if (
+ convertedStep.orientation
+ && !(typeof convertedStep.orientation === 'string')
+ && (convertedStep.orientation as OrientationConfiguration[]).length
+ ) {
+ (convertedStep.orientation as OrientationConfiguration[]).sort((a: OrientationConfiguration, b: OrientationConfiguration) => {
+ if (!b.maximumSize) {
+ return 1;
+ }
+ if (!a.maximumSize) {
+ return -1;
+ }
+ return b.maximumSize - a.maximumSize;
+ });
+
+ let currentOrientation: Orientation = Orientation.Top;
+ (convertedStep.orientation as OrientationConfiguration[]).forEach(
+ (orientationConfig: OrientationConfiguration) => {
+ if (!orientationConfig.maximumSize || this.windowRef.nativeWindow.innerWidth <= orientationConfig.maximumSize) {
+ currentOrientation = orientationConfig.orientationDirection;
+ }
+ }
+ );
+
+ convertedStep.orientation = currentOrientation;
+ }
+ return convertedStep;
+ }
+}
diff --git a/dmp-frontend/src/app/library/guided-tour/windowref.service.ts b/dmp-frontend/src/app/library/guided-tour/windowref.service.ts
new file mode 100644
index 000000000..f5ef09652
--- /dev/null
+++ b/dmp-frontend/src/app/library/guided-tour/windowref.service.ts
@@ -0,0 +1,38 @@
+import { Inject, Injectable, PLATFORM_ID } from "@angular/core";
+import { isPlatformBrowser } from "@angular/common";
+
+function getWindow(): any {
+ return window;
+}
+
+function getMockWindow(): any {
+ return {
+ innerWidth: 0,
+ innerHeight: 0,
+ scrollY: 0,
+ scrollX: 0,
+ pageYOffset: 0,
+ pageXOffset: 0,
+ scroll: () => {},
+ scrollTo: () => {},
+ addEventListener: () => {},
+ removeEventListener: () => {},
+ }
+}
+
+@Injectable()
+export class WindowRefService {
+ private readonly isBrowser: boolean = false;
+
+ get nativeWindow(): any {
+ if (this.isBrowser) {
+ return getWindow();
+ } else {
+ return getMockWindow();
+ }
+ }
+
+ constructor(@Inject(PLATFORM_ID) platformId) {
+ this.isBrowser = isPlatformBrowser(platformId);
+ }
+}
diff --git a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html
index 473a0d08e..1977993fb 100644
--- a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html
+++ b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html
@@ -34,6 +34,11 @@
+
+
+ {{ 'GENERAL.ACTIONS.TAKE-A-TOUR'| translate }}
+
+
search
diff --git a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.scss b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.scss
index 656256f4d..0f3d9beb9 100644
--- a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.scss
+++ b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.scss
@@ -137,6 +137,19 @@
height: 45px;
}
+.center-content {
+ width: 100%;
+ min-width: 10rem;
+ margin: auto;
+ padding: 0 15px;
+ text-align: right;
+ font-size: 0.875rem;
+ font-weight: 600;
+ letter-spacing: 0.02rem;
+ color: #2D72D6;
+ cursor: pointer;
+}
+
.search-form {
// font-size: 12px;
text-align: left;
diff --git a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts
index 6122b3d80..a724aba04 100644
--- a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts
+++ b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts
@@ -24,6 +24,8 @@ import { MatDialog } from '@angular/material';
import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
+import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service';
+import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants';
@Component({
selector: 'app-dataset-listing-component',
@@ -59,6 +61,8 @@ export class DatasetListingComponent extends BaseComponent implements OnInit, IB
scrollbar: boolean;
order = RecentActivityOrder;
+ dmpText: string;
+ datasetText: string;
constructor(
private datasetService: DatasetService,
@@ -68,7 +72,9 @@ export class DatasetListingComponent extends BaseComponent implements OnInit, IB
private dmpService: DmpService,
private language: TranslateService,
private authService: AuthService,
- public enumUtils: EnumUtils
+ public enumUtils: EnumUtils,
+ private authentication: AuthService,
+ private guidedTourService: GuidedTourService
) {
super();
}
@@ -133,6 +139,27 @@ export class DatasetListingComponent extends BaseComponent implements OnInit, IB
this.scrollbar = this.hasScrollbar();
}
+ public dashboardTour: GuidedTour = {
+ tourId: 'dmp-dataset-tour',
+ useOrb: true,
+ steps: [
+ {
+ selector: '.dmp-tour',
+ content: 'Step 1',
+ orientation: Orientation.Right
+ },
+ {
+ selector: '.dataset-tour',
+ content: 'Step 2',
+ orientation: Orientation.Right
+ }
+ ]
+ };
+
+ public isAuthenticated(): boolean {
+ return !(!this.authentication.current());
+ }
+
controlModified(): void {
// this.clearErrorModel();
// if (this.refreshCallback != null &&
@@ -327,6 +354,28 @@ export class DatasetListingComponent extends BaseComponent implements OnInit, IB
return merged;
}
+ public setDashboardTourDmpText(): void {
+ this.dmpText = this.language.instant('DMP-LISTING.TEXT-INFO') + '\n\n' +
+ this.language.instant('DMP-LISTING.TEXT-INFO-QUESTION') + ' ' +
+ this.language.instant('DMP-LISTING.LINK-ZENODO') + ' ' +
+ this.language.instant('DMP-LISTING.GET-IDEA');
+ this.dashboardTour.steps[0].title = this.dmpText;
+ }
+
+ public setDashboardTourDatasetText(): void {
+ this.datasetText = this.language.instant('DATASET-LISTING.TEXT-INFO') +
+ this.language.instant('DATASET-LISTING.LINK-PUBLIC-DATASETS') + ' ' +
+ this.language.instant('DATASET-LISTING.TEXT-INFO-REST') + '\n\n' +
+ this.language.instant('DATASET-LISTING.TEXT-INFO-PAR');
+ this.dashboardTour.steps[1].title = this.datasetText;
+ }
+
+ public restartTour(): void {
+ this.setDashboardTourDmpText();
+ this.setDashboardTourDatasetText();
+ this.guidedTourService.startTour(this.dashboardTour);
+ }
+
// rowClicked(dataset: DatasetListingModel) {
// this.router.navigate(['/datasets/edit/' + dataset.id]);
// }
diff --git a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.html b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.html
index 06fed8dee..1f206f8cc 100644
--- a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.html
+++ b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.html
@@ -29,6 +29,11 @@
+
+
+ {{ 'GENERAL.ACTIONS.TAKE-A-TOUR'| translate }}
+
+
search
diff --git a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.scss b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.scss
index d3541c7dc..ba7fc9fc9 100644
--- a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.scss
+++ b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.scss
@@ -241,6 +241,19 @@
padding: 0.3rem 0rem 0.6rem 0rem !important;
}
+.center-content {
+ width: 100%;
+ min-width: 10rem;
+ margin: auto;
+ padding: 0 15px;
+ text-align: right;
+ font-size: 0.875rem;
+ font-weight: 600;
+ letter-spacing: 0.02rem;
+ color: #2D72D6;
+ cursor: pointer;
+}
+
// .bot-paginator {
// margin-top: auto;
// }
diff --git a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts
index b47e12297..aaa8d5f44 100644
--- a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts
+++ b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts
@@ -26,7 +26,8 @@ import { AuthService } from '@app/core/services/auth/auth.service';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { DmpCriteriaDialogComponent } from './criteria/dmp-criteria-dialog.component';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
-
+import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service';
+import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants';
@Component({
selector: 'app-dmp-listing-component',
@@ -63,6 +64,8 @@ export class DmpListingComponent extends BaseComponent implements OnInit, IBread
scrollbar: boolean;
order = RecentActivityOrder;
+ dmpText: string;
+ datasetText: string;
constructor(
private dmpService: DmpService,
@@ -73,7 +76,8 @@ export class DmpListingComponent extends BaseComponent implements OnInit, IBread
private language: TranslateService,
private grantService: GrantService,
private uiNotificationService: UiNotificationService,
- private authService: AuthService
+ private authService: AuthService,
+ private guidedTourService: GuidedTourService
) {
super();
}
@@ -173,6 +177,26 @@ export class DmpListingComponent extends BaseComponent implements OnInit, IBread
.subscribe(x => this.refresh());
}
+ public dashboardTour: GuidedTour = {
+ tourId: 'dmp-dataset-tour',
+ useOrb: true,
+ steps: [
+ {
+ selector: '.dmp-tour',
+ content: 'Step 1',
+ orientation: Orientation.Right
+ },
+ {
+ selector: '.dataset-tour',
+ content: 'Step 2',
+ orientation: Orientation.Right
+ }
+ ]
+ };
+
+ public isAuthenticated(): boolean {
+ return !(!this.authService.current());
+ }
ngAfterContentChecked(): void {
this.scrollbar = this.hasScrollbar();
}
@@ -426,6 +450,28 @@ export class DmpListingComponent extends BaseComponent implements OnInit, IBread
}
return merged;
}
+
+ public setDashboardTourDmpText(): void {
+ this.dmpText = this.language.instant('DMP-LISTING.TEXT-INFO') + '\n\n' +
+ this.language.instant('DMP-LISTING.TEXT-INFO-QUESTION') + ' ' +
+ this.language.instant('DMP-LISTING.LINK-ZENODO') + ' ' +
+ this.language.instant('DMP-LISTING.GET-IDEA');
+ this.dashboardTour.steps[0].title = this.dmpText;
+ }
+
+ public setDashboardTourDatasetText(): void {
+ this.datasetText = this.language.instant('DATASET-LISTING.TEXT-INFO') +
+ this.language.instant('DATASET-LISTING.LINK-PUBLIC-DATASETS') + ' ' +
+ this.language.instant('DATASET-LISTING.TEXT-INFO-REST') + '\n\n' +
+ this.language.instant('DATASET-LISTING.TEXT-INFO-PAR');
+ this.dashboardTour.steps[1].title = this.datasetText;
+ }
+
+ public restartTour(): void {
+ this.setDashboardTourDmpText();
+ this.setDashboardTourDatasetText();
+ this.guidedTourService.startTour(this.dashboardTour);
+ }
}
// export class DmpDataSource extends DataSource {
diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.html b/dmp-frontend/src/app/ui/sidebar/sidebar.component.html
index e6a7767dc..4c8ec0e74 100644
--- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.html
+++ b/dmp-frontend/src/app/ui/sidebar/sidebar.component.html
@@ -5,7 +5,7 @@
-
+
{{ groupMenuRoute.icon }}
person
{{groupMenuRoute.title | translate}}
diff --git a/dmp-frontend/src/assets/i18n/de.json b/dmp-frontend/src/assets/i18n/de.json
index 30401d5d8..ac72f08db 100644
--- a/dmp-frontend/src/assets/i18n/de.json
+++ b/dmp-frontend/src/assets/i18n/de.json
@@ -79,7 +79,8 @@
"VIEW-ALL": "Alles anzeigen",
"SHOW-MORE": "Mehr anzeigen",
"SHOW-LESS": "Weniger anzeigen",
- "LOG-IN": "Einloggen"
+ "LOG-IN": "Einloggen",
+ "TAKE-A-TOUR": "Do you need help? Take a tour.."
},
"PREPOSITIONS": {
"OF": "von"
diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json
index 279c79908..d21768178 100644
--- a/dmp-frontend/src/assets/i18n/en.json
+++ b/dmp-frontend/src/assets/i18n/en.json
@@ -81,7 +81,8 @@
"SHOW-MORE": "Show more",
"LOAD-MORE": "Load more",
"SHOW-LESS": "Show less",
- "LOG-IN": "Log in"
+ "LOG-IN": "Log in",
+ "TAKE-A-TOUR": "Do you need help? Take a tour.."
},
"PREPOSITIONS": {
"OF": "of"
diff --git a/dmp-frontend/src/assets/i18n/es.json b/dmp-frontend/src/assets/i18n/es.json
index 9b14419b9..87abca842 100644
--- a/dmp-frontend/src/assets/i18n/es.json
+++ b/dmp-frontend/src/assets/i18n/es.json
@@ -80,7 +80,8 @@
"VIEW-ALL": "Ver todo",
"SHOW-MORE": "Mostrar más",
"SHOW-LESS": "Show less",
- "LOG-IN": "Iniciar sesión"
+ "LOG-IN": "Iniciar sesión",
+ "TAKE-A-TOUR": "Do you need help? Take a tour.."
},
"PREPOSITIONS": {
"OF": "de"
diff --git a/dmp-frontend/src/assets/i18n/gr.json b/dmp-frontend/src/assets/i18n/gr.json
index c0a7c8ab1..ef03e38fc 100644
--- a/dmp-frontend/src/assets/i18n/gr.json
+++ b/dmp-frontend/src/assets/i18n/gr.json
@@ -80,7 +80,8 @@
"VIEW-ALL": "Προβολή όλων",
"SHOW-MORE": "Δείτε περισσότερα",
"SHOW-LESS": "Δείτε λιγότερα",
- "LOG-IN": "Σύνδεση"
+ "LOG-IN": "Σύνδεση",
+ "TAKE-A-TOUR": "Do you need help? Take a tour.."
},
"PREPOSITIONS": {
"OF": "of"
@@ -1239,9 +1240,7 @@
"SEARCH": "ΑΝΑΖΗΤΗΣΗ...",
"DATA-MANAGEMENT-PLANS": "ΣΧΕΔΙΑ ΔΙΑΧΕΙΡΙΣΗΣ ΔΕΔΟΜΕΝΩΝ",
"PERSONAL-USAGE": "Προσωπική Χρήση",
- "DMPS": "DMPs",
"DATASET-DESCRIPTIONS": "Περιγραφές Dataset",
- "GRANTS": "Grants",
"RELATED-ORGANISATIONS": "Σχετικοί Οργανισμοί",
"DRAFTS": "Προσχέδια",
"ALL": "Όλα",
diff --git a/dmp-frontend/src/assets/i18n/tr.json b/dmp-frontend/src/assets/i18n/tr.json
index a5ac3b21a..cf3ef2acf 100644
--- a/dmp-frontend/src/assets/i18n/tr.json
+++ b/dmp-frontend/src/assets/i18n/tr.json
@@ -79,7 +79,8 @@
"VIEW-ALL": "Tümüne Gör",
"SHOW-MORE": "Daha fazla göster",
"SHOW-LESS": "Daha az göster",
- "LOG-IN": "Oturum aç"
+ "LOG-IN": "Oturum aç",
+ "TAKE-A-TOUR": "Do you need help? Take a tour.."
},
"PREPOSITIONS": {
"OF": "nın"
diff --git a/dmp-frontend/src/assets/splash/assets/img/argos present.png b/dmp-frontend/src/assets/splash/assets/img/argos present.png
new file mode 100644
index 000000000..9e167acc3
Binary files /dev/null and b/dmp-frontend/src/assets/splash/assets/img/argos present.png differ
diff --git a/dmp-frontend/src/assets/splash/assets/img/argos present@2x.png b/dmp-frontend/src/assets/splash/assets/img/argos present@2x.png
new file mode 100644
index 000000000..1b99808ea
Binary files /dev/null and b/dmp-frontend/src/assets/splash/assets/img/argos present@2x.png differ