Redo comments layout

This commit is contained in:
Adina Țeudan 2021-06-15 15:12:53 +03:00
parent d744eab962
commit b2f5e02673
34 changed files with 333 additions and 297 deletions

View File

@ -24,6 +24,7 @@ export class AnnotationWrapper {
comments: Comment[] = []; comments: Comment[] = [];
firstTopLeftPoint: Point; firstTopLeftPoint: Point;
annotationId: string; annotationId: string;
shortContent: string;
content: string; content: string;
value: string; value: string;
userId: string; userId: string;
@ -361,6 +362,7 @@ export class AnnotationWrapper {
if (entry.section) { if (entry.section) {
content += 'In section: "' + entry.section + '"'; content += 'In section: "' + entry.section + '"';
} }
annotationWrapper.shortContent = entry.reason || content;
annotationWrapper.content = content; annotationWrapper.content = content;
} }
} }

View File

@ -1,4 +1,4 @@
<redaction-side-nav title="type"> <redaction-side-nav [title]="type">
<ng-container *ngFor="let item of items[type]"> <ng-container *ngFor="let item of items[type]">
<div <div
*ngIf=" *ngIf="

View File

@ -107,11 +107,12 @@
</div> </div>
</div> </div>
<div *ngIf="isSearchOpen" class="search-input-container"> <div *ngIf="isSearchOpen" class="search-input-container">
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
placeholder="file-attributes-csv-import.search.placeholder" placeholder="file-attributes-csv-import.search.placeholder"
type="search"
width="full" width="full"
></redaction-search-input> ></redaction-input-with-action>
</div> </div>
<div [class.search-open]="isSearchOpen" class="csv-header-pill-content"> <div [class.search-open]="isSearchOpen" class="csv-header-pill-content">
<div <div

View File

@ -37,10 +37,11 @@
</span> </span>
<div class="attributes-actions-container"> <div class="attributes-actions-container">
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
[placeholder]="'dictionary-listing.search'" [placeholder]="'dictionary-listing.search'"
></redaction-search-input> type="search"
></redaction-input-with-action>
<div class="actions"> <div class="actions">
<redaction-icon-button <redaction-icon-button
(action)="openAddEditDictionaryDialog()" (action)="openAddEditDictionaryDialog()"

View File

@ -35,10 +35,11 @@
</span> </span>
<div class="actions flex-1"> <div class="actions flex-1">
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
[placeholder]="'dossier-templates-listing.search'" [placeholder]="'dossier-templates-listing.search'"
></redaction-search-input> type="search"
></redaction-input-with-action>
<redaction-icon-button <redaction-icon-button
(action)="openAddDossierTemplateDialog()" (action)="openAddDossierTemplateDialog()"

View File

@ -46,10 +46,11 @@
<mat-spinner *ngIf="loading" diameter="15"></mat-spinner> <mat-spinner *ngIf="loading" diameter="15"></mat-spinner>
<div class="attributes-actions-container"> <div class="attributes-actions-container">
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
[placeholder]="'file-attributes-listing.search'" [placeholder]="'file-attributes-listing.search'"
></redaction-search-input> type="search"
></redaction-input-with-action>
<input <input
#fileInput #fileInput
(change)="importCSV($event.target['files'])" (change)="importCSV($event.target['files'])"

View File

@ -8,10 +8,11 @@
<div class="breadcrumb" translate="user-management"></div> <div class="breadcrumb" translate="user-management"></div>
<div class="actions"> <div class="actions">
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
[placeholder]="'user-listing.search'" [placeholder]="'user-listing.search'"
></redaction-search-input> type="search"
></redaction-input-with-action>
<redaction-icon-button <redaction-icon-button
(action)="openAddEditUserDialog($event)" (action)="openAddEditUserDialog($event)"
*ngIf="permissionsService.isUserAdmin()" *ngIf="permissionsService.isUserAdmin()"

View File

@ -50,7 +50,7 @@
.page-header .actions { .page-header .actions {
justify-content: flex-end; justify-content: flex-end;
redaction-search-input:not(:last-child) { redaction-input-with-action:not(:last-child) {
margin-right: 16px; margin-right: 16px;
} }

View File

@ -1,21 +1,7 @@
@import '../../../../../assets/styles/red-variables'; @import '../../../../../assets/styles/red-variables';
:host {
margin-right: 0;
}
.annotation-actions { .annotation-actions {
position: absolute;
right: -8px;
top: -8px;
height: calc(100% + 8px);
box-sizing: border-box;
display: none; display: none;
flex-direction: column;
align-items: flex-end;
justify-content: flex-start;
width: 80px;
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, #f9fafb, #f9fafb);
redaction-circle-button { redaction-circle-button {
display: block; display: block;
@ -28,6 +14,10 @@
&.visible { &.visible {
display: flex; display: flex;
} }
> *:not(:last-child) {
margin-right: 2px;
}
} }
.false-positive-icon { .false-positive-icon {

View File

@ -1,79 +1,34 @@
<div class="wrapper"> <div *ngFor="let comment of annotation.comments" class="comment">
<ng-container *ngIf="expanded"> <div class="comment-details-wrapper">
<div *ngFor="let comment of annotation.comments; let idx = index" class="comment"> <div class="comment-details">
<div <div>{{ getOwnerName(comment) }}</div>
[class.comment-owner]="isCommentOwner(comment)" <div>{{ comment.date | date: 'd MMM. yyyy, hh:mm a' }}</div>
[class.red]="isCommentOwner(comment)"
class="comment-icon"
>
<mat-icon
[svgIcon]="isCommentOwner(comment) ? 'red:comment-fill' : 'red:comment'"
></mat-icon>
</div> </div>
<div <div class="comment-actions">
(click)="deleteComment(comment)"
[class.comment-owner]="isCommentOwner(comment)"
class="trash-icon red"
>
<mat-icon svgIcon="red:trash"></mat-icon>
</div>
<div>
<div class="owner">{{ getOwnerName(comment) }}</div>
<div>{{ comment.text }}</div>
</div>
</div>
</ng-container>
<div class="actions-container all-caps-label">
<div (click)="toggleExpandComments($event)" *ngIf="annotation.comments.length">
{{
expanded
? translateService.instant('comments.hide-comments')
: translateService.instant(
annotation.comments.length === 1
? 'comments.comment'
: 'comments.comments',
{
count: annotation.comments.length
}
)
}}
</div>
<div
(click)="toggleAddingComment($event)"
*ngIf="!addingComment && canAddComment && permissionsService.canAddComment()"
translate="comments.add-comment"
></div>
</div>
<form
(submit)="addComment()"
*ngIf="addingComment && permissionsService.canAddComment()"
[formGroup]="commentForm"
>
<div class="red-input-group">
<input
[placeholder]="translateService.instant('comments.add-comment')"
class="w-full"
formControlName="comment"
name="comment"
type="text"
/>
</div>
</form>
<div *ngIf="addingComment" class="comment-actions-container">
<redaction-circle-button <redaction-circle-button
(action)="addComment()" (action)="deleteComment(comment)"
[disabled]="!commentForm.value.comment" *ngIf="isCommentOwner(comment)"
icon="red:check" [iconSize]="10"
type="primary" [size]="20"
class="pointer"
icon="red:trash"
></redaction-circle-button> ></redaction-circle-button>
<div
(click)="toggleAddingComment($event)"
class="all-caps-label cancel"
translate="comments.cancel"
></div>
</div> </div>
</div>
<div>{{ comment.text }}</div>
</div> </div>
<redaction-input-with-action
(action)="addComment()"
*ngIf="permissionsService.canAddComment()"
[form]="commentForm"
[placeholder]="translateService.instant('comments.add-comment')"
type="submit"
width="full"
></redaction-input-with-action>
<div
(click)="toggleExpandComments($event)"
class="all-caps-label pointer hide-comments"
translate="comments.hide-comments"
></div>

View File

@ -1,74 +1,53 @@
@import '../../../../../assets/styles/red-variables'; @import '../../../../../assets/styles/red-variables';
.wrapper { :host {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-top: 10px; padding: 8px 0 8px 16px;
> *:not(:last-child) {
margin-bottom: 10px;
}
.comment { .comment {
display: flex; margin-bottom: 10px;
> *:not(:last-child) { .comment-details-wrapper {
margin-right: 12px; margin-bottom: 4px;
} position: relative;
.owner { .comment-details {
opacity: 0.7; opacity: 0.7;
} }
.comment-actions {
display: none;
position: absolute;
right: 0;
top: -4px;
mat-icon { mat-icon {
width: 14px; width: 10px;
height: 16px; height: 10px;
&:not(:last-child) {
margin-right: 8px;
}
}
}
} }
.comment-icon { &:hover .comment-details-wrapper .comment-actions {
color: $grey-5;
display: initial;
}
.trash-icon {
display: none;
}
.red {
color: $primary;
}
&:hover {
.comment-icon.comment-owner {
display: none;
}
.trash-icon.comment-owner {
display: initial; display: initial;
} }
} }
redaction-input-with-action {
margin: 5px 0 10px 0;
} }
.comment-actions-container, .hide-comments {
.actions-container { margin-top: 5px;
display: flex;
align-items: center;
> *:not(:last-child) {
margin-right: 15px;
}
} }
.actions-container { .comment,
margin-left: 26px; .hide-comments {
padding-left: 12px;
> *:not(:last-child) {
margin-right: 5px;
}
> div:not(:last-child):after {
content: '';
margin-left: 5px;
}
} }
} }

View File

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, Input } from '@angular/core'; import { ChangeDetectorRef, Component, HostBinding, Input } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Comment } from '@redaction/red-ui-http'; import { Comment } from '@redaction/red-ui-http';
import { ManualAnnotationService } from '../../services/manual-annotation.service'; import { ManualAnnotationService } from '../../services/manual-annotation.service';
@ -15,9 +15,8 @@ import { PermissionsService } from '@services/permissions.service';
}) })
export class CommentsComponent { export class CommentsComponent {
@Input() annotation: AnnotationWrapper; @Input() annotation: AnnotationWrapper;
expanded = false;
commentForm: FormGroup; commentForm: FormGroup;
addingComment = false; @HostBinding('class.hidden') private _hidden = true;
constructor( constructor(
readonly translateService: TranslateService, readonly translateService: TranslateService,
@ -29,34 +28,12 @@ export class CommentsComponent {
private readonly _manualAnnotationService: ManualAnnotationService private readonly _manualAnnotationService: ManualAnnotationService
) { ) {
this.commentForm = this._formBuilder.group({ this.commentForm = this._formBuilder.group({
comment: ['', Validators.required] value: ['', Validators.required]
}); });
} }
get canAddComment() {
return !this.annotation.isChangeLogRemoved;
}
toggleExpandComments($event: MouseEvent): void {
$event.stopPropagation();
if (!this.annotation.comments.length) {
return;
}
this.expanded = !this.expanded;
this._changeDetectorRef.detectChanges();
}
toggleAddingComment($event?: MouseEvent): void {
$event?.stopPropagation();
this.addingComment = !this.addingComment;
if (this.addingComment) {
this.expanded = true;
}
this._changeDetectorRef.detectChanges();
}
addComment(): void { addComment(): void {
const value = this.commentForm.value.comment; const value = this.commentForm.value.value;
if (value) { if (value) {
this._manualAnnotationService this._manualAnnotationService
.addComment(value, this.annotation.id) .addComment(value, this.annotation.id)
@ -68,17 +45,21 @@ export class CommentsComponent {
}); });
}); });
this.commentForm.reset(); this.commentForm.reset();
this.toggleAddingComment();
} }
} }
toggleExpandComments($event?: MouseEvent) {
$event?.stopPropagation();
this._hidden = !this._hidden;
}
deleteComment(comment: Comment): void { deleteComment(comment: Comment): void {
this._manualAnnotationService this._manualAnnotationService
.deleteComment(comment.id, this.annotation.id) .deleteComment(comment.id, this.annotation.id)
.subscribe(() => { .subscribe(() => {
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1); this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
if (!this.annotation.comments.length) { if (!this.annotation.comments.length) {
this.expanded = false; this._hidden = true;
} }
}); });
} }

View File

@ -155,7 +155,11 @@
(action)="logAnnotation(annotation)" (action)="logAnnotation(annotation)"
[requiredClicks]="2" [requiredClicks]="2"
> >
<div class="details"> <div
[matTooltip]="annotation.content"
class="details"
matTooltipPosition="above"
>
<redaction-type-annotation-icon <redaction-type-annotation-icon
[annotation]="annotation" [annotation]="annotation"
></redaction-type-annotation-icon> ></redaction-type-annotation-icon>
@ -174,17 +178,12 @@
>: </strong >: </strong
>{{ annotation.dictionary | humanize: false }} >{{ annotation.dictionary | humanize: false }}
</div> </div>
<div *ngIf="annotation.content && !annotation.isHint"> <div *ngIf="annotation.shortContent && !annotation.isHint">
<strong><span translate="content"></span>: </strong <strong><span translate="content"></span>: </strong
>{{ annotation.content }} >{{ annotation.shortContent }}
</div> </div>
</div> </div>
<ng-container
*ngIf="!multiSelectActive"
[ngTemplateOutletContext]="{ annotation: annotation }"
[ngTemplateOutlet]="annotationActionsTemplate"
>
</ng-container>
<div class="active-icon-marker-container"> <div class="active-icon-marker-container">
<redaction-round-checkbox <redaction-round-checkbox
*ngIf=" *ngIf="
@ -194,8 +193,37 @@
></redaction-round-checkbox> ></redaction-round-checkbox>
</div> </div>
</div> </div>
</redaction-hidden-action>
<div class="actions-wrapper">
<div
(click)="toggleExpandComments(annotation, $event)"
[matTooltip]="
(annotation.comments.length === 1
? 'comments.comment'
: 'comments.comments'
)
| translate
: {
count: annotation.comments.length
}
"
class="comments-counter"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation.comments.length }}
</div>
<div class="actions">
<ng-container
*ngIf="!multiSelectActive"
[ngTemplateOutletContext]="{ annotation: annotation }"
[ngTemplateOutlet]="annotationActionsTemplate"
>
</ng-container>
</div>
</div>
<redaction-comments [annotation]="annotation"></redaction-comments> <redaction-comments [annotation]="annotation"></redaction-comments>
</redaction-hidden-action>
</div> </div>
</div> </div>
</div> </div>

View File

@ -146,7 +146,7 @@
} }
.annotation { .annotation {
padding: 10px 21px 10px 6px; padding: 10px 16px 8px 10px;
font-size: 11px; font-size: 11px;
line-height: 14px; line-height: 14px;
cursor: pointer; cursor: pointer;
@ -164,6 +164,36 @@
position: relative; position: relative;
} }
.actions-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
min-height: 34px;
padding-left: 18px;
.comments-counter {
cursor: pointer;
display: flex;
align-items: center;
padding: 0 8px;
transition: background-color 0.2s;
line-height: 13px;
height: 24px;
border-radius: 12px;
mat-icon {
width: 10px;
height: 10px;
margin-right: 4px;
}
&:hover {
background-color: $grey-4;
}
}
}
redaction-type-annotation-icon { redaction-type-annotation-icon {
margin-top: 6px; margin-top: 6px;
margin-right: 10px; margin-right: 10px;
@ -184,10 +214,8 @@
@include scroll-bar; @include scroll-bar;
} }
&.has-scrollbar:hover { &.has-scrollbar:hover .annotation-wrapper .annotation {
.annotation { padding-right: 5px;
padding-right: 10px;
}
} }
} }
} }

View File

@ -6,8 +6,10 @@ import {
HostListener, HostListener,
Input, Input,
Output, Output,
QueryList,
TemplateRef, TemplateRef,
ViewChild ViewChild,
ViewChildren
} from '@angular/core'; } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { AnnotationProcessingService } from '../../services/annotation-processing.service'; import { AnnotationProcessingService } from '../../services/annotation-processing.service';
@ -16,6 +18,7 @@ import scrollIntoView from 'scroll-into-view-if-needed';
import { debounce } from '@utils/debounce'; import { debounce } from '@utils/debounce';
import { FileDataModel } from '@models/file/file-data.model'; import { FileDataModel } from '@models/file/file-data.model';
import { FilterModel } from '@shared/components/filters/popup-filter/model/filter.model'; import { FilterModel } from '@shared/components/filters/popup-filter/model/filter.model';
import { CommentsComponent } from '../comments/comments.component';
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
@ -46,6 +49,7 @@ export class FileWorkloadComponent {
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>(); @Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
displayedPages: number[] = []; displayedPages: number[] = [];
pagesPanelActive = true; pagesPanelActive = true;
@ViewChildren(CommentsComponent) annotationCommentsComponents: QueryList<CommentsComponent>;
@ViewChild('annotationsElement') private _annotationsElement: ElementRef; @ViewChild('annotationsElement') private _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef; @ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
@ -99,6 +103,13 @@ export class FileWorkloadComponent {
return this.selectedAnnotations?.find(a => a?.id === annotation.id); return this.selectedAnnotations?.find(a => a?.id === annotation.id);
} }
toggleExpandComments(annotation: AnnotationWrapper, $event: MouseEvent) {
$event.stopPropagation();
this.annotationCommentsComponents
.find(c => c.annotation === annotation)
.toggleExpandComments();
}
logAnnotation(annotation: AnnotationWrapper) { logAnnotation(annotation: AnnotationWrapper) {
console.log(annotation); console.log(annotation);
} }

View File

@ -43,12 +43,13 @@
class="info" class="info"
></pre> ></pre>
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
[placeholder]="'assign-dossier-owner.dialog.search' | translate" [placeholder]="'assign-dossier-owner.dialog.search' | translate"
[width]="560" [width]="560"
class="search-container" class="search-container"
></redaction-search-input> type="search"
></redaction-input-with-action>
<div class="members-list"> <div class="members-list">
<div <div

View File

@ -26,7 +26,7 @@
(action)="openAssignDossierMembersDialog.emit()" (action)="openAssignDossierMembersDialog.emit()"
*ngIf="permissionsService.isManager() && canAdd" *ngIf="permissionsService.isManager() && canAdd"
[class.large-spacing]="largeSpacing" [class.large-spacing]="largeSpacing"
[small]="true" [size]="32"
class="member" class="member"
icon="red:plus" icon="red:plus"
tooltip="dossier-details.assign-members" tooltip="dossier-details.assign-members"

View File

@ -109,7 +109,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
dossierTemplateId: this.dossierForm.get('dossierTemplateId').value dossierTemplateId: this.dossierForm.get('dossierTemplateId').value
}; };
const updatedDossier = await this._appStateService.addOrUpdateDossier(dossier); const updatedDossier = await this._appStateService.addOrUpdateDossier(dossier);
this.updateDossier.emit(updatedDossier); if (updatedDossier) this.updateDossier.emit(updatedDossier);
} }
openDeleteDossierDialog($event: MouseEvent) { openDeleteDossierDialog($event: MouseEvent) {

View File

@ -32,10 +32,11 @@
[icon]="'red:template'" [icon]="'red:template'"
[primaryFilters]="dossierTemplateFilters" [primaryFilters]="dossierTemplateFilters"
></redaction-popup-filter> ></redaction-popup-filter>
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
[placeholder]="'dossier-listing.search'" [placeholder]="'dossier-listing.search'"
></redaction-search-input> type="search"
></redaction-input-with-action>
<div <div
(click)="resetFilters()" (click)="resetFilters()"
*ngIf="hasActiveFilters" *ngIf="hasActiveFilters"

View File

@ -25,10 +25,11 @@
[primaryFilters]="needsWorkFilters" [primaryFilters]="needsWorkFilters"
></redaction-popup-filter> ></redaction-popup-filter>
<redaction-search-input <redaction-input-with-action
[form]="searchForm" [form]="searchForm"
[placeholder]="'dossier-overview.search'" [placeholder]="'dossier-overview.search'"
></redaction-search-input> type="search"
></redaction-input-with-action>
<div <div
(click)="resetFilters()" (click)="resetFilters()"

View File

@ -8,10 +8,10 @@
[class.dark-bg]="type === 'dark-bg'" [class.dark-bg]="type === 'dark-bg'"
[class.overlay]="showDot" [class.overlay]="showDot"
[class.primary]="type === 'primary'" [class.primary]="type === 'primary'"
[class.small]="small"
[class.warn]="type === 'warn'" [class.warn]="type === 'warn'"
[disabled]="disabled" [disabled]="disabled"
mat-icon-button mat-icon-button
type="submit"
> >
<mat-icon [svgIcon]="icon"></mat-icon> <mat-icon [svgIcon]="icon"></mat-icon>
</button> </button>

View File

@ -1,34 +1,34 @@
@import '../../../../../../assets/styles/red-variables'; @import '../../../../../../assets/styles/red-variables';
:host { :host {
height: 34px; height: var(--size);
width: 34px; width: var(--size);
align-items: center; align-items: center;
}
button { button {
height: 34px; height: var(--size);
width: 34px; width: var(--size);
line-height: 33px; line-height: var(--size);
&.small {
height: 32px;
width: 32px;
line-height: 32px;
}
mat-icon { mat-icon {
width: 14px; width: var(--iconSize);
height: 34px; height: var(--iconSize);
line-height: var(--iconSize);
margin: 0; margin: 0;
svg {
line-height: var(--iconSize);
}
} }
&.primary {
&.mat-button-disabled { &.mat-button-disabled {
cursor: not-allowed;
}
&.primary.mat-button-disabled {
background-color: $grey-6; background-color: $grey-6;
color: $white !important; color: $white !important;
} }
}
&.warn:not([disabled]) { &.warn:not([disabled]) {
background-color: $yellow-2; background-color: $yellow-2;
@ -37,4 +37,5 @@ button {
background-color: $yellow-2; background-color: $yellow-2;
} }
} }
}
} }

View File

@ -1,6 +1,7 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ElementRef,
EventEmitter, EventEmitter,
Input, Input,
Output, Output,
@ -21,13 +22,21 @@ export class CircleButtonComponent {
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above'; @Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
@Input() tooltipClass: string; @Input() tooltipClass: string;
@Input() disabled = false; @Input() disabled = false;
@Input() small = false;
@Input() type: 'default' | 'primary' | 'warn' | 'dark-bg' = 'default'; @Input() type: 'default' | 'primary' | 'warn' | 'dark-bg' = 'default';
@Input() removeTooltip = false; @Input() removeTooltip = false;
@Output() action = new EventEmitter<any>(); @Output() action = new EventEmitter<any>();
@Input() size = 34;
@Input() iconSize = 14;
@ViewChild(MatTooltip) matTooltip: MatTooltip; @ViewChild(MatTooltip) matTooltip: MatTooltip;
constructor(private _elRef: ElementRef) {}
ngOnInit(): void {
this._elRef.nativeElement.style.setProperty('--size', this.size + 'px');
this._elRef.nativeElement.style.setProperty('--iconSize', this.iconSize + 'px');
}
performAction($event: any) { performAction($event: any) {
if (!this.disabled) { if (!this.disabled) {
if (this.removeTooltip) { if (this.removeTooltip) {

View File

@ -0,0 +1,37 @@
<form (submit)="type === 'submit' && submit()" [formGroup]="form">
<div [style.max-width]="computedWidth" [style.width]="computedWidth" class="red-input-group">
<input
[formControlName]="formControlName"
[name]="formControlName"
[placeholder]="placeholder | translate"
class="with-icon mt-0"
type="text"
/>
<!-- Search-->
<mat-icon
*ngIf="type === 'search' && !hasContent"
class="icon-right"
svgIcon="red:search"
></mat-icon>
<redaction-circle-button
(action)="clearContent()"
*ngIf="type === 'search' && hasContent"
[disabled]="form.invalid"
[size]="25"
icon="red:close"
>
</redaction-circle-button>
<!-- Submit-->
<redaction-circle-button
(action)="submit($event)"
*ngIf="type === 'submit'"
[disabled]="form.invalid"
[size]="25"
icon="red:collapse"
>
</redaction-circle-button>
</div>
</form>

View File

@ -0,0 +1,14 @@
:host {
display: block;
}
mat-icon.disabled {
opacity: 0.7;
cursor: not-allowed;
}
redaction-circle-button {
position: absolute;
bottom: 5px;
right: 5px;
}

View File

@ -0,0 +1,38 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'redaction-input-with-action',
templateUrl: './input-with-action.component.html',
styleUrls: ['./input-with-action.component.scss']
})
export class InputWithActionComponent {
@Input() form: FormGroup;
@Input() placeholder: string;
@Input() width: number | 'full' = 250;
@Input() type: 'search' | 'submit';
@Output() action = new EventEmitter<any>();
get formControlName(): 'query' | 'value' {
return this.type === 'search' ? 'query' : 'value';
}
get hasContent() {
return !!this.form.get(this.formControlName).value.length;
}
get computedWidth() {
return this.width === 'full' ? '100%' : `${this.width}px`;
}
clearContent() {
this.form.patchValue({ query: '' });
}
submit($event?: MouseEvent) {
$event?.stopPropagation();
if (this.hasContent) {
this.action.emit();
}
}
}

View File

@ -1,18 +0,0 @@
<form [formGroup]="form">
<div [style.max-width]="computedWidth" [style.width]="computedWidth" class="red-input-group">
<input
[placeholder]="placeholder | translate"
class="with-icon mt-0"
formControlName="query"
name="query"
type="text"
/>
<mat-icon *ngIf="!hasContent" class="icon-right" svgIcon="red:search"></mat-icon>
<mat-icon
(click)="clearContent()"
*ngIf="hasContent"
class="icon-right pointer"
svgIcon="red:close"
></mat-icon>
</div>
</form>

View File

@ -1,25 +0,0 @@
import { Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'redaction-search-input',
templateUrl: './search-input.component.html',
styleUrls: ['./search-input.component.scss']
})
export class SearchInputComponent {
@Input() form: FormGroup;
@Input() placeholder: string;
@Input() width: number | 'full' = 250;
get hasContent() {
return !!this.form.get('query').value.length;
}
get computedWidth() {
return this.width === 'full' ? '100%' : `${this.width}px`;
}
clearContent() {
this.form.patchValue({ query: '' });
}
}

View File

@ -12,7 +12,6 @@ import { FileDownloadBtnComponent } from './components/buttons/file-download-btn
import { IconButtonComponent } from './components/buttons/icon-button/icon-button.component'; import { IconButtonComponent } from './components/buttons/icon-button/icon-button.component';
import { UserButtonComponent } from './components/buttons/user-button/user-button.component'; import { UserButtonComponent } from './components/buttons/user-button/user-button.component';
import { MatConfigModule } from '../mat-config/mat-config.module'; import { MatConfigModule } from '../mat-config/mat-config.module';
import { SearchInputComponent } from './components/search-input/search-input.component';
import { IconsModule } from '../icons/icons.module'; import { IconsModule } from '../icons/icons.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component'; import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
@ -37,6 +36,7 @@ import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { QuickFiltersComponent } from './components/filters/quick-filters/quick-filters.component'; import { QuickFiltersComponent } from './components/filters/quick-filters/quick-filters.component';
import { PopupFilterComponent } from '@shared/components/filters/popup-filter/popup-filter.component'; import { PopupFilterComponent } from '@shared/components/filters/popup-filter/popup-filter.component';
import { AssignUserDropdownComponent } from './components/assign-user-dropdown/assign-user-dropdown.component'; import { AssignUserDropdownComponent } from './components/assign-user-dropdown/assign-user-dropdown.component';
import { InputWithActionComponent } from '@shared/components/input-with-action/input-with-action.component';
const buttons = [ const buttons = [
ChevronButtonComponent, ChevronButtonComponent,
@ -51,7 +51,7 @@ const components = [
InitialsAvatarComponent, InitialsAvatarComponent,
TableColNameComponent, TableColNameComponent,
PaginationComponent, PaginationComponent,
SearchInputComponent, InputWithActionComponent,
AnnotationIconComponent, AnnotationIconComponent,
SimpleDoughnutChartComponent, SimpleDoughnutChartComponent,
StatusBarComponent, StatusBarComponent,

View File

@ -18,6 +18,14 @@ export class PermissionsService {
return this._userService.user; return this._userService.user;
} }
private get _activeFile(): FileStatusWrapper | undefined {
return this._appStateService.activeFile;
}
private get _activeDossier(): DossierWrapper | undefined {
return this._appStateService.activeDossier;
}
isManager(user?: User) { isManager(user?: User) {
return this._userService.isManager(user); return this._userService.isManager(user);
} }
@ -93,7 +101,6 @@ export class PermissionsService {
canAssignUser(fileStatus = this._activeFile): boolean { canAssignUser(fileStatus = this._activeFile): boolean {
const precondition = const precondition =
this.isDossierMember() &&
!fileStatus.isProcessing && !fileStatus.isProcessing &&
!fileStatus.isError && !fileStatus.isError &&
!fileStatus.isApproved && !fileStatus.isApproved &&
@ -209,12 +216,4 @@ export class PermissionsService {
canAddComment(fileStatus = this._activeFile): boolean { canAddComment(fileStatus = this._activeFile): boolean {
return this.isFileReviewer(fileStatus) || this.isApprover(); return this.isFileReviewer(fileStatus) || this.isApprover();
} }
private get _activeFile(): FileStatusWrapper | undefined {
return this._appStateService.activeFile;
}
private get _activeDossier(): DossierWrapper | undefined {
return this._appStateService.activeDossier;
}
} }

View File

@ -1,6 +1,6 @@
{ {
"OAUTH_URL": "https://dev-08.iqser.cloud/auth/realms/redaction", "OAUTH_URL": "https://dev-06.iqser.cloud/auth/realms/redaction",
"API_URL": "https://dev-08.iqser.cloud/redaction-gateway-v1", "API_URL": "https://dev-06.iqser.cloud/redaction-gateway-v1",
"OAUTH_CLIENT_ID": "redaction", "OAUTH_CLIENT_ID": "redaction",
"BACKEND_APP_VERSION": "4.4.40", "BACKEND_APP_VERSION": "4.4.40",
"FRONTEND_APP_VERSION": "1.1", "FRONTEND_APP_VERSION": "1.1",

View File

@ -162,7 +162,7 @@
}, },
"errors": { "errors": {
"dossier-already-exists": "Dossier with this name already exists!", "dossier-already-exists": "Dossier with this name already exists!",
"generic": "Failed to save dossier" "generic": "Failed to save dossier."
}, },
"actions": { "actions": {
"save": "Save", "save": "Save",
@ -520,8 +520,8 @@
"comments": { "comments": {
"comment": "{{count}} comment", "comment": "{{count}} comment",
"comments": "{{count}} comments", "comments": "{{count}} comments",
"add-comment": "Add a comment", "add-comment": "Enter comment",
"hide-comments": "Hide", "hide-comments": "Hide comments",
"cancel": "Cancel" "cancel": "Cancel"
}, },
"UNPROCESSED": "Unprocessed", "UNPROCESSED": "Unprocessed",

View File

@ -13,6 +13,8 @@
} }
.mat-checkbox-layout { .mat-checkbox-layout {
align-items: center !important;
.mat-checkbox-inner-container { .mat-checkbox-inner-container {
margin-left: 0; margin-left: 0;
} }