diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index 7e9e0061c..274117558 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -26,6 +26,7 @@ "ngx-color": "^8.0.3", "ngx-cookie-service": "^14.0.1", "ngx-file-drop": "^14.0.1", + "ngx-ui-tour-ng-bootstrap": "^11.0.0", "rxjs": "~7.5.7", "tslib": "^2.3.1", "uuid": "^9.0.0", @@ -12936,6 +12937,36 @@ "@angular/core": ">=14.0.0" } }, + "node_modules/ngx-ui-tour-core": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-9.0.0.tgz", + "integrity": "sha512-3HvrprosDaZm9jHi/w+oA8N9bPeaV9k0Y70nsEkRPRQ1jM302JyjGYFuM6/FzbXU5FITGLChtrFJpBn/Q4yJIA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0", + "@angular/core": "^14.0.0", + "@angular/router": "^14.0.0", + "rxjs": ">=6.0.0", + "typescript": ">=3.8.0" + } + }, + "node_modules/ngx-ui-tour-ng-bootstrap": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/ngx-ui-tour-ng-bootstrap/-/ngx-ui-tour-ng-bootstrap-11.0.0.tgz", + "integrity": "sha512-oF5ySZEiO4ib/RfYno81V2UBaX7EMzxSPwfCdedDr38e5BEPTOrcCufOaiOfuQzQftg+7CC/BbFY8Df9kmeL1A==", + "dependencies": { + "ngx-ui-tour-core": "9.0.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0", + "@angular/core": "^14.0.0", + "@ng-bootstrap/ng-bootstrap": "^13.0.0", + "typescript": ">=3.8.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -27156,6 +27187,23 @@ "tslib": "^2.3.0" } }, + "ngx-ui-tour-core": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-9.0.0.tgz", + "integrity": "sha512-3HvrprosDaZm9jHi/w+oA8N9bPeaV9k0Y70nsEkRPRQ1jM302JyjGYFuM6/FzbXU5FITGLChtrFJpBn/Q4yJIA==", + "requires": { + "tslib": "^2.0.0" + } + }, + "ngx-ui-tour-ng-bootstrap": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/ngx-ui-tour-ng-bootstrap/-/ngx-ui-tour-ng-bootstrap-11.0.0.tgz", + "integrity": "sha512-oF5ySZEiO4ib/RfYno81V2UBaX7EMzxSPwfCdedDr38e5BEPTOrcCufOaiOfuQzQftg+7CC/BbFY8Df9kmeL1A==", + "requires": { + "ngx-ui-tour-core": "9.0.0", + "tslib": "^2.0.0" + } + }, "nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", diff --git a/src-ui/package.json b/src-ui/package.json index 513e1b2f7..02afd7e79 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -31,6 +31,7 @@ "ngx-color": "^8.0.3", "ngx-cookie-service": "^14.0.1", "ngx-file-drop": "^14.0.1", + "ngx-ui-tour-ng-bootstrap": "^11.0.0", "rxjs": "~7.5.7", "tslib": "^2.3.1", "uuid": "^9.0.0", diff --git a/src-ui/src/app/app.component.html b/src-ui/src/app/app.component.html index 2b050ed3f..9a605bead 100644 --- a/src-ui/src/app/app.component.html +++ b/src-ui/src/app/app.component.html @@ -11,3 +11,27 @@ + + + +

+
+ {{ tourService.steps?.indexOf(step) + 1 }}/{{ tourService.steps?.length }} + +
+
+
diff --git a/src-ui/src/app/app.component.ts b/src-ui/src/app/app.component.ts index b1ba49c3a..90a1a1905 100644 --- a/src-ui/src/app/app.component.ts +++ b/src-ui/src/app/app.component.ts @@ -1,6 +1,6 @@ import { SettingsService } from './services/settings.service' import { SETTINGS_KEYS } from './data/paperless-uisettings' -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core' import { Router } from '@angular/router' import { Subscription } from 'rxjs' import { ConsumerStatusService } from './services/consumer-status.service' @@ -8,6 +8,7 @@ import { ToastService } from './services/toast.service' import { NgxFileDropEntry } from 'ngx-file-drop' import { UploadDocumentsService } from './services/upload-documents.service' import { TasksService } from './services/tasks.service' +import { TourService } from 'ngx-ui-tour-ng-bootstrap' @Component({ selector: 'app-root', @@ -29,7 +30,9 @@ export class AppComponent implements OnInit, OnDestroy { private toastService: ToastService, private router: Router, private uploadDocumentsService: UploadDocumentsService, - private tasksService: TasksService + private tasksService: TasksService, + public tourService: TourService, + private renderer: Renderer2 ) { let anyWindow = window as any anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js' @@ -112,6 +115,40 @@ export class AppComponent implements OnInit, OnDestroy { }) } }) + + this.tourService.initialize([ + { + anchorId: 'tour.intro', + title: `Hello ${this.settings.displayName}, welcome to Paperless-ngx!`, + content: + "Here's a tutorial to guide you around some of Paperless-ngx's most useful features.", + route: '/dashboard', + }, + { + anchorId: 'tour.dashboard', + title: 'The Dashboard', + content: "Here's some dashboard info", + route: '/dashboard', + }, + { + anchorId: 'tour.documents', + title: 'Documents List', + content: "Here's some dashboard info", + route: '/documents', + delayAfterNavigation: 500, + }, + ]) + + this.tourService.start$.subscribe(() => { + this.renderer.addClass(document.body, 'tour-active') + }) + + this.tourService.end$.subscribe(() => { + // animation time + setTimeout(() => { + this.renderer.removeClass(document.body, 'tour-active') + }, 500) + }) } public get dragDropEnabled(): boolean { diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index 9840deb58..02fd8ea66 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -69,6 +69,11 @@ import { ColorComponent } from './components/common/input/color/color.component' import { DocumentAsnComponent } from './components/document-asn/document-asn.component' import { DocumentCommentsComponent } from './components/document-comments/document-comments.component' import { DirtyDocGuard } from './guards/dirty-doc.guard' +import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component' +import { StoragePathEditDialogComponent } from './components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component' +import { SettingsService } from './services/settings.service' +import { TasksComponent } from './components/manage/tasks/tasks.component' +import { TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap' import localeBe from '@angular/common/locales/be' import localeCs from '@angular/common/locales/cs' @@ -89,10 +94,6 @@ import localeSr from '@angular/common/locales/sr' import localeSv from '@angular/common/locales/sv' import localeTr from '@angular/common/locales/tr' import localeZh from '@angular/common/locales/zh' -import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component' -import { StoragePathEditDialogComponent } from './components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component' -import { SettingsService } from './services/settings.service' -import { TasksComponent } from './components/manage/tasks/tasks.component' registerLocaleData(localeBe) registerLocaleData(localeCs) @@ -188,6 +189,7 @@ function initializeApp(settings: SettingsService) { PdfViewerModule, NgSelectModule, ColorSliderModule, + TourNgBootstrapModule.forRoot(), ], providers: [ { diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index 7d003225d..7a8546640 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -4,8 +4,8 @@ (click)="isMenuCollapsed = !isMenuCollapsed"> - - + + Paperless-ngx diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts index b189409a8..96207e66f 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.ts @@ -52,7 +52,7 @@ export class AppFrameComponent implements OnInit, ComponentCanDeactivate { } this.tasksService.reload() } - + versionString = `${environment.appTitle} ${environment.version}` appRemoteVersion diff --git a/src-ui/src/app/components/dashboard/dashboard.component.html b/src-ui/src/app/components/dashboard/dashboard.component.html index 4b6bc223b..aa4c17402 100644 --- a/src-ui/src/app/components/dashboard/dashboard.component.html +++ b/src-ui/src/app/components/dashboard/dashboard.component.html @@ -19,14 +19,14 @@ -
+
Loading...
- + diff --git a/src-ui/src/app/components/dashboard/dashboard.component.ts b/src-ui/src/app/components/dashboard/dashboard.component.ts index a58140d5a..bb01f130e 100644 --- a/src-ui/src/app/components/dashboard/dashboard.component.ts +++ b/src-ui/src/app/components/dashboard/dashboard.component.ts @@ -1,5 +1,4 @@ -import { Component, OnInit } from '@angular/core' -import { Meta } from '@angular/platform-browser' +import { Component } from '@angular/core' import { SavedViewService } from 'src/app/services/rest/saved-view.service' import { SettingsService } from 'src/app/services/settings.service' @@ -16,9 +15,9 @@ export class DashboardComponent { get subtitle() { if (this.settingsService.displayName) { - return $localize`Hello ${this.settingsService.displayName}, welcome to Paperless-ngx!` + return $localize`Hello ${this.settingsService.displayName}, welcome to Paperless-ngx` } else { - return $localize`Welcome to Paperless-ngx!` + return $localize`Welcome to Paperless-ngx` } } } diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html index 17c7ffe1e..762d5cd32 100644 --- a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html @@ -1,16 +1,10 @@ - - - - -

Paperless is running! :)

-

You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list. - After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and they will appear on the dashboard instead of this message.

-

Paperless offers some more features that try to make your life easier:

-
    -
  • Once you've got a couple documents in paperless and added metadata to them, paperless can assign that metadata to new documents automatically.
  • -
  • You can configure paperless to read your mails and add documents from attached files.
  • -
-

Consult the documentation on how to use these features. The section on basic usage also has some information on how to use paperless in general.

-
- -
+ + +

Paperless-ngx is running!

+

You're ready to start uploading documents! Feel free to explore the various features or start a quick tour using the button below.

+

More detail on how to use and configure paperless is always available in the documentation.

+
+ + diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts index 9873932ee..718f3291c 100644 --- a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core' +import { TourService } from 'ngx-ui-tour-ng-bootstrap' @Component({ selector: 'app-welcome-widget', @@ -6,7 +7,7 @@ import { Component, OnInit } from '@angular/core' styleUrls: ['./welcome-widget.component.scss'], }) export class WelcomeWidgetComponent implements OnInit { - constructor() {} + constructor(public readonly tourService: TourService) {} ngOnInit(): void {} } diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index 60c9bcfb9..f20cf0d40 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -75,10 +75,12 @@
+
+

@@ -103,7 +105,6 @@ -

diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index 10cc93ce0..b2f239b06 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -446,4 +446,9 @@ export class SettingsService { get updateCheckingIsSet(): boolean { return this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) } + + offerTour(): boolean { + return true + // !savedViewService.loading && savedViewService.dashboardViews.length == 0 + } } diff --git a/src-ui/src/assets/save-filter.png b/src-ui/src/assets/save-filter.png deleted file mode 100644 index 0f011f812..000000000 Binary files a/src-ui/src/assets/save-filter.png and /dev/null differ diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss index b66710dcd..795e6c252 100644 --- a/src-ui/src/styles.scss +++ b/src-ui/src/styles.scss @@ -479,6 +479,12 @@ table.table { user-select: none !important; } +.alert-primary { + --bs-alert-color: var(--bs-primary); + --bs-alert-bg: var(--pngx-primary-faded); + --bs-alert-border-color: var(--bs-primary); +} + .alert-danger { color: var(--bs-body-color); background-color: var(--bs-danger); @@ -496,14 +502,23 @@ table.table { } .popover { + background-color: var(--pngx-bg-alt); + .popover-header { + background-color: var(--pngx-bg-alt); + } .popover-header, .popover-body { - background-color: var(--pngx-bg-alt); border-color: var(--bs-border-color); color: var(--bs-body-color); } } +// Tour popovers +.tour-active .popover { + min-width: 400px; + margin: 0 .25rem .25rem !important; +} + // fix popover carat colors .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="left"] { border-left-color: var(--pngx-bg-alt); diff --git a/src-ui/src/theme.scss b/src-ui/src/theme.scss index 99dcb8bab..77260a882 100644 --- a/src-ui/src/theme.scss +++ b/src-ui/src/theme.scss @@ -10,11 +10,12 @@ body { --bs-primary: hsl(var(--pngx-primary), var(--pngx-primary-lightness)); --bs-border-color: var(--bs-gray-400); - --pngx-primary-faded: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 72%)); + --pngx-primary-faded: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 76%)); --pngx-primary-lighten-30: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 30%)); --pngx-primary-darken-5: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 5%)); --pngx-primary-darken-15: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 15%)); --pngx-primary-darken-18: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 18%)); + --pngx-primary-darken-27: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 27%)); --pngx-bg-alt: #fff; --pngx-bg-darker: var(--bs-gray-100); --pngx-focus-alpha: 0.3; @@ -177,6 +178,12 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,