mirror of
https://github.com/valitydev/dashboard.git
synced 2024-11-06 02:25:23 +00:00
FE-1011: Create range-datepicker (#169)
This commit is contained in:
parent
f8f7257aad
commit
7e84c5269f
54
package-lock.json
generated
54
package-lock.json
generated
@ -6525,8 +6525,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -6550,15 +6549,13 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -6575,22 +6572,19 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -6721,8 +6715,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -6736,7 +6729,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -6753,7 +6745,6 @@
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -6762,15 +6753,13 @@
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
|
||||
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -6791,7 +6780,6 @@
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -6880,8 +6868,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -6895,7 +6882,6 @@
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -6991,8 +6977,7 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -7034,7 +7019,6 @@
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -7056,7 +7040,6 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -7105,15 +7088,13 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
|
||||
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11270,6 +11251,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"saturn-datepicker": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/saturn-datepicker/-/saturn-datepicker-8.0.1.tgz",
|
||||
"integrity": "sha512-kUmf8xg5oeGAZtgwqfkNY8Dro6EzX0zGIWnAcQ9cfQ+w3xBq4TbjdBUWuEA19rJlvLLP/uH3PAoXG/SHVLYncQ==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"saucelabs": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
|
||||
@ -12977,6 +12966,11 @@
|
||||
"object.getownpropertydescriptors": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"utility-types": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
|
||||
"integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg=="
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
|
@ -62,8 +62,10 @@
|
||||
"lodash.template": "^4.4.0",
|
||||
"moment": "^2.24.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"saturn-datepicker": "8.0.1",
|
||||
"ts-keycode-enum": "^1.0.6",
|
||||
"tslib": "^1.9.0",
|
||||
"utility-types": "^3.10.0",
|
||||
"uuid": "^3.3.3",
|
||||
"zone.js": "~0.9.1"
|
||||
},
|
||||
|
@ -340,6 +340,11 @@ export class ButtonToggleComponent extends _MatButtonToggleMixinBase implements
|
||||
}
|
||||
}
|
||||
|
||||
@HostBinding('attr.disabled')
|
||||
get attrDisabled() {
|
||||
return this.disabled ? 'disabled' : null;
|
||||
}
|
||||
|
||||
@HostBinding('class.dsh-button-toggle-disabled')
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { FormatInputModule } from './format-input';
|
||||
import { RangeDatepickerModule } from './range-datepicker';
|
||||
|
||||
const EXPORTED_DECLARATIONS = [FormatInputModule];
|
||||
const EXPORTED_DECLARATIONS = [FormatInputModule, RangeDatepickerModule];
|
||||
|
||||
@NgModule({
|
||||
imports: EXPORTED_DECLARATIONS,
|
||||
|
@ -16,5 +16,5 @@ export const binConfig: FormatInputConfig = {
|
||||
mask: binMask,
|
||||
placeholder: '0000 00',
|
||||
postfix: '** **** ****',
|
||||
getValue: (v: string) => (v ? v.replace(' ', '') : '')
|
||||
toPublicValue: (v: string) => (v ? v.replace(' ', '') : '')
|
||||
};
|
||||
|
@ -7,5 +7,6 @@ export interface FormatInputConfig {
|
||||
size?: number;
|
||||
prefix?: string;
|
||||
postfix?: string;
|
||||
getValue?: (value: any) => any;
|
||||
toPublicValue?: (value: string) => string;
|
||||
toInternalValue?: (value: string) => string;
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ export class FormatInputComponent extends CustomFormControl {
|
||||
prefix = '';
|
||||
postfix = '';
|
||||
size: string = null;
|
||||
getValue: (v: any) => any;
|
||||
toInternalValue: (v: any) => any;
|
||||
toPublicValue: (v: any) => any;
|
||||
|
||||
private _format: Type;
|
||||
@Input()
|
||||
@ -28,9 +29,9 @@ export class FormatInputComponent extends CustomFormControl {
|
||||
return this._format;
|
||||
}
|
||||
|
||||
setType(type: Type) {
|
||||
private setType(type: Type) {
|
||||
const c = configs[type];
|
||||
const { placeholder, prefix, postfix, size, mask, getValue } = c;
|
||||
const { placeholder, prefix, postfix, size, mask, toInternalValue, toPublicValue } = c;
|
||||
const sizeFromPlaceholder = c.sizeFromPlaceholder === undefined ? true : c.sizeFromPlaceholder;
|
||||
const estimatedSize = sizeFromPlaceholder && !size && placeholder ? placeholder.length : size;
|
||||
|
||||
@ -39,12 +40,15 @@ export class FormatInputComponent extends CustomFormControl {
|
||||
this.prefix = this.prepareText(prefix);
|
||||
this.postfix = this.prepareText(postfix);
|
||||
this.mask = mask;
|
||||
if (getValue) {
|
||||
this.getValue = getValue;
|
||||
if (toInternalValue) {
|
||||
this.toInternalValue = toInternalValue;
|
||||
}
|
||||
if (toPublicValue) {
|
||||
this.toPublicValue = toPublicValue;
|
||||
}
|
||||
}
|
||||
|
||||
prepareText(str: string): string {
|
||||
private prepareText(str: string): string {
|
||||
return (typeof str === 'string' ? str.replace(/ /g, '\xa0') : str) || '';
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,5 @@ export * from './form-controls.module';
|
||||
export * from './masks';
|
||||
export * from './validators';
|
||||
export * from './format-input';
|
||||
export * from './range-datepicker';
|
||||
export * from './utils';
|
||||
|
@ -0,0 +1,38 @@
|
||||
@import '~@angular/material/theming';
|
||||
|
||||
@mixin dsh-range-datepicker-theme($theme) {
|
||||
$gray: map-get($theme, gray);
|
||||
$bg: map-get($gray, 200);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$text: map-get($foreground, text);
|
||||
|
||||
.dsh-range-datepicker {
|
||||
&-button {
|
||||
border-color: mat-color($foreground, divider, 0.12);
|
||||
color: $text;
|
||||
|
||||
&:enabled {
|
||||
&:hover,
|
||||
&:active {
|
||||
background-color: $bg;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: map-get($foreground, disabled-button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dsh-range-datepicker-typography($config) {
|
||||
.dsh-range-datepicker {
|
||||
&-button {
|
||||
font: {
|
||||
family: mat-font-family($config, button);
|
||||
size: mat-font-size($config, button);
|
||||
weight: mat-font-weight($config, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
src/app/form-controls/range-datepicker/index.ts
Normal file
2
src/app/form-controls/range-datepicker/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './range-datepicker.module';
|
||||
export * from './range-datepicker.component';
|
103
src/app/form-controls/range-datepicker/range-date.pipe.ts
Normal file
103
src/app/form-controls/range-datepicker/range-date.pipe.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import { Pipe, PipeTransform, Inject, LOCALE_ID } from '@angular/core';
|
||||
import { formatDate } from '@angular/common';
|
||||
import { Moment } from 'moment';
|
||||
import { TranslocoService } from '@ngneat/transloco';
|
||||
import moment from 'moment';
|
||||
|
||||
@Pipe({ name: 'rangeDate' })
|
||||
export class RangeDatePipe implements PipeTransform {
|
||||
constructor(@Inject(LOCALE_ID) private locale: string, private transloco: TranslocoService) {}
|
||||
|
||||
transform({ begin, end }: { begin: Moment; end: Moment }): string {
|
||||
if (begin.isSame(begin.clone().startOf('year'), 'day') && end.isSame(end.clone().endOf('year'), 'day')) {
|
||||
return this.toYearStr(begin, end);
|
||||
}
|
||||
if (begin.isSame(begin.clone().startOf('month'), 'day') && end.isSame(end.clone().endOf('month'), 'day')) {
|
||||
return this.toMonthStr(begin, end);
|
||||
}
|
||||
if (begin.isSame(moment().startOf('week'), 'day') && end.isSame(moment().endOf('week'), 'day')) {
|
||||
return this.rangeDateTranslate('currentWeek');
|
||||
}
|
||||
return this.toDateStr(begin, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 2020 год
|
||||
* С 2019 по 2020 год
|
||||
*/
|
||||
private toYearStr(begin: Moment, end: Moment) {
|
||||
const endStr = `${end.year()} ${this.rangeDateTranslate('year')}`;
|
||||
|
||||
if (begin.isSame(end, 'year')) {
|
||||
return endStr;
|
||||
}
|
||||
|
||||
const fromStr = this.rangeDateTranslate('from');
|
||||
const toStr = this.rangeDateTranslate('to');
|
||||
|
||||
return `${fromStr} ${begin.year()} ${toStr} ${endStr}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Январь
|
||||
* Январь 2020
|
||||
*
|
||||
* С января по март
|
||||
* С января 2019 по март 2019 / С декабря 2019 по март 2020
|
||||
*/
|
||||
private toMonthStr(begin: Moment, end: Moment) {
|
||||
const fromStr = this.rangeDateTranslate('from');
|
||||
const toStr = this.rangeDateTranslate('to');
|
||||
|
||||
const currentYear = this.isCurrentYear(begin, end);
|
||||
const beginStr = this.formatDate(begin, false, true, !currentYear);
|
||||
const endStr = this.formatStandaloneDate(end, false, true, !currentYear);
|
||||
|
||||
if (begin.isSame(end, 'month')) {
|
||||
return this.capitalizeFirstLetter(endStr);
|
||||
}
|
||||
|
||||
return `${fromStr} ${beginStr} ${toStr} ${endStr}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2 января
|
||||
* 2 января 2020
|
||||
*
|
||||
* Со 2 по 8 марта / Со 2 января по 8 марта
|
||||
* Со 2 по 8 марта 2019 / Со 2 января 2019 по 8 марта 2020
|
||||
*/
|
||||
private toDateStr(begin: Moment, end: Moment) {
|
||||
const fromByDayStr = this.rangeDateTranslate(begin.date() === 2 ? 'fromStartWith2' : 'from');
|
||||
const toStr = this.rangeDateTranslate('to');
|
||||
|
||||
const beginStr = this.formatDate(begin, true, !begin.isSame(end, 'month'), !begin.isSame(end, 'year'));
|
||||
const endStr = this.formatDate(end, true, true, !this.isCurrentYear(begin, end));
|
||||
|
||||
if (begin.isSame(end, 'day')) {
|
||||
return endStr;
|
||||
}
|
||||
|
||||
return `${fromByDayStr} ${beginStr} ${toStr} ${endStr}`;
|
||||
}
|
||||
|
||||
private isCurrentYear(begin: Moment, end: Moment) {
|
||||
return moment().isSame(begin, 'year') && moment().isSame(end, 'year');
|
||||
}
|
||||
|
||||
private capitalizeFirstLetter(str: string) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
private rangeDateTranslate(key: string) {
|
||||
return this.transloco.translate(`rangeDate.${key}`, null, 'range-datepicker|scoped');
|
||||
}
|
||||
|
||||
private formatDate(date: Moment, d: boolean = false, m: boolean = false, y: boolean = false) {
|
||||
return formatDate(date.toDate(), [d && 'd', m && 'MMMM', y && 'y'].filter(v => v).join(' '), this.locale);
|
||||
}
|
||||
|
||||
private formatStandaloneDate(date: Moment, d: boolean = false, m: boolean = false, y: boolean = false) {
|
||||
return formatDate(date.toDate(), [d && 'd', m && 'LLLL', y && 'y'].filter(v => v).join(' '), this.locale);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<ng-container *transloco="let t; scope: 'range-datepicker'; read: 'rangeDatepicker'">
|
||||
<div fxLayout class="dsh-range-datepicker">
|
||||
<button (click)="back()" class="dsh-range-datepicker-button dsh-range-datepicker-back" [disabled]="isMinDate">
|
||||
<mat-icon>keyboard_arrow_left</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
[matMenuTriggerFor]="menu"
|
||||
fxFlex
|
||||
class="dsh-range-datepicker-button dsh-range-datepicker-input"
|
||||
title="{{ value?.begin | date: 'shortDate' }} - {{ value?.end | date: 'shortDate' }}"
|
||||
>
|
||||
<div class="dsh-range-datepicker-input-content">
|
||||
<ng-container *ngIf="publicValue?.begin && publicValue?.end; else placeholder" [ngSwitch]="period">
|
||||
{{ publicValue | rangeDate }}
|
||||
</ng-container>
|
||||
<ng-template #placeholder>
|
||||
{{ t.selectPeriod }}
|
||||
</ng-template>
|
||||
<input
|
||||
#input
|
||||
matInput
|
||||
[formControl]="formControl"
|
||||
[satDatepicker]="picker"
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
(click)="forward()"
|
||||
class="dsh-range-datepicker-button dsh-range-datepicker-forward"
|
||||
[disabled]="isMaxDate"
|
||||
>
|
||||
<mat-icon>keyboard_arrow_right</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="selectPeriod('week')">{{ t.select.currentWeek }}</button>
|
||||
<button mat-menu-item (click)="selectPeriod('month')">{{ current | date: 'LLLL' | titlecase }}</button>
|
||||
<button mat-menu-item (click)="selectPeriod('3month')">{{ t.select.threeMonths }}</button>
|
||||
<button mat-menu-item (click)="selectPeriod('year')">
|
||||
{{ t.select.year | translocoParams: { year: current | date: 'y' } }}
|
||||
</button>
|
||||
<mat-divider class="dsh-range-datepicker-menu-divider"></mat-divider>
|
||||
<button mat-menu-item (click)="picker.open(); selectPeriod()">
|
||||
{{ t.select.period }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<sat-datepicker #picker [rangeMode]="true"></sat-datepicker>
|
||||
</ng-container>
|
@ -0,0 +1,69 @@
|
||||
$height: 50px;
|
||||
$border-radius: 4px;
|
||||
|
||||
:host {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.dsh-range-datepicker {
|
||||
&-button {
|
||||
white-space: nowrap;
|
||||
transition: background-color 0.25s ease-in-out;
|
||||
background-color: transparent;
|
||||
display: block;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
border: 1px solid;
|
||||
padding: 0 10px;
|
||||
margin: 0;
|
||||
line-height: $height;
|
||||
}
|
||||
|
||||
&-back,
|
||||
&-forward {
|
||||
width: $height;
|
||||
|
||||
& > * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&-back {
|
||||
border-top-left-radius: $border-radius;
|
||||
border-bottom-left-radius: $border-radius;
|
||||
}
|
||||
|
||||
&-forward {
|
||||
border-top-right-radius: $border-radius;
|
||||
border-bottom-right-radius: $border-radius;
|
||||
}
|
||||
|
||||
&-input {
|
||||
position: relative;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
overflow: hidden;
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&-input-content {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&-menu-divider {
|
||||
width: calc(100% + 20px);
|
||||
transform: translate(-10px, -5px);
|
||||
}
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
import { Component, Input, ViewChild, ElementRef } from '@angular/core';
|
||||
import { MatFormFieldControl } from '@angular/material';
|
||||
import moment, { Moment } from 'moment';
|
||||
import { SatDatepickerRangeValue } from 'saturn-datepicker';
|
||||
import { SetIntersection } from 'utility-types';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { CustomFormControl } from '../utils';
|
||||
|
||||
type InternalRange = SatDatepickerRangeValue<Date>;
|
||||
export type Range = SatDatepickerRangeValue<Moment>;
|
||||
|
||||
type MomentPeriod = SetIntersection<moment.unitOfTime.StartOf, 'week' | 'month' | 'year'>;
|
||||
type Period = MomentPeriod | '3month';
|
||||
|
||||
@Component({
|
||||
selector: 'dsh-range-datepicker',
|
||||
templateUrl: 'range-datepicker.component.html',
|
||||
styleUrls: ['range-datepicker.component.scss'],
|
||||
providers: [{ provide: MatFormFieldControl, useExisting: RangeDatepickerComponent }]
|
||||
})
|
||||
export class RangeDatepickerComponent extends CustomFormControl<InternalRange, Range> {
|
||||
minDate = moment()
|
||||
.subtract(15, 'year')
|
||||
.startOf('year')
|
||||
.toDate();
|
||||
@Input()
|
||||
set min(min: Moment) {
|
||||
this.minDate = min.toDate();
|
||||
}
|
||||
get min() {
|
||||
return moment(this.minDate);
|
||||
}
|
||||
|
||||
maxDate = moment()
|
||||
.endOf('day')
|
||||
.toDate();
|
||||
@Input()
|
||||
set max(max: Moment) {
|
||||
this.maxDate = max.toDate();
|
||||
}
|
||||
get max() {
|
||||
return moment(this.maxDate);
|
||||
}
|
||||
|
||||
@ViewChild('input', { static: false })
|
||||
set input(input: ElementRef<HTMLInputElement>) {
|
||||
if (input && input.nativeElement) {
|
||||
this.setInputElement(input.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
current = moment();
|
||||
period: Period = null;
|
||||
formControlSubscription = this.formControl.valueChanges.pipe(map(this.toPublicValue.bind(this))).subscribe(() => {
|
||||
if (!this.period) {
|
||||
this.period = this.takeUnitOfTime();
|
||||
}
|
||||
});
|
||||
|
||||
get isMaxDate() {
|
||||
return this.publicValue.end.isSameOrAfter(this.max, 'day');
|
||||
}
|
||||
|
||||
get isMinDate() {
|
||||
return this.publicValue.begin.isSameOrBefore(this.min, 'day');
|
||||
}
|
||||
|
||||
toPublicValue({ begin, end }: InternalRange): Range {
|
||||
return { begin: moment(begin), end: moment(end) };
|
||||
}
|
||||
|
||||
toInternalValue({ begin, end }: Range): InternalRange {
|
||||
return { begin: begin.toDate(), end: end.toDate() };
|
||||
}
|
||||
|
||||
back() {
|
||||
const { begin, end } = this.publicValue;
|
||||
switch (this.period) {
|
||||
case 'year': {
|
||||
const newBegin = begin.clone().subtract(1, 'year');
|
||||
this.changeRange(newBegin, newBegin.clone().endOf('year'));
|
||||
return;
|
||||
}
|
||||
case '3month': {
|
||||
const newBegin = begin.clone().subtract(3, 'month');
|
||||
this.changeRange(
|
||||
newBegin,
|
||||
newBegin
|
||||
.clone()
|
||||
.add(2, 'month')
|
||||
.endOf('month')
|
||||
);
|
||||
return;
|
||||
}
|
||||
case 'month': {
|
||||
const newBegin = begin.clone().subtract(1, 'month');
|
||||
this.changeRange(newBegin, newBegin.clone().endOf('month'));
|
||||
return;
|
||||
}
|
||||
case 'week': {
|
||||
const newBegin = begin.clone().subtract(1, 'week');
|
||||
this.changeRange(newBegin, newBegin.clone().endOf('week'));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
const diff = end.diff(begin);
|
||||
this.changeRange(begin.subtract(diff).subtract(1, 'day'), end.subtract(diff).subtract(1, 'day'));
|
||||
}
|
||||
}
|
||||
|
||||
forward() {
|
||||
const { begin, end } = this.publicValue;
|
||||
switch (this.period) {
|
||||
case 'year': {
|
||||
const newBegin = begin.clone().add(1, 'year');
|
||||
this.changeRange(newBegin, newBegin.clone().endOf('year'));
|
||||
return;
|
||||
}
|
||||
case '3month': {
|
||||
const newBegin = begin.clone().add(3, 'month');
|
||||
this.changeRange(
|
||||
newBegin,
|
||||
newBegin
|
||||
.clone()
|
||||
.add(2, 'month')
|
||||
.endOf('month')
|
||||
);
|
||||
return;
|
||||
}
|
||||
case 'month': {
|
||||
const newBegin = begin.clone().add(1, 'month');
|
||||
this.changeRange(newBegin, newBegin.clone().endOf('month'));
|
||||
return;
|
||||
}
|
||||
case 'week': {
|
||||
const newBegin = begin.clone().add(1, 'week');
|
||||
this.changeRange(newBegin, newBegin.clone().endOf('week'));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
const diff = end.diff(begin, 'day');
|
||||
this.changeRange(begin.clone().add(diff + 1, 'day'), end.clone().add(diff + 1, 'day'));
|
||||
}
|
||||
}
|
||||
|
||||
selectPeriod(period: Period = null) {
|
||||
this.period = period;
|
||||
switch (period) {
|
||||
case 'year':
|
||||
this.changeRange(moment().startOf('year'), moment().endOf('year'));
|
||||
break;
|
||||
case '3month':
|
||||
this.changeRange(
|
||||
moment()
|
||||
.subtract(2, 'month')
|
||||
.startOf('month'),
|
||||
moment().endOf('month')
|
||||
);
|
||||
break;
|
||||
case 'month':
|
||||
this.changeRange(moment().startOf('month'), moment().endOf('month'));
|
||||
break;
|
||||
case 'week':
|
||||
this.changeRange(moment().startOf('week'), moment().endOf('week'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private checkIsUnitOfTime(unitOfTime: MomentPeriod, countOfUnits = 1): boolean {
|
||||
const { begin, end } = this.publicValue;
|
||||
const beginOfUnit = begin.clone().startOf(unitOfTime);
|
||||
const expectedEndOfPeriodByBegin = beginOfUnit
|
||||
.clone()
|
||||
.add(countOfUnits - 1, unitOfTime)
|
||||
.endOf(unitOfTime);
|
||||
return begin.isSame(beginOfUnit, 'day') && end.isSame(expectedEndOfPeriodByBegin, 'day');
|
||||
}
|
||||
|
||||
private takeUnitOfTime(): Period {
|
||||
if (this.checkIsUnitOfTime('year')) {
|
||||
return 'year';
|
||||
}
|
||||
if (this.checkIsUnitOfTime('month', 3)) {
|
||||
return '3month';
|
||||
}
|
||||
if (this.checkIsUnitOfTime('month')) {
|
||||
return 'month';
|
||||
}
|
||||
if (this.checkIsUnitOfTime('week')) {
|
||||
return 'week';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private changeRange(begin: Moment, end: Moment) {
|
||||
this.publicValue = { begin, end };
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SatDatepickerModule, SatNativeDateModule } from 'saturn-datepicker';
|
||||
import { MatNativeDateModule, MatMenuModule, MatIconModule, MatInputModule, MatDividerModule } from '@angular/material';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslocoModule } from '@ngneat/transloco';
|
||||
|
||||
import { RangeDatepickerComponent } from './range-datepicker.component';
|
||||
import { ButtonToggleModule } from '../../button-toggle';
|
||||
import { RangeDatePipe } from './range-date.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SatDatepickerModule,
|
||||
SatNativeDateModule,
|
||||
MatNativeDateModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
ButtonToggleModule,
|
||||
FlexLayoutModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
CommonModule,
|
||||
TranslocoModule,
|
||||
MatDividerModule
|
||||
],
|
||||
declarations: [RangeDatepickerComponent, RangeDatePipe],
|
||||
exports: [RangeDatepickerComponent]
|
||||
})
|
||||
export class RangeDatepickerModule {}
|
@ -13,7 +13,8 @@ import {
|
||||
Optional,
|
||||
Self,
|
||||
HostListener,
|
||||
OnChanges
|
||||
OnChanges,
|
||||
SimpleChanges
|
||||
} from '@angular/core';
|
||||
import uuid from 'uuid';
|
||||
import { AutofillMonitor } from '@angular/cdk/text-field';
|
||||
@ -21,8 +22,8 @@ import { Platform } from '@angular/cdk/platform';
|
||||
|
||||
import { InputMixinBase } from './input-base';
|
||||
|
||||
export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
implements AfterViewInit, ControlValueAccessor, MatFormFieldControl<T>, OnDestroy, DoCheck, OnChanges {
|
||||
export class CustomFormControl<I extends any = any, P extends any = I> extends InputMixinBase
|
||||
implements AfterViewInit, ControlValueAccessor, MatFormFieldControl<I>, OnDestroy, DoCheck, OnChanges {
|
||||
/** The aria-describedby attribute on the input for improved a11y. */
|
||||
@HostBinding('attr.aria-describedby') _ariaDescribedby: string;
|
||||
|
||||
@ -32,6 +33,7 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
|
||||
autofilled = false;
|
||||
|
||||
protected _disabled = false;
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
if (this.ngControl && this.ngControl.disabled !== null) {
|
||||
@ -49,8 +51,8 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
this.stateChanges.next();
|
||||
}
|
||||
}
|
||||
protected _disabled = false;
|
||||
|
||||
protected _id: string;
|
||||
@HostBinding('attr.id')
|
||||
@Input()
|
||||
get id(): string {
|
||||
@ -59,11 +61,11 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
set id(value: string) {
|
||||
this._id = value || `custom-input-${uuid()}`;
|
||||
}
|
||||
protected _id: string;
|
||||
|
||||
@Input()
|
||||
placeholder: string;
|
||||
|
||||
protected _required = false;
|
||||
@Input()
|
||||
get required(): boolean {
|
||||
return this._required;
|
||||
@ -71,21 +73,27 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
set required(value: boolean) {
|
||||
this._required = coerceBooleanProperty(value);
|
||||
}
|
||||
protected _required = false;
|
||||
|
||||
protected type = 'text';
|
||||
|
||||
@Input()
|
||||
get value(): T {
|
||||
get value() {
|
||||
return this.formControl.value;
|
||||
}
|
||||
set value(value: T) {
|
||||
set value(value: I) {
|
||||
this.formControl.setValue(value);
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
get publicValue() {
|
||||
return this.toPublicValue(this.value);
|
||||
}
|
||||
set publicValue(value: P) {
|
||||
this.value = this.toInternalValue(value);
|
||||
}
|
||||
|
||||
get details() {
|
||||
return this.getDetails(this.value);
|
||||
return this.getDetails(this.publicValue);
|
||||
}
|
||||
|
||||
@HostBinding('class.floating')
|
||||
@ -93,16 +101,13 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
return this.focused || !this.empty;
|
||||
}
|
||||
|
||||
_inputRef = new ElementRef<HTMLInputElement>(null);
|
||||
get inputRef() {
|
||||
this._inputRef.nativeElement = this.elementRef.nativeElement.querySelector('input');
|
||||
return this._inputRef;
|
||||
}
|
||||
inputRef = new ElementRef<HTMLInputElement>(null);
|
||||
|
||||
get empty(): boolean {
|
||||
return !this.formControl.value;
|
||||
}
|
||||
|
||||
private _focused = false;
|
||||
get focused(): boolean {
|
||||
return this._focused;
|
||||
}
|
||||
@ -113,8 +118,8 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
|
||||
formControl = new FormControl();
|
||||
autocompleteOrigin: MatAutocompleteOrigin;
|
||||
monitorsRegistered = false;
|
||||
|
||||
private _focused = false;
|
||||
private _onTouched: () => void;
|
||||
|
||||
constructor(
|
||||
@ -136,7 +141,7 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
ngOnChanges(_changes?: SimpleChanges) {
|
||||
this.stateChanges.next();
|
||||
}
|
||||
|
||||
@ -150,15 +155,7 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
if (this.platform.isBrowser) {
|
||||
this.autofillMonitor.monitor(this.inputRef).subscribe(event => {
|
||||
this.autofilled = event.isAutofilled;
|
||||
this.stateChanges.next();
|
||||
});
|
||||
}
|
||||
this.focusMonitor.monitor(this.elementRef.nativeElement, true).subscribe(focusOrigin => {
|
||||
this.focused = !!focusOrigin;
|
||||
});
|
||||
this.setInputElement();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@ -180,14 +177,29 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
this._onTouched();
|
||||
}
|
||||
|
||||
registerOnChange(onChange: (value: T) => void): void {
|
||||
this.formControl.valueChanges.subscribe(v => onChange(this.getValue(v)));
|
||||
registerOnChange(onChange: (value: P) => void): void {
|
||||
this.formControl.valueChanges.subscribe(v => onChange(this.toPublicValue(v)));
|
||||
}
|
||||
|
||||
registerOnTouched(onTouched: () => void): void {
|
||||
this._onTouched = onTouched;
|
||||
}
|
||||
|
||||
private registerMonitors() {
|
||||
if (!this.monitorsRegistered && this.inputRef.nativeElement) {
|
||||
this.monitorsRegistered = true;
|
||||
if (this.platform.isBrowser) {
|
||||
this.autofillMonitor.monitor(this.inputRef).subscribe(event => {
|
||||
this.autofilled = event.isAutofilled;
|
||||
this.stateChanges.next();
|
||||
});
|
||||
}
|
||||
this.focusMonitor.monitor(this.elementRef.nativeElement, true).subscribe(focusOrigin => {
|
||||
this.focused = !!focusOrigin;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setDescribedByIds(ids: string[]): void {
|
||||
this._ariaDescribedby = ids.join(' ');
|
||||
}
|
||||
@ -202,15 +214,24 @@ export class CustomFormControl<T extends any = string> extends InputMixinBase
|
||||
this.disabled = shouldDisable;
|
||||
}
|
||||
|
||||
writeValue(value: string): void {
|
||||
this.formControl.setValue(value, { emitEvent: false });
|
||||
setInputElement(input: HTMLInputElement = this.elementRef.nativeElement.querySelector('input')) {
|
||||
this.inputRef.nativeElement = input;
|
||||
this.registerMonitors();
|
||||
}
|
||||
|
||||
getDetails(value: T) {
|
||||
return value;
|
||||
writeValue(value: P): void {
|
||||
this.formControl.setValue(this.toInternalValue(value), { emitEvent: false });
|
||||
}
|
||||
|
||||
getValue(value = this.value): T {
|
||||
return value;
|
||||
protected getDetails(value: P): string {
|
||||
return value as any;
|
||||
}
|
||||
|
||||
protected toInternalValue(value: P): I {
|
||||
return value as any;
|
||||
}
|
||||
|
||||
protected toPublicValue(value: I): P {
|
||||
return value as any;
|
||||
}
|
||||
}
|
||||
|
@ -6,22 +6,34 @@
|
||||
}"
|
||||
(dshResized)="cardHeight = $event.height"
|
||||
>
|
||||
<div (dshResized)="setBaseContentHeight($event.height)" class="dsh-float-panel-base">
|
||||
<div fxLayout fxLayout.lt-md="column" [fxLayoutGap]="layoutGap">
|
||||
<div fxFlex.gt-sm>
|
||||
<ng-content></ng-content>
|
||||
<div class="dsh-float-panel-base" (dshResized)="setBaseContentHeight($event.height)">
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap">
|
||||
<div fxLayout fxLayout.lt-md="column" [fxLayoutGap]="layoutGap">
|
||||
<div fxFlex.gt-sm>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="!expanded && floatPanelMore"
|
||||
class="dsh-float-panel-actions"
|
||||
fxLayout
|
||||
fxLayoutAlign="center center"
|
||||
>
|
||||
<button dsh-icon-button (click)="expandToggle()" fxHide.lt-md>
|
||||
<mat-icon svgIcon="keyboard_arrow_down"></mat-icon>
|
||||
</button>
|
||||
<button dsh-button (click)="expandToggle()" fxFlex fxHide.gt-sm>
|
||||
{{ t.showMore }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!expanded" class="dsh-float-panel-actions" fxLayout fxLayoutAlign="center center">
|
||||
<button dsh-icon-button (click)="expandToggle()" fxHide.lt-md>
|
||||
<mat-icon svgIcon="keyboard_arrow_down"></mat-icon>
|
||||
</button>
|
||||
<button dsh-button (click)="expandToggle()" fxFlex fxHide.gt-sm>
|
||||
{{ t.showMore }}
|
||||
</button>
|
||||
<div fxLayout fxLayoutAlign="center" *ngIf="!floatPanelMore && floatPanelActions">
|
||||
<div>
|
||||
<ng-container *ngTemplateOutlet="floatPanelActions?.templateRef"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dsh-float-panel-more" [@expand]="expandTrigger" *ngIf="expanded">
|
||||
<div class="dsh-float-panel-more" [@expand]="expandTrigger" *ngIf="expanded && floatPanelMore">
|
||||
<div (dshResized)="setMoreContentHeight($event.height)">
|
||||
<div fxLayout="column" [fxLayoutGap]="layoutGap">
|
||||
<div class="dsh-float-panel-more-content">
|
||||
|
@ -29,6 +29,7 @@ import { LastUpdatedModule } from '../operations/last-updated/last-updated.modul
|
||||
import { SpinnerModule } from '../../../spinner';
|
||||
import { ActionsComponent } from './table/actions';
|
||||
import { CreateReportDialogComponent } from './create-report-dialog';
|
||||
import { FormControlsModule } from '../../../form-controls';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -54,7 +55,8 @@ import { CreateReportDialogComponent } from './create-report-dialog';
|
||||
SpinnerModule,
|
||||
MatDialogModule,
|
||||
MatSnackBarModule,
|
||||
MatMenuModule
|
||||
MatMenuModule,
|
||||
FormControlsModule
|
||||
],
|
||||
declarations: [
|
||||
ReportsComponent,
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { Moment } from 'moment';
|
||||
|
||||
import { Report } from '../../../../api-codegen/anapi';
|
||||
import { Range } from '../../../../form-controls';
|
||||
|
||||
export interface FormParams {
|
||||
fromTime: Moment;
|
||||
toTime: Moment;
|
||||
date: Range;
|
||||
reportType: Report.ReportTypeEnum;
|
||||
shopID?: string;
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
import { Report } from '../../../../api-codegen/anapi';
|
||||
|
||||
export interface QueryParams {
|
||||
fromTime: string;
|
||||
toTime: string;
|
||||
reportType: Report.ReportTypeEnum;
|
||||
shopID?: string;
|
||||
}
|
@ -1,65 +1,34 @@
|
||||
<dsh-float-panel *transloco="let r; scope: 'reports'; read: 'reports'" [formGroup]="form" [(expanded)]="expanded">
|
||||
<ng-container *transloco="let t">
|
||||
<div
|
||||
fxLayout
|
||||
fxLayout.lt-md="column"
|
||||
fxLayoutGap="20px"
|
||||
fxLayoutAlign="space-between center"
|
||||
fxLayoutAlign.lt-md="space-between stretch"
|
||||
>
|
||||
<dsh-daterange-selector
|
||||
fxFlex.gt-sm
|
||||
[value]="form.value"
|
||||
(selectDaterange)="selectDaterange($event)"
|
||||
(selectMore)="expanded = true"
|
||||
></dsh-daterange-selector>
|
||||
<dsh-justify-wrapper fxFlex.gt-sm fxLayout fxLayout.lt-md="column" fxLayoutGap="20px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ r.filter.shopID }}</mat-label>
|
||||
<mat-select formControlName="shopID">
|
||||
<mat-option>
|
||||
{{ t.any }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let shopInfo of shopsInfo$ | async" [value]="shopInfo.shopID">
|
||||
{{ shopInfo.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ r.filter.type }}</mat-label>
|
||||
<mat-select formControlName="reportType">
|
||||
<mat-option>
|
||||
{{ t.any }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let type of reportTypes" [value]="type">
|
||||
{{ r.type[type] }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</dsh-justify-wrapper>
|
||||
</div>
|
||||
<dsh-float-panel-more>
|
||||
<div fxLayout fxLayout.lt-md="column" fxLayoutGap.gt-sm="20px">
|
||||
<div fxFlex.gt-sm fxLayout fxLayout.xs="column" fxLayoutGap.gt-xs="20px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ t.period.fromTime }}</mat-label>
|
||||
<input required matInput formControlName="fromTime" [matDatepicker]="fromTime" />
|
||||
<mat-datepicker-toggle matSuffix [for]="fromTime"></mat-datepicker-toggle>
|
||||
<mat-datepicker #fromTime></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ t.period.toTime }}</mat-label>
|
||||
<input required matInput formControlName="toTime" [matDatepicker]="toTime" />
|
||||
<mat-datepicker-toggle matSuffix [for]="toTime"></mat-datepicker-toggle>
|
||||
<mat-datepicker #toTime></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</dsh-float-panel-more>
|
||||
<dsh-float-panel-actions>
|
||||
<button dsh-button (click)="reset()">
|
||||
{{ t.resetSearchParams }}
|
||||
</button>
|
||||
</dsh-float-panel-actions>
|
||||
</ng-container>
|
||||
<dsh-float-panel *transloco="let r; scope: 'reports'; read: 'reports'" [formGroup]="form">
|
||||
<dsh-justify-wrapper
|
||||
fxLayout
|
||||
fxLayoutGap="20px"
|
||||
fxLayoutAlign="space-between center"
|
||||
fxLayout.lt-md="column"
|
||||
fxLayoutAlign.lt-md="space-between stretch"
|
||||
*transloco="let t"
|
||||
>
|
||||
<dsh-range-datepicker formControlName="date" fxFlex></dsh-range-datepicker>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ r.filter.shopID }}</mat-label>
|
||||
<mat-select formControlName="shopID">
|
||||
<mat-option>
|
||||
{{ t.any }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let shopInfo of shopsInfo$ | async" [value]="shopInfo.shopID">
|
||||
{{ shopInfo.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>{{ r.filter.type }}</mat-label>
|
||||
<mat-select formControlName="reportType">
|
||||
<mat-option>
|
||||
{{ t.any }}
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let type of reportTypes" [value]="type">
|
||||
{{ r.type[type] }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</dsh-justify-wrapper>
|
||||
</dsh-float-panel>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { Report } from '../../../../api-codegen/anapi/swagger-codegen';
|
||||
import { SearchFormValue } from '../../operations/search-form-value';
|
||||
import { SearchFormService } from './search-form.service';
|
||||
import { ReportsService } from '../reports.service';
|
||||
|
||||
@ -13,16 +12,8 @@ import { ReportsService } from '../reports.service';
|
||||
export class SearchFormComponent {
|
||||
form = this.searchFormService.form;
|
||||
reset = this.searchFormService.reset;
|
||||
|
||||
shopsInfo$ = this.reportsService.shopsInfo$;
|
||||
|
||||
reportTypes = Object.values(Report.ReportTypeEnum);
|
||||
|
||||
expanded = false;
|
||||
|
||||
constructor(private searchFormService: SearchFormService, private reportsService: ReportsService) {}
|
||||
|
||||
selectDaterange(v: SearchFormValue) {
|
||||
this.form.patchValue(v);
|
||||
}
|
||||
}
|
||||
|
@ -8,19 +8,20 @@ import { toSearchParams } from './to-search-params';
|
||||
import { FormParams } from './form-params';
|
||||
import { toFormValue } from './to-form-value';
|
||||
import { toQueryParams } from './to-query-params';
|
||||
import { QueryParams } from './query-params';
|
||||
|
||||
@Injectable()
|
||||
export class SearchFormService {
|
||||
static defaultParams: FormParams = {
|
||||
fromTime: moment()
|
||||
.subtract(1, 'month')
|
||||
.startOf('day'),
|
||||
toTime: moment().endOf('day'),
|
||||
defaultParams: FormParams = {
|
||||
shopID: null,
|
||||
reportType: null
|
||||
reportType: null,
|
||||
date: {
|
||||
begin: moment().startOf('month'),
|
||||
end: moment().endOf('month')
|
||||
}
|
||||
};
|
||||
|
||||
form = this.fb.group(SearchFormService.defaultParams);
|
||||
form = this.fb.group(this.defaultParams);
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
@ -36,7 +37,7 @@ export class SearchFormService {
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.form.setValue(SearchFormService.defaultParams);
|
||||
this.form.setValue(this.defaultParams);
|
||||
}
|
||||
|
||||
private init() {
|
||||
@ -46,8 +47,8 @@ export class SearchFormService {
|
||||
}
|
||||
|
||||
private syncQueryParams() {
|
||||
const queryParams = this.route.snapshot.queryParams as Record<keyof FormParams, string>;
|
||||
const formValue = toFormValue(queryParams, SearchFormService.defaultParams);
|
||||
const queryParams = this.route.snapshot.queryParams as QueryParams;
|
||||
const formValue = toFormValue(queryParams, this.defaultParams);
|
||||
this.form.setValue(formValue);
|
||||
this.setQueryParams(formValue);
|
||||
this.form.valueChanges.subscribe(v => this.setQueryParams(v));
|
||||
|
@ -2,16 +2,19 @@ import moment from 'moment';
|
||||
|
||||
import { FormParams } from './form-params';
|
||||
import { Report } from '../../../../api-codegen/anapi/swagger-codegen';
|
||||
import { QueryParams } from './query-params';
|
||||
|
||||
export function toFormValue(
|
||||
{ fromTime, toTime, reportType, ...params }: Record<keyof FormParams, string>,
|
||||
{ fromTime, toTime, reportType, ...params }: QueryParams,
|
||||
defaultParams: FormParams
|
||||
): FormParams {
|
||||
return {
|
||||
...defaultParams,
|
||||
...params,
|
||||
fromTime: fromTime ? moment(fromTime) : defaultParams.fromTime,
|
||||
toTime: toTime ? moment(toTime) : defaultParams.toTime,
|
||||
date: {
|
||||
begin: fromTime ? moment(fromTime) : defaultParams.date.begin,
|
||||
end: toTime ? moment(toTime) : defaultParams.date.end
|
||||
},
|
||||
reportType: reportType ? (reportType as Report.ReportTypeEnum) : defaultParams.reportType
|
||||
};
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { FormParams } from './form-params';
|
||||
import { QueryParams } from './query-params';
|
||||
|
||||
export function toQueryParams({ fromTime, toTime, ...params }: FormParams): Partial<Record<keyof FormParams, string>> {
|
||||
export function toQueryParams({ date, ...params }: FormParams): QueryParams {
|
||||
return {
|
||||
...params,
|
||||
fromTime: fromTime.utc().format(),
|
||||
toTime: toTime.utc().format()
|
||||
fromTime: date.begin.utc().format(),
|
||||
toTime: date.end.utc().format()
|
||||
};
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ import { SearchParams } from '../search-params';
|
||||
import { Report } from '../../../../api-codegen/anapi';
|
||||
import { FormParams } from './form-params';
|
||||
|
||||
export function toSearchParams({ reportType, fromTime, toTime, ...params }: FormParams): SearchParams {
|
||||
export function toSearchParams({ reportType, date, ...params }: FormParams): SearchParams {
|
||||
return {
|
||||
...params,
|
||||
reportTypes: reportType ? [reportType] : Object.values(Report.ReportTypeEnum),
|
||||
fromTime: fromTime.utc().format(),
|
||||
toTime: toTime.utc().format()
|
||||
fromTime: date.begin.utc().format(),
|
||||
toTime: date.end.utc().format()
|
||||
};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<mat-icon svgIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item>
|
||||
<button mat-menu-item (click)="goToReportDetails(report.id)">
|
||||
{{ t.details }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
16
src/assets/i18n/range-datepicker/ru.json
Normal file
16
src/assets/i18n/range-datepicker/ru.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"selectPeriod": "Выберите период",
|
||||
"select": {
|
||||
"currentWeek": "Текущая неделя",
|
||||
"threeMonths": "3 месяца",
|
||||
"year": "{{year}} год",
|
||||
"period": "Указать период..."
|
||||
},
|
||||
"rangeDate": {
|
||||
"from": "С",
|
||||
"fromStartWith2": "Со",
|
||||
"to": "по",
|
||||
"currentWeek": "Текущая неделя",
|
||||
"year": "год"
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
@import '~@angular/material/theming';
|
||||
@import '~saturn-datepicker/bundle.css';
|
||||
@import './utils/typography';
|
||||
@import '../app/container/container-theme';
|
||||
@import '../app/dropdown/dropdown-theme';
|
||||
@ -27,6 +28,7 @@
|
||||
@import '../app/dadata/dadata-theme';
|
||||
@import '../app/sections/payment-details/payment-details-theme';
|
||||
@import '../app/layout/panel/panel-theme';
|
||||
@import '../app/form-controls/range-datepicker/range-datepicker-theme';
|
||||
|
||||
@mixin dsh-overrides() {
|
||||
@include body-override();
|
||||
@ -53,6 +55,7 @@
|
||||
@include dsh-dadata-autocomplete-typography($config);
|
||||
@include mat-radio-button-override-typography($config);
|
||||
@include dsh-payment-details-typography($config);
|
||||
@include dsh-range-datepicker-typography($config);
|
||||
@include dsh-panel-typography($config);
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
@import '../../app/sections/invoice-details/payments/payments-theme';
|
||||
@import '../../app/sections/payment-section/payouts/payout-panel/payout-panel-theme';
|
||||
@import '../../app/layout/panel/panel-theme';
|
||||
@import '../../app/form-controls/range-datepicker/range-datepicker-theme';
|
||||
|
||||
@mixin dsh-theme($theme) {
|
||||
body.#{map-get($theme, name)} {
|
||||
@ -68,6 +69,7 @@
|
||||
@include dsh-details-secondary-title-theme($theme);
|
||||
@include dsh-file-uploader-theme($theme);
|
||||
@include dsh-panel-theme($theme);
|
||||
@include dsh-range-datepicker-theme($theme);
|
||||
@include dsh-payout-panel-theme($theme);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user