Browse Source

Input widgets

pull/2411/head
Igor Kulikov 7 years ago
parent
commit
538abeb140
  1. 2
      ui-ngx/src/app/core/notification/notification.models.ts
  2. 21
      ui-ngx/src/app/core/services/utils.service.ts
  3. 47
      ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
  4. 8
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  5. 1
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  6. 2
      ui-ngx/src/app/shared/components/snack-bar-component.html
  7. 31
      ui-ngx/src/app/shared/components/toast.directive.ts
  8. 1
      ui-ngx/src/app/shared/pipe/keyboard-shortcut.pipe.ts
  9. 27
      ui-ngx/src/assets/locale/locale.constant-de_DE.json
  10. 40
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  11. 25
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  12. 27
      ui-ngx/src/assets/locale/locale.constant-fr_FR.json
  13. 27
      ui-ngx/src/assets/locale/locale.constant-it_IT.json
  14. 40
      ui-ngx/src/assets/locale/locale.constant-ru_RU.json
  15. 103
      ui-ngx/src/assets/locale/locale.constant-uk_UA.json

2
ui-ngx/src/app/core/notification/notification.models.ts

@ -29,8 +29,10 @@ export class NotificationMessage {
type: NotificationType;
target?: string;
duration?: number;
forceDismiss?: boolean;
horizontalPosition?: NotificationHorizontalPosition;
verticalPosition?: NotificationVerticalPosition;
panelClass?: string | string[];
}
export class HideNotification {

21
ui-ngx/src/app/core/services/utils.service.ts

@ -20,7 +20,7 @@
import { Inject, Injectable, NgZone } from '@angular/core';
import { WINDOW } from '@core/services/window.service';
import { ExceptionData } from '@app/shared/models/error.models';
import { deepClone, deleteNullProperties, guid, isDefined, isUndefined } from '@core/utils';
import { deepClone, deleteNullProperties, guid, isDefined, isDefinedAndNotNull, isUndefined } from '@core/utils';
import { WindowMessage } from '@shared/models/window-message.model';
import { TranslateService } from '@ngx-translate/core';
import { customTranslationsPrefix } from '@app/shared/models/constants';
@ -441,4 +441,23 @@ export class UtilsService {
this.window.history.replaceState({}, '', baseUrl + params);
}
public deepClone<T>(target: T, ignoreFields?: string[]): T {
return deepClone(target, ignoreFields);
}
public isUndefined(value: any): boolean {
return isUndefined(value);
}
public isDefined(value: any): boolean {
return isDefined(value);
}
public defaultValue(value: any, defaultValue: any): any {
if (isDefinedAndNotNull(value)) {
return value;
} else {
return defaultValue;
}
}
}

47
ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts

@ -15,14 +15,19 @@
///
import { PageComponent } from '@shared/components/page.component';
import { Inject, Injector, Input, OnDestroy, OnInit } from '@angular/core';
import { Inject, Injector, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { WidgetContext, IDynamicWidgetComponent } from '@home/models/widget-component.models';
import { ExceptionData } from '@shared/models/error.models';
import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models';
import { HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service';
import { DeviceService } from '@core/http/device.service';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import {
NotificationHorizontalPosition,
NotificationType,
NotificationVerticalPosition
} from '@core/notification/notification.models';
import { FormBuilder, Validators } from '@angular/forms';
export class DynamicWidgetComponent extends PageComponent implements IDynamicWidgetComponent, OnInit, OnDestroy {
@ -33,8 +38,11 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
[key: string]: any;
validators = Validators;
constructor(@Inject(RafService) public raf: RafService,
@Inject(Store) protected store: Store<AppState>,
@Inject(FormBuilder) public fb: FormBuilder,
@Inject(Injector) private $injector: Injector,
@Inject('widgetContext') public readonly ctx: WidgetContext,
@Inject('errorMessages') public readonly errorMessages: string[]) {
@ -63,4 +71,35 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
}
}
showSuccessToast(message: string, duration: number = 1000,
verticalPosition: NotificationVerticalPosition = 'bottom',
horizontalPosition: NotificationHorizontalPosition = 'left',
target?: string) {
this.showToast('success', message, duration, verticalPosition, horizontalPosition, target);
}
showErrorToast(message: string,
verticalPosition: NotificationVerticalPosition = 'bottom',
horizontalPosition: NotificationHorizontalPosition = 'left',
target?: string) {
this.showToast('error', message, undefined, verticalPosition, horizontalPosition, target);
}
showToast(type: NotificationType, message: string, duration: number = 1000,
verticalPosition: NotificationVerticalPosition = 'bottom',
horizontalPosition: NotificationHorizontalPosition = 'left',
target?: string) {
this.store.dispatch(new ActionNotificationShow(
{
message,
type,
duration,
verticalPosition,
horizontalPosition,
target,
panelClass: this.ctx.widgetNamespace,
forceDismiss: true
}));
}
}

8
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -95,6 +95,8 @@ import { DialogService } from '@core/services/dialog.service';
import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service';
import { DatePipe } from '@angular/common';
import { AttributeService } from '@core/http/attribute.service';
import { TranslateService } from '@ngx-translate/core';
import { HttpClient } from '@angular/common/http';
const ServicesMap = new Map<string, Type<any>>();
ServicesMap.set('deviceService', DeviceService);
@ -104,6 +106,8 @@ ServicesMap.set('dialogs', DialogService);
ServicesMap.set('customDialog', CustomDialogService);
ServicesMap.set('date', DatePipe);
ServicesMap.set('utils', UtilsService);
ServicesMap.set('translate', TranslateService);
ServicesMap.set('http', HttpClient);
@Component({
selector: 'tb-widget',
@ -390,10 +394,10 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
}
private loadFromWidgetInfo() {
const widgetNamespace = `widget-type-${(this.widget.isSystemType ? 'sys-' : '')}${this.widget.bundleAlias}-${this.widget.typeAlias}`;
this.widgetContext.widgetNamespace = `widget-type-${(this.widget.isSystemType ? 'sys-' : '')}${this.widget.bundleAlias}-${this.widget.typeAlias}`;
const elem = this.elementRef.nativeElement;
elem.classList.add('tb-widget');
elem.classList.add(widgetNamespace);
elem.classList.add(this.widgetContext.widgetNamespace);
this.widgetType = this.widgetInfo.widgetTypeFunction;
this.typeParameters = this.widgetInfo.typeParameters;

1
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -161,6 +161,7 @@ export class WidgetContext {
isEdit: boolean;
isMobile: boolean;
widgetNamespace?: string;
subscriptionApi?: WidgetSubscriptionApi;
actionsApi?: WidgetActionsApi;

2
ui-ngx/src/app/shared/components/snack-bar-component.html

@ -22,5 +22,5 @@
'info-toast': notification.type === 'info'
}">
<div class="toast-text" [innerHTML]="notification.message"></div>
<button mat-button (click)="action()">{{ 'action.close' | translate }}</button>
<button #actionButton mat-button (click)="action()">{{ 'action.close' | translate }}</button>
</div>

31
ui-ngx/src/app/shared/components/toast.directive.ts

@ -15,12 +15,12 @@
///
import {
AfterViewInit,
AfterViewInit, ApplicationRef, ChangeDetectorRef,
Component,
Directive,
ElementRef,
Inject, Input, NgZone,
OnDestroy,
OnDestroy, ViewChild,
ViewContainerRef
} from '@angular/core';
import {
@ -35,6 +35,8 @@ import { Subscription } from 'rxjs';
import { NotificationService } from '@app/core/services/notification.service';
import { BreakpointObserver } from '@angular/cdk/layout';
import { MediaBreakpoints } from '@shared/models/constants';
import Timeout = NodeJS.Timeout;
import { MatButton } from '@angular/material/button';
@Directive({
selector: '[tb-toast]'
@ -50,6 +52,8 @@ export class ToastDirective implements AfterViewInit, OnDestroy {
private snackBarRef: MatSnackBarRef<TbSnackBarComponent> = null;
private currentMessage: NotificationMessage = null;
private dismissTimeout: Timeout = null;
constructor(public elementRef: ElementRef,
public viewContainerRef: ViewContainerRef,
private notificationService: NotificationService,
@ -73,6 +77,7 @@ export class ToastDirective implements AfterViewInit, OnDestroy {
verticalPosition: !isGtSm ? 'bottom' : (notificationMessage.verticalPosition || 'top'),
viewContainerRef: this.viewContainerRef,
duration: notificationMessage.duration,
panelClass: notificationMessage.panelClass,
data
};
this.ngZone.run(() => {
@ -80,7 +85,23 @@ export class ToastDirective implements AfterViewInit, OnDestroy {
this.snackBarRef.dismiss();
}
this.snackBarRef = this.snackBar.openFromComponent(TbSnackBarComponent, config);
if (notificationMessage.duration && notificationMessage.duration > 0 && notificationMessage.forceDismiss) {
if (this.dismissTimeout !== null) {
clearTimeout(this.dismissTimeout);
this.dismissTimeout = null;
}
this.dismissTimeout = setTimeout(() => {
if (this.snackBarRef) {
this.snackBarRef.instance.actionButton._elementRef.nativeElement.click();
}
this.dismissTimeout = null;
}, notificationMessage.duration);
}
this.snackBarRef.afterDismissed().subscribe(() => {
if (this.dismissTimeout !== null) {
clearTimeout(this.dismissTimeout);
this.dismissTimeout = null;
}
this.snackBarRef = null;
this.currentMessage = null;
});
@ -134,11 +155,15 @@ export class ToastDirective implements AfterViewInit, OnDestroy {
styleUrls: ['snack-bar-component.scss']
})
export class TbSnackBarComponent implements AfterViewInit, OnDestroy {
@ViewChild('actionButton', {static: true}) actionButton: MatButton;
private parentEl: HTMLElement;
private snackBarContainerEl: HTMLElement;
public snackBarContainerEl: HTMLElement;
private parentScrollSubscription: Subscription = null;
public notification: NotificationMessage;
constructor(@Inject(MAT_SNACK_BAR_DATA) public data: any, private elementRef: ElementRef,
public cd: ChangeDetectorRef,
public snackBarRef: MatSnackBarRef<TbSnackBarComponent>) {
this.notification = data.notification;
}

1
ui-ngx/src/app/shared/pipe/keyboard-shortcut.pipe.ts

@ -44,7 +44,6 @@ export class KeyboardShortcutPipe implements PipeTransform {
const last = index === keys.length - 1;
return last ? key : abbreviations[key];
}).join(seperator);
return (!value) ? '' : value.replace(/ /g, '');
}
}

27
ui-ngx/src/assets/locale/locale.constant-de_DE.json

@ -1607,6 +1607,31 @@
"Step size": "Schrittlänge",
"Ok": "Ok"
}
},
"input-widgets": {
"attribute-not-allowed": "Attributparameter können in diesem Widget nicht verwendet werden",
"date": "Datum",
"discard-changes": "Änderungen verwerfen",
"entity-attribute-required": "Entitätsattribut ist erforderlich",
"entity-timeseries-required": "Zeitreihen für Entitäten sind erforderlich",
"not-allowed-entity": "Die ausgewählte Entität kann keine gemeinsamen Attribute haben",
"no-attribute-selected": "Es ist kein Attribut ausgewählt",
"no-datakey-selected": "Es ist kein Datenschlüssel ausgewählt",
"no-entity-selected": "Keine Entität ausgewählt",
"no-image": "Kein Bild",
"no-support-web-camera": "Keine unterstützte Webcam",
"no-timeseries-selected": "Keine Zeitreihen ausgewählt",
"switch-attribute-value": "Entitätsattributwert wechseln",
"switch-camera": "Kamera wechseln",
"switch-timeseries-value": "Wert für Zeitreihen von Entitäten wechseln",
"take-photo": "Foto machen",
"time": "Zeit",
"timeseries-not-allowed": "Der Timeseries-Parameter kann in diesem Widget nicht verwendet werden",
"update-failed": "Aktualisierung fehlgeschlagen",
"update-successful": "Aktualisierung erfolgreich",
"update-attribute": "Attribut aktualisieren",
"update-timeseries": "Zeitreihen aktualisieren",
"value": "Wert"
}
},
"icon": {
@ -1641,7 +1666,7 @@
"tr_TR": "Türkisch",
"fa_IR": "Persisch",
"uk_UA": "Ukrainisch",
"cs_CZ": "Tschechisch"
"cs_CZ": "Tschechisch"
}
}
}

40
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1728,6 +1728,46 @@
"Step size": "Step size",
"Ok": "Ok"
}
},
"input-widgets": {
"attribute-not-allowed": "Attribute parameter cannot be used in this widget",
"blocked-location": "Geolocation is blocked in your browser",
"claim-device": "Claim device",
"claim-failed": "Failed to claim the device!",
"claim-not-found": "Device not found!",
"claim-successful": "Device was successfully claimed!",
"date": "Date",
"device-name": "Device name",
"device-name-required": "Device name is required",
"discard-changes": "Discard changes",
"entity-attribute-required": "Entity attribute is required",
"entity-coordinate-required": "Both fields, latitude and longitude, are required",
"entity-timeseries-required": "Entity timeseries is required",
"get-location": "Get current location",
"latitude": "Latitude",
"longitude": "Longitude",
"not-allowed-entity": "Selected entity cannot have shared attributes",
"no-attribute-selected": "No attribute is selected",
"no-datakey-selected": "No datakey is selected",
"no-coordinate-specified": "Datakey for latitude/longitude doesn't specified",
"no-entity-selected": "No entity selected",
"no-image": "No image",
"no-support-geolocation": "Your browser doesn't support geolocation",
"no-support-web-camera": "No supported web camera",
"no-timeseries-selected": "No timeseries selected",
"secret-key": "Secret key",
"secret-key-required": "Secret key is required",
"switch-attribute-value": "Switch entity attribute value",
"switch-camera": "Switch camera",
"switch-timeseries-value": "Switch entity timeseries value",
"take-photo": "Take photo",
"time": "Time",
"timeseries-not-allowed": "Timeseries parameter cannot be used in this widget",
"update-failed": "Update failed",
"update-successful": "Update successful",
"update-attribute": "Update attribute",
"update-timeseries": "Update timeseries",
"value": "Value"
}
},
"icon": {

25
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -1680,6 +1680,31 @@
"Step size": "Numero de pie",
"Ok": "De acuerdo"
}
},
"input-widgets": {
"attribute-not-allowed": "El parámetro de atributo no se puede usar en este widget",
"date": "Fecha",
"discard-changes": "Descartar los cambios",
"entity-attribute-required": "Se requiere atributo de entidad",
"entity-timeseries-required": "Se requiere la serie de tiempo de la entidad",
"not-allowed-entity": "La entidad seleccionada no puede tener atributos compartidos",
"no-attribute-selected": "No se seleccionó ningún atributo",
"no-datakey-selected": "No se seleccionó ninguna clave de datos",
"no-entity-selected": "Ninguna entidad seleccionada",
"no-image": "Sin imágen",
"no-support-web-camera": "No hay cámara web compatible",
"no-timeseries-selected": "No hay series de tiempo seleccionadas",
"switch-attribute-value": "Cambiar el valor del atributo de entidad",
"switch-camera": "Cambiar de cámara",
"switch-timeseries-value": "Cambiar el valor de la serie de tiempo de la entidad",
"take-photo": "Tomar foto",
"time": "Tiempo",
"timeseries-not-allowed": "El parámetro Timeseries no se puede usar en este widget",
"update-failed": "Actualización fallida",
"update-successful": "Actualización exitosa",
"update-attribute": "Actualizar atributo",
"update-timeseries": "Actualizar series de tiempo",
"value": "Valor"
}
},
"icon": {

27
ui-ngx/src/assets/locale/locale.constant-fr_FR.json

@ -1018,7 +1018,7 @@
"tr_TR": "Turc",
"fa_IR": "Persane",
"uk_UA": "Ukrainien",
"cs_CZ": "Tchèque"
"cs_CZ": "Tchèque"
}
},
"layout": {
@ -1501,6 +1501,31 @@
"Step size": "Taille de pas",
"Ok": "Ok"
}
},
"input-widgets": {
"attribute-not-allowed": "Le paramètre d'attribut ne peut pas être utilisé dans ce widget",
"date": "Date",
"discard-changes": "Annuler les modifications",
"entity-attribute-required": "L'attribut d'entité est requis",
"entity-timeseries-required": "Entité timeseries est requis",
"not-allowed-entity": "L'entité sélectionnée ne peut pas avoir d'attributs partagés",
"no-attribute-selected": "Aucun attribut n'est sélectionné",
"no-datakey-selected": "Aucune date n'est sélectionnée",
"no-entity-selected": "Aucune entité sélectionnée",
"no-image": "Pas d'image",
"no-support-web-camera": "Pas de webcam supportée",
"no-timeseries-selected": "Aucune série temporelle sélectionnée",
"switch-attribute-value": "Changer la valeur de l'attribut d'entité",
"switch-camera": "Changer de caméra",
"switch-timeseries-value": "Changer la valeur de l'entité série temporelle",
"take-photo": "Prendre une photo",
"time": "Temps",
"timeseries-not-allowed": "Le paramètre série temporelle ne peut pas être utilisé dans ce widget",
"update-failed": "Mise à jour a échoué",
"update-successful": "Mise à jour réussie",
"update-attribute": "Attribut de mise à jour",
"update-timeseries": "Mise à jour de la série temporelle",
"value": "Valeur"
}
},
"widgets-bundle": {

27
ui-ngx/src/assets/locale/locale.constant-it_IT.json

@ -1615,6 +1615,31 @@
"Step size": "Dimensione del passo",
"Ok": "Ok"
}
},
"input-widgets": {
"attribute-not-allowed": "Questo widget non può usare un parametro di tipo attributo",
"date": "Data",
"discard-changes": "Annulla modifiche",
"entity-attribute-required": "E' richiesta un'entità di tipo attributo",
"entity-timeseries-required": "E' richiesta un'entità di tipo serie temporale",
"not-allowed-entity": "L'entità selezionata non può avere attributi condivisi",
"no-attribute-selected": "Nessun attributo selezionato",
"no-datakey-selected": "Nessuna datakey selezionata",
"no-entity-selected": "Nessuna entità selezionata",
"no-image": "Nessuna immagine",
"no-support-web-camera": "Web camera non supportata",
"no-timeseries-selected": "Nessuna serie temporale selezionata",
"switch-attribute-value": "Cambia il valore dell'attributo",
"switch-camera": "Cambia camera",
"switch-timeseries-value": "Cambia il valore della serie temporale",
"take-photo": "Fai una foto",
"time": "Tempo",
"timeseries-not-allowed": "Questo widget non può usare un parametro di tipo serie temporale",
"update-failed": "Aggiornamento fallito",
"update-successful": "Aggiornamento eseguito con successo",
"update-attribute": "Aggiorna attributo",
"update-timeseries": "Aggiorna serie temporale",
"value": "Valore"
}
},
"icon": {
@ -1649,7 +1674,7 @@
"tr_TR": "Turco",
"fa_IR": "Persiana",
"uk_UA": "Ucraino",
"cs_CZ": "Ceco"
"cs_CZ": "Ceco"
}
}
}

40
ui-ngx/src/assets/locale/locale.constant-ru_RU.json

@ -1611,6 +1611,46 @@
"Step size": "Размер шага",
"Ok": "Ok"
}
},
"input-widgets": {
"attribute-not-allowed": "Атрибут не может быть выбран в этом виджете",
"date": "Дата",
"blocked-location": "Геолокация заблокирована в вашем браузере",
"claim-device": "Подтвердить устройство",
"claim-failed": "Не удалось подтвердить устройство!",
"claim-not-found": "Устройство не найдено!",
"claim-successful": "Устройство успешно подтверждено!",
"discard-changes": "Отменить изменения",
"device-name": "Название устройства",
"device-name-required": "Необходимо указать название устройства",
"entity-attribute-required": "Значение атрибута обязателено",
"entity-coordinate-required": "Необходимо указать широту и долготу",
"entity-timeseries-required": "Значение телеметрии обязательно",
"get-location": "Получить текущее местоположение",
"latitude": "Широта",
"longitude": "Долгота",
"not-allowed-entity": "Выбраный объект не имеет общих атрибутов",
"no-attribute-selected": "Атрибут не выбран",
"no-datakey-selected": "Ни один datakey не выбран",
"no-entity-selected": "Объект не выбран",
"no-coordinate-specified": "Ключ для широты/долготы не указан",
"no-support-geolocation": "Ваш браузер не поддерживает геолокацию",
"no-image": "Нет изображения",
"no-support-web-camera": "Нет поддерживаемой веб-камеры",
"no-timeseries-selected": "Параметр телеметрии не выбран",
"secret-key": "Секретный ключ",
"secret-key-required": "Необходимо указать секретный ключ",
"switch-attribute-value": "Изменить значение атрибута",
"switch-camera": "Изменить камеру",
"switch-timeseries-value": "Изменить значение телеметрии",
"take-photo": "Сделать фото",
"time": "Время",
"timeseries-not-allowed": "Телеметрия не может быть выбрана в этом виджете",
"update-failed": "Не удалось обновить",
"update-successful": "Успешно обновлено",
"update-attribute": "Обновить атрибут",
"update-timeseries": "Обновить телеметрию",
"value": "Значение"
}
},
"icon": {

103
ui-ngx/src/assets/locale/locale.constant-uk_UA.json

@ -1,7 +1,7 @@
{
"access": {
"unauthorized": "Неавторизований",
"unauthorized-access": "Неавторизований доступ",
"unauthorized-access": "Неавторизований доступ",
"unauthorized-access-text": "Щоб отримати доступ до цього ресурсу, потрібно ввійти в систему!",
"access-forbidden": "Доступ заборонено",
"access-forbidden-text": "Недостатньо прав для доступу! <br/> Спробуйте увійти як інший користувач, якщо ви все ще хочете отримати доступ до цього ресурсу.",
@ -246,7 +246,7 @@
"assign-assets": "Надати активи",
"assign-assets-text": "Надати { count, plural, 1 {1 актив} other {# активи} } клієнту",
"delete-assets": "Видалити активи",
"unassign-assets": "Позбавити активів",
"unassign-assets": "Позбавити активів",
"unassign-assets-action-title": "Позбавити { count, plural, 1 {1 актив} other {# активи} } клієнта",
"assign-new-asset": "Надати новий актив",
"delete-asset-title": "Ви впевнені, що хочете видалити актив '{{assetName}}'?",
@ -2123,6 +2123,105 @@
"widget-type-file": "Файл типу віджета",
"invalid-widget-type-file-error": "Неможливо імпортувати тип віджету: неправильна структура даних типу віджета."
},
"widgets": {
"date-range-navigator": {
"localizationMap": {
"Sun": "Нд",
"Mon": "Пн",
"Tue": "Вт",
"Wed": "Ср",
"Thu": "Чт",
"Fri": "Пт",
"Sat": "Сб",
"Jan": "Січ.",
"Feb": "Лют.",
"Mar": "Берез.",
"Apr": "Квіт.",
"May": "Трав.",
"Jun": "Черв.",
"Jul": "Лип.",
"Aug": "Серп.",
"Sep": "Верес.",
"Oct": "Жовт.",
"Nov": "Листоп.",
"Dec": "Груд.",
"January": "Січень",
"February": "Лютий",
"March": "Березень",
"April": "Квітень",
"June": "Червень",
"July": "Липень",
"August": "Серпень",
"September": "Вересень",
"October": "Жовтень",
"November": "Листопад",
"December": "Грудень",
"Custom Date Range": "Користувацький діапазон дат",
"Date Range Template": "Шаблон діапазону дат",
"Today": "Сьогодні",
"Yesterday": "Вчора",
"This Week": "Цього тижня",
"Last Week": "Минулий тиждень",
"This Month": "Цей місяць",
"Last Month": "Минулий місяць",
"Year": "Рік",
"This Year": "Цього року",
"Last Year": "Минулий рік",
"Date picker": "Вибір дати",
"Hour": "Година",
"Day": "День",
"Week": "Тиждень",
"2 weeks": "2 Тижні",
"Month": "Місяць",
"3 months": "3 Місяці",
"6 months": "6 Місяців",
"Custom interval": "Користувацький інтервал",
"Interval": "Інтервал",
"Step size": "Розмір кроку",
"Ok": "Ok"
}
},
"input-widgets": {
"attribute-not-allowed": "Атрибут не може бути вибраний в цьому віджеті",
"date": "Дата",
"blocked-location": "Геолокація заблокована у вашому браузері",
"claim-device": "Підтвердити пристрій",
"claim-failed": "Не вдалося підтвердити пристрій!",
"claim-not-found": "Пристрій не знайдено!",
"claim-successful": "Пристрій успішно підтверджено!",
"discard-changes": "Скасувати зміни",
"device-name": "Назва пристрою",
"device-name-required": "Необхідно вказати назву пристрою",
"entity-attribute-required": "Значення атрибута обов'язкове",
"entity-coordinate-required": "Необхідно вказати широту та довготу",
"entity-timeseries-required": "Значення телеметрії обов'язкове",
"get-location": "Отримати поточне місцезнаходження",
"latitude": "Широта",
"longitude": "Довгота",
"not-allowed-entity": "Обрана сутність не має спільних атрибутів",
"no-attribute-selected": "Атрибут не вибрано",
"no-datakey-selected": "Ні один datakey не обраний",
"no-entity-selected": "Сутність не вибрано",
"no-coordinate-specified": "Ключ для широти/довготи не вказаний",
"no-support-geolocation": "Ваш браузер не підтримує геолокацію",
"no-image": "Немає зображення",
"no-support-web-camera": "Нет поддерживаемой веб-камеры",
"no-timeseries-selected": "Параметр телеметрії не вибрано",
"secret-key": "Секретний ключ",
"secret-key-required": "Необхідно вказати секретний ключ",
"switch-attribute-value": "Змінити значення атрибута",
"switch-camera": "Змінити камеру",
"switch-timeseries-value": "Змінити значення телеметрії",
"take-photo": "Зробити фото",
"time": "Час",
"timeseries-not-allowed": "Телеметрія не може бути вибрана в цьому віджеті",
"update-failed": "Не вдалося оновити",
"update-successful": "Успішно оновлено",
"update-attribute": "Оновити атрибут",
"update-timeseries": "Оновити телеметрію",
"value": "Значення"
}
},
"white-labeling": {
"white-labeling": "Білий маркування",
"login-white-labeling": "Login White Labeling",

Loading…
Cancel
Save