Compare commits
4 Commits
v0.0.24
...
feat/langu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
302bdd2222 | ||
|
|
14968013dd | ||
|
|
efa6aed8eb | ||
|
|
533c68ee09 |
17
.github/workflows/hub.yml
vendored
17
.github/workflows/hub.yml
vendored
@@ -12,18 +12,25 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
id: qemu
|
id: qemu
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v1
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
- name: Available platforms
|
- name: Available platforms
|
||||||
run: echo ${{ steps.qemu.outputs.platforms }}
|
run: echo ${{ steps.qemu.outputs.platforms }}
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Set up build cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: /tmp/.buildx-cache
|
||||||
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-buildx-
|
||||||
- name: Parse the git tag
|
- name: Parse the git tag
|
||||||
id: get_tag
|
id: get_tag
|
||||||
run: echo ::set-output name=TAG::$(echo $GITHUB_REF | cut -d / -f 3)
|
run: echo ::set-output name=TAG::$(echo $GITHUB_REF | cut -d / -f 3)
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v1
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
@@ -34,10 +41,12 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
|
#platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
|
||||||
|
#platforms: linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
push: true
|
push: true
|
||||||
# cache-from: type=local,src=/tmp/.buildx-cache
|
# cache-from: type=local,src=/tmp/.buildx-cache
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
ARG GO_VERSION=1.20.6
|
ARG GO_VERSION=1.16.2
|
||||||
FROM golang:${GO_VERSION}-alpine AS builder
|
FROM golang:${GO_VERSION}-alpine AS builder
|
||||||
RUN apk update && apk add alpine-sdk git && rm -rf /var/cache/apk/*
|
RUN apk update && apk add alpine-sdk git && rm -rf /var/cache/apk/*
|
||||||
RUN mkdir -p /api
|
RUN mkdir -p /api
|
||||||
@@ -9,11 +9,9 @@ RUN go mod download
|
|||||||
COPY ./server .
|
COPY ./server .
|
||||||
RUN go build -o ./app ./main.go
|
RUN go build -o ./app ./main.go
|
||||||
|
|
||||||
FROM node:16-alpine as build-stage
|
FROM node:14 as build-stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY ./ui/package*.json ./
|
COPY ./ui/package*.json ./
|
||||||
RUN apk add --no-cache autoconf automake build-base nasm libc6-compat python3 py3-pip make g++ libpng-dev zlib-dev pngquant
|
|
||||||
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
COPY ./ui .
|
COPY ./ui .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
@@ -27,7 +25,7 @@ ENV UID=998
|
|||||||
ENV PID=100
|
ENV PID=100
|
||||||
ENV GIN_MODE=release
|
ENV GIN_MODE=release
|
||||||
VOLUME ["/config", "/assets"]
|
VOLUME ["/config", "/assets"]
|
||||||
RUN apk update && apk add ca-certificates tzdata && rm -rf /var/cache/apk/*
|
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
|
||||||
RUN mkdir -p /config; \
|
RUN mkdir -p /config; \
|
||||||
mkdir -p /assets; \
|
mkdir -p /assets; \
|
||||||
mkdir -p /api
|
mkdir -p /api
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<h1 align="center" style="margin-bottom:0">Hammond</h1>
|
<h1 align="center" style="margin-bottom:0">Hammond</h1>
|
||||||
|
<p align="center">Current Version - 2022.07.06</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
A self-hosted vehicle expense tracking system with support for multiple users.
|
A self-hosted vehicle expense tracking system with support for multiple users.
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"hammond/models"
|
|
||||||
"hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -13,7 +12,6 @@ import (
|
|||||||
func RegisteImportController(router *gin.RouterGroup) {
|
func RegisteImportController(router *gin.RouterGroup) {
|
||||||
router.POST("/import/fuelly", fuellyImport)
|
router.POST("/import/fuelly", fuellyImport)
|
||||||
router.POST("/import/drivvo", drivvoImport)
|
router.POST("/import/drivvo", drivvoImport)
|
||||||
router.POST("/import/generic", genericImport)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fuellyImport(c *gin.Context) {
|
func fuellyImport(c *gin.Context) {
|
||||||
@@ -54,21 +52,3 @@ func drivvoImport(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func genericImport(c *gin.Context) {
|
|
||||||
var json models.ImportData
|
|
||||||
if err := c.ShouldBindJSON(&json); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if json.VehicleId == "" {
|
|
||||||
c.JSON(http.StatusUnprocessableEntity, "Missing Vehicle ID")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errors := service.GenericImport(json, c.MustGet("userId").(string))
|
|
||||||
if len(errors) > 0 {
|
|
||||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"errors": errors})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ func RegisterAnonMasterConroller(router *gin.RouterGroup) {
|
|||||||
"distanceUnits": db.DistanceUnitDetails,
|
"distanceUnits": db.DistanceUnitDetails,
|
||||||
"roles": db.RoleDetails,
|
"roles": db.RoleDetails,
|
||||||
"currencies": models.GetCurrencyMasterList(),
|
"currencies": models.GetCurrencyMasterList(),
|
||||||
|
"languages": models.GetLanguageMastersList(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -52,7 +53,7 @@ func udpateSettings(c *gin.Context) {
|
|||||||
func udpateMySettings(c *gin.Context) {
|
func udpateMySettings(c *gin.Context) {
|
||||||
var model models.UpdateSettingModel
|
var model models.UpdateSettingModel
|
||||||
if err := c.ShouldBind(&model); err == nil {
|
if err := c.ShouldBind(&model); err == nil {
|
||||||
err := service.UpdateUserSettings(c.MustGet("userId").(string), model.Currency, *model.DistanceUnit, model.DateFormat)
|
err := service.UpdateUserSettings(c.MustGet("userId").(string), model.Currency, *model.DistanceUnit, model.DateFormat, model.Language)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusUnprocessableEntity, common.NewError("udpateMySettings", err))
|
c.JSON(http.StatusUnprocessableEntity, common.NewError("udpateMySettings", err))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type User struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Vehicles []Vehicle `gorm:"many2many:user_vehicles;" json:"vehicles"`
|
Vehicles []Vehicle `gorm:"many2many:user_vehicles;" json:"vehicles"`
|
||||||
IsDisabled bool `json:"isDisabled"`
|
IsDisabled bool `json:"isDisabled"`
|
||||||
|
Language string `json:"language"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *User) MarshalJSON() ([]byte, error) {
|
func (b *User) MarshalJSON() ([]byte, error) {
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ var migrations = []localMigration{
|
|||||||
Name: "2022_03_08_13_16_AddVIN",
|
Name: "2022_03_08_13_16_AddVIN",
|
||||||
Query: "ALTER TABLE vehicles ADD COLUMN vin text",
|
Query: "ALTER TABLE vehicles ADD COLUMN vin text",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "2023_02_26_13_42_AddLanguage",
|
||||||
|
Query: "ALTER TABLE users ADD COLUMN language text default 'en'",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunMigrations() {
|
func RunMigrations() {
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
type ImportData struct {
|
|
||||||
Data []ImportFillup `json:"data" binding:"required"`
|
|
||||||
VehicleId string `json:"vehicleId" binding:"required"`
|
|
||||||
TimeZone string `json:"timezone" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ImportFillup struct {
|
|
||||||
VehicleID string `json:"vehicleId"`
|
|
||||||
FuelQuantity float32 `json:"fuelQuantity"`
|
|
||||||
PerUnitPrice float32 `json:"perUnitPrice"`
|
|
||||||
TotalAmount float32 `json:"totalAmount"`
|
|
||||||
OdoReading int `json:"odoReading"`
|
|
||||||
IsTankFull *bool `json:"isTankFull"`
|
|
||||||
HasMissedFillup *bool `json:"hasMissedFillup"`
|
|
||||||
Comments string `json:"comments"`
|
|
||||||
FillingStation string `json:"fillingStation"`
|
|
||||||
UserID string `json:"userId"`
|
|
||||||
Date string `json:"date"`
|
|
||||||
FuelSubType string `json:"fuelSubType"`
|
|
||||||
}
|
|
||||||
24
server/models/language.go
Normal file
24
server/models/language.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type LanguageModel struct {
|
||||||
|
Emoji string `json:"emoji"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NameNative string `json:"nameNative"`
|
||||||
|
Shorthand string `json:"shorthand"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLanguageMastersList() []LanguageModel {
|
||||||
|
return []LanguageModel{
|
||||||
|
{
|
||||||
|
Emoji: "🇬🇧",
|
||||||
|
Name: "English",
|
||||||
|
NameNative: "English",
|
||||||
|
Shorthand: "en",
|
||||||
|
}, {
|
||||||
|
Emoji: "🇩🇪",
|
||||||
|
Name: "German",
|
||||||
|
NameNative: "Deutsch",
|
||||||
|
Shorthand: "de",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ type UpdateSettingModel struct {
|
|||||||
Currency string `json:"currency" form:"currency" query:"currency"`
|
Currency string `json:"currency" form:"currency" query:"currency"`
|
||||||
DateFormat string `json:"dateFormat" form:"dateFormat" query:"dateFormat"`
|
DateFormat string `json:"dateFormat" form:"dateFormat" query:"dateFormat"`
|
||||||
DistanceUnit *db.DistanceUnit `json:"distanceUnit" form:"distanceUnit" query:"distanceUnit" `
|
DistanceUnit *db.DistanceUnit `json:"distanceUnit" form:"distanceUnit" query:"distanceUnit" `
|
||||||
|
Language string `json:"language" form:"language" query:"language"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClarksonMigrationModel struct {
|
type ClarksonMigrationModel struct {
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"hammond/db"
|
|
||||||
"hammond/models"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GenericParseRefuelings(content []models.ImportFillup, user *db.User, vehicle *db.Vehicle, timezone string) ([]db.Fillup, []string) {
|
|
||||||
var errors []string
|
|
||||||
var fillups []db.Fillup
|
|
||||||
dateLayout := "2006-01-02T15:04:05.000Z"
|
|
||||||
loc, _ := time.LoadLocation(timezone)
|
|
||||||
for _, record := range content {
|
|
||||||
date, err := time.ParseInLocation(dateLayout, record.Date, loc)
|
|
||||||
if err != nil {
|
|
||||||
date = time.Date(2000, time.December, 0, 0, 0, 0, 0, loc)
|
|
||||||
}
|
|
||||||
|
|
||||||
var missedFillup bool
|
|
||||||
if record.HasMissedFillup == nil {
|
|
||||||
missedFillup = false
|
|
||||||
} else {
|
|
||||||
missedFillup = *record.HasMissedFillup
|
|
||||||
}
|
|
||||||
|
|
||||||
fillups = append(fillups, db.Fillup{
|
|
||||||
VehicleID: vehicle.ID,
|
|
||||||
UserID: user.ID,
|
|
||||||
Date: date,
|
|
||||||
IsTankFull: record.IsTankFull,
|
|
||||||
HasMissedFillup: &missedFillup,
|
|
||||||
FuelQuantity: float32(record.FuelQuantity),
|
|
||||||
PerUnitPrice: float32(record.PerUnitPrice),
|
|
||||||
FillingStation: record.FillingStation,
|
|
||||||
OdoReading: record.OdoReading,
|
|
||||||
TotalAmount: float32(record.TotalAmount),
|
|
||||||
FuelUnit: vehicle.FuelUnit,
|
|
||||||
Currency: user.Currency,
|
|
||||||
DistanceUnit: user.DistanceUnit,
|
|
||||||
Comments: record.Comments,
|
|
||||||
Source: "Generic Import",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return fillups, errors
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"hammond/db"
|
"hammond/db"
|
||||||
"hammond/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func WriteToDB(fillups []db.Fillup, expenses []db.Expense) []string {
|
func WriteToDB(fillups []db.Fillup, expenses []db.Expense) []string {
|
||||||
@@ -106,27 +105,3 @@ func FuellyImport(content []byte, userId string) []string {
|
|||||||
|
|
||||||
return WriteToDB(fillups, expenses)
|
return WriteToDB(fillups, expenses)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenericImport(content models.ImportData, userId string) []string {
|
|
||||||
var errors []string
|
|
||||||
user, err := GetUserById(userId)
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
vehicle, err := GetVehicleById(content.VehicleId)
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
var fillups []db.Fillup
|
|
||||||
fillups, errors = GenericParseRefuelings(content.Data, user, vehicle, content.TimeZone)
|
|
||||||
|
|
||||||
if len(errors) != 0 {
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
return WriteToDB(fillups, nil)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"hammond/db"
|
"hammond/db"
|
||||||
|
"hammond/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CanInitializeSystem() (bool, error) {
|
func CanInitializeSystem() (bool, error) {
|
||||||
@@ -14,15 +16,30 @@ func UpdateSettings(currency string, distanceUnit db.DistanceUnit) error {
|
|||||||
setting.DistanceUnit = distanceUnit
|
setting.DistanceUnit = distanceUnit
|
||||||
return db.UpdateSettings(setting)
|
return db.UpdateSettings(setting)
|
||||||
}
|
}
|
||||||
func UpdateUserSettings(userId, currency string, distanceUnit db.DistanceUnit, dateFormat string) error {
|
func UpdateUserSettings(userId, currency string, distanceUnit db.DistanceUnit, dateFormat string, language string) error {
|
||||||
user, err := db.GetUserById(userId)
|
user, err := db.GetUserById(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Pull into function
|
||||||
|
languageExists := false
|
||||||
|
languages := models.GetLanguageMastersList();
|
||||||
|
for _, lang := range languages {
|
||||||
|
if (language == lang.Shorthand){
|
||||||
|
languageExists = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!languageExists) {
|
||||||
|
return errors.New("Language not in masters list")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
user.Currency = currency
|
user.Currency = currency
|
||||||
user.DistanceUnit = distanceUnit
|
user.DistanceUnit = distanceUnit
|
||||||
user.DateFormat = dateFormat
|
user.DateFormat = dateFormat
|
||||||
|
user.Language = language
|
||||||
return db.UpdateUser(user)
|
return db.UpdateUser(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
"node-gyp": "^9.3.1",
|
"node-gyp": "^9.3.1",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"papaparse": "^5.4.1",
|
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-chartjs": "^3.5.1",
|
"vue-chartjs": "^3.5.1",
|
||||||
"vue-i18n": "^8.28.2",
|
"vue-i18n": "^8.28.2",
|
||||||
@@ -63,6 +62,8 @@
|
|||||||
"eslint-plugin-vue": "^9.9.0",
|
"eslint-plugin-vue": "^9.9.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"hygen": "^6.2.11",
|
"hygen": "^6.2.11",
|
||||||
|
"imagemin-lint-staged": "^0.5.1",
|
||||||
|
"lint-staged": "^13.1.1",
|
||||||
"markdownlint-cli": "^0.33.0",
|
"markdownlint-cli": "^0.33.0",
|
||||||
"npm-run-all": "^4.1.1",
|
"npm-run-all": "^4.1.1",
|
||||||
"sass": "^1.58.0",
|
"sass": "^1.58.0",
|
||||||
|
|||||||
@@ -1,231 +0,0 @@
|
|||||||
{
|
|
||||||
"quickentry": "Ingen hurtige indtastninger | Hurtig indtastning | Hurtige indtastninger",
|
|
||||||
"statistics": "Statistik",
|
|
||||||
"thisweek": "Denne uge",
|
|
||||||
"thismonth": "Denne måned",
|
|
||||||
"pastxdays": "Forrige en dag | Forrige {count} dage",
|
|
||||||
"pastxmonths": "Forrige en måned | Forrige {count} måneder",
|
|
||||||
"thisyear": "Dette år",
|
|
||||||
"alltime": "Hele tiden",
|
|
||||||
"noattachments": "Ingen vedhæftede filer indtil videre",
|
|
||||||
"attachments": "Vedhæftede filer",
|
|
||||||
"choosefile": "Vælg fil",
|
|
||||||
"addattachment": "Tilføj vedhæftning",
|
|
||||||
"sharedwith": "Delt med",
|
|
||||||
"share": "Del",
|
|
||||||
"you": "Du",
|
|
||||||
"addfillup": "Tilføj påfyldning",
|
|
||||||
"createfillup": "Opret påfyldning",
|
|
||||||
"deletefillup": "Slet denne påfyldning",
|
|
||||||
"addexpense": "Tilføj udgift",
|
|
||||||
"createexpense": "Opret udgift",
|
|
||||||
"deleteexpense": "Slet denne udgift",
|
|
||||||
"nofillups": "Ingen påfyldninger indtil videre",
|
|
||||||
"transfervehicle": "Overfør køretøj",
|
|
||||||
"settingssaved": "Indstillingerne er gemt",
|
|
||||||
"yoursettings": "Dine indstillinger",
|
|
||||||
"settings": "Indstillinger",
|
|
||||||
"changepassword": "Skift adgangskode",
|
|
||||||
"oldpassword": "Gammel adgangskode",
|
|
||||||
"newpassword": "Ny adgangskode",
|
|
||||||
"repeatnewpassword": "Gentag ny adgangskode",
|
|
||||||
"passworddontmatch": "Adgangskoderne stemmer ikke overens",
|
|
||||||
"save": "Gem",
|
|
||||||
"supportthedeveloper": "Støt udvikleren",
|
|
||||||
"buyhimabeer": "Køb ham en øl!",
|
|
||||||
"featurerequest": "Ønske om funktion",
|
|
||||||
"foundabug": "Fundet en fejl",
|
|
||||||
"currentversion": "Nuværende version",
|
|
||||||
"moreinfo": "Mere info",
|
|
||||||
"currency": "Valuta",
|
|
||||||
"distanceunit": "Afstandsenhed",
|
|
||||||
"dateformat": "Datoformat",
|
|
||||||
"createnow": "Opret nu",
|
|
||||||
"yourvehicles": "Dine køretøjer",
|
|
||||||
"menu": {
|
|
||||||
"quickentries": "Hurtige indtastninger",
|
|
||||||
"logout": "Log ud",
|
|
||||||
"import": "Import",
|
|
||||||
"home": "Hjem",
|
|
||||||
"settings": "Indstillinger",
|
|
||||||
"admin": "Admin",
|
|
||||||
"sitesettings": "Webstedsindstillinger",
|
|
||||||
"users": "Brugere",
|
|
||||||
"login": "Log ind"
|
|
||||||
},
|
|
||||||
"enterusername": "Indtast dit brugernavn",
|
|
||||||
"enterpassword": "Indtast din adgangskode",
|
|
||||||
"email": "E-mail",
|
|
||||||
"password": "Adgangskode",
|
|
||||||
"login": "Log ind",
|
|
||||||
"totalexpenses": "Samlede udgifter",
|
|
||||||
"fillupcost": "Tankningsomkostninger",
|
|
||||||
"otherexpenses": "Andre udgifter",
|
|
||||||
"addvehicle": "Tilføj køretøj",
|
|
||||||
"editvehicle": "Rediger køretøj",
|
|
||||||
"deletevehicle": "Slet køretøj",
|
|
||||||
"sharevehicle": "Del køretøj",
|
|
||||||
"makeowner": "Gør til ejer",
|
|
||||||
"lastfillup": "Seneste tankning",
|
|
||||||
"quickentrydesc": "Tag et billede af fakturaen eller brændstofpumpens display for at foretage en indtastning senere.",
|
|
||||||
"quickentrycreatedsuccessfully": "Hurtig indtastning oprettet med succes",
|
|
||||||
"uploadfile": "Upload fil",
|
|
||||||
"uploadphoto": "Upload foto",
|
|
||||||
"details": "Detaljer",
|
|
||||||
"odometer": "Kilometertæller",
|
|
||||||
"language": "Sprog",
|
|
||||||
"date": "Dato",
|
|
||||||
"pastfillups": "Tidligere tankninger",
|
|
||||||
"fuelsubtype": "Brændstoftype",
|
|
||||||
"fueltype": "Brændstoftype",
|
|
||||||
"quantity": "Mængde",
|
|
||||||
"gasstation": "Tankstation",
|
|
||||||
"fuel": {
|
|
||||||
"petrol": "Benzin",
|
|
||||||
"diesel": "Diesel",
|
|
||||||
"cng": "CNG",
|
|
||||||
"lpg": "LPG",
|
|
||||||
"electric": "Elektrisk",
|
|
||||||
"ethanol": "Ethanol"
|
|
||||||
},
|
|
||||||
"unit": {
|
|
||||||
"long": {
|
|
||||||
"litre": "Liter",
|
|
||||||
"gallon": "Gallon",
|
|
||||||
"kilowatthour": "Kilowatt-time",
|
|
||||||
"kilogram": "Kilogram",
|
|
||||||
"usgallon": "US Gallon",
|
|
||||||
"minutes": "Minutter",
|
|
||||||
"kilometers": "Kilometer",
|
|
||||||
"miles": "Miles"
|
|
||||||
},
|
|
||||||
"short": {
|
|
||||||
"litre": "L",
|
|
||||||
"gallon": "Gal",
|
|
||||||
"kilowatthour": "KwH",
|
|
||||||
"kilogram": "Kg",
|
|
||||||
"usgallon": "US Gal",
|
|
||||||
"minutes": "Min",
|
|
||||||
"kilometers": "Km",
|
|
||||||
"miles": "Mi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"avgfillupqty": "Gns. påfyldningsmængde",
|
|
||||||
"avgfillupexpense": "Gns. påfyldningsudgift",
|
|
||||||
"avgfuelcost": "Gns. brændstofpris",
|
|
||||||
"per": "{0} per {1}",
|
|
||||||
"price": "Pris",
|
|
||||||
"total": "Total",
|
|
||||||
"fulltank": "Tank fuld",
|
|
||||||
"partialfillup": "Delvis påfyldning",
|
|
||||||
"getafulltank": "Fik du en fuld tank?",
|
|
||||||
"tankpartialfull": "Hvad sporer du?",
|
|
||||||
"by": "Ved",
|
|
||||||
"expenses": "Udgifter",
|
|
||||||
"expensetype": "Udgiftstype",
|
|
||||||
"noexpenses": "Ingen udgifter indtil videre",
|
|
||||||
"download": "Download",
|
|
||||||
"title": "Titel",
|
|
||||||
"name": "Navn",
|
|
||||||
"delete": "Slet",
|
|
||||||
"importdata": "Importer data til Hammond",
|
|
||||||
"importdatadesc": "Vælg en af følgende muligheder for at importere data til Hammond",
|
|
||||||
"import": "Importer",
|
|
||||||
"importcsv": "Hvis du har brugt {name} til at gemme dine køretøjsdata, skal du eksportere CSV-filen fra {name} og klikke her for at importere.",
|
|
||||||
"importgeneric": "Generisk påfyldningsimport",
|
|
||||||
"importgenericdesc": "CSV-import af påfyldninger.",
|
|
||||||
"choosecsv": "Vælg CSV",
|
|
||||||
"choosephoto": "Vælg foto",
|
|
||||||
"importsuccessfull": "Data importeret med succes",
|
|
||||||
"importerror": "Der opstod et problem med at importere filen. Kontrollér fejlmeddelelsen",
|
|
||||||
"importfrom": "Importer fra {0}",
|
|
||||||
"stepstoimport": "Trin til import af data fra {name}",
|
|
||||||
"choosecsvimport": "Vælg {name} CSV-filen og klik på importknappen.",
|
|
||||||
"choosedatafile": "Vælg CSV-filen og klik derefter på importknappen.",
|
|
||||||
"dontimportagain": "Sørg for ikke at importere filen igen, da det vil skabe gentagne indtastninger.",
|
|
||||||
"checkpointsimportcsv": "Når du har kontrolleret alle disse punkter, kan du blot importere CSV'en nedenfor.",
|
|
||||||
"importhintunits": "På samme måde skal du sørge for, at <u>Brændstofenhed</u> og <u>Brændstoftype</u> er korrekt indstillet for køretøjet.",
|
|
||||||
"importhintcurrdist": "Sørg for, at <u>Valuta</u> og <u>Afstandsenhed</u> er korrekt indstillet i Hammond. Importen vil ikke automatisk registrere valutaen fra filen, men bruge den valuta, der er indstillet for brugeren.",
|
|
||||||
"importhintnickname": "Sørg for, at køretøjets kaldenavn i Hammond er præcis det samme som navnet i Fuelly CSV-filen, ellers vil importen ikke fungere.",
|
|
||||||
"importhintvehiclecreated": "Sørg for, at du allerede har oprettet køretøjerne i Hammond-platformen.",
|
|
||||||
"importhintcreatecsv": "Eksportér dine data fra {name} i CSV-format. Instruktioner til at gøre dette kan findes",
|
|
||||||
"importgenerichintdata": "Data skal være i CSV-format.",
|
|
||||||
"here": "her",
|
|
||||||
"unprocessedquickentries": "Du har en hurtig indtastning, der skal behandles. | Du har {0} hurtige indtastninger, der venter på at blive behandlet.",
|
|
||||||
"show": "Vis",
|
|
||||||
"loginerror": "Der opstod en fejl ved login til din konto. {msg}",
|
|
||||||
"showunprocessed": "Vis kun ubehandlede",
|
|
||||||
"unprocessed": "ubehandlede",
|
|
||||||
"sitesettingdesc": "Opdater indstillinger på webstedsniveau. Disse vil blive brugt som standardværdier for nye brugere.",
|
|
||||||
"settingdesc": "Disse vil blive brugt som standardværdier, når du opretter en ny påfyldning eller udgift.",
|
|
||||||
"areyousure": "Er du sikker på, at du vil fortsætte med dette?",
|
|
||||||
"adduser": "Tilføj bruger",
|
|
||||||
"usercreatedsuccessfully": "Bruger oprettet med succes",
|
|
||||||
"userdisabledsuccessfully": "Bruger deaktiveret med succes",
|
|
||||||
"userenabledsuccessfully": "Bruger aktiveret med succes",
|
|
||||||
"role": "Rolle",
|
|
||||||
"created": "Oprettet",
|
|
||||||
"createnewuser": "Opret ny bruger",
|
|
||||||
"cancel": "Annuller",
|
|
||||||
"novehicles": "Det ser ud til, at du endnu ikke har oprettet et køretøj i systemet. Begynd med at oprette en indtastning for et af de køretøjer, du gerne vil spore.",
|
|
||||||
"processed": "Marker som behandlet",
|
|
||||||
"notfound": "Ikke fundet",
|
|
||||||
"timeout": "Siden fik en timeout under indlæsningen. Er du stadig forbundet til internettet?",
|
|
||||||
"clicktoselect": "Klik for at vælge...",
|
|
||||||
"expenseby": "Udgift af",
|
|
||||||
"selectvehicle": "Vælg et køretøj",
|
|
||||||
"expensedate": "Udgiftsdato",
|
|
||||||
"totalamountpaid": "Samlet betalt beløb",
|
|
||||||
"fillmoredetails": "Udfyld flere oplysninger",
|
|
||||||
"markquickentryprocessed": "Marker valgt hurtig indtastning som behandlet",
|
|
||||||
"referquickentry": "Henvis hurtig indtastning",
|
|
||||||
"deletequickentry": "Dette vil slette denne hurtige indtastning. Dette trin kan ikke fortrydes. Er du sikker?",
|
|
||||||
"fuelunit": "Brændstofenhed",
|
|
||||||
"fillingstation": "Navn på tankstation",
|
|
||||||
"comments": "Kommentarer",
|
|
||||||
"missfillupbefore": "Glemte du at indtaste påfyldningen før denne?",
|
|
||||||
"missedfillup": "Glemt påfyldning",
|
|
||||||
"fillupdate": "Påfyldningsdato",
|
|
||||||
"fillupsavedsuccessfully": "Påfyldning gemt med succes",
|
|
||||||
"expensesavedsuccessfully": "Udgift gemt med succes",
|
|
||||||
"vehiclesavedsuccessfully": "Køretøj gemt med succes",
|
|
||||||
"settingssavedsuccessfully": "Indstillinger gemt med succes",
|
|
||||||
"back": "Tilbage",
|
|
||||||
"nickname": "Kælenavn",
|
|
||||||
"registration": "Registrering",
|
|
||||||
"createvehicle": "Opret køretøj",
|
|
||||||
"make": "Mærke / Firma",
|
|
||||||
"model": "Model",
|
|
||||||
"yearmanufacture": "Produktionsår",
|
|
||||||
"enginesize": "Motorsize (i cc)",
|
|
||||||
"mysqlconnstr": "MySQL-forbindelsesstreng",
|
|
||||||
"testconn": "Test forbindelse",
|
|
||||||
"migrate": "Migrer",
|
|
||||||
"init": {
|
|
||||||
"migrateclarkson": "Migrer fra Clarkson",
|
|
||||||
"migrateclarksondesc": "Hvis du har en eksisterende Clarkson-installation, og du vil migrere dine data fra den, skal du trykke på følgende knap.",
|
|
||||||
"freshinstall": "Ny installation",
|
|
||||||
"freshinstalldesc": "Hvis du vil have en ny installation af Hammond, skal du trykke på følgende knap.",
|
|
||||||
"clarkson": {
|
|
||||||
"desc": "<p>Du skal sørge for, at denne installation af Hammond kan få adgang til MySQL-databasen, der bruges af Clarkson.</p><p>Hvis det ikke er direkte muligt, kan du lave en kopi af den database et sted, der er tilgængeligt fra denne instans.</p><p>Når det er gjort, skal du indtaste forbindelsesstrengen til MySQL-instansen i følgende format.</p><p>Alle brugerne importeret fra Clarkson vil have deres brugernavn som deres e-mail i Clarkson-databasen, og adgangskoden er indstillet til<span class='' style='font-weight:bold'>hammond</span></p><code>bruger:adgangskode@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local</code><br/><br/>",
|
|
||||||
"success": "Vi har migreret dataene fra Clarkson med succes. Du vil snart blive omdirigeret til login-siden, hvor du kan logge ind med din eksisterende e-mail og adgangskode: hammond"
|
|
||||||
},
|
|
||||||
"fresh": {
|
|
||||||
"setupadminuser": "Opsætning af administratorbrugere",
|
|
||||||
"yourpassword": "Din adgangskode",
|
|
||||||
"youremail": "Din e-mail",
|
|
||||||
"yourname": "Dit navn",
|
|
||||||
"success": "Du er blevet registreret med succes. Du vil snart blive omdirigeret til login-siden, hvor du kan logge ind og begynde at bruge systemet."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"ADMIN": "ADMIN",
|
|
||||||
"USER": "BRUGER"
|
|
||||||
},
|
|
||||||
"profile": "Profil",
|
|
||||||
"processedon": "Behandlet den",
|
|
||||||
"enable": "Aktivér",
|
|
||||||
"disable": "Deaktiver",
|
|
||||||
"confirm": "Fortsæt",
|
|
||||||
"labelforfile": "Mærke for denne fil"
|
|
||||||
}
|
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
"dontimportagain": "Achte darauf, dass du die Datei nicht erneut importierst, da dies zu mehrfachen Einträgen führen würde.",
|
"dontimportagain": "Achte darauf, dass du die Datei nicht erneut importierst, da dies zu mehrfachen Einträgen führen würde.",
|
||||||
"checkpointsimportcsv": "Wenn du alle diese Punkte überprüft hast kannst du unten die CSV importieren.",
|
"checkpointsimportcsv": "Wenn du alle diese Punkte überprüft hast kannst du unten die CSV importieren.",
|
||||||
"importhintunits": "Vergewissere dich ebenfalls, dass die <u>Kraftstoffeinheit</u> und der <u>Kraftstofftyp</u> im Fahrzeug richtig eingestellt sind.",
|
"importhintunits": "Vergewissere dich ebenfalls, dass die <u>Kraftstoffeinheit</u> und der <u>Kraftstofftyp</u> im Fahrzeug richtig eingestellt sind.",
|
||||||
"importhintcurrdist": "Stelle sicher, dass die <u>Währung</u> und die <u>Entfernungseinheit</u> in Hammond korrekt eingestellt sind. Der Import erkennt die Währung nicht automatisch aus der datei, sondern verwendet die für den Benutzer eingestellte Währung.",
|
"importhintcurrdist": "Stelle sicher, dass die <u>Währung</u> und die <u>Entfernungseinheit</u> in Hammond korrekt eingestellt sind. Der Import erkennt die Währung nicht automatisch aus der CSV-Datei, sondern verwendet die für den Benutzer eingestellte Währung.",
|
||||||
"importhintnickname": "Vergewissere dich, dass der Fahrzeugname in Hammond genau mit dem Namen in der Fuelly-CSV-Datei übereinstimmt, sonst funktioniert der Import nicht.",
|
"importhintnickname": "Vergewissere dich, dass der Fahrzeugname in Hammond genau mit dem Namen in der Fuelly-CSV-Datei übereinstimmt, sonst funktioniert der Import nicht.",
|
||||||
"importhintvehiclecreated": "Vergewissere dich, dass du die Fahrzeuge bereits in Hammond erstellt hast.",
|
"importhintvehiclecreated": "Vergewissere dich, dass du die Fahrzeuge bereits in Hammond erstellt hast.",
|
||||||
"importhintcreatecsv": "Exportiere deine Daten aus {name} im CSV-Format. Die Schritte dazu findest du",
|
"importhintcreatecsv": "Exportiere deine Daten aus {name} im CSV-Format. Die Schritte dazu findest du",
|
||||||
|
|||||||
@@ -43,15 +43,15 @@
|
|||||||
"createnow": "Create Now",
|
"createnow": "Create Now",
|
||||||
"yourvehicles": "Your Vehicles",
|
"yourvehicles": "Your Vehicles",
|
||||||
"menu": {
|
"menu": {
|
||||||
"quickentries": "Quick Entries",
|
"quickentries": "Quick Entries",
|
||||||
"logout": "Log out",
|
"logout": "Log out",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"admin": "Admin",
|
"admin": "Admin",
|
||||||
"sitesettings": "Site Settings",
|
"sitesettings": "Site Settings",
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
"login": "Log in"
|
"login": "Log in"
|
||||||
},
|
},
|
||||||
"enterusername": "Enter your username",
|
"enterusername": "Enter your username",
|
||||||
"enterpassword": "Enter your password",
|
"enterpassword": "Enter your password",
|
||||||
@@ -81,34 +81,34 @@
|
|||||||
"quantity": "Quantity",
|
"quantity": "Quantity",
|
||||||
"gasstation": "Gas Station",
|
"gasstation": "Gas Station",
|
||||||
"fuel": {
|
"fuel": {
|
||||||
"petrol": "Petrol",
|
"petrol": "Petrol",
|
||||||
"diesel": "Diesel",
|
"diesel": "Diesel",
|
||||||
"cng": "CNG",
|
"cng": "CNG",
|
||||||
"lpg": "LPG",
|
"lpg": "LPG",
|
||||||
"electric": "Electric",
|
"electric": "Electric",
|
||||||
"ethanol": "Ethanol"
|
"ethanol": "Ethanol"
|
||||||
},
|
},
|
||||||
"unit": {
|
"unit": {
|
||||||
"long": {
|
"long": {
|
||||||
"litre": "Litre",
|
"litre": "Litre",
|
||||||
"gallon": "Gallon",
|
"gallon": "Gallon",
|
||||||
"kilowatthour": "Kilowatt Hour",
|
"kilowatthour": "Kilowatt Hour",
|
||||||
"kilogram": "Kilogram",
|
"kilogram": "Kilogram",
|
||||||
"usgallon": "US Gallon",
|
"usgallon": "US Gallon",
|
||||||
"minutes": "Minutes",
|
"minutes": "Minutes",
|
||||||
"kilometers": "Kilometers",
|
"kilometers": "Kilometers",
|
||||||
"miles": "Miles"
|
"miles": "Miles"
|
||||||
},
|
},
|
||||||
"short": {
|
"short": {
|
||||||
"litre": "Lt",
|
"litre": "Lt",
|
||||||
"gallon": "Gal",
|
"gallon": "Gal",
|
||||||
"kilowatthour": "KwH",
|
"kilowatthour": "KwH",
|
||||||
"kilogram": "Kg",
|
"kilogram": "Kg",
|
||||||
"usgallon": "US Gal",
|
"usgallon": "US Gal",
|
||||||
"minutes": "Mins",
|
"minutes": "Mins",
|
||||||
"kilometers": "Km",
|
"kilometers": "Km",
|
||||||
"miles": "Mi"
|
"miles": "Mi"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"avgfillupqty": "Avg Fillup Qty",
|
"avgfillupqty": "Avg Fillup Qty",
|
||||||
"avgfillupexpense": "Avg Fillup Expense",
|
"avgfillupexpense": "Avg Fillup Expense",
|
||||||
@@ -117,9 +117,7 @@
|
|||||||
"price": "Price",
|
"price": "Price",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"fulltank": "Tank Full",
|
"fulltank": "Tank Full",
|
||||||
"partialfillup": "Partial Fillup",
|
|
||||||
"getafulltank": "Did you get a full tank?",
|
"getafulltank": "Did you get a full tank?",
|
||||||
"tankpartialfull": "Which do you track?",
|
|
||||||
"by": "By",
|
"by": "By",
|
||||||
"expenses": "Expenses",
|
"expenses": "Expenses",
|
||||||
"expensetype": "Expense Type",
|
"expensetype": "Expense Type",
|
||||||
@@ -132,8 +130,6 @@
|
|||||||
"importdatadesc": "Choose from the following options to import data into Hammond",
|
"importdatadesc": "Choose from the following options to import data into Hammond",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"importcsv": "If you have been using {name} to store your vehicle data, export the CSV file from {name} and click here to import.",
|
"importcsv": "If you have been using {name} to store your vehicle data, export the CSV file from {name} and click here to import.",
|
||||||
"importgeneric": "Generic Fillups Import",
|
|
||||||
"importgenericdesc": "Fillups CSV import.",
|
|
||||||
"choosecsv": "Choose CSV",
|
"choosecsv": "Choose CSV",
|
||||||
"choosephoto": "Choose Photo",
|
"choosephoto": "Choose Photo",
|
||||||
"importsuccessfull": "Data Imported Successfully",
|
"importsuccessfull": "Data Imported Successfully",
|
||||||
@@ -141,15 +137,13 @@
|
|||||||
"importfrom": "Import from {0}",
|
"importfrom": "Import from {0}",
|
||||||
"stepstoimport": "Steps to import data from {name}",
|
"stepstoimport": "Steps to import data from {name}",
|
||||||
"choosecsvimport": "Choose the {name} CSV and press the import button.",
|
"choosecsvimport": "Choose the {name} CSV and press the import button.",
|
||||||
"choosedatafile": "Choose the CSV file and then press the import button.",
|
|
||||||
"dontimportagain": "Make sure that you do not import the file again because that will create repeat entries.",
|
"dontimportagain": "Make sure that you do not import the file again because that will create repeat entries.",
|
||||||
"checkpointsimportcsv": "Once you have checked all these points, just import the CSV below.",
|
"checkpointsimportcsv": "Once you have checked all these points, just import the CSV below.",
|
||||||
"importhintunits": "Similiarly, make sure that the <u>Fuel Unit</u> and <u>Fuel Type</u> are correctly set in the Vehicle.",
|
"importhintunits": "Similiarly, make sure that the <u>Fuel Unit</u> and <u>Fuel Type</u> are correctly set in the Vehicle.",
|
||||||
"importhintcurrdist": "Make sure that the <u>Currency</u> and <u>Distance Unit</u> are set correctly in Hammond. Import will not autodetect Currency from the file but use the one set for the user.",
|
"importhintcurrdist": "Make sure that the <u>Currency</u> and <u>Distance Unit</u> are set correctly in Hammond. Import will not autodetect Currency from the CSV but use the one set for the user.",
|
||||||
"importhintnickname": "Make sure that the Vehicle nickname in Hammond is exactly the same as the name on Fuelly CSV or the import will not work.",
|
"importhintnickname": "Make sure that the Vehicle nickname in Hammond is exactly the same as the name on Fuelly CSV or the import will not work.",
|
||||||
"importhintvehiclecreated": "Make sure that you have already created the vehicles in Hammond platform.",
|
"importhintvehiclecreated": "Make sure that you have already created the vehicles in Hammond platform.",
|
||||||
"importhintcreatecsv": "Export your data from {name} in the CSV format. Steps to do that can be found",
|
"importhintcreatecsv": "Export your data from {name} in the CSV format. Steps to do that can be found",
|
||||||
"importgenerichintdata": "Data must be in CSV format.",
|
|
||||||
"here": "here",
|
"here": "here",
|
||||||
"unprocessedquickentries": "You have one quick entry to be processed. | You have {0} quick entries pending to be processed.",
|
"unprocessedquickentries": "You have one quick entry to be processed. | You have {0} quick entries pending to be processed.",
|
||||||
"show": "Show",
|
"show": "Show",
|
||||||
@@ -184,7 +178,6 @@
|
|||||||
"fillingstation": "Filling Station Name",
|
"fillingstation": "Filling Station Name",
|
||||||
"comments": "Comments",
|
"comments": "Comments",
|
||||||
"missfillupbefore": "Did you miss the fillup entry before this one?",
|
"missfillupbefore": "Did you miss the fillup entry before this one?",
|
||||||
"missedfillup": "Missed Fillup",
|
|
||||||
"fillupdate": "Fillup Date",
|
"fillupdate": "Fillup Date",
|
||||||
"fillupsavedsuccessfully": "Fillup Saved Successfully",
|
"fillupsavedsuccessfully": "Fillup Saved Successfully",
|
||||||
"expensesavedsuccessfully": "Expense Saved Successfully",
|
"expensesavedsuccessfully": "Expense Saved Successfully",
|
||||||
@@ -202,25 +195,25 @@
|
|||||||
"testconn": "Test Connection",
|
"testconn": "Test Connection",
|
||||||
"migrate": "Migrate",
|
"migrate": "Migrate",
|
||||||
"init": {
|
"init": {
|
||||||
"migrateclarkson": "Migrate from Clarkson",
|
"migrateclarkson": "Migrate from Clarkson",
|
||||||
"migrateclarksondesc": "If you have an existing Clarkson deployment and you want to migrate your data from that, press the following button.",
|
"migrateclarksondesc": "If you have an existing Clarkson deployment and you want to migrate your data from that, press the following button.",
|
||||||
"freshinstall": "Fresh Install",
|
"freshinstall": "Fresh Install",
|
||||||
"freshinstalldesc": "If you want a fresh install of Hammond, press the following button.",
|
"freshinstalldesc": "If you want a fresh install of Hammond, press the following button.",
|
||||||
"clarkson": {
|
"clarkson": {
|
||||||
"desc": "<p>You need to make sure that this deployment of Hammond can access the MySQL database used by Clarkson.</p><p>If that is not directly possible, you can make a copy of that database somewhere accessible from this instance.</p><p>Once that is done, enter the connection string to the MySQL instance in the following format.</p><p>All the users imported from Clarkson will have their username as their email in Clarkson database and pasword set to<span class='' style='font-weight:bold'>hammond</span></p><code>user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local</code><br/><br/>",
|
"desc": "<p>You need to make sure that this deployment of Hammond can access the MySQL database used by Clarkson.</p><p>If that is not directly possible, you can make a copy of that database somewhere accessible from this instance.</p><p>Once that is done, enter the connection string to the MySQL instance in the following format.</p><p>All the users imported from Clarkson will have their username as their email in Clarkson database and pasword set to<span class='' style='font-weight:bold'>hammond</span></p><code>user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local</code><br/><br/>",
|
||||||
"success": "We have successfully migrated the data from Clarkson. You will be redirected to the login screen shortly where you can login using your existing email and password : hammond"
|
"success": "We have successfully migrated the data from Clarkson. You will be redirected to the login screen shortly where you can login using your existing email and password : hammond"
|
||||||
},
|
},
|
||||||
"fresh": {
|
"fresh": {
|
||||||
"setupadminuser": "Setup Admin Users",
|
"setupadminuser": "Setup Admin Users",
|
||||||
"yourpassword": "Your Password",
|
"yourpassword": "Your Password",
|
||||||
"youremail": "Your Email",
|
"youremail": "Your Email",
|
||||||
"yourname": "Your Name",
|
"yourname": "Your Name",
|
||||||
"success": "You have been registered successfully. You will be redirected to the login screen shortly where you can login and start using the system."
|
"success": "You have been registered successfully. You will be redirected to the login screen shortly where you can login and start using the system."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"ADMIN": "ADMIN",
|
"ADMIN": "ADMIN",
|
||||||
"USER": "USER"
|
"USER": "USER"
|
||||||
},
|
},
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"processedon": "Processed on",
|
"processedon": "Processed on",
|
||||||
@@ -228,4 +221,4 @@
|
|||||||
"disable": "Disable",
|
"disable": "Disable",
|
||||||
"confirm": "Go Ahead",
|
"confirm": "Go Ahead",
|
||||||
"labelforfile": "Label for this file"
|
"labelforfile": "Label for this file"
|
||||||
}
|
}
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
{
|
|
||||||
"quickentry": "Pas d'entrée rapide | Entrée rapide | Entrées rapides",
|
|
||||||
"statistics": "Statistiques",
|
|
||||||
"thisweek": "Cette semaine",
|
|
||||||
"thismonth": "Ce mois",
|
|
||||||
"pastxdays": "Dernier jour | Derniers {count} jours",
|
|
||||||
"pastxmonths": "Dernier mois | Derniers {count} mois",
|
|
||||||
"thisyear": "Cette année",
|
|
||||||
"alltime": "Tout le temps",
|
|
||||||
"noattachments": "Pas de piece jointe",
|
|
||||||
"attachments": "Pièces jointes",
|
|
||||||
"choosefile": "Choose File",
|
|
||||||
"addattachment": "Ajouter une pièce jointe",
|
|
||||||
"sharedwith": "Partager avec",
|
|
||||||
"share": "Partager",
|
|
||||||
"you": "Vous",
|
|
||||||
"addfillup": "Ajouter un plein",
|
|
||||||
"createfillup": "Créer un plein",
|
|
||||||
"deletefillup": "Supprimer ce plein",
|
|
||||||
"addexpense": "Ajouter une dépense",
|
|
||||||
"createexpense": "Créer une dépense",
|
|
||||||
"deleteexpense": "Supprimer cette dépense",
|
|
||||||
"nofillups": "Pas de plein",
|
|
||||||
"transfervehicle": "Transferer le Véhicule",
|
|
||||||
"settingssaved": "Paramètres sauvegardés avec succès",
|
|
||||||
"yoursettings": "Vos paramètres",
|
|
||||||
"settings": "Paramètres",
|
|
||||||
"changepassword": "Changer votre mot de passe",
|
|
||||||
"oldpassword": "Ancien mot de passe",
|
|
||||||
"newpassword": "Nouveau mot de passe",
|
|
||||||
"repeatnewpassword": "Répéter votre nouveau mot de passe",
|
|
||||||
"passworddontmatch": "Les mots de passe ne correspondent pas",
|
|
||||||
"save": "Sauvegarder",
|
|
||||||
"supportthedeveloper": "Supporter le développeur",
|
|
||||||
"buyhimabeer": "Acheter lui un café!",
|
|
||||||
"featurerequest": "Demande de fonctionnalité",
|
|
||||||
"foundabug": "Trouvé un bug",
|
|
||||||
"currentversion": "Version actuelle",
|
|
||||||
"moreinfo": "Plus d'informations",
|
|
||||||
"currency": "Monnaie",
|
|
||||||
"distanceunit": "Unité de distance",
|
|
||||||
"dateformat": "Format de data",
|
|
||||||
"createnow": "Créer Maintenant",
|
|
||||||
"yourvehicles": "Vos Véhicules",
|
|
||||||
"menu": {
|
|
||||||
"quickentries": "Entrée rapide",
|
|
||||||
"logout": "Se déconnecter",
|
|
||||||
"import": "Importer",
|
|
||||||
"home": "Accueil",
|
|
||||||
"settings": "Paramètres",
|
|
||||||
"admin": "Admin",
|
|
||||||
"sitesettings": "Paramètres du site",
|
|
||||||
"users": "Utilisateurs",
|
|
||||||
"login": "Connexion"
|
|
||||||
},
|
|
||||||
"enterusername": "Entrez votre nom d'utilisateur",
|
|
||||||
"enterpassword": "Entrez votre mot de passe",
|
|
||||||
"email": "Email",
|
|
||||||
"password": "Mot de passe",
|
|
||||||
"login": "connexion",
|
|
||||||
"totalexpenses": "Dépenses totales",
|
|
||||||
"fillupcost": "Coût des pleins",
|
|
||||||
"otherexpenses": "Autres dépenses",
|
|
||||||
"addvehicle": "Ajouter un Véhicule",
|
|
||||||
"editvehicle": "Editer un Véhicule",
|
|
||||||
"deletevehicle": "Supprimer un Véhicule",
|
|
||||||
"sharevehicle": "Partager un Véhicule",
|
|
||||||
"makeowner": "Changer le propriétaire",
|
|
||||||
"lastfillup": "Dernier plein",
|
|
||||||
"quickentrydesc": "Prendre une photo de la facture ou de l'écran de la pompe à essence pour créer une entrée plus tard.",
|
|
||||||
"quickentrycreatedsuccessfully": "Entrée rapide créée avec succès",
|
|
||||||
"uploadfile": "Téléverser un fichier",
|
|
||||||
"uploadphoto": "Téléverser une photo",
|
|
||||||
"details": "Détails",
|
|
||||||
"odometer": "Odomètre",
|
|
||||||
"language": "Langue",
|
|
||||||
"date": "Date",
|
|
||||||
"pastfillups": "Derniers pleins",
|
|
||||||
"fuelsubtype": "Sous-type de combustible",
|
|
||||||
"fueltype": "Type de combustible",
|
|
||||||
"quantity": "Quantité",
|
|
||||||
"gasstation": "Station service",
|
|
||||||
"fuel": {
|
|
||||||
"petrol": "Pétrol",
|
|
||||||
"diesel": "Diesel",
|
|
||||||
"cng": "CNG",
|
|
||||||
"lpg": "LPG",
|
|
||||||
"electric": "Electrique",
|
|
||||||
"ethanol": "Éthanol"
|
|
||||||
},
|
|
||||||
"unit": {
|
|
||||||
"long": {
|
|
||||||
"litre": "Litre",
|
|
||||||
"gallon": "Gallon",
|
|
||||||
"kilowatthour": "Kilowatt Heure",
|
|
||||||
"kilogram": "Kilogram",
|
|
||||||
"usgallon": "US Gallon",
|
|
||||||
"minutes": "Minutes",
|
|
||||||
"kilometers": "Kilometres",
|
|
||||||
"miles": "Miles"
|
|
||||||
},
|
|
||||||
"short": {
|
|
||||||
"litre": "Lt",
|
|
||||||
"gallon": "Gal",
|
|
||||||
"kilowatthour": "KwH",
|
|
||||||
"kilogram": "Kg",
|
|
||||||
"usgallon": "US Gal",
|
|
||||||
"minutes": "Mins",
|
|
||||||
"kilometers": "Km",
|
|
||||||
"miles": "Mi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"avgfillupqty": "Qté de plein moyen",
|
|
||||||
"avgfillupexpense": "Prix du plein moyen",
|
|
||||||
"avgfuelcost": "Prix de l'essence moyen",
|
|
||||||
"per": "{0} par {1}",
|
|
||||||
"price": "Prix",
|
|
||||||
"total": "Total",
|
|
||||||
"fulltank": "Reservoir complet",
|
|
||||||
"partialfillup": "Plein partiel",
|
|
||||||
"getafulltank": "Est-ce que vous avez rempli tout votre reservoir?",
|
|
||||||
"tankpartialfull": "Le quel traquez-vous?",
|
|
||||||
"by": "Par",
|
|
||||||
"expenses": "Dépenses",
|
|
||||||
"expensetype": "Type de dépense",
|
|
||||||
"noexpenses": "Pas de dépense",
|
|
||||||
"download": "Télécharger",
|
|
||||||
"title": "Titre",
|
|
||||||
"name": "Nom",
|
|
||||||
"delete": "Supprimer",
|
|
||||||
"importdata": "Importer des données dans Hammond",
|
|
||||||
"importdatadesc": "Choisissez une option pour importer des données dans Hammond",
|
|
||||||
"import": "Importer",
|
|
||||||
"importcsv": "Si vous utilisiez {name} pour stocker les données de vos véhicules, exportez les données en format CSV depuis {name} et cliquez ici pour importer.",
|
|
||||||
"importgeneric": "Importation de plein générique",
|
|
||||||
"importgenericdesc": "Importation de plein avec un SVC.",
|
|
||||||
"choosecsv": "Choisir un CSV",
|
|
||||||
"choosephoto": "Choisir une Photo",
|
|
||||||
"importsuccessfull": "Données importée avec succès",
|
|
||||||
"importerror": "Il y a eu un problème lors de l'importation. Veuillez regarder le message d'erreur",
|
|
||||||
"importfrom": "Importer depuis {0}",
|
|
||||||
"stepstoimport": "Étapes pour importer des données depuis {name}",
|
|
||||||
"choosecsvimport": "Choisissez le fichier CSV de {name} et appuyez sur le bouton pour importer.",
|
|
||||||
"choosedatafile": "Choisissez le fichier CSV et appuyez sur le bouton pour importer.",
|
|
||||||
"dontimportagain": "Faites attention à ne pas importer le fichier à nouveau car cela va créer des entrées dupliquées.",
|
|
||||||
"checkpointsimportcsv": "Dès que vous avez vérifié tous ces points, importez le CSV ci-dessous.",
|
|
||||||
"importhintunits": "De la même manière, make sure that the <u>Fuel Unit</u> and <u>Fuel Type</u> are correctly set in the Vehicle.",
|
|
||||||
"importhintcurrdist": "Soyez sûre que la <u>Monnaie</u> et l'<u>Unité de distance</u> sont mises correctement dans Hammond. L'importation ne detectera pas automatiquement la Monnaie du fichier mais utilisera les valeurs de l'utilisateur.",
|
|
||||||
"importhintnickname": "Soyez sûre que le nom du véhicule dans Hammon est exactement le même que le nom dans Fuelly, sinon, l'importation ne fonctionnera pas.",
|
|
||||||
"importhintvehiclecreated": "Soyez sûre d'avoir déjà créé le véhicule dans la plate-forme Hammond.",
|
|
||||||
"importhintcreatecsv": "Exportez vos données depuis {name} en format CSV. Les étapes pour faire ceci peuvent être trouvées",
|
|
||||||
"importgenerichintdata": "Les données doivent être au format CSV.",
|
|
||||||
"here": "ici",
|
|
||||||
"unprocessedquickentries": "Vous avez 1 entrée rapide en attente d'être traîtée. | Vous avez {0} entrée rapide en attente d'être traîtée.",
|
|
||||||
"show": "montrer",
|
|
||||||
"loginerror": "Il y a eu une erreur lors de la connexion a votre compte: {msg}",
|
|
||||||
"showunprocessed": "Montrer seulement les non-traîtées",
|
|
||||||
"unprocessed": "non-traîtée",
|
|
||||||
"sitesettingdesc": "Mettre à jour les paramètres du site. Ces valeurs seront utilisées par défaut pour les nouveaux utilisateurs.",
|
|
||||||
"settingdesc": "Ces valeurs seront utilisées par défaut lorsque vous créez un nouveau plein ou une nouvelle dépense.",
|
|
||||||
"areyousure": "Êtes-vous sûre de vouloir faire ceci?",
|
|
||||||
"adduser": "Ajouter un utilisateur",
|
|
||||||
"usercreatedsuccessfully": "Utilisateur créé avec succès",
|
|
||||||
"userdisabledsuccessfully": "Utilisateur désactivé avec succès",
|
|
||||||
"userenabledsuccessfully": "Utilisateur activé avec succès",
|
|
||||||
"role": "Rôle",
|
|
||||||
"created": "Créé",
|
|
||||||
"createnewuser": "Créer un nouvel utilisateur",
|
|
||||||
"cancel": "Annuler",
|
|
||||||
"novehicles": "Il semble que vous n'avez pas encore créé de véhicule dans le système pour le moment. Commencez par créer une entrée pour un des véhicule que vous voulez traquer.",
|
|
||||||
"processed": "Marquer en tant que traîté",
|
|
||||||
"notfound": "Non Trouvé",
|
|
||||||
"timeout": "La page a expiré lors du chargement. Êtes-vous sûre d'être toujours connecté à Internet?",
|
|
||||||
"clicktoselect": "Cliquer pour sélectionner...",
|
|
||||||
"expenseby": "Dépense par",
|
|
||||||
"selectvehicle": "Selectionner un véhicule",
|
|
||||||
"expensedate": "Date de la dépense",
|
|
||||||
"totalamountpaid": "Montant payé total",
|
|
||||||
"fillmoredetails": "Entrer plus de détails",
|
|
||||||
"markquickentryprocessed": "Marquer l'entrée rapide séléctionnée en tant que traîtée",
|
|
||||||
"referquickentry": "Faire référence à une entrée rapide",
|
|
||||||
"deletequickentry": "Ceci va supprimer l'entrée rapide. Cette action ne peut pas être annulée. Êtes-vous sûre?",
|
|
||||||
"fuelunit": "Unité de combustible",
|
|
||||||
"fillingstation": "Nom de la station service",
|
|
||||||
"comments": "Commentaires",
|
|
||||||
"missfillupbefore": "Est-ce que vous avez manqué un plein avant celui-ci?",
|
|
||||||
"missedfillup": "Plein manqué",
|
|
||||||
"fillupdate": "Date du plein",
|
|
||||||
"fillupsavedsuccessfully": "Plein sauvegardé avec succès",
|
|
||||||
"expensesavedsuccessfully": "Dépense sauvegardé avec succès",
|
|
||||||
"vehiclesavedsuccessfully": "Véhicule sauvegardé avec succès",
|
|
||||||
"settingssavedsuccessfully": "Paramètres sauvegardés avec succès",
|
|
||||||
"back": "Retour",
|
|
||||||
"nickname": "Surnom",
|
|
||||||
"registration": "Immatriculation",
|
|
||||||
"createvehicle": "Créer un Véhicule",
|
|
||||||
"make": "Marque",
|
|
||||||
"model": "Modèle",
|
|
||||||
"yearmanufacture": "Année de production",
|
|
||||||
"enginesize": "Taille du moteur (en chevaux)",
|
|
||||||
"mysqlconnstr": "Chaîne de caractère pour la connexion MySQL",
|
|
||||||
"testconn": "Tester la Connexion",
|
|
||||||
"migrate": "Migrer",
|
|
||||||
"init": {
|
|
||||||
"migrateclarkson": "Migrer depuis Clarkson",
|
|
||||||
"migrateclarksondesc": "Si vous avez un déploiement Clarkson existant et que vous souhaitez migrer vos données à partir de celui-ci, appuyez sur le bouton suivant.",
|
|
||||||
"freshinstall": "Nouvelle Installation",
|
|
||||||
"freshinstalldesc": "Si vous voulez une nouvelle installation de Hammond, appuyez sur le bouton suivant.",
|
|
||||||
"clarkson": {
|
|
||||||
"desc": "<p>Vous devez vous assurer que ce déploiement de Hammond peut accéder à la base de données MySQL utilisée par Clarkson.</p><p>Si ce n'est pas directement possible, vous pouvez faire une copie de cette base de données autre part qui est accessible à partir de cette instance.</p><p>Une fois cela fait, entrez la chaîne de connexion à l'instance MySQL au format suivant.</p><p>Tous les utilisateurs importés de Clarkson auront leur nom d'utilisateur comme e-mail dans la base de données Clarkson et le mot de passe défini sur <span class='' style='font-weight:bold'>hammond</span></p><code>user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local</code><br/><br/>",
|
|
||||||
"success": "Nous avons migré avec succès les données depuis Clarkson. Vous serez bientôt redirigé vers l'écran de connexion où vous pourrez vous connecter en utilisant votre adresse e-mail et votre mot de passe existants : hammond"
|
|
||||||
},
|
|
||||||
"fresh": {
|
|
||||||
"setupadminuser": "Configurer le compte administrateur",
|
|
||||||
"yourpassword": "Votre Mot de passe",
|
|
||||||
"youremail": "Votre Email",
|
|
||||||
"yourname": "Votre Nom",
|
|
||||||
"success": "Vous avez été inscrit avec succès. Vous allez être redirigé vers la page de connexion très bientôt, vous pourrez vous connecter et commencer à utiliser le système."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"ADMIN": "ADMIN",
|
|
||||||
"USER": "USER"
|
|
||||||
},
|
|
||||||
"profile": "Profile",
|
|
||||||
"processedon": "Traîté le",
|
|
||||||
"enable": "Activer",
|
|
||||||
"disable": "Désactiver",
|
|
||||||
"confirm": "Continuer",
|
|
||||||
"labelforfile": "Label pour ce fichier"
|
|
||||||
}
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
{
|
|
||||||
"quickentry": "Ni hitrih vnosov | Hiter vnos | Hitri vnosi",
|
|
||||||
"statistics": "Statistika",
|
|
||||||
"thisweek": "Ta teden",
|
|
||||||
"thismonth": "Ta mesec",
|
|
||||||
"pastxdays": "Zadnji dan | Zadnjih {count} dni",
|
|
||||||
"pastxmonths": "Zadnji mesec | Zadnji {count} mesecev",
|
|
||||||
"thisyear": "To leto",
|
|
||||||
"alltime": "Celoten čas",
|
|
||||||
"noattachments": "Zaenkrat še ni priponk",
|
|
||||||
"attachments": "Priloge",
|
|
||||||
"choosefile": "Izberi datoteko",
|
|
||||||
"addattachment": "Dodaj prilogo",
|
|
||||||
"sharedwith": "V skupni rabi z",
|
|
||||||
"share": "Deli",
|
|
||||||
"you": "Ti",
|
|
||||||
"addfillup": "Dodaj polnjenje",
|
|
||||||
"createfillup": "Ustvarite polnjenje",
|
|
||||||
"deletefillup": "Izbriši to polnjenje",
|
|
||||||
"addexpense": "Dodaj strošek",
|
|
||||||
"createexpense": "Ustvari strošek",
|
|
||||||
"deleteexpense": "Izbriši ta strošek",
|
|
||||||
"nofillups": "Zaenkrat še brez polnjenja",
|
|
||||||
"transfervehicle": "Prevozno sredstvo",
|
|
||||||
"settingssaved": "Nastavitve so uspešno shranjene",
|
|
||||||
"yoursettings": "Vaše nastavitve",
|
|
||||||
"settings": "Nastavitve",
|
|
||||||
"changepassword": "Spremeni geslo",
|
|
||||||
"oldpassword": "Staro geslo",
|
|
||||||
"newpassword": "Novo geslo",
|
|
||||||
"repeatnewpassword": "Ponovite novo geslo",
|
|
||||||
"passworddontmatch": "Vrednosti gesel se ne ujemajo",
|
|
||||||
"save": "Shrani",
|
|
||||||
"supportthedeveloper": "Podprite razvijalca",
|
|
||||||
"buyhimabeer": "Kupi mu pivo!",
|
|
||||||
"featurerequest": "Nova funkcionalnost",
|
|
||||||
"foundabug": "Našel sem hrošča",
|
|
||||||
"currentversion": "Trenutna verzija",
|
|
||||||
"moreinfo": "Več informacij",
|
|
||||||
"currency": "Valuta",
|
|
||||||
"distanceunit": "Enota razdalje",
|
|
||||||
"dateformat": "Format datuma",
|
|
||||||
"createnow": "Ustvari zdaj",
|
|
||||||
"yourvehicles": "Vaša vozila",
|
|
||||||
"menu": {
|
|
||||||
"quickentries": "Hitri vnosi",
|
|
||||||
"logout": "Odjava",
|
|
||||||
"import": "Uvoz",
|
|
||||||
"home": "Domov",
|
|
||||||
"settings": "Nastavitve",
|
|
||||||
"admin": "Skrbnik",
|
|
||||||
"sitesettings": "Nastavitve spletnega mesta",
|
|
||||||
"users": "Uporabniki",
|
|
||||||
"login": "Vpiši se"
|
|
||||||
},
|
|
||||||
"enterusername": "Vnesite svoje uporabniško ime",
|
|
||||||
"enterpassword": "Vnesite vaše geslo",
|
|
||||||
"email": "E-naslov",
|
|
||||||
"password": "Geslo",
|
|
||||||
"login": "Vpiši se",
|
|
||||||
"totalexpenses": "Skupni stroški",
|
|
||||||
"fillupcost": "Stroški polnjenja",
|
|
||||||
"otherexpenses": "Drugi stroški",
|
|
||||||
"addvehicle": "Dodaj vozilo",
|
|
||||||
"editvehicle": "Uredi vozilo",
|
|
||||||
"deletevehicle": "Izbriši vozilo",
|
|
||||||
"sharevehicle": "Deli vozilo",
|
|
||||||
"makeowner": "Postani lastnik",
|
|
||||||
"lastfillup": "Zadnje polnjenje",
|
|
||||||
"quickentrydesc": "Posnemite sliko računa ali zaslona črpalke za gorivo, da ju lahko vnesete pozneje.",
|
|
||||||
"quickentrycreatedsuccessfully": "Hitri vnos je bil uspešno ustvarjen",
|
|
||||||
"uploadfile": "Naloži datoteko",
|
|
||||||
"uploadphoto": "Naloži fotografijo",
|
|
||||||
"details": "Podrobnosti",
|
|
||||||
"odometer": "Odometer",
|
|
||||||
"language": "Jezik",
|
|
||||||
"date": "Datum",
|
|
||||||
"pastfillups": "Prejšnja polnjenja",
|
|
||||||
"fuelsubtype": "Podvrsta goriva",
|
|
||||||
"fueltype": "Vrsta goriva",
|
|
||||||
"quantity": "Količina",
|
|
||||||
"gasstation": "Bencinska črpalka",
|
|
||||||
"fuel": {
|
|
||||||
"petrol": "Bencin",
|
|
||||||
"diesel": "Dizelsko gorivo",
|
|
||||||
"cng": "CNG",
|
|
||||||
"lpg": "LPG",
|
|
||||||
"electric": "Elektrika",
|
|
||||||
"ethanol": "Etanol"
|
|
||||||
},
|
|
||||||
"unit": {
|
|
||||||
"long": {
|
|
||||||
"litre": "Liter",
|
|
||||||
"gallon": "Galon",
|
|
||||||
"kilowatthour": "Kilovatna ura",
|
|
||||||
"kilogram": "Kilogram",
|
|
||||||
"usgallon": "Ameriška galona",
|
|
||||||
"minutes": "Minute",
|
|
||||||
"kilometers": "Kilometri",
|
|
||||||
"miles": "Milje"
|
|
||||||
},
|
|
||||||
"short": {
|
|
||||||
"litre": "Lit",
|
|
||||||
"gallon": "Gal",
|
|
||||||
"kilowatthour": "KwH",
|
|
||||||
"kilogram": "Kg",
|
|
||||||
"usgallon": "US Gal",
|
|
||||||
"minutes": "Min",
|
|
||||||
"kilometers": "Km",
|
|
||||||
"miles": "Mi"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"avgfillupqty": "Povprečna količina polnjenja",
|
|
||||||
"avgfillupexpense": "Povprečni stroški polnjenja",
|
|
||||||
"avgfuelcost": "Povprečni strošek goriva",
|
|
||||||
"per": "{0} na {1}",
|
|
||||||
"price": "Cena",
|
|
||||||
"total": "Skupaj",
|
|
||||||
"fulltank": "Rezervoar poln",
|
|
||||||
"partialfillup": "Delno polnjenje",
|
|
||||||
"getafulltank": "Ste dobili poln rezervoar?",
|
|
||||||
"tankpartialfull": "Kateremu sledite?",
|
|
||||||
"by": "Od",
|
|
||||||
"expenses": "Stroški",
|
|
||||||
"expensetype": "Vrsta stroška",
|
|
||||||
"noexpenses": "Zaenkrat ni bilo stroškov",
|
|
||||||
"download": "Prenesi",
|
|
||||||
"title": "Naslov",
|
|
||||||
"name": "Ime",
|
|
||||||
"delete": "Izbriši",
|
|
||||||
"importdata": "Uvozite podatke v Hammond",
|
|
||||||
"importdatadesc": "Za uvoz podatkov v Hammond izberite med naslednjimi možnostmi",
|
|
||||||
"import": "Uvozi",
|
|
||||||
"importcsv": "Če ste za shranjevanje podatkov o vozilu uporabljali {name}, izvozite datoteko CSV iz {name} in kliknite tukaj za uvoz.",
|
|
||||||
"importgeneric": "Generični uvoz polnjenja",
|
|
||||||
"importgenericdesc": "CSV uvoz poljenja.",
|
|
||||||
"choosecsv": "Izberite CSV",
|
|
||||||
"choosephoto": "Izberite fotografijo",
|
|
||||||
"importsuccessfull": "Podatki so bili uspešno uvoženi",
|
|
||||||
"importerror": "Pri uvozu datoteke je prišlo do težave. ",
|
|
||||||
"importfrom": "Uvoz iz {0}",
|
|
||||||
"stepstoimport": "Koraki za uvoz podatkov iz {name}",
|
|
||||||
"choosecsvimport": "Izberite {name} CSV in pritisnite gumb za uvoz.",
|
|
||||||
"choosedatafile": "Izberite datoteko CSV in pritisnite gumb za uvoz.",
|
|
||||||
"dontimportagain": "Prepričajte se, da datoteke ne uvozite znova, ker boste s tem ustvarili ponavljajoče se vnose.",
|
|
||||||
"checkpointsimportcsv": "Ko preverite vse te točke, preprosto uvozite spodnji CSV.",
|
|
||||||
"importhintunits": "Podobno se prepričajte, da sta <u>Enota za gorivo</u> in <u>Vrsta goriva</u> pravilno nameščeni pod Vozilo.",
|
|
||||||
"importhintcurrdist": "Prepričajte se, da sta <u>Valuta</u> in <u>Enota razdalje</u> pri Hammondu uporabniku pravilno nastavljeni.",
|
|
||||||
"importhintnickname": "Prepričajte se, da je vzdevek vozila v Hammondu popolnoma enak imenu v CSV datoteki polnjenj, sicer uvoz ne bo deloval.",
|
|
||||||
"importhintvehiclecreated": "Prepričajte se, da ste že ustvarili vozila na platformi Hammond.",
|
|
||||||
"importhintcreatecsv": "Izvozite svoje podatke iz {name} v formatu CSV. ",
|
|
||||||
"importgenerichintdata": "Podatki morajo biti v formatu CSV.",
|
|
||||||
"here": "tukaj",
|
|
||||||
"unprocessedquickentries": "Za obdelavo imate en hiter vnos. | Za obdelavo imate {0} hitrih vnosov.",
|
|
||||||
"show": "Prikaži",
|
|
||||||
"loginerror": "Pri prijavi v vaš račun je prišlo do napake. ",
|
|
||||||
"showunprocessed": "Pokaži samo neobdelano",
|
|
||||||
"unprocessed": "neobdelano",
|
|
||||||
"sitesettingdesc": "Posodobite nastavitve na ravni aplikacije. Te bodo uporabljene kot privzete vrednosti za nove uporabnike.",
|
|
||||||
"settingdesc": "Te bodo uporabljene kot privzete vrednosti vsakič, ko ustvarite novo polnjenje ali strošek.",
|
|
||||||
"areyousure": "Ste prepričani, da želite to narediti?",
|
|
||||||
"adduser": "Dodaj uporabnika",
|
|
||||||
"usercreatedsuccessfully": "Uporabnik je bil uspešno ustvarjen",
|
|
||||||
"userdisabledsuccessfully": "Uporabnik uspešno onemogočen",
|
|
||||||
"userenabledsuccessfully": "Uporabnik je uspešno omogočen",
|
|
||||||
"role": "Vloga",
|
|
||||||
"created": "Ustvarjeno",
|
|
||||||
"createnewuser": "Ustvari novega uporabnika",
|
|
||||||
"cancel": "Prekliči",
|
|
||||||
"novehicles": "Videti je, da še niste ustvarili vozila v sistemu. ",
|
|
||||||
"processed": "Označi obdelano",
|
|
||||||
"notfound": "Ni najdeno",
|
|
||||||
"timeout": "Med nalaganjem strani je potekla časovna omejitev. ",
|
|
||||||
"clicktoselect": "Kliknite za izbiro ...",
|
|
||||||
"expenseby": "Stroški po",
|
|
||||||
"selectvehicle": "Izberite vozilo",
|
|
||||||
"expensedate": "Datum izdatka",
|
|
||||||
"totalamountpaid": "Skupni plačani znesek",
|
|
||||||
"fillmoredetails": "Izpolnite več podrobnosti",
|
|
||||||
"markquickentryprocessed": "Označi izbrani hitri vnos kot obdelan",
|
|
||||||
"referquickentry": "Oglejte si hiter vnos",
|
|
||||||
"deletequickentry": "S tem boste izbrisali ta hitri vnos. ",
|
|
||||||
"fuelunit": "Enota za gorivo",
|
|
||||||
"fillingstation": "Ime bencinske postaje",
|
|
||||||
"comments": "Komentarji",
|
|
||||||
"missfillupbefore": "Ste pred tem zamudili vnos zapolnitve?",
|
|
||||||
"missedfillup": "Zamujeno polnjenje",
|
|
||||||
"fillupdate": "Datum polnjenja",
|
|
||||||
"fillupsavedsuccessfully": "Polnjenje je bil uspešno shranjeno",
|
|
||||||
"expensesavedsuccessfully": "Stroški so uspešno shranjeni",
|
|
||||||
"vehiclesavedsuccessfully": "Vozilo je uspešno shranjeno",
|
|
||||||
"settingssavedsuccessfully": "Nastavitve so uspešno shranjene",
|
|
||||||
"back": "Nazaj",
|
|
||||||
"nickname": "Vzdevek",
|
|
||||||
"registration": "Registracija",
|
|
||||||
"createvehicle": "Ustvarite vozilo",
|
|
||||||
"make": "Znamka / Podjetje",
|
|
||||||
"model": "Model",
|
|
||||||
"yearmanufacture": "Leto izdelave",
|
|
||||||
"enginesize": "Prostornina motorja (v cc)",
|
|
||||||
"mysqlconnstr": "Niz povezave Mysql",
|
|
||||||
"testconn": "Testna povezava",
|
|
||||||
"migrate": "Preseli",
|
|
||||||
"init": {
|
|
||||||
"migrateclarkson": "Selitev iz Clarksona",
|
|
||||||
"migrateclarksondesc": "Če imate obstoječo Clarkson namestitev in želite iz nje preseliti svoje podatke, pritisnite naslednji gumb.",
|
|
||||||
"freshinstall": "Sveža namestitev",
|
|
||||||
"freshinstalldesc": "Če želite novo namestitev Hammonda, pritisnite naslednji gumb.",
|
|
||||||
"clarkson": {
|
|
||||||
"desc": "<p>Zagotoviti morate, da lahko ta uvedba Hammonda dostopa do baze podatkov MySQL, ki jo uporablja Clarkson.</p><p>Če to ni neposredno mogoče, lahko naredite kopijo te zbirke podatkov nekje, kjer je dostopna iz tega primerka.</p><p>Ko je to storjeno, vnesite povezovalni niz v primerek MySQL v naslednji obliki.</p><p>Vsi uporabniki, uvoženi iz Clarksona, bodo imeli svoje uporabniško ime kot e-pošto v bazi podatkov Clarkson in geslo nastavljeno na<span class='' style='font-weight:bold'>hammond</span></p><code>uporabnik:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4</code><br/><br/>",
|
|
||||||
"success": "Uspešno smo prenesli podatke iz Clarksona. "
|
|
||||||
},
|
|
||||||
"fresh": {
|
|
||||||
"setupadminuser": "Nastavitev skrbniških uporabnikov",
|
|
||||||
"yourpassword": "Vaše geslo",
|
|
||||||
"youremail": "Vaš e-poštni naslov",
|
|
||||||
"yourname": "Vaše ime",
|
|
||||||
"success": "Uspešno ste se registrirali. "
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"roles": {
|
|
||||||
"ADMIN": "SKRBNIK",
|
|
||||||
"USER": "UPORABNIK"
|
|
||||||
},
|
|
||||||
"profile": "Profil",
|
|
||||||
"processedon": "Obdelano dne",
|
|
||||||
"enable": "Omogoči",
|
|
||||||
"disable": "Onemogoči",
|
|
||||||
"confirm": "Kar daj",
|
|
||||||
"labelforfile": "Oznaka za to datoteko"
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
faCheck,
|
faCheck,
|
||||||
faTimes,
|
faTimes,
|
||||||
faArrowUp,
|
faArrowUp,
|
||||||
faArrowRotateLeft,
|
|
||||||
faAngleLeft,
|
faAngleLeft,
|
||||||
faAngleRight,
|
faAngleRight,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
@@ -39,7 +38,6 @@ library.add(
|
|||||||
faCheck,
|
faCheck,
|
||||||
faTimes,
|
faTimes,
|
||||||
faArrowUp,
|
faArrowUp,
|
||||||
faArrowRotateLeft,
|
|
||||||
faAngleLeft,
|
faAngleLeft,
|
||||||
faAngleRight,
|
faAngleRight,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
|
|||||||
@@ -419,15 +419,6 @@ export default [
|
|||||||
},
|
},
|
||||||
props: (route) => ({ user: store.state.auth.currentUser || {} }),
|
props: (route) => ({ user: store.state.auth.currentUser || {} }),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/import/generic',
|
|
||||||
name: 'import-generic',
|
|
||||||
component: () => lazyLoadView(import('@views/import-generic.vue')),
|
|
||||||
meta: {
|
|
||||||
authRequired: true,
|
|
||||||
},
|
|
||||||
props: (route) => ({ user: store.state.auth.currentUser || {} }),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/logout',
|
path: '/logout',
|
||||||
name: 'logout',
|
name: 'logout',
|
||||||
|
|||||||
@@ -76,9 +76,6 @@ export default {
|
|||||||
this.fetchVehicleFuelSubTypes()
|
this.fetchVehicleFuelSubTypes()
|
||||||
if (!this.fillup.id) {
|
if (!this.fillup.id) {
|
||||||
this.fillupModel = this.getEmptyFillup()
|
this.fillupModel = this.getEmptyFillup()
|
||||||
if (this.vehicle.fillups.length > 0) {
|
|
||||||
this.fillupModel.odoReading = this.vehicle.fillups[0].odoReading
|
|
||||||
}
|
|
||||||
this.fillupModel.userId = this.me.id
|
this.fillupModel.userId = this.me.id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -280,15 +277,7 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<br />
|
<br />
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-button
|
<b-button tag="button" native-type="submit" :disabled="tryingToCreate" type="is-primary" :value="$t('save')" :label="$t('createfillup')" expanded/>
|
||||||
tag="button"
|
|
||||||
native-type="submit"
|
|
||||||
:disabled="tryingToCreate"
|
|
||||||
type="is-primary"
|
|
||||||
:value="$t('save')"
|
|
||||||
:label="$t('createfillup')"
|
|
||||||
expanded
|
|
||||||
/>
|
|
||||||
<p v-if="authError">
|
<p v-if="authError">
|
||||||
There was an error logging in to your account.
|
There was an error logging in to your account.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import ImportGeneric from './import-generic'
|
|
||||||
|
|
||||||
describe('@views/import-generic', () => {
|
|
||||||
it('is a valid view', () => {
|
|
||||||
expect(ImportGeneric).toBeAViewComponent()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,411 +0,0 @@
|
|||||||
<script>
|
|
||||||
import Layout from '@layouts/main.vue'
|
|
||||||
import { mapState } from 'vuex'
|
|
||||||
import axios from 'axios'
|
|
||||||
import Papa from 'papaparse'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
page: {
|
|
||||||
title: 'Generic Import',
|
|
||||||
meta: [{ name: 'description', content: 'The Generic Import page.' }],
|
|
||||||
},
|
|
||||||
components: { Layout },
|
|
||||||
props: {
|
|
||||||
user: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
file: null,
|
|
||||||
tryingToCreate: false,
|
|
||||||
errors: [],
|
|
||||||
papaConfig: { dynamicTyping: true, skipEmptyLines: true, complete: this.assignResults },
|
|
||||||
fileData: null,
|
|
||||||
fileHeadings: null,
|
|
||||||
myVehicles: [],
|
|
||||||
selectedVehicle: null,
|
|
||||||
invertFullTank: null,
|
|
||||||
filledValueString: '',
|
|
||||||
notFilledValueString: '',
|
|
||||||
isFullTankString: false,
|
|
||||||
fileHeadingMap: {
|
|
||||||
fuelQuantity: null,
|
|
||||||
perUnitPrice: null,
|
|
||||||
totalAmount: null,
|
|
||||||
odoReading: null,
|
|
||||||
isTankFull: null,
|
|
||||||
hasMissedFillup: null,
|
|
||||||
comments: [], // [int]
|
|
||||||
fillingStation: null,
|
|
||||||
date: null,
|
|
||||||
fuelSubType: null,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState('utils', ['isMobile']),
|
|
||||||
...mapState('vehicles', ['vehicles']),
|
|
||||||
uploadButtonLabel() {
|
|
||||||
if (this.isMobile) {
|
|
||||||
if (this.file == null) {
|
|
||||||
return this.$t('choosephoto')
|
|
||||||
} else {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.file == null) {
|
|
||||||
return this.$t('choosefile')
|
|
||||||
} else {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.myVehicles = this.vehicles
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
assignResults(results, file) {
|
|
||||||
this.fileData = results.data
|
|
||||||
this.fileHeadings = results.data[0]
|
|
||||||
},
|
|
||||||
parseCSV() {
|
|
||||||
if (this.file == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.errorMessage = ''
|
|
||||||
Papa.parse(this.file, this.papaConfig)
|
|
||||||
},
|
|
||||||
getUsedHeadings() {
|
|
||||||
return Object.keys(this.fileHeadingMap).filter((k) => this.fileHeadingMap[k] != null) // filter non-null properties
|
|
||||||
},
|
|
||||||
getTimezone() {
|
|
||||||
return Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
||||||
},
|
|
||||||
csvToJson() {
|
|
||||||
const data = []
|
|
||||||
const headings = this.getUsedHeadings().reduce((a, k) => ({ ...a, [k]: this.fileHeadingMap[k] }), {}) // create new object from filter
|
|
||||||
const comments = (row) => {
|
|
||||||
return this.fileHeadingMap.comments.reduce((a, fi) => {
|
|
||||||
// TODO: sanitize to prevent XSS
|
|
||||||
return `${a}${this.fileHeadings[fi]}: ${row[fi]}\n`
|
|
||||||
}, '')
|
|
||||||
}
|
|
||||||
const calculateTotal = (row) => {
|
|
||||||
return this.fileHeadingMap.totalAmount === -1
|
|
||||||
? row[this.fileHeadingMap.fuelQuantity] * row[this.fileHeadingMap.perUnitPrice]
|
|
||||||
: row[this.fileHeadingMap.totalAmount]
|
|
||||||
}
|
|
||||||
|
|
||||||
const setFullTank = (row) => {
|
|
||||||
if (row[this.fileHeadingMap.isTankFull].toLowerCase() === this.filledValueString.toLowerCase()) {
|
|
||||||
return true
|
|
||||||
} else if (row[this.fileHeadingMap.isTankFull].toLowerCase() === this.notFilledValueString.toLowerCase()) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
// TODO: need to handle errors better
|
|
||||||
throw Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let r = 1; r < this.fileData.length; r++) {
|
|
||||||
const row = this.fileData[r]
|
|
||||||
const item = {}
|
|
||||||
Object.keys(headings).forEach((k) => {
|
|
||||||
if (k === 'comments') {
|
|
||||||
item[k] = comments(row)
|
|
||||||
} else if (k === 'totalAmount') {
|
|
||||||
item[k] = calculateTotal(row)
|
|
||||||
} else if (k === 'isTankFull') {
|
|
||||||
if (this.isFullTankString) {
|
|
||||||
item[k] = setFullTank(row)
|
|
||||||
} else {
|
|
||||||
if (this.invertFullTank) {
|
|
||||||
item[k] = Boolean(!row[headings[k]])
|
|
||||||
} else {
|
|
||||||
item[k] = Boolean(row[headings[k]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (k === 'hasMissedFillup') {
|
|
||||||
// TODO: need to account for this field being a string
|
|
||||||
item[k] = Boolean(row[headings[k]])
|
|
||||||
} else if (k === 'date') {
|
|
||||||
item[k] = new Date(row[headings[k]]).toISOString()
|
|
||||||
} else {
|
|
||||||
item[k] = row[headings[k]]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
data.push(item)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
importData() {
|
|
||||||
if (this.errors.length === 0) {
|
|
||||||
try {
|
|
||||||
const content = {
|
|
||||||
data: this.csvToJson(),
|
|
||||||
vehicleId: this.selectedVehicle.id,
|
|
||||||
timezone: this.getTimezone(),
|
|
||||||
}
|
|
||||||
axios
|
|
||||||
.post('/api/import/generic', content)
|
|
||||||
.then((data) => {
|
|
||||||
this.$buefy.toast.open({
|
|
||||||
message: this.$t('importsuccessfull'),
|
|
||||||
type: 'is-success',
|
|
||||||
duration: 3000,
|
|
||||||
})
|
|
||||||
setTimeout(() => this.$router.push({ name: 'home' }), 1000)
|
|
||||||
})
|
|
||||||
.catch((ex) => {
|
|
||||||
this.$buefy.toast.open({
|
|
||||||
duration: 5000,
|
|
||||||
message: this.$t('importerror'),
|
|
||||||
position: 'is-bottom',
|
|
||||||
type: 'is-danger',
|
|
||||||
})
|
|
||||||
console.log(ex)
|
|
||||||
if (ex.response && ex.response.data.error) {
|
|
||||||
this.errors.push(ex.response.data.error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
// TODO: handle error
|
|
||||||
this.errors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.errors.push('fix errors')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
checkFieldString() {
|
|
||||||
const tankFull = this.fileData[1][this.fileHeadingMap.isTankFull]
|
|
||||||
if (typeof tankFull !== 'boolean' && typeof tankFull === 'string') {
|
|
||||||
this.isFullTankString = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clearHeadingProperty(property) {
|
|
||||||
if (property === 'comments') {
|
|
||||||
this.fileHeadingMap[property] = []
|
|
||||||
} else {
|
|
||||||
this.fileHeadingMap[property] = null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Layout>
|
|
||||||
<div class="columns box">
|
|
||||||
<div class="column">
|
|
||||||
<h1 class="title">{{ $t('importgeneric') }}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<div v-if="fileData === null" class="columns">
|
|
||||||
<div class="column">
|
|
||||||
<p class="subtitle"> {{ $t('stepstoimport', { name: 'CSV' }) }}</p>
|
|
||||||
<ol>
|
|
||||||
<!-- <li>{{ $t('importhintcreatecsv', { 'name': 'Fuelly' }) }} <a href="http://docs.fuelly.com/acar-import-export-center" target="_nofollow">{{ $t('here') }}</a>.</li> -->
|
|
||||||
<li>{{ $t('importgenerichintdata') }}</li>
|
|
||||||
<li>{{ $t('importhintvehiclecreated') }}</li>
|
|
||||||
<li v-html="$t('importhintcurrdist')"></li>
|
|
||||||
<li v-html="$t('importhintunits')"></li>
|
|
||||||
<li>
|
|
||||||
<b>{{ $t('dontimportagain') }}</b>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="fileData === null" class="section box">
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-two-thirds">
|
|
||||||
<p class="subtitle">{{ $t('choosedatafile') }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="column is-one-third is-flex is-align-content-center">
|
|
||||||
<form @submit.prevent="parseCSV">
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column">
|
|
||||||
<b-field class="file is-primary" :class="{ 'has-name': !!file }">
|
|
||||||
<b-upload v-model="file" class="file-label" accept=".csv" required>
|
|
||||||
<span class="file-cta">
|
|
||||||
<b-icon class="file-icon" icon="upload"></b-icon>
|
|
||||||
<span class="file-label">{{ uploadButtonLabel }}</span>
|
|
||||||
</span>
|
|
||||||
<span v-if="file" class="file-name" :class="isMobile ? 'file-name-mobile' : 'file-name-desktop'">
|
|
||||||
{{ file.name }}
|
|
||||||
</span>
|
|
||||||
</b-upload>
|
|
||||||
</b-field>
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
<b-button tag="button" native-type="submit" type="is-primary" class="control">
|
|
||||||
{{ $t('import') }}
|
|
||||||
</b-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="columns">
|
|
||||||
<div class="column">
|
|
||||||
<p class="subtitle">Map Fields</p>
|
|
||||||
<form class="" @submit.prevent="importData">
|
|
||||||
<b-field :label="$t('selectvehicle')">
|
|
||||||
<b-select v-model="selectedVehicle" :placeholder="$t('vehicle')" required expanded>
|
|
||||||
<option v-for="option in myVehicles" :key="option.id" :value="option">
|
|
||||||
{{ option.nickname }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<span v-if="selectedVehicle !== null">
|
|
||||||
<b-field :label="$t('fillupdate')">
|
|
||||||
<b-select v-model="fileHeadingMap.date" required expanded>
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<template v-slot:label>
|
|
||||||
{{ $t('fuelsubtype') }}
|
|
||||||
<b-tooltip type="is-dark" label="Clear selection">
|
|
||||||
<b-button
|
|
||||||
type="is-ghost"
|
|
||||||
size="is-small"
|
|
||||||
icon-pack="fas"
|
|
||||||
icon-right="arrow-rotate-left"
|
|
||||||
@click="clearHeadingProperty('fuelSubType')"
|
|
||||||
></b-button>
|
|
||||||
</b-tooltip>
|
|
||||||
</template>
|
|
||||||
<b-select v-model="fileHeadingMap.fuelSubType" expanded>
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field :label="$t('quantity')">
|
|
||||||
<b-select v-model="fileHeadingMap.fuelQuantity" expanded required>
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field :label="$t('per', { '0': $t('price'), '1': $t('unit.short.' + selectedVehicle.fuelUnitDetail.key) })">
|
|
||||||
<b-select v-model.number="fileHeadingMap.perUnitPrice" type="number" min="0" step=".001" expanded required>
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field :label="$t('totalamountpaid')">
|
|
||||||
<b-select v-model.number="fileHeadingMap.totalAmount" expanded required>
|
|
||||||
<option value="-1">Calculated</option>
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field :label="$t('odometer')">
|
|
||||||
<b-select v-model.number="fileHeadingMap.odoReading" expanded required>
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field :label="$t('tankpartialfull')">
|
|
||||||
<b-radio-button v-model="invertFullTank" native-value="false">{{ $t('fulltank') }}</b-radio-button>
|
|
||||||
<b-radio-button v-model="invertFullTank" native-value="true">{{ $t('partialfillup') }}</b-radio-button>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<b-select v-model="fileHeadingMap.isTankFull" required @input="checkFieldString">
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<span v-if="isFullTankString === true" required>
|
|
||||||
<b-field label="Value when tank is filled">
|
|
||||||
<b-input v-model="filledValueString"></b-input>
|
|
||||||
</b-field>
|
|
||||||
<b-field label="Value when tank was not completely filled">
|
|
||||||
<b-input v-model="notFilledValueString"></b-input>
|
|
||||||
</b-field>
|
|
||||||
</span>
|
|
||||||
<b-field>
|
|
||||||
<template v-slot:label>
|
|
||||||
{{ $t('missedfillup') }}
|
|
||||||
<b-tooltip type="is-dark" label="Clear selection">
|
|
||||||
<b-button
|
|
||||||
type="is-ghost"
|
|
||||||
size="is-small"
|
|
||||||
icon-pack="fas"
|
|
||||||
icon-right="arrow-rotate-left"
|
|
||||||
@click="clearHeadingProperty('hasMissedFillup')"
|
|
||||||
></b-button>
|
|
||||||
</b-tooltip>
|
|
||||||
</template>
|
|
||||||
<b-select v-model="fileHeadingMap.hasMissedFillup">
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<template v-slot:label>
|
|
||||||
{{ $t('fillingstation') }}
|
|
||||||
<b-tooltip type="is-dark" label="Clear selection">
|
|
||||||
<b-button
|
|
||||||
type="is-ghost"
|
|
||||||
size="is-small"
|
|
||||||
icon-pack="fas"
|
|
||||||
icon-right="arrow-rotate-left"
|
|
||||||
@click="clearHeadingProperty('fillingStation')"
|
|
||||||
></b-button>
|
|
||||||
</b-tooltip>
|
|
||||||
</template>
|
|
||||||
<b-select v-model="fileHeadingMap.fillingStation">
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<template v-slot:label>
|
|
||||||
{{ $t('comments') }}
|
|
||||||
<b-tooltip type="is-dark" label="Clear selection">
|
|
||||||
<b-button
|
|
||||||
type="is-ghost"
|
|
||||||
size="is-small"
|
|
||||||
icon-pack="fas"
|
|
||||||
icon-right="arrow-rotate-left"
|
|
||||||
@click="clearHeadingProperty('comments')"
|
|
||||||
></b-button>
|
|
||||||
</b-tooltip>
|
|
||||||
</template>
|
|
||||||
<b-select v-model="fileHeadingMap.comments" type="textarea" multiple expanded>
|
|
||||||
<option v-for="(option, index) in fileHeadings" :key="index" :value="index">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</b-select>
|
|
||||||
</b-field>
|
|
||||||
<br />
|
|
||||||
<b-field>
|
|
||||||
<b-button tag="button" native-type="submit" type="is-primary" :value="$t('save')" :label="$t('import')" expanded />
|
|
||||||
<p v-if="authError"> There was an error logging in to your account. </p>
|
|
||||||
</b-field>
|
|
||||||
</span>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<b-message v-if="errors.length" type="is-danger">
|
|
||||||
<ul>
|
|
||||||
<li v-for="error in errors" :key="error">{{ error }}</li>
|
|
||||||
</ul>
|
|
||||||
</b-message>
|
|
||||||
</Layout>
|
|
||||||
</template>
|
|
||||||
@@ -18,19 +18,18 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Layout>
|
<Layout>
|
||||||
<div class="columns box">
|
<div class="columns box"
|
||||||
<div class="column">
|
><div class="column">
|
||||||
<h1 class="title">{{ $t('importdata') }}</h1>
|
<h1 class="title">{{ $t('importdata') }}</h1>
|
||||||
<p class="subtitle">{{ $t('importdatadesc') }}</p>
|
<p class="subtitle">{{ $t('importdatadesc') }}</p>
|
||||||
</div>
|
</div></div
|
||||||
</div>
|
>
|
||||||
<br />
|
<br />
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-one-third">
|
<div class="column is-one-third">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h1 class="title">Fuelly</h1>
|
<h1 class="title">Fuelly</h1>
|
||||||
<p>If you have been using Fuelly to store your vehicle data, export the CSV file from Fuelly and click here to
|
<p>If you have been using Fuelly to store your vehicle data, export the CSV file from Fuelly and click here to import.</p>
|
||||||
import.</p>
|
|
||||||
<br />
|
<br />
|
||||||
<b-button type="is-primary" tag="router-link" to="/import/fuelly">{{ $t('import') }}</b-button>
|
<b-button type="is-primary" tag="router-link" to="/import/fuelly">{{ $t('import') }}</b-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,16 +43,6 @@ export default {
|
|||||||
<b-button type="is-primary" tag="router-link" to="/import/drivvo">{{ $t('import') }}</b-button>
|
<b-button type="is-primary" tag="router-link" to="/import/drivvo">{{ $t('import') }}</b-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="column is-one-third" to="/import-generic">
|
|
||||||
<div class="box">
|
|
||||||
<h1 class="title">{{ $t('importgeneric') }}</h1>
|
|
||||||
<p>{{ $t('importgenericdesc') }}</p>
|
|
||||||
<br />
|
|
||||||
<b-button type="is-primary" tag="router-link" to="/import/generic">{{ $t('import') }}</b-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<br />
|
<br />
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<b-button type="is-primary" native-type="submit" tag="button" :value="$t('save')">{{ $t('save') }}</b-button>
|
<b-button type="is-primary" native-type="submit" tag="button" :value="$t('save')"></b-button>
|
||||||
|
|
||||||
<b-button type="is-danger is-light" @click="resetMigrationMode">{{ $t('cancel') }}</b-button>
|
<b-button type="is-danger is-light" @click="resetMigrationMode">{{ $t('cancel') }}</b-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ export default {
|
|||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
settingsModel: {
|
settingsModel: {
|
||||||
|
language: this.me.language,
|
||||||
currency: this.me.currency,
|
currency: this.me.currency,
|
||||||
distanceUnit: this.me.distanceUnit,
|
distanceUnit: this.me.distanceUnit,
|
||||||
dateFormat: this.me.dateFormat,
|
dateFormat: this.me.dateFormat,
|
||||||
},
|
},
|
||||||
tryingToSave: false,
|
tryingToSave: false,
|
||||||
|
selectedLanguage: "",
|
||||||
changePassModel: {
|
changePassModel: {
|
||||||
old: '',
|
old: '',
|
||||||
new: '',
|
new: '',
|
||||||
@@ -36,7 +38,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState('vehicles', ['currencyMasters', 'distanceUnitMasters']),
|
...mapState('masters', ['currencyMasters', 'languageMasters', 'distanceUnitMasters']),
|
||||||
passwordValid() {
|
passwordValid() {
|
||||||
if (this.changePassModel.new === '' || this.changePassModel.renew === '') {
|
if (this.changePassModel.new === '' || this.changePassModel.renew === '') {
|
||||||
return true
|
return true
|
||||||
@@ -59,6 +61,9 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.selectedLanguage = this.formatLanguage(this.languageMasters.filter(x => x.shorthand === this.me.language)[0])
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changePassword() {
|
changePassword() {
|
||||||
if (!this.passwordValid) {
|
if (!this.passwordValid) {
|
||||||
@@ -110,6 +115,7 @@ export default {
|
|||||||
type: 'is-success',
|
type: 'is-success',
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
})
|
})
|
||||||
|
this.$i18n.locale = this.settingsModel.language
|
||||||
})
|
})
|
||||||
.catch((ex) => {
|
.catch((ex) => {
|
||||||
this.$buefy.toast.open({
|
this.$buefy.toast.open({
|
||||||
@@ -126,6 +132,9 @@ export default {
|
|||||||
formatCurrency(option) {
|
formatCurrency(option) {
|
||||||
return `${option.namePlural} (${option.code})`
|
return `${option.namePlural} (${option.code})`
|
||||||
},
|
},
|
||||||
|
formatLanguage(option) {
|
||||||
|
return `${option.nameNative} ${option.emoji}`
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -136,9 +145,18 @@ export default {
|
|||||||
<div class="columns"
|
<div class="columns"
|
||||||
><div class="column">
|
><div class="column">
|
||||||
<form class="box " @submit.prevent="saveSettings">
|
<form class="box " @submit.prevent="saveSettings">
|
||||||
<h1 class="subtitle">
|
<b-field :label="$t('language')">
|
||||||
{{ $t('settingdesc') }}
|
<b-autocomplete
|
||||||
</h1>
|
v-model="selectedLanguage"
|
||||||
|
:placeholder="$t('language')"
|
||||||
|
:keep-first="true"
|
||||||
|
:custom-formatter="formatLanguage"
|
||||||
|
:data="languageMasters"
|
||||||
|
:open-on-focus="true"
|
||||||
|
required
|
||||||
|
@select="(option) => (settingsModel.language = option.shorthand)"
|
||||||
|
/>
|
||||||
|
</b-field>
|
||||||
<b-field :label="$t('currency')">
|
<b-field :label="$t('currency')">
|
||||||
<b-autocomplete
|
<b-autocomplete
|
||||||
v-model="settingsModel.currency"
|
v-model="settingsModel.currency"
|
||||||
|
|||||||
52
ui/src/state/modules/masters.js
Normal file
52
ui/src/state/modules/masters.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export const state = {
|
||||||
|
languageMasters: [],
|
||||||
|
fuelUnitMasters: [],
|
||||||
|
distanceUnitMasters: [],
|
||||||
|
currencyMasters: [],
|
||||||
|
fuelTypeMasters: [],
|
||||||
|
roleMasters: [],
|
||||||
|
}
|
||||||
|
export const mutations = {
|
||||||
|
CACHE_LANGUAGE_MASTERS(state, masters) {
|
||||||
|
state.languageMasters = masters
|
||||||
|
},
|
||||||
|
CACHE_FUEL_UNIT_MASTERS(state, masters) {
|
||||||
|
state.fuelUnitMasters = masters
|
||||||
|
},
|
||||||
|
CACHE_DISTANCE_UNIT_MASTERS(state, masters) {
|
||||||
|
state.distanceUnitMasters = masters
|
||||||
|
},
|
||||||
|
CACHE_FUEL_TYPE_MASTERS(state, masters) {
|
||||||
|
state.fuelTypeMasters = masters
|
||||||
|
},
|
||||||
|
CACHE_CURRENCY_MASTERS(state, masters) {
|
||||||
|
state.currencyMasters = masters
|
||||||
|
},
|
||||||
|
CACHE_ROLE_MASTERS(state, roles) {
|
||||||
|
state.roleMasters = roles
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getters = {}
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
init({ dispatch, rootState }) {
|
||||||
|
const { currentUser } = rootState.auth
|
||||||
|
if (currentUser) {
|
||||||
|
dispatch('fetchMasters')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchMasters({ commit, state, rootState }) {
|
||||||
|
return axios.get('/api/masters').then((response) => {
|
||||||
|
commit('CACHE_LANGUAGE_MASTERS', response.data.languages)
|
||||||
|
commit('CACHE_FUEL_UNIT_MASTERS', response.data.fuelUnits)
|
||||||
|
commit('CACHE_FUEL_TYPE_MASTERS', response.data.fuelTypes)
|
||||||
|
commit('CACHE_CURRENCY_MASTERS', response.data.currencies)
|
||||||
|
commit('CACHE_DISTANCE_UNIT_MASTERS', response.data.distanceUnits)
|
||||||
|
commit('CACHE_ROLE_MASTERS', response.data.roles)
|
||||||
|
return response.data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -4,11 +4,6 @@ import { filter } from 'lodash'
|
|||||||
import parseISO from 'date-fns/parseISO'
|
import parseISO from 'date-fns/parseISO'
|
||||||
export const state = {
|
export const state = {
|
||||||
vehicles: [],
|
vehicles: [],
|
||||||
roleMasters: [],
|
|
||||||
fuelUnitMasters: [],
|
|
||||||
distanceUnitMasters: [],
|
|
||||||
currencyMasters: [],
|
|
||||||
fuelTypeMasters: [],
|
|
||||||
quickEntries: [],
|
quickEntries: [],
|
||||||
vehicleStats: new Map(),
|
vehicleStats: new Map(),
|
||||||
}
|
}
|
||||||
@@ -29,24 +24,9 @@ export const mutations = {
|
|||||||
CACHE_VEHICLE_STATS(state, stats) {
|
CACHE_VEHICLE_STATS(state, stats) {
|
||||||
state.vehicleStats.set(stats.vehicleId, stats)
|
state.vehicleStats.set(stats.vehicleId, stats)
|
||||||
},
|
},
|
||||||
CACHE_FUEL_UNIT_MASTERS(state, masters) {
|
|
||||||
state.fuelUnitMasters = masters
|
|
||||||
},
|
|
||||||
CACHE_DISTANCE_UNIT_MASTERS(state, masters) {
|
|
||||||
state.distanceUnitMasters = masters
|
|
||||||
},
|
|
||||||
CACHE_FUEL_TYPE_MASTERS(state, masters) {
|
|
||||||
state.fuelTypeMasters = masters
|
|
||||||
},
|
|
||||||
CACHE_CURRENCY_MASTERS(state, masters) {
|
|
||||||
state.currencyMasters = masters
|
|
||||||
},
|
|
||||||
CACHE_QUICK_ENTRIES(state, entries) {
|
CACHE_QUICK_ENTRIES(state, entries) {
|
||||||
state.quickEntries = entries
|
state.quickEntries = entries
|
||||||
},
|
},
|
||||||
CACHE_ROLE_MASTERS(state, roles) {
|
|
||||||
state.roleMasters = roles
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
@@ -54,22 +34,9 @@ export const actions = {
|
|||||||
const { currentUser } = rootState.auth
|
const { currentUser } = rootState.auth
|
||||||
if (currentUser) {
|
if (currentUser) {
|
||||||
dispatch('fetchVehicles')
|
dispatch('fetchVehicles')
|
||||||
dispatch('fetchMasters')
|
|
||||||
dispatch('fetchQuickEntries', { force: true })
|
dispatch('fetchQuickEntries', { force: true })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchMasters({ commit, state, rootState }) {
|
|
||||||
return axios.get('/api/masters').then((response) => {
|
|
||||||
const fuelUnitMasters = response.data.fuelUnits
|
|
||||||
const fuelTypeMasters = response.data.fuelTypes
|
|
||||||
commit('CACHE_FUEL_UNIT_MASTERS', fuelUnitMasters)
|
|
||||||
commit('CACHE_FUEL_TYPE_MASTERS', fuelTypeMasters)
|
|
||||||
commit('CACHE_CURRENCY_MASTERS', response.data.currencies)
|
|
||||||
commit('CACHE_DISTANCE_UNIT_MASTERS', response.data.distanceUnits)
|
|
||||||
commit('CACHE_ROLE_MASTERS', response.data.roles)
|
|
||||||
return response.data
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fetchVehicles({ commit, state, rootState }) {
|
fetchVehicles({ commit, state, rootState }) {
|
||||||
return axios.get('/api/me/vehicles').then((response) => {
|
return axios.get('/api/me/vehicles').then((response) => {
|
||||||
const data = response.data
|
const data = response.data
|
||||||
|
|||||||
Reference in New Issue
Block a user