let-row="row"
let-value="value">
<span *ngIf="row.backstore === 'user:rbd'">
- {{ value | relativeDate }}
+ {{ value | relativeDate | notAvailable }}
</span>
<span *ngIf="row.backstore !== 'user:rbd'"
class="text-muted">
import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe';
import { DimlessPipe } from '../../../shared/pipes/dimless.pipe';
import { IscsiBackstorePipe } from '../../../shared/pipes/iscsi-backstore.pipe';
-import { RelativeDatePipe } from '../../../shared/pipes/relative-date.pipe';
import { FormatterService } from '../../../shared/services/formatter.service';
import { SharedModule } from '../../../shared/shared.module';
import { IscsiComponent } from './iscsi.component';
CephShortVersionPipe,
DimlessPipe,
FormatterService,
- RelativeDatePipe,
IscsiBackstorePipe,
{ provide: IscsiService, useValue: fakeService }
]
<tr>
<td i18n
class="bold">monmap modified</td>
- <td>{{ mon_status.monmap.modified }}</td>
+ <td>{{ mon_status.monmap.modified | relativeDate }}</td>
</tr>
<tr>
<td i18n
import { configureTestBed } from '../../../../testing/unit-test-helper';
import { MonitorService } from '../../../shared/api/monitor.service';
+import { SharedModule } from '../../../shared/shared.module';
import { MonitorComponent } from './monitor.component';
describe('MonitorComponent', () => {
let getMonitorSpy: jasmine.Spy;
configureTestBed({
- imports: [HttpClientTestingModule],
+ imports: [HttpClientTestingModule, SharedModule],
declarations: [MonitorComponent],
schemas: [NO_ERRORS_SCHEMA],
providers: [MonitorService]
import { CdTableColumn } from '../../../../shared/models/cd-table-column';
import { CdTableFetchDataContext } from '../../../../shared/models/cd-table-fetch-data-context';
import { Daemon } from '../../../../shared/models/daemon.interface';
+import { RelativeDatePipe } from '../../../../shared/pipes/relative-date.pipe';
@Component({
selector: 'cd-service-daemon-list',
constructor(
private hostService: HostService,
private cephServiceService: CephServiceService,
- private orchService: OrchestratorService
+ private orchService: OrchestratorService,
+ private relativeDatePipe: RelativeDatePipe
) {}
ngOnInit() {
{
name: $localize`Last Refreshed`,
prop: 'last_refresh',
+ pipe: this.relativeDatePipe,
flexGrow: 2
}
];
import { OrchestratorStatus } from '../../../shared/models/orchestrator.interface';
import { Permissions } from '../../../shared/models/permissions';
import { CephServiceSpec } from '../../../shared/models/service.interface';
+import { RelativeDatePipe } from '../../../shared/pipes/relative-date.pipe';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
import { ModalService } from '../../../shared/services/modal.service';
import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
private modalService: ModalService,
private orchService: OrchestratorService,
private cephServiceService: CephServiceService,
+ private relativeDatePipe: RelativeDatePipe,
private taskWrapperService: TaskWrapperService,
private urlBuilder: URLBuilderService
) {
{
name: $localize`Last Refreshed`,
prop: 'status.last_refresh',
+ pipe: this.relativeDatePipe,
flexGrow: 1
}
];
<br>
</ng-container>
<small class="date"
- [title]="notification.timestamp | cdDate">{{ notification.timestamp | duration: true }}</small>
+ [title]="notification.timestamp | cdDate">{{ notification.timestamp | relativeDate }}</small>
<i class="float-right custom-icon"
[ngClass]="[notification.applicationClass]"
[title]="notification.application"></i>
-import moment from 'moment';
-
import { DurationPipe } from './duration.pipe';
describe('DurationPipe', () => {
expect(pipe.transform(600)).toBe('10 minutes');
expect(pipe.transform(6000)).toBe('1 hour 40 minutes');
});
-
- it('transforms date into a human readable relative duration', () => {
- const date = moment().subtract(130, 'seconds');
- expect(pipe.transform(date, true)).toBe('2 minutes ago');
- });
});
import { Pipe, PipeTransform } from '@angular/core';
-import moment from 'moment';
-
@Pipe({
name: 'duration',
pure: false
})
export class DurationPipe implements PipeTransform {
- transform(date: any, isRelative = false): string {
- if (isRelative) {
- return moment(date).fromNow();
- } else {
- return this._forHumans(date);
- }
- }
-
/**
* Translates seconds into human readable format of seconds, minutes, hours, days, and years
* source: https://stackoverflow.com/a/34270811
* @param {number} seconds The number of seconds to be processed
* @return {string} The phrase describing the the amount of time
*/
- _forHumans(seconds: number): string {
+ transform(seconds: number): string {
const levels = [
[`${Math.floor(seconds / 31536000)}`, 'years'],
[`${Math.floor((seconds % 31536000) / 86400)}`, 'days'],
expect(pipe).toBeTruthy();
});
- it('transforms not available', () => {
+ it('transforms not available (1)', () => {
expect(pipe.transform('')).toBe('n/a');
});
- it('transforms number', () => {
+ it('transforms not available (2)', () => {
+ expect(pipe.transform('', 'Unknown')).toBe('Unknown');
+ });
+
+ it('transform not necessary (1)', () => {
expect(pipe.transform(0)).toBe(0);
expect(pipe.transform(1)).toBe(1);
});
+
+ it('transform not necessary (2)', () => {
+ expect(pipe.transform('foo')).toBe('foo');
+ });
});
import { Pipe, PipeTransform } from '@angular/core';
+import _ from 'lodash';
+
@Pipe({
name: 'notAvailable'
})
export class NotAvailablePipe implements PipeTransform {
- transform(value: any): any {
+ transform(value: any, text?: string): any {
if (value === '') {
- return $localize`n/a`;
+ return _.defaultTo(text, $localize`n/a`);
}
return value;
}
expect(pipe).toBeTruthy();
});
- it('transforms without value', () => {
- expect(pipe.transform(undefined)).toBe('unknown');
+ it('transforms date into a human readable relative time (1)', () => {
+ const date: Date = moment().subtract(130, 'seconds').toDate();
+ expect(pipe.transform(date)).toBe('2 minutes ago');
});
- it('transforms "in 7 days"', () => {
- const value = moment().add(7, 'days').unix();
- expect(pipe.transform(value)).toBe('in 7 days');
+ it('transforms date into a human readable relative time (2)', () => {
+ const date: Date = moment().subtract(65, 'minutes').toDate();
+ expect(pipe.transform(date)).toBe('An hour ago');
});
- it('transforms "7 days ago"', () => {
- const value = moment().subtract(7, 'days').unix();
- expect(pipe.transform(value)).toBe('7 days ago');
+ it('transforms date into a human readable relative time (3)', () => {
+ const date: string = moment().subtract(130, 'minutes').toISOString();
+ expect(pipe.transform(date)).toBe('2 hours ago');
+ });
+
+ it('transforms date into a human readable relative time (4)', () => {
+ const date: string = moment().subtract(30, 'seconds').toISOString();
+ expect(pipe.transform(date, false)).toBe('a few seconds ago');
+ });
+
+ it('transforms date into a human readable relative time (5)', () => {
+ const date: number = moment().subtract(3, 'days').unix();
+ expect(pipe.transform(date)).toBe('3 days ago');
+ });
+
+ it('invalid input (1)', () => {
+ expect(pipe.transform('')).toBe('');
+ });
+
+ it('invalid input (2)', () => {
+ expect(pipe.transform('2011-10-10T10:20:90')).toBe('');
});
});
import { Pipe, PipeTransform } from '@angular/core';
+import _ from 'lodash';
import moment from 'moment';
+moment.updateLocale('en', {
+ relativeTime: {
+ future: $localize`in %s`,
+ past: $localize`%s ago`,
+ s: $localize`a few seconds`,
+ ss: $localize`%d seconds`,
+ m: $localize`a minute`,
+ mm: $localize`%d minutes`,
+ h: $localize`an hour`,
+ hh: $localize`%d hours`,
+ d: $localize`a day`,
+ dd: $localize`%d days`,
+ w: $localize`a week`,
+ ww: $localize`%d weeks`,
+ M: $localize`a month`,
+ MM: $localize`%d months`,
+ y: $localize`a year`,
+ yy: $localize`%d years`
+ }
+});
+
@Pipe({
- name: 'relativeDate'
+ name: 'relativeDate',
+ pure: false
})
export class RelativeDatePipe implements PipeTransform {
- transform(value: any): any {
- if (!value) {
- return 'unknown';
+ /**
+ * Convert a time into a human readable form, e.g. '2 minutes ago'.
+ * @param {Date | string | number} value The date to convert, should be
+ * an ISO8601 string, an Unix timestamp (seconds) or Date object.
+ * @param {boolean} upperFirst Set to `true` to start the sentence
+ * upper case. Defaults to `true`.
+ * @return {string} The time in human readable form or an empty string
+ * on failure (e.g. invalid input).
+ */
+ transform(value: Date | string | number, upperFirst = true): string {
+ let date: moment.Moment;
+ if (_.isNumber(value)) {
+ date = moment.unix(value);
+ } else {
+ date = moment(value);
+ }
+ if (!date.isValid()) {
+ return '';
+ }
+ let relativeDate: string = date.fromNow();
+ if (upperFirst) {
+ relativeDate = _.upperFirst(relativeDate);
}
- return moment(value * 1000).fromNow();
+ return relativeDate;
}
}