diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index adee1eb04..edbb49309 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -1,129 +1,144 @@ -import {BrowserModule} from '@angular/platform-browser'; -import {APP_INITIALIZER, NgModule} from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; -import {AppComponent} from './app.component'; -import {RouterModule} from '@angular/router'; -import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; -import {ReactiveFormsModule} from "@angular/forms"; -import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule} from "@angular/common/http"; -import {BaseScreenComponent} from './screens/base-screen/base-screen.component'; -import {ProjectListingScreenComponent} from './screens/project-listing-screen/project-listing-screen.component'; -import {ProjectOverviewScreenComponent} from './screens/project-overview-screen/project-overview-screen.component'; -import {MatToolbarModule} from "@angular/material/toolbar"; -import {ApiModule} from "@redaction/red-ui-http"; -import {ApiPathInterceptorService} from "./interceptor/api-path-interceptor.service"; -import {MatButtonModule} from "@angular/material/button"; -import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; -import {TranslateHttpLoader} from '@ngx-translate/http-loader'; -import {MatMenuModule} from "@angular/material/menu"; -import {languageInitializer} from "./i18n/language.initializer"; -import {LanguageService} from "./i18n/language.service"; -import {MatIconModule} from "@angular/material/icon"; -import {IconsModule} from "./icons/icons.module"; -import {AddEditProjectDialogComponent} from './screens/project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component'; -import {MatDialogModule} from '@angular/material/dialog'; -import {MatSnackBarModule} from "@angular/material/snack-bar"; -import {MatTooltipModule} from "@angular/material/tooltip"; -import {ConfirmationDialogComponent} from './common/confirmation-dialog/confirmation-dialog.component'; -import {FilePreviewScreenComponent} from './screens/file/file-preview-screen/file-preview-screen.component'; -import {PdfViewerComponent} from './screens/file/pdf-viewer/pdf-viewer.component'; -import {MatTabsModule} from "@angular/material/tabs"; -import {MatButtonToggleModule} from "@angular/material/button-toggle"; -import {NgpSortModule} from "ngp-sort-pipe"; -import {MatFormFieldModule} from "@angular/material/form-field"; -import {MatSelectModule} from "@angular/material/select"; -import {NgxDropzoneModule} from "ngx-dropzone"; -import {MatSidenavModule} from "@angular/material/sidenav"; -import {FileDetailsDialogComponent} from './screens/file/file-preview-screen/file-details-dialog/file-details-dialog.component'; -import {ToastrModule} from "ngx-toastr"; -import {ServiceWorkerModule} from '@angular/service-worker'; -import {environment} from '../environments/environment'; -import {ProjectDetailsDialogComponent} from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component'; -import {AuthModule} from "./auth/auth.module"; -import {AuthGuard} from "./auth/auth.guard"; -import {FileUploadModule} from "./upload/file-upload.module"; +import { AppComponent } from './app.component'; +import { RouterModule } from '@angular/router'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ReactiveFormsModule } from '@angular/forms'; +import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; +import { BaseScreenComponent } from './screens/base-screen/base-screen.component'; +import { ProjectListingScreenComponent } from './screens/project-listing-screen/project-listing-screen.component'; +import { ProjectOverviewScreenComponent } from './screens/project-overview-screen/project-overview-screen.component'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { ApiModule } from '@redaction/red-ui-http'; +import { ApiPathInterceptorService } from './interceptor/api-path-interceptor.service'; +import { MatButtonModule } from '@angular/material/button'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; +import { MatMenuModule } from '@angular/material/menu'; +import { languageInitializer } from './i18n/language.initializer'; +import { LanguageService } from './i18n/language.service'; +import { MatIconModule } from '@angular/material/icon'; +import { IconsModule } from './icons/icons.module'; +import { AddEditProjectDialogComponent } from './screens/project-listing-screen/add-edit-project-dialog/add-edit-project-dialog.component'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ConfirmationDialogComponent } from './common/confirmation-dialog/confirmation-dialog.component'; +import { FilePreviewScreenComponent } from './screens/file/file-preview-screen/file-preview-screen.component'; +import { PdfViewerComponent } from './screens/file/pdf-viewer/pdf-viewer.component'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { NgpSortModule } from 'ngp-sort-pipe'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSidenavModule } from '@angular/material/sidenav'; +import { FileDetailsDialogComponent } from './screens/file/file-preview-screen/file-details-dialog/file-details-dialog.component'; +import { ToastrModule } from 'ngx-toastr'; +import { ServiceWorkerModule } from '@angular/service-worker'; +import { environment } from '../environments/environment'; +import { ProjectDetailsDialogComponent } from './screens/project-overview-screen/project-details-dialog/project-details-dialog.component'; +import { AuthModule } from './auth/auth.module'; +import { AuthGuard } from './auth/auth.guard'; +import { FileUploadModule } from './upload/file-upload.module'; import { FullPageLoadingIndicatorComponent } from './utils/full-page-loading-indicator/full-page-loading-indicator.component'; -import {MatProgressSpinnerModule} from "@angular/material/progress-spinner"; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { InitialsAvatarComponent } from './common/initials-avatar/initials-avatar.component'; +import { StatusBarComponent } from './components/status-bar/status-bar.component'; export function HttpLoaderFactory(httpClient: HttpClient) { return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json'); } @NgModule({ - declarations: [AppComponent, BaseScreenComponent, ProjectListingScreenComponent, ProjectOverviewScreenComponent, AddEditProjectDialogComponent, ConfirmationDialogComponent, FilePreviewScreenComponent, PdfViewerComponent, FileDetailsDialogComponent, ProjectDetailsDialogComponent, FullPageLoadingIndicatorComponent], - imports: [ - BrowserModule, - BrowserAnimationsModule, - ReactiveFormsModule, - HttpClientModule, - AuthModule, - IconsModule, - ApiModule, - MatDialogModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useFactory: HttpLoaderFactory, - deps: [HttpClient] - } - }), - RouterModule.forRoot([ - { - path: '', - redirectTo: 'ui/projects', - pathMatch: 'full', - }, - { - path: 'ui', - component: BaseScreenComponent, - children: [ - { - path: 'projects', - component: ProjectListingScreenComponent, - canActivate: [AuthGuard] - }, - { - path: 'projects/:projectId', - component: ProjectOverviewScreenComponent, - canActivate: [AuthGuard] - }, - { - path: 'projects/:projectId/file/:fileId', - component: FilePreviewScreenComponent, - canActivate: [AuthGuard] - } - ] - } + declarations: [ + AppComponent, + BaseScreenComponent, + ProjectListingScreenComponent, + ProjectOverviewScreenComponent, + AddEditProjectDialogComponent, + ConfirmationDialogComponent, + FilePreviewScreenComponent, + PdfViewerComponent, + FileDetailsDialogComponent, + ProjectDetailsDialogComponent, + FullPageLoadingIndicatorComponent, + InitialsAvatarComponent, + StatusBarComponent + ], + imports: [ + BrowserModule, + BrowserAnimationsModule, + ReactiveFormsModule, + HttpClientModule, + AuthModule, + IconsModule, + ApiModule, + MatDialogModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }), + RouterModule.forRoot([ + { + path: '', + redirectTo: 'ui/projects', + pathMatch: 'full' + }, + { + path: 'ui', + component: BaseScreenComponent, + children: [ + { + path: 'projects', + component: ProjectListingScreenComponent, + canActivate: [AuthGuard] + }, + { + path: 'projects/:projectId', + component: ProjectOverviewScreenComponent, + canActivate: [AuthGuard] + }, + { + path: 'projects/:projectId/file/:fileId', + component: FilePreviewScreenComponent, + canActivate: [AuthGuard] + } + ] + } - ]), - NgpSortModule, - MatToolbarModule, - MatButtonModule, - MatMenuModule, - MatIconModule, - MatTooltipModule, - MatSnackBarModule, - MatTabsModule, - MatButtonToggleModule, - MatFormFieldModule, - ToastrModule.forRoot(), - MatSelectModule, - MatSidenavModule, - FileUploadModule, - ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}), - MatProgressSpinnerModule - ], - providers: [ { + ]), + NgpSortModule, + MatToolbarModule, + MatButtonModule, + MatMenuModule, + MatIconModule, + MatTooltipModule, + MatSnackBarModule, + MatTabsModule, + MatButtonToggleModule, + MatFormFieldModule, + ToastrModule.forRoot(), + MatSelectModule, + MatSidenavModule, + FileUploadModule, + ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }), + MatProgressSpinnerModule + ], + providers: [{ provide: HTTP_INTERCEPTORS, multi: true, - useClass: ApiPathInterceptorService, + useClass: ApiPathInterceptorService }, { provide: APP_INITIALIZER, multi: true, useFactory: languageInitializer, deps: [LanguageService] }], - bootstrap: [AppComponent], + bootstrap: [AppComponent] }) export class AppModule { } diff --git a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.html b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.html new file mode 100644 index 000000000..98decd5c8 --- /dev/null +++ b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.html @@ -0,0 +1,4 @@ +
+
{{initials}}
+
{{username || ('initials-avatar.unassigned.label' | translate)}}
+
diff --git a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.scss b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.scss new file mode 100644 index 000000000..cba6e1ef9 --- /dev/null +++ b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.scss @@ -0,0 +1,11 @@ +@import "../../../assets/styles/red-variables"; + +* { + font-size: 13px; + line-height: 16px; +} + +.flex-row { + flex-wrap: wrap; + gap: 12px; +} diff --git a/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.ts b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.ts new file mode 100644 index 000000000..a799c774f --- /dev/null +++ b/apps/red-ui/src/app/common/initials-avatar/initials-avatar.component.ts @@ -0,0 +1,37 @@ +import { Component, Input, OnInit } from '@angular/core'; + +@Component({ + selector: 'redaction-initials-avatar', + templateUrl: './initials-avatar.component.html', + styleUrls: ['./initials-avatar.component.scss'] +}) +export class InitialsAvatarComponent implements OnInit { + @Input() + public username: string; + + @Input() + public color: 'gray' | 'red' = 'gray'; + + @Input() + public size: 'small' | 'large' = 'small'; + + @Input() + public withName = false; + + constructor() { } + + ngOnInit(): void { + } + + public get initials(): string { + if (!this.username) { + return '?' + } + + return this.username + .split(' ') + .filter((value, idx) => idx < 2) + .map((str) => str[0]) + .join(''); + } +} diff --git a/apps/red-ui/src/app/components/status-bar/status-bar.component.html b/apps/red-ui/src/app/components/status-bar/status-bar.component.html new file mode 100644 index 000000000..3e36c7142 --- /dev/null +++ b/apps/red-ui/src/app/components/status-bar/status-bar.component.html @@ -0,0 +1,6 @@ +
+
+
{{ rect.title }}
+
+
+
diff --git a/apps/red-ui/src/app/components/status-bar/status-bar.component.scss b/apps/red-ui/src/app/components/status-bar/status-bar.component.scss new file mode 100644 index 000000000..bf1cd8db1 --- /dev/null +++ b/apps/red-ui/src/app/components/status-bar/status-bar.component.scss @@ -0,0 +1,49 @@ +@import '../../../assets/styles/red-variables'; + +.rectangle-container { + flex: 1; + display: flex; + width: 100%; + + .subtitle { + margin-bottom: 8px; + } + + .section-wrapper:first-child { + .rectangle { + border-radius: 6px 0 0 6px; + } + } + + .section-wrapper:last-child { + .rectangle { + border-radius: 0 6px 6px 0; + } + + &:first-child { + .rectangle { + border-radius: 6px; + } + } + } + + .rectangle { + height: 4px; + + &.grey { + background-color: $grey-4; + } + + &.yellow { + background-color: $yellow-1; + } + + &.blue { + background-color: $blue-1; + } + + &.green { + background-color: $green-1; + } + } +} diff --git a/apps/red-ui/src/app/components/status-bar/status-bar.component.ts b/apps/red-ui/src/app/components/status-bar/status-bar.component.ts new file mode 100644 index 000000000..12f646bf2 --- /dev/null +++ b/apps/red-ui/src/app/components/status-bar/status-bar.component.ts @@ -0,0 +1,22 @@ +import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; + +@Component({ + selector: 'redaction-status-bar', + templateUrl: './status-bar.component.html', + styleUrls: ['./status-bar.component.scss'], + encapsulation: ViewEncapsulation.None, +}) +export class StatusBarComponent implements OnInit { + @Input() + public config: { + length: number, + color: 'green' | 'blue' | 'red' | 'grey' | 'yellow', + title?: string, + }[] = []; + + constructor() { + } + + ngOnInit(): void { + } +} diff --git a/apps/red-ui/src/app/screens/base-screen/base-screen.component.html b/apps/red-ui/src/app/screens/base-screen/base-screen.component.html index bda54d1f7..0123f9eff 100644 --- a/apps/red-ui/src/app/screens/base-screen/base-screen.component.html +++ b/apps/red-ui/src/app/screens/base-screen/base-screen.component.html @@ -8,25 +8,31 @@ - -
-
- -
+
diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html index 02dd3fff2..3f33cf2c6 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html @@ -1,35 +1,68 @@
+
-
-
-
-
- {{project.projectName}} -
-
- {{project.description}} -
-
- {{project.date | date:'short'}} + +
+
+
+ + {{'projects.table-header.title.label'| translate:{ length: appStateService.allProjects?.length || 0 } }} + +
+
+
-
- - + +
+
+
+
+ {{project.projectName}} +
+
+
12
+
9
+
25 Dec. 2020
+
+
+
+ +
+
+ +
+ +
+
+
d
+
s
+
+
+
+ +
diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.scss b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.scss index f3f39e181..ce8355379 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.scss +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.scss @@ -1,7 +1,9 @@ @import "../../../assets/styles/red-mixins"; -:host { - max-width: 1100px; - margin: 0 auto; - padding: 20px; +.stats-subtitle { + margin-top: 6px; +} + +.stats-bar { + width: 160px; } diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html index 148fd4a80..575db46ca 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html @@ -5,66 +5,130 @@ -
-
{{appStateService.activeProject?.description}}
- - {{'project-overview.sorting.label' | translate}} - - - - {{option.label | translate}} - - - -
-
-
-
-
-
- {{fileStatus.filename}} -
-
- {{'project-overview.file-listing.file-entry.status.label'| translate:fileStatus}} -
-
- {{'project-overview.file-listing.file-entry.number-of-pages.label'| translate:fileStatus}} -
-
- {{'project-overview.file-listing.file-entry.number-of-analyses.label'| translate:fileStatus}} -
-
- {{'project-overview.file-listing.file-entry.added.label'| translate:{added: fileStatus.added | date:'short'} }} -
-
- {{'project-overview.file-listing.file-entry.last-updated.label'| translate:{lastUpdated: fileStatus.lastUpdated | date:'short'} }} + +
+
+
+ + {{'project-overview.table-header.title.label'| translate:{ length: appStateService.projectFiles?.length || 0 } }} + +
+
+
-
- - + +
+ +
+ +
+
+
+
+
+
+
+ +
+
+ {{ fileStatus.filename }} +
+ +
+ {{ fileStatus.added | date:'d MMM. yyyy, hh:mm a' }} +
+ +
+ Timo Bejan +
+ +
+ +
+ +
+
+ +
+
+ +
+
+
d
+
s
+
v
+
+
+
+
+ +
+
+
Edit
+
Delete
+
View
+
+ +
+
+ {{ appStateService.projectFiles.length }} +
+
9
+
+ {{ appStateService.activeProject.date | date:'d MMM. yyyy' }} +
+
+ +
+ {{ appStateService.activeProject.projectName }} +
+ +
+ +
+ +
+ {{ appStateService.activeProject.description }} +
+ +
+
+
+
+ +
+
+
+2
+
+
+
+
+
+
- - - + diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.scss b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.scss index 3829d5597..893b28e27 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.scss +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.scss @@ -1,14 +1,45 @@ - - - -.listing { - position: relative; - height: calc(100vh - (61px + 80px + 70px + 80px)); - overflow: auto; -} +@import "../../../assets/styles/red-variables"; .file-upload-input { display: none; } +.min-width { + min-width: 60px; +} +.status-container { + display: flex; + justify-content: flex-end; + width: 40px; + + .status-bar-wrapper { + width: 40px; + } +} + +.table-header redaction-status-bar { + width: 100%; + padding-bottom: 10px; +} + +.project-details-container { + .actions-row { + display: flex; + + > div { + padding: 10px; + } + } + + .description { + font-size: 13px; + line-height: 18px; + } + + .project-team { + .member:not(:last-child) { + margin-right: 5px; + } + } +} diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts index f79f200d1..1a6416b9c 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.ts @@ -1,19 +1,19 @@ -import {ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild} from '@angular/core'; -import {ActivatedRoute, Router} from "@angular/router"; +import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; import { FileStatus, FileUploadControllerService, ProjectControllerService, ReanalysisControllerService, StatusControllerService -} from "@redaction/red-ui-http"; -import {NotificationService, NotificationType} from "../../notification/notification.service"; -import {TranslateService} from "@ngx-translate/core"; -import {ConfirmationDialogComponent} from "../../common/confirmation-dialog/confirmation-dialog.component"; -import {MatDialog} from "@angular/material/dialog"; -import {AppStateService} from "../../state/app-state.service"; -import {ProjectDetailsDialogComponent} from "./project-details-dialog/project-details-dialog.component"; -import {FileDropOverlayService} from "../../upload/file-drop/service/file-drop-overlay.service"; +} from '@redaction/red-ui-http'; +import { NotificationService, NotificationType } from '../../notification/notification.service'; +import { TranslateService } from '@ngx-translate/core'; +import { ConfirmationDialogComponent } from '../../common/confirmation-dialog/confirmation-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { AppStateService } from '../../state/app-state.service'; +import { ProjectDetailsDialogComponent } from './project-details-dialog/project-details-dialog.component'; +import { FileDropOverlayService } from '../../upload/file-drop/service/file-drop-overlay.service'; @Component({ @@ -25,24 +25,26 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { viewReady = false; - @ViewChild('dropzoneComponent', {static: true}) dropZoneComponent; + @ViewChild('dropzoneComponent', { static: true }) dropZoneComponent; dragActive = false; sortOptions: any[] = [{ - value: {name: 'lastUpdated', order: 'desc'}, + value: { name: 'lastUpdated', order: 'desc' }, label: 'project-overview.sorting.last-updated-desc.label', icon: 'red:sort-desc' }, { - value: {name: 'lastUpdated', order: 'asc'}, + value: { name: 'lastUpdated', order: 'asc' }, label: 'project-overview.sorting.last-updated-asc.label', icon: 'red:sort-asc' }, { - value: {name: 'filename', order: 'desc'}, + value: { name: 'filename', order: 'desc' }, label: 'project-overview.sorting.file-name-desc.label', icon: 'red:sort-desc' }, { - value: {name: 'filename', order: 'asc'}, label: 'project-overview.sorting.file-name-asc.label', icon: 'red:sort-asc' + value: { name: 'filename', order: 'asc' }, + label: 'project-overview.sorting.file-name-asc.label', + icon: 'red:sort-asc' }]; sorting: any = this.sortOptions[0].value; projectId: string; @@ -78,7 +80,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { const dialogRef = this._dialog.open(ConfirmationDialogComponent, { width: '400px', maxWidth: '90vw', - autoFocus: false, + autoFocus: false }); dialogRef.afterClosed().subscribe(result => { @@ -116,7 +118,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy { this._fileDropOverlayService.cleanupFileDropHandling(); if (this._fileStatusInterval) { this._fileStatusInterval = null; - clearInterval(this._fileStatusInterval) + clearInterval(this._fileStatusInterval); } } diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 1a9223b18..d4f289e75 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -68,7 +68,38 @@ } } }, + "filters": { + "filter-by": { + "label": "Filter by:" + }, + "status": { + "label": "Status" + }, + "people": { + "label": "People" + }, + "due-date": { + "label": "Due Date" + }, + "project": { + "label": "Project" + }, + "document": { + "label": "Document" + } + }, "projects": { + "table-header": { + "title": { + "label": "{{length}} active projects" + }, + "bulk-select": { + "label": "Bulk select" + }, + "recent": { + "label": "Recent" + } + }, "add-edit-dialog": { "header-new": { "label": "New Project" @@ -146,6 +177,37 @@ } }, "project-overview": { + "table-header": { + "title": { + "label": "{{length}} documents" + }, + "bulk-select": { + "label": "Bulk select" + }, + "recent": { + "label": "Recent" + } + }, + "table-col-names": { + "name": { + "label": "Name" + }, + + "added-on": { + "label": "Added on" + }, + + "added-by": { + "label": "Added by" + }, + + "assigned-to": { + "label": "Assigned to" + }, + "status": { + "label": "Status" + } + }, "sorting": { "label": "Sorting", "last-updated-desc": { @@ -196,14 +258,24 @@ } } }, + "project-details": { + "project-team": { + "label": "Project team" + } + }, "header": { "label": "Project Overview" }, - "upload-files": { - "label": "Upload Files" + "upload-document": { + "label": "Upload Document" }, "no-project": { "label": "Requested project: {{projectId}} does not exist! Back to Project Listing. " } + }, + "initials-avatar": { + "unassigned": { + "label": "Unassigned" + } } } diff --git a/apps/red-ui/src/assets/styles/red-components.scss b/apps/red-ui/src/assets/styles/red-components.scss new file mode 100644 index 000000000..ddd72c182 --- /dev/null +++ b/apps/red-ui/src/assets/styles/red-components.scss @@ -0,0 +1,38 @@ +@import "red-variables"; + +.oval { + font-weight: 600; + display: flex; + justify-content: center; + align-items: center; + height: 24px; + width: 24px; + border-radius: 12px; + font-size: 10px; + border: 1px solid #E2E4E9; + + &.large { + height: 32px; + width: 32px; + border-radius: 16px; + font-size: 13px; + } + + &.gray { + background-color: $grey-4; + border: none; + } + + &.red { + background-color: $red-1; + color: $white; + border: none; + } +} + +.stats-subtitle { + display: flex; + > div:not(:last-child) { + margin-right: 12px; + } +} diff --git a/apps/red-ui/src/assets/styles/red-input.scss b/apps/red-ui/src/assets/styles/red-input.scss index 7fb8cdc5a..bb5b4ec4b 100644 --- a/apps/red-ui/src/assets/styles/red-input.scss +++ b/apps/red-ui/src/assets/styles/red-input.scss @@ -10,7 +10,6 @@ label { height: 14px; opacity: 0.6; - color: $grey-1; font-size: 11px; font-weight: 500; letter-spacing: 0; diff --git a/apps/red-ui/src/assets/styles/red-mixins.scss b/apps/red-ui/src/assets/styles/red-mixins.scss index 9a2460661..317437d89 100644 --- a/apps/red-ui/src/assets/styles/red-mixins.scss +++ b/apps/red-ui/src/assets/styles/red-mixins.scss @@ -6,4 +6,5 @@ -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } diff --git a/apps/red-ui/src/assets/styles/red-page-layout.scss b/apps/red-ui/src/assets/styles/red-page-layout.scss index a6b44a892..232f4784a 100644 --- a/apps/red-ui/src/assets/styles/red-page-layout.scss +++ b/apps/red-ui/src/assets/styles/red-page-layout.scss @@ -1,154 +1,81 @@ @import "red-variables"; @import "red-mixins"; -@media only screen and (max-width: 720px) { - .listing { - .list-entry { - width: 100% !important; - } - } -} - - html, body { margin: 0; padding: 0; height: 100vh; font-family: 'Inter', sans-serif; + color: $grey-1; } .page-header { display: flex; justify-content: space-between; align-items: center; - padding-bottom: 20px; - - &.slim { - padding-bottom: 0; - } + padding: 0 24px; + height: 50px; + box-shadow: 0 2px 4px 0 $grey-4; + position: fixed; + top: 61px; + width: 100vw; + box-sizing: border-box; + background-color: $white; + z-index: 1; } - -.listing { - width: 100%; - display: inline-flex; - flex-wrap: wrap; - - .list-entry { - box-sizing: border-box; - height: 122px; - width: 310px; - border: 1px solid $grey-1; - border-radius: 2px; - background-color: $white; - padding: 20px 24px; - margin-right: 22px; - margin-bottom: 22px; - display: flex; - flex-direction: row; - transition: background-color 0.35s ease-in-out; - - &.xl { - width: 100%; - padding: 16px 24px; - } - - &:hover { - background-color: $grey-2; - - .list-entry-actions { - mat-icon { - opacity: 1; - } - } - } - - &.clickable:hover { - background-color: $grey-1; - cursor: pointer; - color: $white; - - .list-entry-actions { - button.mat-primary { - mat-icon { - color: $white; - } - } - } - - .listing-title, .listing-subtitle { - color: $white; - } - } - - .list-entry-content { - display: flex; - flex: 1; - flex-direction: column; - } - - .list-entry-actions { - width: 18px; - margin-left: 22px; - display: flex; - flex-direction: column; - margin-top: -4px; - - mat-icon { - opacity: 0; - transition: opacity 0.35s ease-in-out; - width: 20px; - height: 20px; - } - } - - .listing-title { - height: 44px; - color: #283241; - font-family: Inter, sans-serf, serif; - font-size: 18px; - font-weight: bold; - letter-spacing: 0; - margin-bottom: 17px; - line-height: 22px; - @include line-clamp(2); - transition: color 0.35s ease-in-out; - - &.one-line { - height: 22px; - @include line-clamp(1); - } - - &.slim { - margin-bottom: 9px; - } - - &.break-all { - word-break: break-all; - } - } - - .listing-subtitle { - height: 12px; - opacity: 0.7; - color: #283241; - font-family: Inconsolata, monospace, monospace; - font-size: 12px; - letter-spacing: 0; - line-height: 12px; - @include line-clamp(1); - transition: color 0.35s ease-in-out; - } - } - +.red-content-inner { + margin-top: 50px; } +.right-fixed-container { + border-left: 1px solid $grey-4; + height: 100%; + width: 330px; + padding: 24px; + position: fixed; + right: 0; +} + +.filters { + font-size: 13px; + line-height: 14px; + + > div { + padding: 10px 14px; + } + } + .flex-row { display: flex; - justify-content: space-between; align-items: center; } +.flex { + display: flex; +} + +.flex-1 { + flex: 1; +} + +.flex-2 { + flex: 2; +} + +.flex-3 { + flex: 3; +} + +.mt-20 { + margin-top: 20px; +} + +.table-container { + height: calc(100vh - 61px - 50px); + width: calc(100vw - 379px); +} + .break-20 { height: 20px; background: transparent; @@ -163,7 +90,6 @@ html, body { .detail-row { opacity: 1; - color: $grey-1; font-family: Inconsolata, monospace, monospace; font-size: 14px; letter-spacing: 0; @@ -171,13 +97,6 @@ html, body { height: 18px; } -.center-section { - max-width: 1100px; - margin: 0 auto; - padding: 20px; -} - - .red-top-bar { height: 61px; width: 100%; @@ -189,8 +108,7 @@ html, body { height: 60px; display: flex; justify-content: space-between; - padding-left: 60px; - padding-right: 60px; + padding: 0 24px; .menu { display: flex; @@ -199,15 +117,23 @@ html, body { } .breadcrumb { - padding-left: 8px; - padding-right: 8px; - color: $yellow-1; + text-decoration: none; + color: $grey-1; + font-size: 13px; + line-height: 18px; + font-weight: 600; + @include line-clamp(1); + max-width: 320px; mat-icon { - height: 14px; - width: 14px; + height: 10px; + width: 10px; + padding: 0 8px; } + &:last-child { + color: $red-1; + } } .divider { @@ -221,7 +147,6 @@ html, body { width: 100vw; height: calc(100vh - 61px); overflow: auto; - } .hidden { diff --git a/apps/red-ui/src/assets/styles/red-tables.scss b/apps/red-ui/src/assets/styles/red-tables.scss new file mode 100644 index 000000000..ad044b9b5 --- /dev/null +++ b/apps/red-ui/src/assets/styles/red-tables.scss @@ -0,0 +1,88 @@ +@import "red-variables"; +@import "red-mixins"; + +.table-header { + background-color: rgba(226, 228, 233, 0.9); + padding: 7px 24px 9px; + display: flex; + justify-content: space-between; + align-items: center; + + .actions { + display: flex; + font-size: 13px; + + div { + padding: 10px 14px; + } + } +} + +.table-col-names { + display: flex; + text-transform: uppercase; + + > div { + padding: 8px 24px; + font-weight: 600; + border-bottom: 1px solid rgba(226, 228, 233, 0.9); + } +} + +.table-item { + display: flex; + align-items: center; + height: 80px; + border-bottom: 1px solid rgba(226, 228, 233, 0.9); + + > div:not(.on-hover) { + padding: 0 24px; + } + + .table-item-title { + font-size: 13px; + font-weight: 600; + line-height: 18px; + @include line-clamp(1); + } + + .table-item-title--large { + font-size: 16px; + line-height: 20px; + } + + .on-hover-wrapper { + width: 0; + height: 0; + padding: 0 !important; + align-self: flex-start; + + .on-hover { + position: relative; + right: 142px; + height: 80px; + width: 142px; + background: linear-gradient(90deg, rgba(249, 250, 251, 0) 0%, #F9FAFB 100%); + display: none; + justify-content: flex-end; + align-items: center; + + div { + margin-right: 12px; + } + } + } + + + &:hover { + background-color: #F9FAFB; + + .on-hover { + display: flex; + } + + .rectangle { + background-color: $grey-4 !important; + } + } +} diff --git a/apps/red-ui/src/assets/styles/red-text-styles.scss b/apps/red-ui/src/assets/styles/red-text-styles.scss index 24ac63bb3..bf40dd5fe 100644 --- a/apps/red-ui/src/assets/styles/red-text-styles.scss +++ b/apps/red-ui/src/assets/styles/red-text-styles.scss @@ -2,8 +2,9 @@ @import "red-mixins"; button { - font-family: Inconsolata, monospace !important; - font-weight: 700 !important; + font-family: Inter, sans-serif !important; + font-weight: 400 !important; + border-radius: 17px !important; } a { @@ -16,23 +17,34 @@ a { } .heading-xl { - color: $grey-1; - font-family: Inter, sans-serf; - font-size: 32px; - font-weight: bold; - letter-spacing: 0; - line-height: 39px; + font-size: 24px; + font-weight: 600; + line-height: 29px; } .heading-l { color: #283241; - font-family: Inter, sans-serf; + font-family: Inter, sans-serif; font-size: 18px; font-weight: bold; letter-spacing: 0; line-height: 22px; } +.subheading { + text-transform: uppercase; + opacity: 0.7; + font-size: 11px; + font-weight: 600; + letter-spacing: 0; + line-height: 14px; +} + +.subtitle { + opacity: 0.7; + font-size: 11px; + line-height: 14px; +} .clamp-1 { @include line-clamp(1); diff --git a/apps/red-ui/src/assets/styles/red-theme.scss b/apps/red-ui/src/assets/styles/red-theme.scss index 047268421..2db368afb 100644 --- a/apps/red-ui/src/assets/styles/red-theme.scss +++ b/apps/red-ui/src/assets/styles/red-theme.scss @@ -7,4 +7,6 @@ @import "red-dialog"; @import "red-input"; @import "red-media-queries"; +@import "red-tables"; +@import "red-components"; @import "red-controls"; diff --git a/apps/red-ui/src/assets/styles/red-variables.scss b/apps/red-ui/src/assets/styles/red-variables.scss index 51ba5541c..684280ec9 100644 --- a/apps/red-ui/src/assets/styles/red-variables.scss +++ b/apps/red-ui/src/assets/styles/red-variables.scss @@ -9,9 +9,9 @@ $dark: #000; $grey-1: #283241; $grey-2: #ECECEE; $grey-3: #aaacb3; +$grey-4: #E2E4E9; $blue-1: #4875F7; $red-1: #F65757; $yellow-1: #FFB83B; $green-1: #46CE7D; - diff --git a/yarn.lock b/yarn.lock index 9019d5655..6e3215bc0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2694,7 +2694,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base64-js@1.3.1, base64-js@^1.0.2: +base64-js@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== @@ -6782,11 +6782,6 @@ jest@26.2.2: import-local "^3.0.2" jest-cli "^26.2.2" -js-sha256@0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6949,21 +6944,6 @@ karma-source-map-support@1.4.0: dependencies: source-map-support "^0.5.5" -keycloak-angular@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/keycloak-angular/-/keycloak-angular-8.0.1.tgz#29851e7aded21925faa051c69dfa5872bda6661f" - integrity sha512-q68vcaFiSYNjbzPM1v+6LohMpWUyus9hcQBi2rNz06xOtWuRU4U6t5vQgoim6bDhtkhWpR5+a3SYl0lzUJKyrw== - dependencies: - tslib "^2.0.0" - -keycloak-js@10.0.2: - version "10.0.2" - resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-10.0.2.tgz#f0cf5b942627c5221f1466552c40e4624503b77b" - integrity sha512-7nkg4Ob1khHGcNbuK36AMndKUEuIQFpNlWU9ygWs7nSBPCI9VZ8dJjjXfKJHm0ewgcqLFGPIJ6bxxRlfcQ6sLg== - dependencies: - base64-js "1.3.1" - js-sha256 "0.9.0" - killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"