Merge branch 'master' into VM/TestsIds

This commit is contained in:
Valentin Mihai 2022-03-28 22:10:28 +03:00
commit bbdff8e525
10 changed files with 154 additions and 149 deletions

View File

@ -13,11 +13,11 @@
<div *ngFor="let group of groups; let first = first">
<div class="all-caps-label flex-align-items-center">
<div>{{ group.date }}</div>
<div *ngIf="(hasUnreadNotifications$ | async) && first" class="view-all" (click)="markRead($event)">View all</div>
<div (click)="markRead($event)" *ngIf="(hasUnreadNotifications$ | async) && first" class="view-all">View all</div>
</div>
<div
(click)="markRead($event, [notification.id])"
(click)="markRead($event, [notification], true)"
*ngFor="let notification of group.notifications | sortBy: 'desc':'creationDate'"
[class.unread]="!notification.readDate"
class="notification"
@ -29,7 +29,7 @@
<div class="small-label mt-2">{{ notification.time }}</div>
</div>
<div
(click)="markRead($event, [notification.id], !notification.readDate)"
(click)="markRead($event, [notification], !notification.readDate)"
class="dot"
matTooltip="{{ 'notifications.mark-as' | translate: { type: notification.readDate ? 'unread' : 'read' } }}"
matTooltipPosition="before"

View File

@ -30,6 +30,7 @@
position: relative;
line-height: 18px;
align-items: flex-start;
cursor: default;
.notification-content {
margin-top: 4px;
@ -50,6 +51,7 @@
background-color: variables.$grey-4;
top: 8px;
right: 8px;
cursor: pointer;
}
&:hover {

View File

@ -1,16 +1,13 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@shared/pipes/date.pipe';
import { UserService } from '@services/user.service';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { NotificationsService } from '@services/notifications.service';
import { Notification } from '@red/domain';
import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import { BehaviorSubject, firstValueFrom, Observable, timer } from 'rxjs';
import { AutoUnsubscribe, List, shareLast } from '@iqser/common-ui';
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
const INCLUDE_SEEN = false;
import { distinctUntilChanged, map } from 'rxjs/operators';
import { firstValueFrom, Observable } from 'rxjs';
import { shareLast } from '@iqser/common-ui';
interface NotificationsGroup {
date: string;
@ -23,11 +20,9 @@ interface NotificationsGroup {
styleUrls: ['./notifications.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NotificationsComponent extends AutoUnsubscribe implements OnInit {
notifications$: Observable<Notification[]>;
hasUnreadNotifications$: Observable<boolean>;
groupedNotifications$: Observable<NotificationsGroup[]>;
private _notifications$ = new BehaviorSubject<Notification[]>([]);
export class NotificationsComponent {
readonly hasUnreadNotifications$: Observable<boolean>;
readonly groupedNotifications$: Observable<NotificationsGroup[]>;
constructor(
private readonly _translateService: TranslateService,
@ -36,40 +31,30 @@ export class NotificationsComponent extends AutoUnsubscribe implements OnInit {
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _datePipe: DatePipe,
) {
super();
this.notifications$ = this._notifications$.asObservable().pipe(shareLast());
this.groupedNotifications$ = this.notifications$.pipe(map(notifications => this._groupNotifications(notifications)));
this.groupedNotifications$ = this._notificationsService.all$.pipe(map(notifications => this._groupNotifications(notifications)));
this.hasUnreadNotifications$ = this._hasUnreadNotifications$;
}
private get _hasUnreadNotifications$(): Observable<boolean> {
return this.notifications$.pipe(
return this._notificationsService.all$.pipe(
map(notifications => notifications.filter(n => !n.readDate).length > 0),
distinctUntilChanged(),
shareLast(),
);
}
async ngOnInit(): Promise<void> {
await this._loadData();
this.addSubscription = timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL)
.pipe(
switchMap(() => this._notificationsService.getNotificationsIfChanged(INCLUDE_SEEN)),
tap(notifications => this._notifications$.next(notifications)),
)
.subscribe();
}
async markRead($event, notifications: List<string> = this._notifications$.getValue().map(n => n.id), isRead = true): Promise<void> {
async markRead($event, notifications: Notification[] = this._notificationsService.all, isRead = true): Promise<void> {
$event.stopPropagation();
await firstValueFrom(this._notificationsService.toggleNotificationRead(notifications, isRead));
await this._loadData();
}
private async _loadData(): Promise<void> {
const notifications = await firstValueFrom(this._notificationsService.getNotifications(INCLUDE_SEEN));
this._notifications$.next(notifications);
if (!notifications.find(notification => !!notification.readDate !== isRead)) {
// If no notification changes status after the request, abort
return;
}
await firstValueFrom(
this._notificationsService.toggleNotificationRead(
notifications.map(n => n.id),
isRead,
),
);
}
private _groupNotifications(notifications: Notification[]): NotificationsGroup[] {

View File

@ -22,7 +22,7 @@ export class AccountSideNavComponent {
},
{
screen: 'notifications',
label: _('notifications'),
label: _('notifications.label'),
hideIf: !this._userService.currentUser.isUser,
},
];

View File

@ -1,12 +1,13 @@
import { Component, OnDestroy } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { lastIndexOfEnd } from '@utils/functions';
import { AutoUnsubscribe, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
import { RouterHistoryService } from '@services/router-history.service';
import { DigitalSignatureService } from '../../services/digital-signature.service';
import { IDigitalSignature, IDigitalSignatureRequest } from '@red/domain';
import { IDigitalSignature } from '@red/domain';
import { firstValueFrom } from 'rxjs';
import { HttpStatusCode } from '@angular/common/http';
@Component({
@ -14,7 +15,7 @@ import { HttpStatusCode } from '@angular/common/http';
templateUrl: './digital-signature-screen.component.html',
styleUrls: ['./digital-signature-screen.component.scss'],
})
export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements OnDestroy {
export class DigitalSignatureScreenComponent implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
readonly currentUser = this._userService.currentUser;
@ -30,16 +31,18 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
private readonly _loadingService: LoadingService,
readonly routerHistoryService: RouterHistoryService,
private readonly _digitalSignatureService: DigitalSignatureService,
) {
super();
this.loadDigitalSignatureAndInitializeForm();
}
) {}
get hasDigitalSignatureSet() {
return this.digitalSignatureExists || !!this.form.get('base64EncodedPrivateKey').value;
}
saveDigitalSignature() {
async ngOnInit(): Promise<void> {
await this.loadDigitalSignatureAndInitializeForm();
}
async saveDigitalSignature(): Promise<void> {
this._loadingService.start();
const formValue = this.form.getRawValue();
const digitalSignature: IDigitalSignature = {
...formValue,
@ -51,29 +54,32 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
? this._digitalSignatureService.update(digitalSignature)
: this._digitalSignatureService.save(digitalSignature);
this.addSubscription = observable.subscribe({
next: () => {
this.loadDigitalSignatureAndInitializeForm();
this._toaster.success(_('digital-signature-screen.action.save-success'));
},
error: error => {
if (error.status === HttpStatusCode.BadRequest) {
this._toaster.error(_('digital-signature-screen.action.certificate-not-valid-error'));
} else {
this._toaster.error(_('digital-signature-screen.action.save-error'));
}
},
});
try {
await firstValueFrom(observable);
await this.loadDigitalSignatureAndInitializeForm();
this._toaster.success(_('digital-signature-screen.action.save-success'));
} catch (error) {
console.error(error);
if (error.status === HttpStatusCode.BadRequest) {
this._toaster.error(_('digital-signature-screen.action.certificate-not-valid-error'));
} else {
this._toaster.error(_('digital-signature-screen.action.save-error'));
}
}
this._loadingService.stop();
}
removeDigitalSignature() {
this.addSubscription = this._digitalSignatureService.delete().subscribe(
() => {
this.loadDigitalSignatureAndInitializeForm();
this._toaster.success(_('digital-signature-screen.action.delete-success'));
},
() => this._toaster.error(_('digital-signature-screen.action.delete-error')),
);
async removeDigitalSignature(): Promise<void> {
this._loadingService.start();
try {
await firstValueFrom(this._digitalSignatureService.delete());
await this.loadDigitalSignatureAndInitializeForm();
this._toaster.success(_('digital-signature-screen.action.delete-success'));
} catch (error) {
console.error(error);
this._toaster.error(_('digital-signature-screen.action.delete-error'));
}
}
fileChanged(event, input: HTMLInputElement) {
@ -89,24 +95,19 @@ export class DigitalSignatureScreenComponent extends AutoUnsubscribe implements
fileReader.readAsDataURL(file as Blob);
}
loadDigitalSignatureAndInitializeForm() {
async loadDigitalSignatureAndInitializeForm(): Promise<void> {
this._loadingService.start();
this._digitalSignatureService
.getSignature()
.subscribe({
next: digitalSignature => {
this.digitalSignatureExists = true;
this.digitalSignature = digitalSignature;
},
error: () => {
this.digitalSignatureExists = false;
this.digitalSignature = {};
},
})
.add(() => {
this.form = this._getForm();
this._loadingService.stop();
});
try {
const digitalSignature = await firstValueFrom(this._digitalSignatureService.getSignature());
this.digitalSignatureExists = true;
this.digitalSignature = digitalSignature;
} catch (error) {
this.digitalSignatureExists = false;
this.digitalSignature = {};
}
this.form = this._getForm();
this._loadingService.stop();
}
private _getForm(): FormGroup {

View File

@ -1,56 +1,65 @@
import { Injectable, Injector } from '@angular/core';
import { GenericService, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
import { Inject, Injectable, Injector } from '@angular/core';
import { EntitiesService, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
import { TranslateService } from '@ngx-translate/core';
import { EMPTY, iif, Observable } from 'rxjs';
import { INotification, Notification, NotificationTypes } from '@red/domain';
import { map, switchMap } from 'rxjs/operators';
import { EMPTY, iif, Observable, of, timer } from 'rxjs';
import { Dossier, INotification, Notification, NotificationTypes } from '@red/domain';
import { map, switchMap, tap } from 'rxjs/operators';
import { notificationsTranslations } from '../translations/notifications-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ActiveDossiersService } from './dossiers/active-dossiers.service';
import { UserService } from '@services/user.service';
import { FilesMapService } from '@services/entity-services/files-map.service';
import dayjs from 'dayjs';
import { CHANGED_CHECK_INTERVAL } from '@utils/constants';
import { BASE_HREF } from '../tokens';
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
const INCLUDE_SEEN = false;
@Injectable({
providedIn: 'root',
})
export class NotificationsService extends GenericService<Notification> {
export class NotificationsService extends EntitiesService<Notification, INotification> {
constructor(
@Inject(BASE_HREF) private readonly _baseHref: string,
protected readonly _injector: Injector,
private readonly _translateService: TranslateService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _archivedDossiersService: ArchivedDossiersService,
private readonly _userService: UserService,
private readonly _filesMapService: FilesMapService,
) {
super(_injector, 'notification');
super(_injector, Notification, 'notification');
timer(0, CHANGED_CHECK_INTERVAL)
.pipe(
switchMap(() => (this._activeDossiersService.all.length ? of(null) : this._activeDossiersService.loadAll())),
switchMap(() => (this._archivedDossiersService.all.length ? of(null) : this._archivedDossiersService.loadAll())),
switchMap(() => this.#loadNotificationsIfChanged()),
)
.subscribe();
}
@Validate()
getNotifications(@RequiredParam() includeSeen: boolean): Observable<Notification[]> {
let queryParam: QueryParam;
if (includeSeen !== undefined && includeSeen !== null) {
queryParam = { key: 'includeSeen', value: includeSeen };
}
return this.getAll<{ notifications: Notification[] }>(this._defaultModelPath, [queryParam]).pipe(
map(response => response.notifications.filter(n => n.notificationType in NotificationTypes)),
mapEach(notification => this._new(notification)),
);
}
@Validate()
getNotificationsIfChanged(@RequiredParam() includeSeen: boolean): Observable<Notification[]> {
return this.hasChanges$().pipe(switchMap(changed => iif(() => changed, this.getNotifications(includeSeen), EMPTY)));
}
@Validate()
toggleNotificationRead(@RequiredParam() body: List, @RequiredParam() setRead: boolean) {
toggleNotificationRead(@RequiredParam() body: List, @RequiredParam() setRead: boolean): Observable<unknown> {
let queryParam: QueryParam;
if (setRead !== undefined && setRead !== null) {
queryParam = { key: 'setRead', value: setRead };
}
return this._post(body, `${this._defaultModelPath}/toggle-read`, [queryParam]);
return this._post(body, `${this._defaultModelPath}/toggle-read`, [queryParam]).pipe(switchMap(() => this.#loadAll()));
}
#loadAll(includeSeen = INCLUDE_SEEN): Observable<Notification[]> {
const queryParam: QueryParam = { key: 'includeSeen', value: includeSeen };
return this.getAll<{ notifications: Notification[] }>(this._defaultModelPath, [queryParam]).pipe(
map(response => response.notifications.filter(n => n.notificationType in NotificationTypes)),
mapEach(notification => this._new(notification)),
tap(notifications => this.setEntities(notifications)),
);
}
#loadNotificationsIfChanged(): Observable<Notification[]> {
return this.hasChanges$().pipe(switchMap(changed => iif(() => changed, this.#loadAll(), EMPTY)));
}
private _new(notification: INotification) {
@ -66,25 +75,27 @@ export class NotificationsService extends GenericService<Notification> {
private _translate(notification: INotification, translation: string): string {
const fileId = notification.target.fileId;
const dossierId = notification.target.dossierId;
const dossier = this._activeDossiersService.find(dossierId);
const dossier = this._activeDossiersService.find(dossierId) || this._archivedDossiersService.find(dossierId);
const fileName = notification.target.fileName;
return this._translateService.instant(translation, {
fileHref: this._getFileHref(dossierId, fileId),
dossierHref: this._getDossierHref(dossierId),
dossierName: notification.target?.dossierName || dossier?.dossierName || this._translateService.instant(_('dossier')),
fileHref: this._getFileHref(dossier, fileId),
dossierHref: this._getDossierHref(dossier),
dossierName:
notification.target?.dossierName ||
dossier?.dossierName ||
this._translateService.instant(_('notifications.deleted-dossier')),
fileName: fileName || this._translateService.instant(_('file')),
user: this._getUsername(notification.userId),
});
}
private _getFileHref(dossierId: string, fileId: string): string {
const dossierHref = this._getDossierHref(dossierId);
return `${dossierHref}/file/${fileId}`;
private _getFileHref(dossier: Dossier, fileId: string): string {
return dossier ? `${this._getDossierHref(dossier)}/file/${fileId}` : null;
}
private _getDossierHref(dossierId: string): string {
return `/ui/main/dossiers/${dossierId}`;
private _getDossierHref(dossier: Dossier): string {
return dossier ? `${this._baseHref}${dossier.routerLink}` : null;
}
private _getUsername(userId: string | undefined) {

View File

@ -665,7 +665,6 @@
"save": "Dokumenteninformation speichern",
"title": "Datei-Attribute anlegen"
},
"dossier": "Dossier",
"dossier-attribute-types": {
"date": "Datum",
"image": "Bild",
@ -1530,21 +1529,20 @@
}
},
"notification": {
"assign-approver": "Sie wurden dem Dokument <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> im Dossier <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b> als Genehmiger zugewiesen!",
"assign-reviewer": "Sie wurden dem Dokument <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> im Dossier <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b> als Reviewer zugewiesen!",
"document-approved": "<b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> wurde genehmigt!",
"assign-approver": "Sie wurden dem Dokument <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}<b> als Genehmiger zugewiesen!",
"assign-reviewer": "Sie wurden dem Dokument <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}<b> als Reviewer zugewiesen!",
"document-approved": "<b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> wurde genehmigt!",
"dossier-deleted": "Dossier: <b>{dossierName}</b> wurde gelöscht!",
"dossier-owner-removed": "Der Dossier-Owner von <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> wurde entfernt!",
"dossier-owner-set": "Eigentümer von <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> geändert zu <b>{user}</b>!",
"dossier-owner-removed": "Der Dossier-Owner von <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> wurde entfernt!",
"dossier-owner-set": "Eigentümer von <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> geändert zu <b>{user}</b>!",
"download-ready": "Ihr <b><a href='/ui/main/downloads', target=\"_blank\">Download</a></b> ist fertig!",
"no-data": "Du hast aktuell keine Benachrichtigungen",
"unassigned-from-file": "Sie wurden vom Dokument <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> im Dossier <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b> entfernt!",
"user-becomes-dossier-member": "<b>{user}</b> ist jetzt Mitglied des Dossiers <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
"user-demoted-to-reviewer": "<b>{user}</b> wurde im Dossier <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> auf die Reviewer-Berechtigung heruntergestuft!",
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> zum Genehmiger ernannt!",
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> entfernt!"
"unassigned-from-file": "Sie wurden vom Dokument <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}<b> entfernt!",
"user-becomes-dossier-member": "<b>{user}</b> ist jetzt Mitglied des Dossiers <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"user-demoted-to-reviewer": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> auf die Reviewer-Berechtigung heruntergestuft!",
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
},
"notifications": "Benachrichtigungen",
"notifications-screen": {
"category": {
"email-notifications": "E-Mail Benachrichtigungen",
@ -1583,6 +1581,11 @@
},
"title": "Benachrichtigungseinstellungen"
},
"notifications": {
"deleted-dossier": "",
"label": "Benachrichtigungen",
"mark-as": ""
},
"ocr": {
"confirmation-dialog": {
"cancel": "",

View File

@ -665,7 +665,6 @@
"save": "Save Document Info",
"title": "Introduce File Attributes"
},
"dossier": "Dossier",
"dossier-attribute-types": {
"date": "Date",
"image": "Image",
@ -1530,21 +1529,20 @@
}
},
"notification": {
"assign-approver": "You have been assigned as approver for <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
"assign-reviewer": "You have been assigned as reviewer for <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
"document-approved": " <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> has been approved!",
"assign-approver": "You have been assigned as approver for <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"assign-reviewer": "You have been assigned as reviewer for <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"document-approved": " <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> has been approved!",
"dossier-deleted": "Dossier: <b>{dossierName}</b> has been deleted!",
"dossier-owner-removed": "You have been removed as dossier owner from <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
"dossier-owner-set": "You are now the dossier owner of <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
"dossier-owner-removed": "You have been removed as dossier owner from <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"dossier-owner-set": "You are now the dossier owner of <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"download-ready": "Your <b><a href='/ui/main/downloads', target=\"_blank\">download</a></b> is ready!",
"no-data": "You currently have no notifications",
"unassigned-from-file": "You have been unassigned from <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
"user-becomes-dossier-member": "You have been added to dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
"user-demoted-to-reviewer": "You have been demoted to reviewer in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
"user-promoted-to-approver": "You have been promoted to approver in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
"user-removed-as-dossier-member": "You have been removed as a member from dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> !"
"unassigned-from-file": "You have been unassigned from <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> in dossier: <b>{dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}}}</b>!",
"user-becomes-dossier-member": "You have been added to dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"user-demoted-to-reviewer": "You have been demoted to reviewer in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"user-promoted-to-approver": "You have been promoted to approver in dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
"user-removed-as-dossier-member": "You have been removed as a member from dossier: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!"
},
"notifications": "Notifications",
"notifications-screen": {
"category": {
"email-notifications": "Email Notifications",
@ -1583,6 +1581,11 @@
},
"title": "Notifications Preferences"
},
"notifications": {
"deleted-dossier": "Deleted Dossier",
"label": "Notifications",
"mark-as": "Mark as {type, select, read{read} unread{unread} other{}}"
},
"ocr": {
"confirmation-dialog": {
"cancel": "Cancel",

View File

@ -1,6 +1,6 @@
{
"name": "redaction",
"version": "3.376.0",
"version": "3.377.0",
"private": true,
"license": "MIT",
"scripts": {

Binary file not shown.