Merge branch 'master' into workflow
This commit is contained in:
commit
ba4fbdabe6
1
src/assets/icons/offline.svg
Normal file
1
src/assets/icons/offline.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M21 11l2-2c-3.73-3.73-8.87-5.15-13.7-4.31l2.58 2.58c3.3-.02 6.61 1.22 9.12 3.73zm-2 2c-1.08-1.08-2.36-1.85-3.72-2.33l3.02 3.02.7-.69zM9 17l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zM3.41 1.64L2 3.05 5.05 6.1C3.59 6.83 2.22 7.79 1 9l2 2c1.23-1.23 2.65-2.16 4.17-2.78l2.24 2.24C7.79 10.89 6.27 11.74 5 13l2 2c1.35-1.35 3.11-2.04 4.89-2.06l7.08 7.08 1.41-1.41L3.41 1.64z"/></svg>
|
||||
|
After Width: | Height: | Size: 517 B |
3
src/assets/styles/_common-functions.scss
Normal file
3
src/assets/styles/_common-functions.scss
Normal file
@ -0,0 +1,3 @@
|
||||
@function hexToRgb($color) {
|
||||
@return #{red($color) + ', ' + green($color) + ', ' + blue($color)};
|
||||
}
|
||||
@ -1,5 +1,3 @@
|
||||
@import 'variables';
|
||||
|
||||
@mixin line-clamp($lines) {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: $lines;
|
||||
@ -25,7 +23,7 @@
|
||||
}
|
||||
|
||||
@mixin scroll-bar {
|
||||
scrollbar-color: $grey-5 $grey-2;
|
||||
scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2);
|
||||
scrollbar-width: thin;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@ -34,19 +32,19 @@
|
||||
|
||||
/* Track */
|
||||
&::-webkit-scrollbar-track {
|
||||
background: $grey-2;
|
||||
background: var(--iqser-grey-2);
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $grey-5;
|
||||
background: var(--iqser-quick-filter-border);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin inset-shadow {
|
||||
box-shadow: inset 0 4px 3px -2px $grey-4;
|
||||
box-shadow: inset 0 4px 3px -2px var(--iqser-btn-bg-hover);
|
||||
}
|
||||
|
||||
@mixin drop-shadow {
|
||||
box-shadow: 0 4px 3px 2px $grey-4;
|
||||
box-shadow: 0 4px 3px 2px var(--iqser-btn-bg-hover);
|
||||
}
|
||||
31
src/assets/styles/_common-variables.scss
Normal file
31
src/assets/styles/_common-variables.scss
Normal file
@ -0,0 +1,31 @@
|
||||
@use 'sass:meta';
|
||||
|
||||
:root {
|
||||
--iqser-primary: lightblue;
|
||||
--iqser-primary-rgb: 220, 230, 234;
|
||||
--iqser-primary-2: orange;
|
||||
--iqser-accent: blue;
|
||||
--iqser-accent-rgb: 123, 234, 111;
|
||||
--iqser-disabled: #9398a0;
|
||||
--iqser-not-disabled-table-item: #f9fafb;
|
||||
--iqser-btn-bg-hover: #e2e4e9;
|
||||
--iqser-btn-bg: #f0f1f4;
|
||||
--iqser-warn: #fdbd00;
|
||||
--iqser-white: white;
|
||||
--iqser-separator: rgba(226, 228, 233, 0.9);
|
||||
--iqser-quick-filter-border: #d3d5da;
|
||||
--iqser-grey-2: #f4f5f7;
|
||||
--iqser-grey-3: #aaacb3;
|
||||
--iqser-grey-4: #e2e4e9;
|
||||
--iqser-grey-5: #d3d5da;
|
||||
--iqser-grey-6: #f0f1f4;
|
||||
--iqser-helpmode-primary: green;
|
||||
}
|
||||
|
||||
@mixin configure($args...) {
|
||||
:root {
|
||||
@each $name, $value in meta.keywords($args) {
|
||||
--#{$name}: #{$value};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
// This rebel line is crying (in WebStorm) but it actually works
|
||||
@import '../../../../../apps/red-ui/src/assets/styles/variables';
|
||||
|
||||
$btn-bg-hover: #e2e4e9 !default;
|
||||
$btn-bg: #f0f1f4 !default;
|
||||
$warn: #fdbd00 !default;
|
||||
$white: white !default;
|
||||
$separator: rgba(226, 228, 233, 0.9) !default;
|
||||
$quick-filter-border: #d3d5da !default;
|
||||
$filter-bg: #f4f5f7 !default;
|
||||
@ -1,5 +1,3 @@
|
||||
@import 'variables';
|
||||
|
||||
.mat-button,
|
||||
.mat-flat-button {
|
||||
border-radius: 17px !important;
|
||||
@ -36,19 +34,19 @@
|
||||
padding: 0 14px;
|
||||
transition: background-color 0.2s, color 0.2s;
|
||||
|
||||
background-color: $primary;
|
||||
background-color: var(--iqser-primary);
|
||||
|
||||
&.mat-button-disabled {
|
||||
background-color: $primary;
|
||||
background-color: var(--iqser-primary);
|
||||
|
||||
.mat-button-wrapper {
|
||||
color: $white;
|
||||
color: var(--iqser-white);
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.mat-button-disabled):hover {
|
||||
background-color: $primary-2;
|
||||
background-color: var(--iqser-primary-2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,30 +62,30 @@ iqser-circle-button {
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&.overlay {
|
||||
background: rgba($primary, 0.1);
|
||||
background: rgba(var(--iqser-primary-rgb), 0.1);
|
||||
}
|
||||
|
||||
&:not(.overlay):hover {
|
||||
background-color: $btn-bg;
|
||||
background-color: var(--iqser-btn-bg);
|
||||
}
|
||||
|
||||
&.primary {
|
||||
font-weight: 500 !important;
|
||||
background-color: $primary;
|
||||
color: $white;
|
||||
background-color: var(--iqser-primary);
|
||||
color: var(--iqser-white);
|
||||
|
||||
&:hover {
|
||||
background-color: $primary-2;
|
||||
background-color: var(--iqser-primary-2);
|
||||
}
|
||||
}
|
||||
|
||||
&.dark-bg:hover {
|
||||
background-color: $btn-bg-hover;
|
||||
background-color: var(--iqser-btn-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.dot {
|
||||
background: $primary;
|
||||
background: var(--iqser-primary);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
@ -102,10 +100,10 @@ iqser-circle-button,
|
||||
iqser-icon-button {
|
||||
&[aria-expanded='true'] {
|
||||
button {
|
||||
background: rgba($primary, 0.1);
|
||||
background: rgba(var(--iqser-primary-rgb), 0.1);
|
||||
|
||||
&.primary {
|
||||
background: $primary-2;
|
||||
background: var(--iqser-primary-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
@import 'apps/red-ui/src/assets/styles/variables';
|
||||
|
||||
.mat-dialog-container {
|
||||
color: $accent;
|
||||
color: var(--iqser-accent);
|
||||
padding: 0 !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
@ -27,7 +25,7 @@
|
||||
.dialog-actions {
|
||||
height: 81px;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid $separator;
|
||||
border-top: 1px solid var(--iqser-separator);
|
||||
padding: 0 32px;
|
||||
align-items: center;
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
@import 'variables';
|
||||
|
||||
.full-page-section,
|
||||
.full-page-content {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.full-page-section {
|
||||
opacity: 0.7;
|
||||
background: $white;
|
||||
background: var(--iqser-white);
|
||||
z-index: 900;
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
@use 'common-mixins' as mixins;
|
||||
|
||||
form .iqser-input-group:not(first-of-type) {
|
||||
margin-top: 14px;
|
||||
@ -22,10 +21,10 @@ form .iqser-input-group:not(first-of-type) {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
background: $quick-filter-border;
|
||||
background: var(--iqser-quick-filter-border);
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
border-left: 1px solid $quick-filter-border;
|
||||
border-left: 1px solid var(--iqser-quick-filter-border);
|
||||
border-top-right-radius: 7px;
|
||||
border-bottom-right-radius: 7px;
|
||||
cursor: pointer;
|
||||
@ -35,13 +34,13 @@ form .iqser-input-group:not(first-of-type) {
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
background: $btn-bg;
|
||||
background: var(--iqser-btn-bg);
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: $accent;
|
||||
color: var(--iqser-accent);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
@ -60,7 +59,7 @@ form .iqser-input-group:not(first-of-type) {
|
||||
|
||||
.mat-form-field-label {
|
||||
opacity: 0.7 !important;
|
||||
color: $accent !important;
|
||||
color: var(--iqser-accent) !important;
|
||||
transform: translateY(-1.34em) !important;
|
||||
}
|
||||
|
||||
@ -83,9 +82,9 @@ form .iqser-input-group:not(first-of-type) {
|
||||
}
|
||||
|
||||
.mat-button-toggle-checked {
|
||||
background: $primary;
|
||||
background: var(--iqser-primary);
|
||||
transition: background-color 0.25s ease;
|
||||
color: $white;
|
||||
color: var(--iqser-white);
|
||||
}
|
||||
|
||||
input,
|
||||
@ -94,7 +93,7 @@ form .iqser-input-group:not(first-of-type) {
|
||||
box-sizing: border-box;
|
||||
padding-left: 11px;
|
||||
padding-right: 11px;
|
||||
border: 1px solid $quick-filter-border;
|
||||
border: 1px solid var(--iqser-quick-filter-border);
|
||||
font-family: Inter, sans-serif;
|
||||
font-size: 13px;
|
||||
background-color: #ffffff;
|
||||
@ -108,26 +107,26 @@ form .iqser-input-group:not(first-of-type) {
|
||||
}
|
||||
|
||||
&:focus:not(:disabled):not(.mat-select-disabled) {
|
||||
border-color: $accent;
|
||||
border-color: var(--iqser-accent);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: $accent;
|
||||
color: var(--iqser-accent);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&.ng-invalid.ng-touched {
|
||||
border-color: rgba($primary, 0.3);
|
||||
border-color: rgba(var(--iqser-primary-rgb), 0.3);
|
||||
|
||||
&:focus {
|
||||
border-color: $primary;
|
||||
border-color: var(--iqser-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&.mat-select-disabled {
|
||||
background-color: $filter-bg;
|
||||
color: rgba($accent, 0.3);
|
||||
background-color: var(--iqser-grey-2);
|
||||
color: rgba(var(--iqser-accent-rgb), 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +153,7 @@ form .iqser-input-group:not(first-of-type) {
|
||||
resize: vertical;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
@include scroll-bar;
|
||||
@include mixins.scroll-bar;
|
||||
|
||||
&.has-scrollbar {
|
||||
border-top-right-radius: 0;
|
||||
@ -168,7 +167,7 @@ form .iqser-input-group:not(first-of-type) {
|
||||
letter-spacing: 0;
|
||||
line-height: 14px;
|
||||
margin-bottom: 2px;
|
||||
color: $accent;
|
||||
color: var(--iqser-accent);
|
||||
|
||||
&.mat-checkbox-layout {
|
||||
opacity: 1;
|
||||
@ -178,7 +177,7 @@ form .iqser-input-group:not(first-of-type) {
|
||||
|
||||
&.required label:after {
|
||||
content: ' *';
|
||||
color: $primary;
|
||||
color: var(--iqser-primary);
|
||||
}
|
||||
|
||||
&.datepicker-wrapper {
|
||||
@ -196,10 +195,10 @@ form .iqser-input-group:not(first-of-type) {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 1px;
|
||||
color: $accent;
|
||||
color: var(--iqser-accent);
|
||||
|
||||
&.mat-datepicker-toggle-active {
|
||||
color: $primary;
|
||||
color: var(--iqser-primary);
|
||||
}
|
||||
|
||||
.mat-icon-button {
|
||||
7
src/assets/styles/common-styles.scss
Normal file
7
src/assets/styles/common-styles.scss
Normal file
@ -0,0 +1,7 @@
|
||||
@use 'common-inputs';
|
||||
@use 'common-buttons';
|
||||
@use 'common-texts';
|
||||
@use 'common-tables';
|
||||
@use 'common-full-pages';
|
||||
@use 'common-layout';
|
||||
@use 'common-dialogs';
|
||||
@ -1,5 +1,4 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
@use 'common-mixins' as mixins;
|
||||
|
||||
.all-caps-label {
|
||||
text-transform: uppercase;
|
||||
@ -24,17 +23,18 @@
|
||||
}
|
||||
|
||||
&.primary {
|
||||
color: $primary;
|
||||
color: var(--iqser-primary);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $primary;
|
||||
color: var(--iqser-primary);
|
||||
transition: color 0.1s;
|
||||
|
||||
&:hover {
|
||||
color: lighten($primary, 10%);
|
||||
color: var(--iqser-primary);
|
||||
filter: brightness(140%);
|
||||
}
|
||||
|
||||
&.with-underline {
|
||||
@ -48,7 +48,7 @@ a {
|
||||
|
||||
pre {
|
||||
font-family: Inter, sans-serif;
|
||||
color: $accent;
|
||||
color: var(--iqser-accent);
|
||||
}
|
||||
|
||||
.heading-xl {
|
||||
@ -102,17 +102,17 @@ pre {
|
||||
}
|
||||
|
||||
.large-label {
|
||||
color: $accent;
|
||||
color: var(--iqser-accent);
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.clamp-1 {
|
||||
@include line-clamp(1);
|
||||
@include mixins.line-clamp(1);
|
||||
}
|
||||
|
||||
.clamp-2 {
|
||||
@include line-clamp(2);
|
||||
@include mixins.line-clamp(2);
|
||||
}
|
||||
|
||||
.no-wrap {
|
||||
@ -1,7 +0,0 @@
|
||||
@import 'inputs';
|
||||
@import 'buttons';
|
||||
@import 'texts';
|
||||
@import 'tables';
|
||||
@import 'full-pages';
|
||||
@import 'layout';
|
||||
@import 'dialogs';
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
:host {
|
||||
height: var(--size);
|
||||
width: var(--size);
|
||||
@ -26,15 +24,15 @@
|
||||
}
|
||||
|
||||
&.primary.mat-button-disabled {
|
||||
background-color: $btn-bg;
|
||||
color: $white !important;
|
||||
background-color: var(--iqser-btn-bg);
|
||||
color: var(--iqser-white) !important;
|
||||
}
|
||||
|
||||
&.warn:not([disabled]) {
|
||||
background-color: $warn;
|
||||
background-color: var(--iqser-warn);
|
||||
|
||||
&:hover {
|
||||
background-color: $warn;
|
||||
background-color: var(--iqser-warn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
button {
|
||||
padding: 0 14px;
|
||||
width: 100%;
|
||||
@ -9,10 +7,10 @@ button {
|
||||
}
|
||||
|
||||
&.show-bg {
|
||||
background-color: $btn-bg;
|
||||
background-color: var(--iqser-btn-bg);
|
||||
|
||||
&:not(.mat-button-disabled):hover {
|
||||
background-color: $btn-bg-hover;
|
||||
background-color: var(--iqser-btn-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import { IqserIconsModule } from './icons';
|
||||
import { IqserButtonsModule } from './buttons';
|
||||
import { IqserScrollbarModule } from './scrollbar';
|
||||
import { IqserEmptyStatesModule } from './empty-states';
|
||||
import { LogPipe } from './utils/pipes/log.pipe';
|
||||
|
||||
const matModules = [MatIconModule, MatProgressSpinnerModule];
|
||||
const modules = [
|
||||
@ -33,8 +34,8 @@ const components = [StatusBarComponent, FullPageLoadingIndicatorComponent, FullP
|
||||
const pipes = [SortByPipe, HumanizePipe];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components, ...pipes],
|
||||
declarations: [...components, ...pipes, LogPipe],
|
||||
imports: [CommonModule, ...matModules, ...modules],
|
||||
exports: [...components, ...pipes, ...modules]
|
||||
exports: [...components, ...pipes, ...modules, LogPipe]
|
||||
})
|
||||
export class CommonUiModule {}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -13,7 +11,7 @@
|
||||
}
|
||||
|
||||
.heading-l {
|
||||
color: $grey-7;
|
||||
color: var(--iqser-disabled);
|
||||
}
|
||||
|
||||
> .heading-l,
|
||||
|
||||
@ -1,15 +1,36 @@
|
||||
<ng-container *ngIf="errorService.error$ | async as error">
|
||||
<section class="full-page-section"></section>
|
||||
<section class="full-page-content">
|
||||
<mat-icon svgIcon="iqser:failure"></mat-icon>
|
||||
<div class="heading-l mt-24" translate="error.title"></div>
|
||||
<div class="mt-16 error">{{ error.message }}</div>
|
||||
<iqser-icon-button
|
||||
(action)="reload()"
|
||||
[label]="'error.reload' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
class="mt-20"
|
||||
icon="iqser:refresh"
|
||||
></iqser-icon-button>
|
||||
</section>
|
||||
<ng-container *ngIf="error.status === 0; else serverError">
|
||||
<div class="offline-box flex-align-items-center" [@animateOpenClose]="'open'">
|
||||
<mat-icon svgIcon="iqser:offline"></mat-icon>
|
||||
|
||||
<div class="heading-l ml-14" [translate]="'error.offline'"></div>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="errorService.set(undefined)"
|
||||
[tooltip]="'error.close' | translate"
|
||||
icon="iqser:close"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #serverError>
|
||||
<section class="full-page-section"></section>
|
||||
|
||||
<section class="full-page-content flex-align-items-center">
|
||||
<mat-icon svgIcon="iqser:failure"></mat-icon>
|
||||
|
||||
<div class="heading-l mt-24" [translate]="'error.title'"></div>
|
||||
|
||||
<div class="mt-16 error">{{ error.message }}</div>
|
||||
|
||||
<iqser-icon-button
|
||||
(action)="reload()"
|
||||
[label]="'error.reload' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
class="mt-20"
|
||||
icon="iqser:refresh"
|
||||
></iqser-icon-button>
|
||||
</section>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
@ -1,13 +1,30 @@
|
||||
@import '../../../assets/styles/variables';
|
||||
.offline-box {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
height: 40px;
|
||||
width: 300px;
|
||||
background: var(--iqser-white);
|
||||
border: 1px solid var(--iqser-separator);
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
|
||||
> mat-icon {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
> iqser-circle-button {
|
||||
flex-grow: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.full-page-section {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.full-page-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
> mat-icon {
|
||||
@ -16,12 +33,12 @@
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.heading-l {
|
||||
color: $grey-7;
|
||||
font-weight: initial;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: $red-1;
|
||||
color: var(--iqser-primary);
|
||||
}
|
||||
}
|
||||
|
||||
:is(.offline-box, .full-page-content) .heading-l {
|
||||
color: #9398a0;
|
||||
font-weight: initial;
|
||||
}
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { IconButtonTypes } from '../../buttons';
|
||||
import { ErrorService } from '../error.service';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'iqser-full-page-error',
|
||||
templateUrl: './full-page-error.component.html',
|
||||
styleUrls: ['./full-page-error.component.scss'],
|
||||
animations: [
|
||||
trigger('animateOpenClose', [
|
||||
state('open', style({ bottom: '20px' })),
|
||||
state('void', style({ bottom: '-70px' })),
|
||||
transition('* => open, open => void', animate('1s ease-in-out'))
|
||||
])
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FullPageErrorComponent {
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
export * from './error.service';
|
||||
export * from './max-retries.token';
|
||||
export * from './server-error-interceptor';
|
||||
export * from './full-page-error/full-page-error.component';
|
||||
|
||||
3
src/lib/error/max-retries.token.ts
Normal file
3
src/lib/error/max-retries.token.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const MAX_RETRIES_ON_SERVER_ERROR = new InjectionToken<number>('Number of retries before giving up');
|
||||
52
src/lib/error/server-error-interceptor.ts
Normal file
52
src/lib/error/server-error-interceptor.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Inject, Injectable, Optional } from '@angular/core';
|
||||
import { MonoTypeOperatorFunction, Observable, throwError, timer } from 'rxjs';
|
||||
import { catchError, mergeMap, retryWhen, tap } from 'rxjs/operators';
|
||||
import { MAX_RETRIES_ON_SERVER_ERROR } from './max-retries.token';
|
||||
import { ErrorService } from './error.service';
|
||||
|
||||
function updateSeconds(seconds: number) {
|
||||
if (seconds === 0 || seconds === 1) {
|
||||
return seconds + 1;
|
||||
} else {
|
||||
return seconds * seconds;
|
||||
}
|
||||
}
|
||||
|
||||
function backoffOnServerError(maxRetries = 3): MonoTypeOperatorFunction<HttpEvent<unknown>> {
|
||||
let seconds = 0;
|
||||
return retryWhen(attempts =>
|
||||
attempts.pipe(
|
||||
tap(() => (seconds = updateSeconds(seconds))),
|
||||
mergeMap((error: HttpErrorResponse, index) => {
|
||||
if ((error.status < 500 && error.status !== 0) || index === maxRetries) {
|
||||
return throwError(error);
|
||||
} else {
|
||||
console.error('An error occurred: ', error);
|
||||
console.error(`Retrying in ${seconds} seconds...`);
|
||||
return timer(seconds * 1000);
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ServerErrorInterceptor implements HttpInterceptor {
|
||||
constructor(
|
||||
private readonly _errorService: ErrorService,
|
||||
@Optional() @Inject(MAX_RETRIES_ON_SERVER_ERROR) private readonly _maxRetries: number
|
||||
) {}
|
||||
|
||||
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
return next.handle(req).pipe(
|
||||
catchError((error: HttpErrorResponse) => {
|
||||
if (error.status >= 500 || error.status === 0) {
|
||||
this._errorService.set(error);
|
||||
}
|
||||
return throwError(error);
|
||||
}),
|
||||
backoffOnServerError(this._maxRetries || 3)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../assets/styles/variables';
|
||||
|
||||
.filter-menu-options,
|
||||
.filter-menu-header {
|
||||
display: flex;
|
||||
@ -27,7 +25,7 @@
|
||||
}
|
||||
|
||||
.filter-options {
|
||||
background-color: $filter-bg;
|
||||
background-color: var(--iqser-grey-2);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../assets/styles/variables';
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
@ -8,9 +6,9 @@
|
||||
|
||||
.quick-filter {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid $quick-filter-border;
|
||||
border: 1px solid var(--iqser-quick-filter-border);
|
||||
border-radius: 17px;
|
||||
background-color: $btn-bg;
|
||||
background-color: var(--iqser-btn-bg);
|
||||
padding: 0 14px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
@ -19,11 +17,11 @@
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: $white;
|
||||
background-color: var(--iqser-white);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $white;
|
||||
background-color: var(--iqser-white);
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../../../../apps/red-ui/src/assets/styles/variables';
|
||||
|
||||
.help-button {
|
||||
width: 44px;
|
||||
height: 40px;
|
||||
@ -7,7 +5,7 @@
|
||||
bottom: 20px;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background: $green-2;
|
||||
background: var(--iqser-helpmode-primary);
|
||||
border-top-left-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
box-shadow: -1px 1px 5px 0 rgba(40, 50, 65, 0.25);
|
||||
@ -40,10 +38,10 @@
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-left: 8px solid $green-2;
|
||||
border-right: 8px solid $green-2;
|
||||
border-top: 8px solid $green-2;
|
||||
border-bottom: 60px solid $green-2;
|
||||
border-left: 8px solid var(--iqser-helpmode-primary);
|
||||
border-right: 8px solid var(--iqser-helpmode-primary);
|
||||
border-top: 8px solid var(--iqser-helpmode-primary);
|
||||
border-bottom: 60px solid var(--iqser-helpmode-primary);
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
|
||||
@ -20,6 +20,7 @@ export class IqserIconsModule {
|
||||
'help-outline',
|
||||
'lanes',
|
||||
'list',
|
||||
'offline',
|
||||
'refresh',
|
||||
'search',
|
||||
'sort-asc',
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
:host {
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -12,22 +10,22 @@
|
||||
|
||||
&.inactive {
|
||||
border: 1px solid #d3d5da;
|
||||
background-color: $white;
|
||||
background-color: var(--iqser-white);
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
color: $primary;
|
||||
color: var(--iqser-primary);
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
}
|
||||
|
||||
&.with-bg {
|
||||
.mat-icon {
|
||||
color: $white;
|
||||
color: var(--iqser-white);
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
border: 1px solid $btn-bg;
|
||||
border: 1px solid var(--iqser-btn-bg);
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.scroll-button {
|
||||
background-color: $white;
|
||||
background-color: var(--iqser-white);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 40px;
|
||||
@ -26,5 +24,5 @@
|
||||
mat-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
color: $grey-7;
|
||||
color: var(--iqser-disabled);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, HostListener, Input, OnInit } from '@angular/core';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { concatMap, delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
|
||||
import { combineLatest, fromEvent, Observable } from 'rxjs';
|
||||
import { Required } from '../../utils';
|
||||
|
||||
const ButtonTypes = {
|
||||
@ -34,11 +34,14 @@ export class ScrollButtonComponent implements OnInit {
|
||||
const showScrollUp = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.top);
|
||||
const showScrollDown = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.bottom);
|
||||
|
||||
const scroll$ = this.scrollViewport.elementScrolled().pipe(
|
||||
startWith(''),
|
||||
/** Delay first value so that we can wait for items to be rendered in viewport and get correct values */
|
||||
concatMap((value, index) => (index === 0 ? of(value).pipe(delay(0)) : of(value)))
|
||||
);
|
||||
/** Force an initial emit, so combineLatest works */
|
||||
const scrolled$ = this.scrollViewport.elementScrolled().pipe(startWith(null));
|
||||
const resized$ = fromEvent(window, 'resize').pipe(startWith(null));
|
||||
const rangeChange$ = this.scrollViewport.renderedRangeStream.pipe(startWith(null));
|
||||
|
||||
/** Delay so that we can wait for items to be rendered in viewport and get correct values */
|
||||
const scroll$ = combineLatest([scrolled$, resized$, rangeChange$]).pipe(delay(0));
|
||||
|
||||
this.showScrollUp$ = scroll$.pipe(map(showScrollUp), distinctUntilChanged());
|
||||
this.showScrollDown$ = scroll$.pipe(map(showScrollDown), distinctUntilChanged());
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ export class SyncWidthDirective implements OnDestroy {
|
||||
private readonly _interval: number;
|
||||
|
||||
constructor(private readonly _elementRef: ElementRef) {
|
||||
this._interval = setInterval(() => {
|
||||
this._interval = window.setInterval(() => {
|
||||
this._matchWidth();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
height: 30px;
|
||||
@ -38,7 +36,7 @@
|
||||
|
||||
.sort-arrows-container {
|
||||
display: none;
|
||||
color: $primary;
|
||||
color: var(--iqser-primary);
|
||||
margin-left: 8px;
|
||||
|
||||
mat-icon {
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
border-bottom: 1px solid $separator;
|
||||
border-bottom: 1px solid var(--iqser-separator);
|
||||
|
||||
&.no-data.selection-enabled:not([synced='true']) {
|
||||
padding-left: 30px;
|
||||
@ -10,12 +8,12 @@
|
||||
}
|
||||
|
||||
.header-item {
|
||||
background-color: $btn-bg;
|
||||
background-color: var(--iqser-btn-bg);
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
border-bottom: 1px solid $separator;
|
||||
border-bottom: 1px solid var(--iqser-separator);
|
||||
box-sizing: border-box;
|
||||
padding: 0 24px;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
@import '../../../assets/styles/common';
|
||||
@use '../../../assets/styles/common-mixins' as mixins;
|
||||
|
||||
:host cdk-virtual-scroll-viewport {
|
||||
height: calc(100vh - 50px - 31px - 111px);
|
||||
@ -25,7 +25,7 @@
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid $separator;
|
||||
border-bottom: 1px solid var(--iqser-separator);
|
||||
height: var(--itemSize);
|
||||
padding: 0 10px;
|
||||
|
||||
@ -61,8 +61,8 @@
|
||||
}
|
||||
|
||||
&.disabled > div {
|
||||
background-color: $grey-2;
|
||||
color: $grey-7;
|
||||
background-color: var(--iqser-grey-2);
|
||||
color: var(--iqser-disabled);
|
||||
|
||||
.action-buttons {
|
||||
color: initial;
|
||||
@ -71,7 +71,7 @@
|
||||
|
||||
.table-item-title {
|
||||
font-weight: 600;
|
||||
@include line-clamp(1);
|
||||
@include mixins.line-clamp(1);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
@ -86,7 +86,7 @@
|
||||
padding-left: 100px;
|
||||
padding-right: 21px;
|
||||
z-index: 1;
|
||||
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, $grey-2 35%);
|
||||
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, var(--iqser-grey-2) 35%);
|
||||
|
||||
mat-icon {
|
||||
width: 14px;
|
||||
@ -113,14 +113,14 @@
|
||||
}
|
||||
|
||||
&:hover:not(.disabled) > div {
|
||||
background-color: $grey-8;
|
||||
background-color: var(--iqser-not-disabled-table-item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
overflow-y: auto !important;
|
||||
@include scroll-bar;
|
||||
@include mixins.scroll-bar;
|
||||
|
||||
&.has-scrollbar {
|
||||
.table-item {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
@import '../../../assets/styles/common';
|
||||
@import '../../../assets/styles/common-variables';
|
||||
@import '../../../assets/styles/common-mixins';
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
@ -15,7 +16,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
background-color: $grey-2;
|
||||
background-color: var(--iqser-grey-2);
|
||||
border-radius: 6px;
|
||||
padding-top: 18px;
|
||||
position: relative;
|
||||
@ -53,10 +54,10 @@
|
||||
&:not(.list-can-receive):not(.list-dragging) {
|
||||
background: repeating-linear-gradient(
|
||||
-45deg,
|
||||
$separator,
|
||||
$separator 1px,
|
||||
$white 1px,
|
||||
$white 8px
|
||||
var(--iqser-separator),
|
||||
var(--iqser-separator) 1px,
|
||||
var(--iqser-white) 1px,
|
||||
var(--iqser-white) 8px
|
||||
);
|
||||
|
||||
> .heading, ::ng-deep.cdk-drag > * {
|
||||
@ -76,7 +77,7 @@
|
||||
}
|
||||
|
||||
.cdk-drag {
|
||||
background-color: $white;
|
||||
background-color: var(--iqser-white);
|
||||
transition: background-color 0.2s, box-shadow 0.2s;
|
||||
border-radius: 8px;
|
||||
margin: 0 8px 4px 8px;
|
||||
@ -86,13 +87,13 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $grey-6;
|
||||
background-color: var(--iqser-grey-6);
|
||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.cdk-drag-placeholder {
|
||||
border: 1px dashed $grey-5;
|
||||
border: 1px dashed var(--iqser-grey-5);
|
||||
border-radius: 8px;
|
||||
min-height: var(--height);
|
||||
margin: 0 8px 4px 8px;
|
||||
|
||||
@ -6,7 +6,7 @@ export function Debounce(delay = 300): MethodDecorator {
|
||||
|
||||
descriptorCopy.value = function _new(...args: unknown[]) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => (descriptor.value as (...params: unknown[]) => unknown).apply(this, args), delay);
|
||||
timeout = window.setTimeout(() => (descriptor.value as (...params: unknown[]) => unknown).apply(this, args), delay);
|
||||
};
|
||||
|
||||
return descriptorCopy;
|
||||
|
||||
60
src/lib/utils/decorators/on-change.decorator.ts
Normal file
60
src/lib/utils/decorators/on-change.decorator.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { FunctionKeys } from '../types/utility-types';
|
||||
|
||||
export interface SimpleChange<T> {
|
||||
readonly previousValue: T;
|
||||
readonly currentValue: T;
|
||||
readonly isFirstChange: boolean;
|
||||
}
|
||||
|
||||
export type CallBackFunction<T> = (value: T, change: SimpleChange<T>) => void;
|
||||
|
||||
type TypedPropertyDecorator<C> = (target: C, key: PropertyKey) => void;
|
||||
|
||||
const CACHED_VALUE_KEY = Symbol();
|
||||
const IS_FIRST_CHANGE_KEY = Symbol();
|
||||
|
||||
interface Instance<T> {
|
||||
[CACHED_VALUE_KEY]: T;
|
||||
[IS_FIRST_CHANGE_KEY]: boolean;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export function OnChange<T>(callback: CallBackFunction<T> | string): PropertyDecorator;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function OnChange<T, C extends Object = Object>(callback: CallBackFunction<T> | FunctionKeys<C>): TypedPropertyDecorator<C>;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function OnChange<T, C extends Object = Object>(callback: CallBackFunction<T> | FunctionKeys<C>): TypedPropertyDecorator<C> {
|
||||
return function _onChange(target: C, key: PropertyKey) {
|
||||
Object.defineProperty(target, key, {
|
||||
set(value: T) {
|
||||
const instance = this as Instance<T>;
|
||||
const callBackFn = <CallBackFunction<T>>(typeof callback === 'string' ? instance[callback] : callback);
|
||||
if (!callBackFn) {
|
||||
throw new Error(`Cannot find method ${String(callback)} in class ${target.constructor.name}`);
|
||||
}
|
||||
|
||||
instance[IS_FIRST_CHANGE_KEY] = instance[IS_FIRST_CHANGE_KEY] === undefined;
|
||||
|
||||
// No operation if new value is same as old value
|
||||
if (!instance[IS_FIRST_CHANGE_KEY] && instance[CACHED_VALUE_KEY] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldValue = instance[CACHED_VALUE_KEY];
|
||||
instance[CACHED_VALUE_KEY] = value;
|
||||
|
||||
const simpleChange: SimpleChange<T> = {
|
||||
previousValue: oldValue,
|
||||
currentValue: instance[CACHED_VALUE_KEY],
|
||||
isFirstChange: instance[IS_FIRST_CHANGE_KEY]
|
||||
};
|
||||
|
||||
callBackFn.call(instance, instance[CACHED_VALUE_KEY], simpleChange);
|
||||
},
|
||||
get(): T {
|
||||
return (this as Instance<T>)[CACHED_VALUE_KEY];
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -8,3 +8,4 @@ export * from './types/tooltip-positions.type';
|
||||
export * from './decorators/bind.decorator';
|
||||
export * from './decorators/required.decorator';
|
||||
export * from './decorators/debounce.decorator';
|
||||
export * from './decorators/on-change.decorator';
|
||||
|
||||
11
src/lib/utils/pipes/log.pipe.ts
Normal file
11
src/lib/utils/pipes/log.pipe.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'log'
|
||||
})
|
||||
export class LogPipe implements PipeTransform {
|
||||
transform<T>(value: T, message = ''): T {
|
||||
console.log(message, value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user