Compare commits
66 Commits
v0.0.1
...
feat/langu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
302bdd2222 | ||
|
|
14968013dd | ||
|
|
efa6aed8eb | ||
|
|
533c68ee09 | ||
|
|
9da21b2192 | ||
|
|
a8c85bcd7d | ||
|
|
d597a4ed30 | ||
|
|
45456280b4 | ||
|
|
1d5794e344 | ||
|
|
cd558ba744 | ||
|
|
3a2c82c789 | ||
|
|
d196536d74 | ||
|
|
630a7f2ec6 | ||
|
|
f2bc01289a | ||
|
|
a8d2b37087 | ||
|
|
7436399d90 | ||
|
|
bc3e1f0982 | ||
|
|
d429fa34bd | ||
|
|
5095cb4c61 | ||
|
|
e0df7ee80e | ||
|
|
431de8c3eb | ||
|
|
41793784ea | ||
|
|
85b5ad28bf | ||
|
|
b386012e13 | ||
|
|
df2d7288df | ||
|
|
7a6f796561 | ||
|
|
aee52d0594 | ||
|
|
f8b1de8d15 | ||
|
|
a7896340e1 | ||
|
|
f2a3bb2e9f | ||
|
|
d343619f13 | ||
|
|
adce0efa8b | ||
|
|
fc6f4bc00d | ||
|
|
a16bcf850f | ||
|
|
4ace38f8f3 | ||
|
|
63e330ffb0 | ||
|
|
fc9796081e | ||
|
|
440913af9c | ||
|
|
66d01afe6e | ||
|
|
ad4a399dc8 | ||
|
|
2137bf7702 | ||
|
|
47bdf7b505 | ||
|
|
669bffa955 | ||
|
|
05c5381a06 | ||
|
|
e623e3ad1a | ||
|
|
c43a2f639a | ||
|
|
e66e5b7724 | ||
|
|
adfd70fe98 | ||
|
|
ebebcacdc9 | ||
|
|
3299c13181 | ||
|
|
2661f8ae36 | ||
|
|
091cfdcc99 | ||
|
|
9771dc4c25 | ||
|
|
c0db2c5c1e | ||
|
|
2ecb113918 | ||
|
|
966cac280f | ||
|
|
2749707546 | ||
|
|
f1bf36bcb9 | ||
|
|
3322e2f6bd | ||
|
|
9ef929dbd5 | ||
|
|
dc33aaad49 | ||
|
|
15cf09f326 | ||
|
|
1e099ec8b6 | ||
|
|
e8f7815d8d | ||
|
|
bfaebf17d0 | ||
|
|
d314ed4a16 |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Don't track .vscode directory
|
||||||
|
.vscode
|
||||||
|
!.vscode/launch.json
|
||||||
|
|
||||||
15
README.md
15
README.md
@@ -116,6 +116,21 @@ services:
|
|||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Install on Kubernetes
|
||||||
|
|
||||||
|
You can install Hammond on Kubernetes by using Helm. The
|
||||||
|
[Helm chart for Hammond](https://github.com/djjudas21/charts/tree/main/charts/hammond)
|
||||||
|
is maintained by djjudas21.
|
||||||
|
|
||||||
|
Check out the default [`values.yaml`](https://github.com/djjudas21/charts/blob/main/charts/hammond/values.yaml)
|
||||||
|
to see what you can override.
|
||||||
|
|
||||||
|
```console
|
||||||
|
helm repo add djjudas21 https://djjudas21.github.io/charts/
|
||||||
|
helm repo update djjudas21
|
||||||
|
helm install djjudas21/hammond
|
||||||
|
```
|
||||||
|
|
||||||
### Build from Source / Ubuntu Installation
|
### Build from Source / Ubuntu Installation
|
||||||
|
|
||||||
Although personally I feel that using the docker container is the best way of using
|
Although personally I feel that using the docker container is the best way of using
|
||||||
|
|||||||
3
server/.gitignore
vendored
3
server/.gitignore
vendored
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
# MS VSCode
|
# MS VSCode
|
||||||
.vscode
|
.vscode
|
||||||
|
!.vscode/launch.json
|
||||||
__debug_bin
|
__debug_bin
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
@@ -22,4 +23,4 @@ assets/*
|
|||||||
keys/*
|
keys/*
|
||||||
backups/*
|
backups/*
|
||||||
nodemon.json
|
nodemon.json
|
||||||
dist/*
|
dist/*
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
@@ -25,6 +26,33 @@ func RandString(n int) string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A helper to convert from litres to gallon
|
||||||
|
func LitreToGallon(litres float32) float32 {
|
||||||
|
gallonConversionFactor := 0.21997
|
||||||
|
return litres * float32(gallonConversionFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper to convert from gallon to litres
|
||||||
|
func GallonToLitre(gallons float32) float32 {
|
||||||
|
litreConversionFactor := 3.785412
|
||||||
|
return gallons * float32(litreConversionFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A helper to convert from km to miles
|
||||||
|
func KmToMiles(km float32) float32 {
|
||||||
|
kmConversionFactor := 0.62137119
|
||||||
|
return km * float32(kmConversionFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper to convert from miles to km
|
||||||
|
func MilesToKm(miles float32) float32 {
|
||||||
|
milesConversionFactor := 1.609344
|
||||||
|
return miles * float32(milesConversionFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// A Util function to generate jwt_token which can be used in the request header
|
// A Util function to generate jwt_token which can be used in the request header
|
||||||
func GenToken(id string, role db.Role) (string, string) {
|
func GenToken(id string, role db.Role) (string, string) {
|
||||||
jwt_token := jwt.New(jwt.GetSigningMethod("HS256"))
|
jwt_token := jwt.New(jwt.GetSigningMethod("HS256"))
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/common"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/common"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fuellyImport(c *gin.Context) {
|
func fuellyImport(c *gin.Context) {
|
||||||
@@ -24,3 +27,28 @@ func fuellyImport(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{})
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func drivvoImport(c *gin.Context) {
|
||||||
|
bytes, err := getFileBytes(c, "file")
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vehicleId := c.PostForm("vehicleID")
|
||||||
|
if vehicleId == "" {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, "Missing Vehicle ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
importLocation, err := strconv.ParseBool(c.PostForm("importLocation"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, "Please include importLocation option.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
errors := service.DrivvoImport(bytes, c.MustGet("userId").(string), vehicleId, importLocation)
|
||||||
|
if len(errors) > 0 {
|
||||||
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"errors": errors})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/common"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,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(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -51,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
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/dgrijalva/jwt-go/request"
|
"github.com/dgrijalva/jwt-go/request"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/common"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ func getMileageForVehicle(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fillups, err := service.GetMileageByVehicleId(searchByIdQuery.Id, model.Since)
|
fillups, err := service.GetMileageByVehicleId(searchByIdQuery.Id, model.Since, model.MileageOption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusUnprocessableEntity, common.NewError("getMileageForVehicle", err))
|
c.JSON(http.StatusUnprocessableEntity, common.NewError("getMileageForVehicle", err))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/common"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/common"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/common"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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,4 +1,4 @@
|
|||||||
module github.com/akhilrex/hammond
|
module hammond
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/controllers"
|
"hammond/controllers"
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/service"
|
"hammond/service"
|
||||||
|
|
||||||
"github.com/gin-contrib/location"
|
"github.com/gin-contrib/location"
|
||||||
"github.com/gin-gonic/contrib/static"
|
"github.com/gin-gonic/contrib/static"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package models
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateAlertModel struct {
|
type CreateAlertModel struct {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "github.com/akhilrex/hammond/db"
|
import "hammond/db"
|
||||||
|
|
||||||
type LoginResponse struct {
|
type LoginResponse struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|||||||
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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "github.com/akhilrex/hammond/db"
|
import "hammond/db"
|
||||||
|
|
||||||
type UpdateSettingModel struct {
|
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 {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MileageModel struct {
|
type MileageModel struct {
|
||||||
@@ -14,7 +14,7 @@ type MileageModel struct {
|
|||||||
FuelQuantity float32 `form:"fuelQuantity" json:"fuelQuantity" binding:"required"`
|
FuelQuantity float32 `form:"fuelQuantity" json:"fuelQuantity" binding:"required"`
|
||||||
PerUnitPrice float32 `form:"perUnitPrice" json:"perUnitPrice" binding:"required"`
|
PerUnitPrice float32 `form:"perUnitPrice" json:"perUnitPrice" binding:"required"`
|
||||||
Currency string `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
|
DistanceUnit db.DistanceUnit `form:"distanceUnit" json:"distanceUnit"`
|
||||||
Mileage float32 `form:"mileage" json:"mileage" binding:"mileage"`
|
Mileage float32 `form:"mileage" json:"mileage" binding:"mileage"`
|
||||||
CostPerMile float32 `form:"costPerMile" json:"costPerMile" binding:"costPerMile"`
|
CostPerMile float32 `form:"costPerMile" json:"costPerMile" binding:"costPerMile"`
|
||||||
OdoReading int `form:"odoReading" json:"odoReading" binding:"odoReading"`
|
OdoReading int `form:"odoReading" json:"odoReading" binding:"odoReading"`
|
||||||
@@ -35,4 +35,5 @@ func (b *MileageModel) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
type MileageQueryModel struct {
|
type MileageQueryModel struct {
|
||||||
Since time.Time `json:"since" query:"since" form:"since"`
|
Since time.Time `json:"since" query:"since" form:"since"`
|
||||||
|
MileageOption string `json:"mileageOption" query:"mileageOption" form:"mileageOption"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package models
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
|
|
||||||
_ "github.com/go-playground/validator/v10"
|
_ "github.com/go-playground/validator/v10"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateAlert(model models.CreateAlertModel, vehicleId, userId string) (*db.VehicleAlert, error) {
|
func CreateAlert(model models.CreateAlertModel, vehicleId, userId string) (*db.VehicleAlert, error) {
|
||||||
|
|||||||
142
server/service/drivvoImportService.go
Normal file
142
server/service/drivvoImportService.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"hammond/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DrivvoParseExpenses(content []byte, user *db.User, vehicle *db.Vehicle) ([]db.Expense, []string) {
|
||||||
|
expenseReader := csv.NewReader(bytes.NewReader(content))
|
||||||
|
expenseReader.Comment = '#'
|
||||||
|
// Read headers (there is a trailing comma at the end, that's why we have to read the first line)
|
||||||
|
expenseReader.Read()
|
||||||
|
expenseReader.FieldsPerRecord = 6
|
||||||
|
expenseRecords, err := expenseReader.ReadAll()
|
||||||
|
|
||||||
|
var errors []string
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
println(err.Error())
|
||||||
|
return nil, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
var expenses []db.Expense
|
||||||
|
for index, record := range expenseRecords {
|
||||||
|
date, err := time.Parse("2006-01-02 15:04:05", record[1])
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid date/time at service/expense row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCost, err := strconv.ParseFloat(record[2], 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found and invalid total cost at service/expense row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
odometer, err := strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid odometer reading at service/expense row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
notes := fmt.Sprintf("Location: %s\nNotes: %s\n", record[4], record[5])
|
||||||
|
|
||||||
|
expenses = append(expenses, db.Expense{
|
||||||
|
UserID: user.ID,
|
||||||
|
VehicleID: vehicle.ID,
|
||||||
|
Date: date,
|
||||||
|
OdoReading: odometer,
|
||||||
|
Amount: float32(totalCost),
|
||||||
|
ExpenseType: record[3],
|
||||||
|
Currency: user.Currency,
|
||||||
|
DistanceUnit: user.DistanceUnit,
|
||||||
|
Comments: notes,
|
||||||
|
Source: "Drivvo",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return expenses, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func DrivvoParseRefuelings(content []byte, user *db.User, vehicle *db.Vehicle, importLocation bool) ([]db.Fillup, []string) {
|
||||||
|
refuelingReader := csv.NewReader(bytes.NewReader(content))
|
||||||
|
refuelingReader.Comment = '#'
|
||||||
|
refuelingRecords, err := refuelingReader.ReadAll()
|
||||||
|
|
||||||
|
var errors []string
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
println(err.Error())
|
||||||
|
return nil, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
var fillups []db.Fillup
|
||||||
|
for index, record := range refuelingRecords {
|
||||||
|
// Skip column titles
|
||||||
|
if index == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
date, err := time.Parse("2006-01-02 15:04:05", record[1])
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid date/time at refuel row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCost, err := strconv.ParseFloat(record[4], 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found and invalid total cost at refuel row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
odometer, err := strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid odometer reading at refuel row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
location := ""
|
||||||
|
if importLocation {
|
||||||
|
location = record[17]
|
||||||
|
}
|
||||||
|
|
||||||
|
pricePerUnit, err := strconv.ParseFloat(record[3], 32)
|
||||||
|
if err != nil {
|
||||||
|
unit := strings.ToLower(db.FuelUnitDetails[vehicle.FuelUnit].Key)
|
||||||
|
errors = append(errors, fmt.Sprintf("Found an invalid cost per %s at refuel row %d", unit, index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
quantity, err := strconv.ParseFloat(record[5], 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid quantity at refuel row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
isTankFull := record[6] == "Yes"
|
||||||
|
|
||||||
|
// Unfortunatly, drivvo doesn't expose this info in their export
|
||||||
|
fal := false
|
||||||
|
|
||||||
|
notes := fmt.Sprintf("Reason: %s\nNotes: %s\nFuel: %s\n", record[18], record[19], record[2])
|
||||||
|
|
||||||
|
fillups = append(fillups, db.Fillup{
|
||||||
|
VehicleID: vehicle.ID,
|
||||||
|
UserID: user.ID,
|
||||||
|
Date: date,
|
||||||
|
HasMissedFillup: &fal,
|
||||||
|
IsTankFull: &isTankFull,
|
||||||
|
FuelQuantity: float32(quantity),
|
||||||
|
PerUnitPrice: float32(pricePerUnit),
|
||||||
|
FillingStation: location,
|
||||||
|
OdoReading: odometer,
|
||||||
|
TotalAmount: float32(totalCost),
|
||||||
|
FuelUnit: vehicle.FuelUnit,
|
||||||
|
Currency: user.Currency,
|
||||||
|
DistanceUnit: user.DistanceUnit,
|
||||||
|
Comments: notes,
|
||||||
|
Source: "Drivvo",
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
return fillups, errors
|
||||||
|
}
|
||||||
@@ -13,9 +13,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/internal/sanitize"
|
"hammond/internal/sanitize"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
|
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
141
server/service/fuellyImportService.go
Normal file
141
server/service/fuellyImportService.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"hammond/db"
|
||||||
|
|
||||||
|
"github.com/leekchan/accounting"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuellyParseAll(content []byte, userId string) ([]db.Fillup, []db.Expense, []string) {
|
||||||
|
stream := bytes.NewReader(content)
|
||||||
|
reader := csv.NewReader(stream)
|
||||||
|
records, err := reader.ReadAll()
|
||||||
|
|
||||||
|
var errors []string
|
||||||
|
user, err := GetUserById(userId)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
return nil, nil, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
vehicles, err := GetUserVehicles(userId)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
return nil, nil, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
return nil, nil, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
var vehicleMap map[string]db.Vehicle = make(map[string]db.Vehicle)
|
||||||
|
for _, vehicle := range *vehicles {
|
||||||
|
vehicleMap[vehicle.Nickname] = vehicle
|
||||||
|
}
|
||||||
|
|
||||||
|
var fillups []db.Fillup
|
||||||
|
var expenses []db.Expense
|
||||||
|
layout := "2006-01-02 15:04"
|
||||||
|
altLayout := "2006-01-02 3:04 PM"
|
||||||
|
|
||||||
|
for index, record := range records {
|
||||||
|
if index == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var vehicle db.Vehicle
|
||||||
|
var ok bool
|
||||||
|
if vehicle, ok = vehicleMap[record[4]]; !ok {
|
||||||
|
errors = append(errors, "Found an unmapped vehicle entry at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
dateStr := record[2] + " " + record[3]
|
||||||
|
date, err := time.Parse(layout, dateStr)
|
||||||
|
if err != nil {
|
||||||
|
date, err = time.Parse(altLayout, dateStr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid date/time at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCostStr := accounting.UnformatNumber(record[9], 3, user.Currency)
|
||||||
|
totalCost64, err := strconv.ParseFloat(totalCostStr, 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid total cost at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCost := float32(totalCost64)
|
||||||
|
odoStr := accounting.UnformatNumber(record[5], 0, user.Currency)
|
||||||
|
odoreading, err := strconv.Atoi(odoStr)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid odo reading at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
location := record[12]
|
||||||
|
|
||||||
|
//Create Fillup
|
||||||
|
if record[0] == "Gas" {
|
||||||
|
rateStr := accounting.UnformatNumber(record[7], 3, user.Currency)
|
||||||
|
ratet64, err := strconv.ParseFloat(rateStr, 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid cost per gallon at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
rate := float32(ratet64)
|
||||||
|
|
||||||
|
quantity64, err := strconv.ParseFloat(record[8], 32)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, "Found an invalid quantity at row "+strconv.Itoa(index+1))
|
||||||
|
}
|
||||||
|
quantity := float32(quantity64)
|
||||||
|
|
||||||
|
notes := fmt.Sprintf("Octane:%s\nGas Brand:%s\nLocation%s\nTags:%s\nPayment Type:%s\nTire Pressure:%s\nNotes:%s\nMPG:%s",
|
||||||
|
record[10], record[11], record[12], record[13], record[14], record[15], record[16], record[1],
|
||||||
|
)
|
||||||
|
|
||||||
|
isTankFull := record[6] == "Full"
|
||||||
|
fal := false
|
||||||
|
fillups = append(fillups, db.Fillup{
|
||||||
|
VehicleID: vehicle.ID,
|
||||||
|
FuelUnit: vehicle.FuelUnit,
|
||||||
|
FuelQuantity: quantity,
|
||||||
|
PerUnitPrice: rate,
|
||||||
|
TotalAmount: totalCost,
|
||||||
|
OdoReading: odoreading,
|
||||||
|
IsTankFull: &isTankFull,
|
||||||
|
Comments: notes,
|
||||||
|
FillingStation: location,
|
||||||
|
HasMissedFillup: &fal,
|
||||||
|
UserID: userId,
|
||||||
|
Date: date,
|
||||||
|
Currency: user.Currency,
|
||||||
|
DistanceUnit: user.DistanceUnit,
|
||||||
|
Source: "Fuelly",
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
if record[0] == "Service" {
|
||||||
|
notes := fmt.Sprintf("Tags:%s\nPayment Type:%s\nNotes:%s",
|
||||||
|
record[13], record[14], record[16],
|
||||||
|
)
|
||||||
|
expenses = append(expenses, db.Expense{
|
||||||
|
VehicleID: vehicle.ID,
|
||||||
|
Amount: totalCost,
|
||||||
|
OdoReading: odoreading,
|
||||||
|
Comments: notes,
|
||||||
|
ExpenseType: record[17],
|
||||||
|
UserID: userId,
|
||||||
|
Currency: user.Currency,
|
||||||
|
Date: date,
|
||||||
|
DistanceUnit: user.DistanceUnit,
|
||||||
|
Source: "Fuelly",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return fillups, expenses, errors
|
||||||
|
}
|
||||||
@@ -2,144 +2,12 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/csv"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/leekchan/accounting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func FuellyImport(content []byte, userId string) []string {
|
func WriteToDB(fillups []db.Fillup, expenses []db.Expense) []string {
|
||||||
stream := bytes.NewReader(content)
|
|
||||||
reader := csv.NewReader(stream)
|
|
||||||
records, err := reader.ReadAll()
|
|
||||||
|
|
||||||
var errors []string
|
var errors []string
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
vehicles, err := GetUserVehicles(userId)
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
user, err := GetUserById(userId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
var vehicleMap map[string]db.Vehicle = make(map[string]db.Vehicle)
|
|
||||||
for _, vehicle := range *vehicles {
|
|
||||||
vehicleMap[vehicle.Nickname] = vehicle
|
|
||||||
}
|
|
||||||
|
|
||||||
var fillups []db.Fillup
|
|
||||||
var expenses []db.Expense
|
|
||||||
layout := "2006-01-02 15:04"
|
|
||||||
altLayout := "2006-01-02 3:04 PM"
|
|
||||||
|
|
||||||
for index, record := range records {
|
|
||||||
if index == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var vehicle db.Vehicle
|
|
||||||
var ok bool
|
|
||||||
if vehicle, ok = vehicleMap[record[4]]; !ok {
|
|
||||||
errors = append(errors, "Found an unmapped vehicle entry at row "+strconv.Itoa(index+1))
|
|
||||||
}
|
|
||||||
dateStr := record[2] + " " + record[3]
|
|
||||||
date, err := time.Parse(layout, dateStr)
|
|
||||||
if err != nil {
|
|
||||||
date, err = time.Parse(altLayout, dateStr)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, "Found an invalid date/time at row "+strconv.Itoa(index+1))
|
|
||||||
}
|
|
||||||
|
|
||||||
totalCostStr := accounting.UnformatNumber(record[9], 3, user.Currency)
|
|
||||||
totalCost64, err := strconv.ParseFloat(totalCostStr, 32)
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, "Found an invalid total cost at row "+strconv.Itoa(index+1))
|
|
||||||
}
|
|
||||||
|
|
||||||
totalCost := float32(totalCost64)
|
|
||||||
odoStr := accounting.UnformatNumber(record[5], 0, user.Currency)
|
|
||||||
odoreading, err := strconv.Atoi(odoStr)
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, "Found an invalid odo reading at row "+strconv.Itoa(index+1))
|
|
||||||
}
|
|
||||||
location := record[12]
|
|
||||||
|
|
||||||
//Create Fillup
|
|
||||||
if record[0] == "Gas" {
|
|
||||||
rateStr := accounting.UnformatNumber(record[7], 3, user.Currency)
|
|
||||||
ratet64, err := strconv.ParseFloat(rateStr, 32)
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, "Found an invalid cost per gallon at row "+strconv.Itoa(index+1))
|
|
||||||
}
|
|
||||||
rate := float32(ratet64)
|
|
||||||
|
|
||||||
quantity64, err := strconv.ParseFloat(record[8], 32)
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, "Found an invalid quantity at row "+strconv.Itoa(index+1))
|
|
||||||
}
|
|
||||||
quantity := float32(quantity64)
|
|
||||||
|
|
||||||
notes := fmt.Sprintf("Octane:%s\nGas Brand:%s\nLocation%s\nTags:%s\nPayment Type:%s\nTire Pressure:%s\nNotes:%s\nMPG:%s",
|
|
||||||
record[10], record[11], record[12], record[13], record[14], record[15], record[16], record[1],
|
|
||||||
)
|
|
||||||
|
|
||||||
isTankFull := record[6] == "Full"
|
|
||||||
fal := false
|
|
||||||
fillups = append(fillups, db.Fillup{
|
|
||||||
VehicleID: vehicle.ID,
|
|
||||||
FuelUnit: vehicle.FuelUnit,
|
|
||||||
FuelQuantity: quantity,
|
|
||||||
PerUnitPrice: rate,
|
|
||||||
TotalAmount: totalCost,
|
|
||||||
OdoReading: odoreading,
|
|
||||||
IsTankFull: &isTankFull,
|
|
||||||
Comments: notes,
|
|
||||||
FillingStation: location,
|
|
||||||
HasMissedFillup: &fal,
|
|
||||||
UserID: userId,
|
|
||||||
Date: date,
|
|
||||||
Currency: user.Currency,
|
|
||||||
DistanceUnit: user.DistanceUnit,
|
|
||||||
Source: "Fuelly",
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
if record[0] == "Service" {
|
|
||||||
notes := fmt.Sprintf("Tags:%s\nPayment Type:%s\nNotes:%s",
|
|
||||||
record[13], record[14], record[16],
|
|
||||||
)
|
|
||||||
expenses = append(expenses, db.Expense{
|
|
||||||
VehicleID: vehicle.ID,
|
|
||||||
Amount: totalCost,
|
|
||||||
OdoReading: odoreading,
|
|
||||||
Comments: notes,
|
|
||||||
ExpenseType: record[17],
|
|
||||||
UserID: userId,
|
|
||||||
Currency: user.Currency,
|
|
||||||
Date: date,
|
|
||||||
DistanceUnit: user.DistanceUnit,
|
|
||||||
Source: "Fuelly",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if len(errors) != 0 {
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
tx := db.DB.Begin()
|
tx := db.DB.Begin()
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -150,19 +18,90 @@ func FuellyImport(content []byte, userId string) []string {
|
|||||||
errors = append(errors, err.Error())
|
errors = append(errors, err.Error())
|
||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
if err := tx.Create(&fillups).Error; err != nil {
|
if fillups != nil {
|
||||||
tx.Rollback()
|
if err := tx.Create(&fillups).Error; err != nil {
|
||||||
errors = append(errors, err.Error())
|
tx.Rollback()
|
||||||
return errors
|
errors = append(errors, err.Error())
|
||||||
|
return errors
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := tx.Create(&expenses).Error; err != nil {
|
if expenses != nil {
|
||||||
tx.Rollback()
|
if err := tx.Create(&expenses).Error; err != nil {
|
||||||
errors = append(errors, err.Error())
|
tx.Rollback()
|
||||||
return errors
|
errors = append(errors, err.Error())
|
||||||
|
return errors
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err = tx.Commit().Error
|
err := tx.Commit().Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = append(errors, err.Error())
|
errors = append(errors, err.Error())
|
||||||
}
|
}
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func DrivvoImport(content []byte, userId string, vehicleId string, importLocation bool) []string {
|
||||||
|
var errors []string
|
||||||
|
user, err := GetUserById(userId)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
vehicle, err := GetVehicleById(vehicleId)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
endParseIndex := bytes.Index(content, []byte("#Income"))
|
||||||
|
if endParseIndex == -1 {
|
||||||
|
endParseIndex = bytes.Index(content, []byte("#Route"))
|
||||||
|
if endParseIndex == -1 {
|
||||||
|
endParseIndex = len(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceEndIndex := bytes.Index(content, []byte("#Expense"))
|
||||||
|
if serviceEndIndex == -1 {
|
||||||
|
serviceEndIndex = endParseIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
refuelEndIndex := bytes.Index(content, []byte("#Service"))
|
||||||
|
if refuelEndIndex == -1 {
|
||||||
|
refuelEndIndex = serviceEndIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
var fillups []db.Fillup
|
||||||
|
fillups, errors = DrivvoParseRefuelings(content[:refuelEndIndex], user, vehicle, importLocation)
|
||||||
|
|
||||||
|
var allExpenses []db.Expense
|
||||||
|
services, parseErrors := DrivvoParseExpenses(content[refuelEndIndex:serviceEndIndex], user, vehicle)
|
||||||
|
if parseErrors != nil {
|
||||||
|
errors = append(errors, parseErrors...)
|
||||||
|
}
|
||||||
|
allExpenses = append(allExpenses, services...)
|
||||||
|
|
||||||
|
expenses, parseErrors := DrivvoParseExpenses(content[serviceEndIndex:endParseIndex], user, vehicle)
|
||||||
|
if parseErrors != nil {
|
||||||
|
errors = append(errors, parseErrors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
allExpenses = append(allExpenses, expenses...)
|
||||||
|
|
||||||
|
if len(errors) != 0 {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteToDB(fillups, allExpenses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuellyImport(content []byte, userId string) []string {
|
||||||
|
fillups, expenses, errors := FuellyParseAll(content, userId)
|
||||||
|
if len(errors) != 0 {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteToDB(fillups, expenses)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/akhilrex/hammond/db"
|
"errors"
|
||||||
|
"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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/common"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/db"
|
||||||
|
"hammond/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetMileageByVehicleId(vehicleId string, since time.Time) (mileage []models.MileageModel, err error) {
|
func GetMileageByVehicleId(vehicleId string, since time.Time, mileageOption string) (mileage []models.MileageModel, err error) {
|
||||||
data, err := db.GetFillupsByVehicleIdSince(vehicleId, since)
|
data, err := db.GetFillupsByVehicleIdSince(vehicleId, since)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -36,14 +37,48 @@ func GetMileageByVehicleId(vehicleId string, since time.Time) (mileage []models.
|
|||||||
PerUnitPrice: currentFillup.PerUnitPrice,
|
PerUnitPrice: currentFillup.PerUnitPrice,
|
||||||
OdoReading: currentFillup.OdoReading,
|
OdoReading: currentFillup.OdoReading,
|
||||||
Currency: currentFillup.Currency,
|
Currency: currentFillup.Currency,
|
||||||
|
DistanceUnit: currentFillup.DistanceUnit,
|
||||||
Mileage: 0,
|
Mileage: 0,
|
||||||
CostPerMile: 0,
|
CostPerMile: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentFillup.IsTankFull != nil && *currentFillup.IsTankFull && (currentFillup.HasMissedFillup == nil || !(*currentFillup.HasMissedFillup)) {
|
if currentFillup.IsTankFull != nil && *currentFillup.IsTankFull && (currentFillup.HasMissedFillup == nil || !(*currentFillup.HasMissedFillup)) {
|
||||||
distance := float32(currentFillup.OdoReading - lastFillup.OdoReading)
|
currentOdoReading := float32(currentFillup.OdoReading);
|
||||||
mileage.Mileage = distance / currentFillup.FuelQuantity
|
lastFillupOdoReading := float32(lastFillup.OdoReading);
|
||||||
mileage.CostPerMile = distance / currentFillup.TotalAmount
|
currentFuelQuantity := float32(currentFillup.FuelQuantity);
|
||||||
|
// If miles per gallon option and distanceUnit is km, convert from km to miles
|
||||||
|
// then check if fuel unit is litres. If it is, convert to gallons
|
||||||
|
if (mileageOption == "mpg" && mileage.DistanceUnit == db.KILOMETERS) {
|
||||||
|
currentOdoReading = common.KmToMiles(currentOdoReading);
|
||||||
|
lastFillupOdoReading = common.KmToMiles(lastFillupOdoReading);
|
||||||
|
if (mileage.FuelUnit == db.LITRE) {
|
||||||
|
currentFuelQuantity = common.LitreToGallon(currentFuelQuantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If km_litre option or litre per 100km and distanceUnit is miles, convert from miles to km
|
||||||
|
// then check if fuel unit is not litres. If it isn't, convert to litres
|
||||||
|
|
||||||
|
if ((mileageOption == "km_litre" || mileageOption == "litre_100km") && mileage.DistanceUnit == db.MILES) {
|
||||||
|
currentOdoReading = common.MilesToKm(currentOdoReading);
|
||||||
|
lastFillupOdoReading = common.MilesToKm(lastFillupOdoReading);
|
||||||
|
|
||||||
|
if (mileage.FuelUnit == db.US_GALLON) {
|
||||||
|
currentFuelQuantity = common.GallonToLitre(currentFuelQuantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
distance := float32(currentOdoReading - lastFillupOdoReading);
|
||||||
|
if (mileageOption == "litre_100km") {
|
||||||
|
mileage.Mileage = currentFuelQuantity / distance * 100;
|
||||||
|
} else {
|
||||||
|
mileage.Mileage = distance / currentFuelQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
mileage.CostPerMile = distance / currentFillup.TotalAmount;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package service
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateUser(userModel *models.RegisterRequest, role db.Role) error {
|
func CreateUser(userModel *models.RegisterRequest, role db.Role) error {
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package service
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/akhilrex/hammond/db"
|
"hammond/db"
|
||||||
"github.com/akhilrex/hammond/models"
|
"hammond/models"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ module.exports = {
|
|||||||
'no-console': process.env.PRE_COMMIT
|
'no-console': process.env.PRE_COMMIT
|
||||||
? ['error', { allow: ['warn', 'error'] }]
|
? ['error', { allow: ['warn', 'error'] }]
|
||||||
: 'off',
|
: 'off',
|
||||||
|
'vue/multi-word-component-names': 0,
|
||||||
'import/no-relative-parent-imports': 'error',
|
'import/no-relative-parent-imports': 'error',
|
||||||
'import/order': 'error',
|
'import/order': 'error',
|
||||||
'vue/array-bracket-spacing': 'error',
|
'vue/array-bracket-spacing': 'error',
|
||||||
|
|||||||
5
ui/.gitignore
vendored
5
ui/.gitignore
vendored
@@ -29,3 +29,8 @@ yarn-error.log*
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw*
|
*.sw*
|
||||||
|
|
||||||
|
#Vs code files
|
||||||
|
.vscode
|
||||||
|
!.vscode/launch.json
|
||||||
|
|
||||||
|
|||||||
30
ui/.vscode/_components.code-snippets
vendored
30
ui/.vscode/_components.code-snippets
vendored
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"BaseButton": {
|
|
||||||
"scope": "vue-html",
|
|
||||||
"prefix": "BaseButton",
|
|
||||||
"body": ["<BaseButton>", "\t${3}", "</BaseButton>"],
|
|
||||||
"description": "<BaseButton>"
|
|
||||||
},
|
|
||||||
"BaseIcon": {
|
|
||||||
"scope": "vue-html",
|
|
||||||
"prefix": "BaseIcon",
|
|
||||||
"body": ["<BaseIcon name=\"${1}\">", "\t${2}", "</BaseIcon>"],
|
|
||||||
"description": "<BaseIcon>"
|
|
||||||
},
|
|
||||||
"BaseInputText": {
|
|
||||||
"scope": "vue-html",
|
|
||||||
"prefix": "BaseInputText",
|
|
||||||
"body": ["<BaseInputText ${1}/>"],
|
|
||||||
"description": "<BaseInputText>"
|
|
||||||
},
|
|
||||||
"BaseLink": {
|
|
||||||
"scope": "vue-html",
|
|
||||||
"prefix": "BaseLink",
|
|
||||||
"body": [
|
|
||||||
"<BaseLink ${1|name,:to,href|}=\"${2:route}\">",
|
|
||||||
"\t${3}",
|
|
||||||
"</BaseLink>"
|
|
||||||
],
|
|
||||||
"description": "<BaseLink>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
26
ui/.vscode/_sfc-blocks.code-snippets
vendored
26
ui/.vscode/_sfc-blocks.code-snippets
vendored
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"script": {
|
|
||||||
"scope": "vue",
|
|
||||||
"prefix": "script",
|
|
||||||
"body": ["<script>", "export default {", "\t${0}", "}", "</script>"],
|
|
||||||
"description": "<script>"
|
|
||||||
},
|
|
||||||
"template": {
|
|
||||||
"scope": "vue",
|
|
||||||
"prefix": "template",
|
|
||||||
"body": ["<template>", "\t${0}", "</template>"],
|
|
||||||
"description": "<template>"
|
|
||||||
},
|
|
||||||
"style": {
|
|
||||||
"scope": "vue",
|
|
||||||
"prefix": "style",
|
|
||||||
"body": [
|
|
||||||
"<style lang=\"scss\" module>",
|
|
||||||
"@import '@design';",
|
|
||||||
"",
|
|
||||||
"${0}",
|
|
||||||
"</style>"
|
|
||||||
],
|
|
||||||
"description": "<style lang=\"scss\" module>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
37
ui/.vscode/extensions.json
vendored
37
ui/.vscode/extensions.json
vendored
@@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
|
||||||
// for the documentation about the extensions.json format
|
|
||||||
"recommendations": [
|
|
||||||
// Syntax highlighting and more for .vue files
|
|
||||||
// https://github.com/vuejs/vetur
|
|
||||||
"octref.vetur",
|
|
||||||
|
|
||||||
// Peek and go-to-definition for .vue files
|
|
||||||
// https://github.com/fuzinato/vscode-vue-peek
|
|
||||||
"dariofuzinato.vue-peek",
|
|
||||||
|
|
||||||
// Lint-on-save with ESLint
|
|
||||||
// https://github.com/microsoft/vscode-eslint
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
|
|
||||||
// Lint-on-save with Stylelint
|
|
||||||
// https://github.com/stylelint/vscode-stylelint
|
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
|
|
||||||
// Lint-on-save markdown in README files
|
|
||||||
// https://github.com/DavidAnson/vscode-markdownlint
|
|
||||||
"DavidAnson.vscode-markdownlint",
|
|
||||||
|
|
||||||
// Format-on-save with Prettier
|
|
||||||
// https://github.com/prettier/prettier-vscode
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
|
|
||||||
// SCSS intellisense
|
|
||||||
// https://github.com/mrmlnc/vscode-scss
|
|
||||||
"mrmlnc.vscode-scss",
|
|
||||||
|
|
||||||
// Test `.unit.js` files on save with Jest
|
|
||||||
// https://github.com/jest-community/vscode-jest
|
|
||||||
"Orta.vscode-jest"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
93
ui/.vscode/settings.json
vendored
93
ui/.vscode/settings.json
vendored
@@ -1,93 +0,0 @@
|
|||||||
{
|
|
||||||
// ===
|
|
||||||
// Spacing
|
|
||||||
// ===
|
|
||||||
|
|
||||||
"editor.insertSpaces": true,
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.trimAutoWhitespace": true,
|
|
||||||
"files.trimTrailingWhitespace": true,
|
|
||||||
"files.eol": "\n",
|
|
||||||
"files.insertFinalNewline": true,
|
|
||||||
"files.trimFinalNewlines": true,
|
|
||||||
|
|
||||||
// ===
|
|
||||||
// Files
|
|
||||||
// ===
|
|
||||||
|
|
||||||
"files.exclude": {
|
|
||||||
"**/*.log": true,
|
|
||||||
"**/*.log*": true,
|
|
||||||
"**/dist": true,
|
|
||||||
"**/coverage": true
|
|
||||||
},
|
|
||||||
"files.associations": {
|
|
||||||
".markdownlintrc": "jsonc"
|
|
||||||
},
|
|
||||||
|
|
||||||
// ===
|
|
||||||
// Event Triggers
|
|
||||||
// ===
|
|
||||||
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.eslint": true,
|
|
||||||
"source.fixAll.stylelint": true,
|
|
||||||
"source.fixAll.markdownlint": true
|
|
||||||
},
|
|
||||||
"eslint.validate": [
|
|
||||||
"javascript",
|
|
||||||
"javascriptreact",
|
|
||||||
"vue",
|
|
||||||
"vue-html",
|
|
||||||
"html"
|
|
||||||
],
|
|
||||||
"vetur.format.enable": false,
|
|
||||||
"vetur.completion.scaffoldSnippetSources": {
|
|
||||||
"user": "🗒️",
|
|
||||||
"workspace": "💼",
|
|
||||||
"vetur": ""
|
|
||||||
},
|
|
||||||
"prettier.disableLanguages": [],
|
|
||||||
|
|
||||||
// ===
|
|
||||||
// HTML
|
|
||||||
// ===
|
|
||||||
|
|
||||||
"html.format.enable": false,
|
|
||||||
"vetur.validation.template": false,
|
|
||||||
"emmet.triggerExpansionOnTab": true,
|
|
||||||
"emmet.includeLanguages": {
|
|
||||||
"vue-html": "html"
|
|
||||||
},
|
|
||||||
"vetur.completion.tagCasing": "initial",
|
|
||||||
|
|
||||||
// ===
|
|
||||||
// JS(ON)
|
|
||||||
// ===
|
|
||||||
|
|
||||||
"jest.autoEnable": false,
|
|
||||||
"jest.enableCodeLens": false,
|
|
||||||
"javascript.format.enable": false,
|
|
||||||
"json.format.enable": false,
|
|
||||||
"vetur.validation.script": false,
|
|
||||||
|
|
||||||
// ===
|
|
||||||
// CSS
|
|
||||||
// ===
|
|
||||||
|
|
||||||
"stylelint.enable": true,
|
|
||||||
"css.validate": false,
|
|
||||||
"scss.validate": false,
|
|
||||||
"vetur.validation.style": false,
|
|
||||||
|
|
||||||
// ===
|
|
||||||
// MARKDOWN
|
|
||||||
// ===
|
|
||||||
|
|
||||||
"[markdown]": {
|
|
||||||
"editor.wordWrap": "wordWrapColumn",
|
|
||||||
"editor.wordWrapColumn": 80
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,10 +6,9 @@ WORKDIR /app
|
|||||||
|
|
||||||
# Copy dependency-related files
|
# Copy dependency-related files
|
||||||
COPY package.json ./
|
COPY package.json ./
|
||||||
COPY yarn.lock ./
|
|
||||||
|
|
||||||
# Install project dependencies
|
# Install project dependencies
|
||||||
RUN yarn install
|
RUN npm install
|
||||||
|
|
||||||
# Expose ports 8080, which the dev server will be bound to
|
# Expose ports 8080, which the dev server will be bound to
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|||||||
30847
ui/package-lock.json
generated
30847
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
102
ui/package.json
102
ui/package.json
@@ -6,91 +6,81 @@
|
|||||||
"dev": "vue-cli-service serve",
|
"dev": "vue-cli-service serve",
|
||||||
"dev:e2e": "cross-env VUE_APP_TEST=e2e vue-cli-service test:e2e --mode=development",
|
"dev:e2e": "cross-env VUE_APP_TEST=e2e vue-cli-service test:e2e --mode=development",
|
||||||
"build": "vue-cli-service build --modern",
|
"build": "vue-cli-service build --modern",
|
||||||
"build:ci": "yarn build --report",
|
"build:ci": "npm build --report",
|
||||||
"lint:eslint": "eslint --fix",
|
"lint:eslint": "eslint --fix",
|
||||||
"lint:stylelint": "stylelint --fix",
|
"lint:stylelint": "stylelint --fix",
|
||||||
"lint:markdownlint": "markdownlint",
|
"lint:markdownlint": "markdownlint",
|
||||||
"lint:prettier": "prettier --write --loglevel warn",
|
"lint:prettier": "prettier --write --loglevel warn",
|
||||||
"lint:all:eslint": "yarn lint:eslint --ext .js,.vue .",
|
|
||||||
"lint:all:stylelint": "yarn lint:stylelint \"src/**/*.{vue,scss}\"",
|
|
||||||
"lint:all:markdownlint": "yarn lint:markdownlint \"docs/*.md\" \"*.md\"",
|
|
||||||
"lint:all:prettier": "yarn lint:prettier \"**/*.{js,json,css,scss,vue,html,md}\"",
|
|
||||||
"lint": "run-s lint:all:*",
|
"lint": "run-s lint:all:*",
|
||||||
"test:unit": "cross-env VUE_APP_TEST=unit vue-cli-service test:unit",
|
"test:unit": "cross-env VUE_APP_TEST=unit vue-cli-service test:unit",
|
||||||
"test:unit:file": "yarn test:unit --bail --findRelatedTests",
|
|
||||||
"test:unit:watch": "yarn test:unit --watch --notify --notifyMode change",
|
|
||||||
"test:unit:ci": "yarn test:unit --coverage --ci",
|
|
||||||
"test:e2e": "cross-env VUE_APP_TEST=e2e vue-cli-service test:e2e --headless",
|
"test:e2e": "cross-env VUE_APP_TEST=e2e vue-cli-service test:e2e --headless",
|
||||||
"test": "run-s test:unit test:e2e",
|
"test": "run-s test:unit test:e2e",
|
||||||
"test:ci": "run-s test:unit:ci test:e2e",
|
"test:ci": "run-s test:unit:ci test:e2e",
|
||||||
"new": "cross-env HYGEN_TMPLS=generators hygen new",
|
"new": "cross-env HYGEN_TMPLS=generators hygen new",
|
||||||
"docs": "vuepress dev",
|
"docs": "vuepress dev"
|
||||||
"docker": "docker-compose exec dev yarn"
|
|
||||||
},
|
},
|
||||||
"gitHooks": {
|
"gitHooks": {
|
||||||
"pre-commit": "cross-env PRE_COMMIT=true lint-staged"
|
"pre-commit": "cross-env PRE_COMMIT=true lint-staged"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
"@fortawesome/fontawesome-svg-core": "^6.3.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.12.1",
|
"@fortawesome/free-solid-svg-icons": "^6.3.0",
|
||||||
"@fortawesome/vue-fontawesome": "0.1.9",
|
"@fortawesome/vue-fontawesome": "^2.0.10",
|
||||||
"axios": "^0.27.2",
|
"axios": "^1.3.2",
|
||||||
"buefy": "^0.9.7",
|
"buefy": "^0.9.22",
|
||||||
"chart.js": "^2.9.4",
|
"chart.js": "^2.9.4",
|
||||||
"core-js": "3.6.4",
|
"core-js": "^3.27.2",
|
||||||
"currency-formatter": "^1.5.7",
|
"currency-formatter": "^1.5.9",
|
||||||
"date-fns": "2.10.0",
|
"date-fns": "^2.29.3",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"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",
|
||||||
"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",
|
||||||
"vue-meta": "2.3.3",
|
"vue-meta": "^2.4.0",
|
||||||
"vue-router": "3.1.6",
|
"vue-router": "^3.6.5",
|
||||||
"vuex": "3.1.2"
|
"vuex": "^3.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "4.2.x",
|
"@vue/cli-plugin-babel": "^4.5.19",
|
||||||
"@vue/cli-plugin-eslint": "4.2.x",
|
"@vue/cli-plugin-eslint": "^4.5.19",
|
||||||
"@vue/cli-plugin-unit-jest": "4.2.x",
|
"@vue/cli-plugin-unit-jest": "^4.5.19",
|
||||||
"@vue/cli-service": "4.2.x",
|
"@vue/cli-service": "^4.5.19",
|
||||||
"@vue/eslint-config-prettier": "6.0.x",
|
"@vue/eslint-config-prettier": "6.0.x",
|
||||||
"@vue/eslint-config-standard": "5.1.x",
|
"@vue/eslint-config-standard": "^5.1.1",
|
||||||
"@vue/test-utils": "1.0.0-beta.31",
|
"@vue/test-utils": "^1.3.4",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-eslint": "10.1.x",
|
"babel-eslint": "10.1.x",
|
||||||
"cross-env": "7.0.x",
|
"cross-env": "^7.0.1",
|
||||||
"eslint": "6.8.x",
|
"eslint": "^6.8.0",
|
||||||
"eslint-plugin-import": "2.20.x",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"eslint-plugin-node": "11.0.x",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "4.2.x",
|
"eslint-plugin-promise": "^4.3.1",
|
||||||
"eslint-plugin-standard": "4.0.x",
|
"eslint-plugin-standard": "^5.0.0",
|
||||||
"eslint-plugin-vue": "6.2.x",
|
"eslint-plugin-vue": "^9.9.0",
|
||||||
"express": "4.17.x",
|
"express": "^4.18.2",
|
||||||
"hygen": "4.0.x",
|
"hygen": "^6.2.11",
|
||||||
"imagemin-lint-staged": "0.4.x",
|
"imagemin-lint-staged": "^0.5.1",
|
||||||
"lint-staged": "10.0.x",
|
"lint-staged": "^13.1.1",
|
||||||
"markdownlint-cli": "^0.31.1",
|
"markdownlint-cli": "^0.33.0",
|
||||||
"npm-run-all": "4.1.x",
|
"npm-run-all": "^4.1.1",
|
||||||
"sass": "1.26.x",
|
"sass": "^1.58.0",
|
||||||
"sass-loader": "8.0.x",
|
"sass-loader": "^8.0.2",
|
||||||
"stylelint": "13.2.x",
|
"stylelint": "^14.16.1",
|
||||||
"stylelint-config-css-modules": "2.2.x",
|
"stylelint-config-css-modules": "^4.1.0",
|
||||||
"stylelint-config-prettier": "8.0.x",
|
"stylelint-config-prettier": "^9.0.4",
|
||||||
"stylelint-config-recess-order": "2.0.x",
|
"stylelint-config-standard": "^29.0.0",
|
||||||
"stylelint-config-standard": "20.0.x",
|
"stylelint-scss": "^4.3.0",
|
||||||
"stylelint-scss": "3.14.x",
|
"vue-template-compiler": "^2.6.11",
|
||||||
"vue-template-compiler": "2.6.11",
|
"vuepress": "^1.9.8"
|
||||||
"vuepress": "1.3.x"
|
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@vue/cli-plugin-unit-jest/jest": "25.1.x",
|
"@vue/cli-plugin-unit-jest/jest": "25.1.x",
|
||||||
"@vue/cli-plugin-unit-jest/babel-jest": "25.1.x"
|
"@vue/cli-plugin-unit-jest/babel-jest": "25.1.x"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.13.3",
|
"node": ">=16.0.0"
|
||||||
"yarn": ">=1.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
<link rel="shortcut icon" href="<%= webpackConfig.output.publicPath %>hammond.png" />
|
<link rel="shortcut icon" href="<%= webpackConfig.output.publicPath %>hammond.png" />
|
||||||
<link rel="apple-touch-icon" href="<%= webpackConfig.output.publicPath %>touch-icon.png" />
|
<link rel="apple-touch-icon" href="<%= webpackConfig.output.publicPath %>touch-icon.png" />
|
||||||
<title><%= webpackConfig.name %></title>
|
<title><%= webpackConfig.name %></title>
|
||||||
|
<!-- Temporary until fontawesome 6 is supported in buefy (see issue: https://github.com/FortAwesome/Font-Awesome/issues/18663) -->
|
||||||
|
<style>
|
||||||
|
.icon svg { width: 1em; height: 1em; max-width: 80%; max-height: 80%; }
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- This is where our app will be mounted. -->
|
<!-- This is where our app will be mounted. -->
|
||||||
|
|||||||
@@ -95,11 +95,10 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<b-button
|
<b-button
|
||||||
tag="input"
|
tag="button"
|
||||||
native-type="submit"
|
native-type="submit"
|
||||||
:disabled="tryingToCreate"
|
:disabled="tryingToCreate"
|
||||||
type="is-primary"
|
type="is-primary"
|
||||||
:value="this.$t('uploadfile')"
|
|
||||||
class="control"
|
class="control"
|
||||||
>
|
>
|
||||||
{{ $t('uploadfile') }}
|
{{ $t('uploadfile') }}
|
||||||
|
|||||||
@@ -3,9 +3,20 @@ import { Line } from 'vue-chartjs'
|
|||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
import { string } from 'yargs'
|
||||||
export default {
|
export default {
|
||||||
extends: Line,
|
extends: Line,
|
||||||
props: { vehicle: { type: Object, required: true }, since: { type: Date, default: '' }, user: { type: Object, required: true } },
|
props: {
|
||||||
|
vehicle: { type: Object, required: true },
|
||||||
|
since: { type: Date, default: '' },
|
||||||
|
user: { type: Object, required: true },
|
||||||
|
mileageOption: { type: string, default: 'litre_100km' },
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
chartData: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState('utils', ['isMobile']),
|
...mapState('utils', ['isMobile']),
|
||||||
},
|
},
|
||||||
@@ -17,20 +28,28 @@ export default {
|
|||||||
this.fetchMileage()
|
this.fetchMileage()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
chartData: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchMileage()
|
this.fetchMileage()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showChart() {
|
showChart() {
|
||||||
|
let mileageLabel = ''
|
||||||
|
switch (this.mileageOption) {
|
||||||
|
case 'litre_100km':
|
||||||
|
mileageLabel = 'L/100km'
|
||||||
|
break
|
||||||
|
case 'km_litre':
|
||||||
|
mileageLabel = 'km/L'
|
||||||
|
break
|
||||||
|
case 'mpg':
|
||||||
|
mileageLabel = 'mpg'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
var labels = this.chartData.map((x) => x.date.substr(0, 10))
|
var labels = this.chartData.map((x) => x.date.substr(0, 10))
|
||||||
var dataset = {
|
var dataset = {
|
||||||
steppedLine: true,
|
steppedLine: true,
|
||||||
label: `${this.$t('odometer')} (${this.$t('unit.short.' + this.user.distanceUnitDetail.key)}/${this.$t('unit.short.' + this.vehicle.fuelUnitDetail.key)})`,
|
label: `Mileage (${mileageLabel})`,
|
||||||
fill: true,
|
fill: true,
|
||||||
data: this.chartData.map((x) => x.mileage),
|
data: this.chartData.map((x) => x.mileage),
|
||||||
}
|
}
|
||||||
@@ -41,6 +60,7 @@ export default {
|
|||||||
.get(`/api/vehicles/${this.vehicle.id}/mileage`, {
|
.get(`/api/vehicles/${this.vehicle.id}/mileage`, {
|
||||||
params: {
|
params: {
|
||||||
since: this.since,
|
since: this.since,
|
||||||
|
mileageOption: this.mileageOption,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default {
|
|||||||
<b-select
|
<b-select
|
||||||
v-if="unprocessedQuickEntries.length"
|
v-if="unprocessedQuickEntries.length"
|
||||||
v-model="quickEntry"
|
v-model="quickEntry"
|
||||||
:placeholder="this.$t('referquickentry')"
|
:placeholder="$t('referquickentry')"
|
||||||
expanded
|
expanded
|
||||||
@input="showQuickEntry($event)"
|
@input="showQuickEntry($event)"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export default {
|
|||||||
<div class="box" style="max-width:600px">
|
<div class="box" style="max-width:600px">
|
||||||
<h1 class="subtitle">{{ $t('share') }} {{ vehicle.nickname }}</h1>
|
<h1 class="subtitle">{{ $t('share') }} {{ vehicle.nickname }}</h1>
|
||||||
<section>
|
<section>
|
||||||
<div class="columns is-mobile" v-for="model in models" :key="model.id">
|
<div v-for="model in models" :key="model.id" class="columns is-mobile">
|
||||||
<div class="column is-one-third">
|
<div class="column is-one-third">
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-switch v-model="model.isShared" :disabled="model.isOwner" @input="changeShareStatus(model)">
|
<b-switch v-model="model.isShared" :disabled="model.isOwner" @input="changeShareStatus(model)">
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ $size-input-padding-vertical: 0.75em;
|
|||||||
$size-input-padding-horizontal: 1em;
|
$size-input-padding-horizontal: 1em;
|
||||||
$size-input-padding: $size-input-padding-vertical $size-input-padding-horizontal;
|
$size-input-padding: $size-input-padding-vertical $size-input-padding-horizontal;
|
||||||
$size-input-border: 1px;
|
$size-input-border: 1px;
|
||||||
$size-input-border-radius: (1em + $size-input-padding-vertical * 2) / 10;
|
$size-input-border-radius: calc((1em + $size-input-padding-vertical * 2) / 10);
|
||||||
|
|
||||||
// BUTTONS
|
// BUTTONS
|
||||||
$size-button-padding-vertical: $size-grid-padding / 2;
|
$size-button-padding-vertical: calc($size-grid-padding / 2);
|
||||||
$size-button-padding-horizontal: $size-grid-padding / 1.5;
|
$size-button-padding-horizontal: calc($size-grid-padding / 1.5);
|
||||||
$size-button-padding: $size-button-padding-vertical
|
$size-button-padding: $size-button-padding-vertical
|
||||||
$size-button-padding-horizontal;
|
$size-button-padding-horizontal;
|
||||||
|
|||||||
@@ -147,7 +147,7 @@
|
|||||||
$max-screen,
|
$max-screen,
|
||||||
$max-value
|
$max-value
|
||||||
) {
|
) {
|
||||||
$a: ($max-value - $min-value) / ($max-screen - $min-screen);
|
$a: calc(($max-value - $min-value) / ($max-screen - $min-screen));
|
||||||
$b: $min-value - $a * $min-screen;
|
$b: $min-value - $a * $min-screen;
|
||||||
|
|
||||||
$sign: '+';
|
$sign: '+';
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"addattachment": "Anhang hinzufügen",
|
"addattachment": "Anhang hinzufügen",
|
||||||
"sharedwith": "Geteilt mit",
|
"sharedwith": "Geteilt mit",
|
||||||
"share": "Teile",
|
"share": "Teile",
|
||||||
"you": "Sie",
|
"you": "du",
|
||||||
"addfillup": "Tankfüllung erfassen",
|
"addfillup": "Tankfüllung erfassen",
|
||||||
"createfillup": "Erfasse Tankfüllung",
|
"createfillup": "Erfasse Tankfüllung",
|
||||||
"deletefillup": "Lösche diese Tankfüllung",
|
"deletefillup": "Lösche diese Tankfüllung",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"changepassword": "Passwort ändern",
|
"changepassword": "Passwort ändern",
|
||||||
"oldpassword": "Bisheriges Passwort",
|
"oldpassword": "Bisheriges Passwort",
|
||||||
"newpassword": "Neues Passwort",
|
"newpassword": "Neues Passwort",
|
||||||
"repeatnewpassword": "Neues Passwort wiederhiolen",
|
"repeatnewpassword": "Neues Passwort wiederholen",
|
||||||
"passworddontmatch": "Passwörter stimmen nicht überein",
|
"passworddontmatch": "Passwörter stimmen nicht überein",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"supportthedeveloper": "Unterstütze den Entwickler",
|
"supportthedeveloper": "Unterstütze den Entwickler",
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"totalexpenses": "Gesamtausgaben",
|
"totalexpenses": "Gesamtausgaben",
|
||||||
"fillupcost": "Tank Ausgaben",
|
"fillupcost": "Tank-Ausgaben",
|
||||||
"otherexpenses": "Andere Ausgaben",
|
"otherexpenses": "Andere Ausgaben",
|
||||||
"addvehicle": "Fahrzeug hinzufügen",
|
"addvehicle": "Fahrzeug hinzufügen",
|
||||||
"editvehicle": "Fahrzeug bearbeiten",
|
"editvehicle": "Fahrzeug bearbeiten",
|
||||||
@@ -124,17 +124,17 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"importdata": "Importiere Daten in Hammond",
|
"importdata": "Importiere Daten in Hammond",
|
||||||
"importdatadesc": "Wähle eine der folgenden Optionen um Daten in Hammond zu importieren",
|
"importdatadesc": "Wähle eine der folgenden Optionen, um Daten in Hammond zu importieren",
|
||||||
"import": "Importieren",
|
"import": "Importieren",
|
||||||
"importcsv": "Wenn du {name} nutzt um deine Fahrzeugdaten zu verwalten, exportiere die CSV Datei aus {name} und klicke hier zum importieren.",
|
"importcsv": "Wenn du {name} nutzt, um deine Fahrzeugdaten zu verwalten, exportiere die CSV Datei aus {name} und klicke hier, um zu importieren.",
|
||||||
"choosecsv": "CSV auswählen",
|
"choosecsv": "CSV auswählen",
|
||||||
"choosephoto": "Foto auswählen",
|
"choosephoto": "Foto auswählen",
|
||||||
"importsuccessfull": "Daten erfolgreich importiert",
|
"importsuccessfull": "Daten erfolgreich importiert",
|
||||||
"importerror": "Beim importieren der Datei ist ein Fehler aufgetreten. Details findest du in der Fehlermeldung",
|
"importerror": "Beim Importieren der Datei ist ein Fehler aufgetreten. Details findest du in der Fehlermeldung",
|
||||||
"importfrom": "Importiere von {name}",
|
"importfrom": "Importiere von {name}",
|
||||||
"stepstoimport": "Schritte um Daten aus {name} zu importieren",
|
"stepstoimport": "Schritte, um Daten aus {name} zu importieren",
|
||||||
"choosecsvimport": "Wähle die {name} CSV aus und klicke den Button zum importieren.",
|
"choosecsvimport": "Wähle die {name} CSV aus und klicke den Button, um zu importieren.",
|
||||||
"dontimportagain": "Achte darauf, dass du die Datei nicht erneut importierst, da dies zu wiederholten 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 CSV-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.",
|
||||||
@@ -156,11 +156,11 @@
|
|||||||
"created": "Erstellt",
|
"created": "Erstellt",
|
||||||
"createnewuser": "Erstelle neuen Benutzer",
|
"createnewuser": "Erstelle neuen Benutzer",
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"novehicles": "Du hast noch kein Fahrzeug erstellt. Lege jetzt einen Eintrag für das zu Verwaltende Auto an.",
|
"novehicles": "Du hast noch kein Fahrzeug erstellt. Lege jetzt einen Eintrag für das zu verwaltende Fahrzeug an.",
|
||||||
"processed": "Bearbeitet",
|
"processed": "Bearbeitet",
|
||||||
"notfound": "Nicht gefunden",
|
"notfound": "Nicht gefunden",
|
||||||
"timeout": "Das Laden der Seite hat eine Zeitüberschreitung verursacht. Bist du sicher, dass du noch mit dem Internet verbunden bist?",
|
"timeout": "Das Laden der Seite hat eine Zeitüberschreitung verursacht. Bist du sicher, dass du noch mit dem Internet verbunden bist?",
|
||||||
"clicktoselect": "Klicke zum auswählen...",
|
"clicktoselect": "Klicke, um auszuwählen...",
|
||||||
"expenseby": "Ausgabe von",
|
"expenseby": "Ausgabe von",
|
||||||
"selectvehicle": "Wähle ein Fahrzeug aus",
|
"selectvehicle": "Wähle ein Fahrzeug aus",
|
||||||
"expensedate": "Datum der Ausgabe",
|
"expensedate": "Datum der Ausgabe",
|
||||||
@@ -168,11 +168,11 @@
|
|||||||
"fillmoredetails": "Weitere Details ausfüllen",
|
"fillmoredetails": "Weitere Details ausfüllen",
|
||||||
"markquickentryprocessed": "Markiere gewählten Schnelleintrag als bearbeitet",
|
"markquickentryprocessed": "Markiere gewählten Schnelleintrag als bearbeitet",
|
||||||
"referquickentry": "Wähle Schnelleintrag",
|
"referquickentry": "Wähle Schnelleintrag",
|
||||||
"deletequickentry": "Willst du diesen Schnelleintrag wirklcih Löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
|
"deletequickentry": "Willst du diesen Schnelleintrag wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden!",
|
||||||
"fuelunit": "Kraftstoffeinheit",
|
"fuelunit": "Kraftstoffeinheit",
|
||||||
"fillingstation": "Tankstelle",
|
"fillingstation": "Tankstelle",
|
||||||
"comments": "Kommentare",
|
"comments": "Kommentare",
|
||||||
"missfillupbefore": "Hast du vergessen die vorherige Tankfüllung zu erfassen?",
|
"missfillupbefore": "Hast du vergessen, die vorherige Tankfüllung zu erfassen?",
|
||||||
"fillupdate": "Tankdatum",
|
"fillupdate": "Tankdatum",
|
||||||
"fillupsavedsuccessfully": "Tankfüllung erfolgreich gespeichert",
|
"fillupsavedsuccessfully": "Tankfüllung erfolgreich gespeichert",
|
||||||
"expensesavedsuccessfully": "Ausgabe erfolgreich gespeichert",
|
"expensesavedsuccessfully": "Ausgabe erfolgreich gespeichert",
|
||||||
@@ -184,7 +184,7 @@
|
|||||||
"make": "Marke",
|
"make": "Marke",
|
||||||
"model": "Modell",
|
"model": "Modell",
|
||||||
"yearmanufacture": "Jahr der Erstzulassung",
|
"yearmanufacture": "Jahr der Erstzulassung",
|
||||||
"enginesize": "Hubraum (in cc)",
|
"enginesize": "Hubraum (in ccm)",
|
||||||
"testconn": "Teste Verbindung",
|
"testconn": "Teste Verbindung",
|
||||||
"migrate": "Migrieren",
|
"migrate": "Migrieren",
|
||||||
"init": {
|
"init": {
|
||||||
@@ -199,7 +199,7 @@
|
|||||||
"fresh": {
|
"fresh": {
|
||||||
"setupadminuser": "Erstelle einen Administrator",
|
"setupadminuser": "Erstelle einen Administrator",
|
||||||
"yourpassword": "Dein Passwort",
|
"yourpassword": "Dein Passwort",
|
||||||
"youremail": "Deine E-Mail Adresse",
|
"youremail": "Deine E-Mail-Adresse",
|
||||||
"yourname": "Dein Name",
|
"yourname": "Dein Name",
|
||||||
"success": "Du hast dich erfolgreich registriert. Du wirst in kürze zur Anmeldung weitergeleitet und kannst Anfangen Hammond zu verwenden."
|
"success": "Du hast dich erfolgreich registriert. Du wirst in kürze zur Anmeldung weitergeleitet und kannst Anfangen Hammond zu verwenden."
|
||||||
}
|
}
|
||||||
@@ -214,4 +214,4 @@
|
|||||||
"disable": "Sperren",
|
"disable": "Sperren",
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"labelforfile": "Bezeichnung für diese Datei"
|
"labelforfile": "Bezeichnung für diese Datei"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import '@components/_globals'
|
|||||||
import 'buefy/dist/buefy.css'
|
import 'buefy/dist/buefy.css'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
|
|
||||||
Vue.component('vue-fontawesome', FontAwesomeIcon)
|
|
||||||
library.add(
|
library.add(
|
||||||
faCheck,
|
faCheck,
|
||||||
faTimes,
|
faTimes,
|
||||||
@@ -54,7 +53,8 @@ library.add(
|
|||||||
faShare,
|
faShare,
|
||||||
faUserFriends,
|
faUserFriends,
|
||||||
faTimesCircle
|
faTimesCircle
|
||||||
)
|
);
|
||||||
|
Vue.component('VueFontawesome', FontAwesomeIcon)
|
||||||
|
|
||||||
Vue.use(Buefy, {
|
Vue.use(Buefy, {
|
||||||
defaultIconComponent: 'vue-fontawesome',
|
defaultIconComponent: 'vue-fontawesome',
|
||||||
|
|||||||
@@ -410,6 +410,15 @@ export default [
|
|||||||
},
|
},
|
||||||
props: (route) => ({ user: store.state.auth.currentUser || {} }),
|
props: (route) => ({ user: store.state.auth.currentUser || {} }),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/import/drivvo',
|
||||||
|
name: 'import-drivvo',
|
||||||
|
component: () => lazyLoadView(import('@views/import-drivvo.vue')),
|
||||||
|
meta: {
|
||||||
|
authRequired: true,
|
||||||
|
},
|
||||||
|
props: (route) => ({ user: store.state.auth.currentUser || {} }),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/logout',
|
path: '/logout',
|
||||||
name: 'logout',
|
name: 'logout',
|
||||||
|
|||||||
@@ -162,41 +162,41 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form @submit.prevent="createExpense">
|
<form @submit.prevent="createExpense">
|
||||||
<b-field :label="this.$t('selectvehicle')">
|
<b-field :label="$t('selectvehicle')">
|
||||||
<b-select v-model="selectedVehicle" :placeholder="this.$t('vehicle')" required expanded :disabled="expense.id">
|
<b-select v-model="selectedVehicle" :placeholder="$t('vehicle')" required expanded :disabled="expense.id">
|
||||||
<option v-for="option in myVehicles" :key="option.id" :value="option">
|
<option v-for="option in myVehicles" :key="option.id" :value="option">
|
||||||
{{ option.nickname }}
|
{{ option.nickname }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('expenseby')">
|
<b-field :label="$t('expenseby')">
|
||||||
<b-select v-model="expenseModel.userId" :placeholder="this.$t('user')" required expanded :disabled="expense.id">
|
<b-select v-model="expenseModel.userId" :placeholder="$t('user')" required expanded :disabled="expense.id">
|
||||||
<option v-for="option in users" :key="option.userId" :value="option.userId">
|
<option v-for="option in users" :key="option.userId" :value="option.userId">
|
||||||
{{ option.name }}
|
{{ option.name }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('expensedate')">
|
<b-field :label="$t('expensedate')">
|
||||||
<b-datepicker
|
<b-datepicker
|
||||||
v-model="expenseModel.date"
|
v-model="expenseModel.date"
|
||||||
:date-formatter="formatDate"
|
:date-formatter="formatDate"
|
||||||
:placeholder="this.$t('clicktoselect')"
|
:placeholder="$t('clicktoselect')"
|
||||||
icon="calendar"
|
icon="calendar"
|
||||||
:max-date="new Date()"
|
:max-date="new Date()"
|
||||||
>
|
>
|
||||||
</b-datepicker>
|
</b-datepicker>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('expensetype') + `*`">
|
<b-field :label="$t('expensetype') + `*`">
|
||||||
<b-input v-model="expenseModel.expenseType" expanded required></b-input>
|
<b-input v-model="expenseModel.expenseType" expanded required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field :label="this.$t('totalamountpaid')">
|
<b-field :label="$t('totalamountpaid')">
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<span class="button is-static">{{ me.currency }}</span>
|
<span class="button is-static">{{ me.currency }}</span>
|
||||||
</p>
|
</p>
|
||||||
<b-input v-model.number="expenseModel.amount" type="number" min="0" expanded step=".001" required></b-input>
|
<b-input v-model.number="expenseModel.amount" type="number" min="0" expanded step=".001" required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('odometer')">
|
<b-field :label="$t('odometer')">
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<span class="button is-static">{{ $t('unit.short.' + me.distanceUnitDetail.key) }}</span>
|
<span class="button is-static">{{ $t('unit.short.' + me.distanceUnitDetail.key) }}</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -207,7 +207,7 @@ export default {
|
|||||||
<b-switch v-model="showMore">{{ $t('fillmoredetails') }}</b-switch>
|
<b-switch v-model="showMore">{{ $t('fillmoredetails') }}</b-switch>
|
||||||
</b-field>
|
</b-field>
|
||||||
<fieldset v-if="showMore">
|
<fieldset v-if="showMore">
|
||||||
<b-field :label="this.$t('details')">
|
<b-field :label="$t('details')">
|
||||||
<b-input v-model="expenseModel.comments" type="textarea" expanded></b-input>
|
<b-input v-model="expenseModel.comments" type="textarea" expanded></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -216,7 +216,7 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<br />
|
<br />
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-button tag="input" native-type="submit" :value="this.$t('save')" :disabled="tryingToCreate" type="is-primary" label="Create Expense" expanded> </b-button>
|
<b-button tag="button" native-type="submit" :value="$t('save')" :disabled="tryingToCreate" type="is-primary" label="Create Expense" expanded/>
|
||||||
</b-field>
|
</b-field>
|
||||||
</form>
|
</form>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -193,21 +193,21 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form class="" @submit.prevent="createFillup">
|
<form class="" @submit.prevent="createFillup">
|
||||||
<b-field :label="this.$t('selectvehicle')">
|
<b-field :label="$t('selectvehicle')">
|
||||||
<b-select v-model="selectedVehicle" :placeholder="this.$t('vehicle')" required expanded :disabled="fillup.id">
|
<b-select v-model="selectedVehicle" :placeholder="$t('vehicle')" required expanded :disabled="fillup.id">
|
||||||
<option v-for="option in myVehicles" :key="option.id" :value="option">
|
<option v-for="option in myVehicles" :key="option.id" :value="option">
|
||||||
{{ option.nickname }}
|
{{ option.nickname }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('expenseby')">
|
<b-field :label="$t('expenseby')">
|
||||||
<b-select v-model="fillupModel.userId" :placeholder="this.$t('user')" required expanded :disabled="fillup.id">
|
<b-select v-model="fillupModel.userId" :placeholder="$t('user')" required expanded :disabled="fillup.id">
|
||||||
<option v-for="option in users" :key="option.userId" :value="option.userId">
|
<option v-for="option in users" :key="option.userId" :value="option.userId">
|
||||||
{{ option.name }}
|
{{ option.name }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('fillupdate')">
|
<b-field :label="$t('fillupdate')">
|
||||||
<b-datepicker
|
<b-datepicker
|
||||||
v-model="fillupModel.date"
|
v-model="fillupModel.date"
|
||||||
:date-formatter="formatDate"
|
:date-formatter="formatDate"
|
||||||
@@ -218,7 +218,7 @@ export default {
|
|||||||
>
|
>
|
||||||
</b-datepicker>
|
</b-datepicker>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('fuelsubtype')">
|
<b-field :label="$t('fuelsubtype')">
|
||||||
<b-autocomplete
|
<b-autocomplete
|
||||||
v-model="fillupModel.fuelSubType"
|
v-model="fillupModel.fuelSubType"
|
||||||
:data="filteredFuelSubtypes"
|
:data="filteredFuelSubtypes"
|
||||||
@@ -229,27 +229,27 @@ export default {
|
|||||||
>
|
>
|
||||||
</b-autocomplete>
|
</b-autocomplete>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('quantity') + `*`" addons>
|
<b-field :label="$t('quantity') + `*`" addons>
|
||||||
<b-input v-model.number="fillupModel.fuelQuantity" type="number" step=".001" min="0" expanded required></b-input>
|
<b-input v-model.number="fillupModel.fuelQuantity" type="number" step=".001" min="0" expanded required></b-input>
|
||||||
<b-select v-model="fillupModel.fuelUnit" :placeholder="this.$t('fuelunit')" required>
|
<b-select v-model="fillupModel.fuelUnit" :placeholder="$t('fuelunit')" required>
|
||||||
<option v-for="(option, key) in fuelUnitMasters" :key="key" :value="key">
|
<option v-for="(option, key) in fuelUnitMasters" :key="key" :value="key">
|
||||||
{{ $t('unit.long.' + option.key) }}
|
{{ $t('unit.long.' + option.key) }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('per', { '0': this.$t('price'), '1': $t('unit.short.' + vehicle.fuelUnitDetail.key) })"
|
<b-field :label="$t('per', { '0': $t('price'), '1': $t('unit.short.' + vehicle.fuelUnitDetail.key) })"
|
||||||
><p class="control">
|
><p class="control">
|
||||||
<span class="button is-static">{{ me.currency }}</span>
|
<span class="button is-static">{{ me.currency }}</span>
|
||||||
</p>
|
</p>
|
||||||
<b-input v-model.number="fillupModel.perUnitPrice" type="number" min="0" step=".001" expanded required></b-input>
|
<b-input v-model.number="fillupModel.perUnitPrice" type="number" min="0" step=".001" expanded required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('totalamountpaid')">
|
<b-field :label="$t('totalamountpaid')">
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<span class="button is-static">{{ me.currency }}</span>
|
<span class="button is-static">{{ me.currency }}</span>
|
||||||
</p>
|
</p>
|
||||||
<b-input v-model.number="fillupModel.totalAmount" type="number" min="0" step=".001" expanded required></b-input>
|
<b-input v-model.number="fillupModel.totalAmount" type="number" min="0" step=".001" expanded required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('odometer')">
|
<b-field :label="$t('odometer')">
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<span class="button is-static">{{ $t('unit.short.' + me.distanceUnitDetail.key) }}</span>
|
<span class="button is-static">{{ $t('unit.short.' + me.distanceUnitDetail.key) }}</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -265,10 +265,10 @@ export default {
|
|||||||
<b-switch v-model="showMore">{{ $t('fillmoredetails') }}</b-switch>
|
<b-switch v-model="showMore">{{ $t('fillmoredetails') }}</b-switch>
|
||||||
</b-field>
|
</b-field>
|
||||||
<fieldset v-if="showMore">
|
<fieldset v-if="showMore">
|
||||||
<b-field :label="this.$t('fillingstation')">
|
<b-field :label="$t('fillingstation')">
|
||||||
<b-input v-model="fillupModel.fillingStation" type="text" expanded></b-input>
|
<b-input v-model="fillupModel.fillingStation" type="text" expanded></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('comments')">
|
<b-field :label="$t('comments')">
|
||||||
<b-input v-model="fillupModel.comments" type="textarea" expanded></b-input>
|
<b-input v-model="fillupModel.comments" type="textarea" expanded></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -277,7 +277,7 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<br />
|
<br />
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-button tag="input" native-type="submit" :disabled="tryingToCreate" type="is-primary" :value="this.$t('save')" :label="this.$t('createfillup')" expanded> </b-button>
|
<b-button 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>
|
||||||
|
|||||||
@@ -134,47 +134,55 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form @submit.prevent="createVehicle">
|
<form @submit.prevent="createVehicle">
|
||||||
<b-field :label="this.$t('nickname') + `*`">
|
<b-field :label="$t('nickname') + `*`">
|
||||||
<b-input v-model="vehicleModel.nickname" type="text" expanded required></b-input>
|
<b-input v-model="vehicleModel.nickname" type="text" expanded required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('registration') + `*`">
|
<b-field :label="$t('registration') + `*`">
|
||||||
<b-input v-model="vehicleModel.registration" type="text" expanded required></b-input>
|
<b-input v-model="vehicleModel.registration" type="text" expanded required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field label="VIN">
|
<b-field label="VIN">
|
||||||
<b-input v-model="vehicleModel.vin" type="text" expanded></b-input>
|
<b-input v-model="vehicleModel.vin" type="text" expanded></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('fueltype') + `*`">
|
<b-field :label="$t('fueltype') + `*`">
|
||||||
<b-select v-model.number="vehicleModel.fuelType" :placeholder="this.$t('fueltype')" required expanded>
|
<b-select v-model.number="vehicleModel.fuelType" :placeholder="$t('fueltype')" required expanded>
|
||||||
<option v-for="(option, key) in fuelTypeMasters" :key="key" :value="key">
|
<option v-for="(option, key) in fuelTypeMasters" :key="key" :value="key">
|
||||||
{{ $t('fuel.' + option.key) }}
|
{{ $t('fuel.' + option.key) }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field :label="this.$t('fuelunit') + `*`">
|
<b-field :label="$t('fuelunit') + `*`">
|
||||||
<b-select v-model.number="vehicleModel.fuelUnit" :placeholder="this.$t('fuelunit')" required expanded>
|
<b-select v-model.number="vehicleModel.fuelUnit" :placeholder="$t('fuelunit')" required expanded>
|
||||||
<option v-for="(option, key) in fuelUnitMasters" :key="key" :value="key">
|
<option v-for="(option, key) in fuelUnitMasters" :key="key" :value="key">
|
||||||
{{ $t('unit.long.' + option.key) }}
|
{{ $t('unit.long.' + option.key) }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field :label="this.$t('make') + `*`">
|
<b-field :label="$t('make') + `*`">
|
||||||
<b-input v-model="vehicleModel.make" type="text" required expanded></b-input>
|
<b-input v-model="vehicleModel.make" type="text" required expanded></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('model') + `*`">
|
<b-field :label="$t('model') + `*`">
|
||||||
<b-input v-model="vehicleModel.model" type="text" required expanded></b-input>
|
<b-input v-model="vehicleModel.model" type="text" required expanded></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('yearmanufacture') + `*`">
|
<b-field :label="$t('yearmanufacture') + `*`">
|
||||||
<b-input v-model.number="vehicleModel.yearOfManufacture" type="number" expanded number></b-input>
|
<b-input v-model.number="vehicleModel.yearOfManufacture" type="number" expanded number></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('yearmanufacture')">
|
<b-field :label="$t('enginesize')">
|
||||||
<b-input v-model.number="vehicleModel.engineSize" type="number" expanded number></b-input>
|
<b-input v-model.number="vehicleModel.engineSize" type="number" expanded number></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-button tag="input" native-type="submit" :disabled="tryingToCreate" type="is-primary" :value="this.$t('save')" :label="this.$t('createvehicle')" expanded>
|
<b-button
|
||||||
|
tag="button"
|
||||||
|
native-type="submit"
|
||||||
|
:disabled="tryingToCreate"
|
||||||
|
type="is-primary"
|
||||||
|
:value="$t('save')"
|
||||||
|
:label="$t('createvehicle')"
|
||||||
|
expanded
|
||||||
|
>
|
||||||
<BaseIcon v-if="tryingToCreate" name="sync" spin />
|
<BaseIcon v-if="tryingToCreate" name="sync" spin />
|
||||||
</b-button>
|
</b-button>
|
||||||
<p v-if="authError">
|
<p v-if="authError">
|
||||||
|
|||||||
172
ui/src/router/views/import-drivvo.vue
Normal file
172
ui/src/router/views/import-drivvo.vue
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<script>
|
||||||
|
import Layout from '@layouts/main.vue'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
page: {
|
||||||
|
title: 'Import Drivvo',
|
||||||
|
meta: [{ name: 'description', content: 'The Import Drivvo page.' }],
|
||||||
|
},
|
||||||
|
components: { Layout },
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
myVehicles: [],
|
||||||
|
file: null,
|
||||||
|
selectedVehicle: null,
|
||||||
|
tryingToCreate: false,
|
||||||
|
errors: [],
|
||||||
|
importLocation: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('utils', ['isMobile']),
|
||||||
|
...mapState('vehicles', ['vehicles']),
|
||||||
|
uploadButtonLabel() {
|
||||||
|
if (this.isMobile) {
|
||||||
|
if (this.file == null) {
|
||||||
|
return 'Choose Photo'
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.file == null) {
|
||||||
|
return 'Choose CSV'
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.myVehicles = this.vehicles
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
importDrivvo() {
|
||||||
|
console.log('Import from drivvo')
|
||||||
|
if (this.file == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.tryingToCreate = true
|
||||||
|
this.errorMessage = ''
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('vehicleID', this.selectedVehicle)
|
||||||
|
formData.append('importLocation', this.importLocation)
|
||||||
|
formData.append('file', this.file, this.file.name)
|
||||||
|
axios
|
||||||
|
.post(`/api/import/drivvo`, formData)
|
||||||
|
.then((data) => {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: 'Data Imported Successfully',
|
||||||
|
type: 'is-success',
|
||||||
|
duration: 3000,
|
||||||
|
})
|
||||||
|
this.file = null
|
||||||
|
setTimeout(() => this.$router.push({ name: 'home' }), 1000)
|
||||||
|
})
|
||||||
|
.catch((ex) => {
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
duration: 5000,
|
||||||
|
message: 'There was some issue with importing the file. Please check the error message',
|
||||||
|
position: 'is-bottom',
|
||||||
|
type: 'is-danger',
|
||||||
|
})
|
||||||
|
if (ex.response && ex.response.data.errors) {
|
||||||
|
this.errors = ex.response.data.errors
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.tryingToCreate = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Layout>
|
||||||
|
<div class="columns box">
|
||||||
|
<div class="column">
|
||||||
|
<h1 class="title">Import from Drivvo</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<p class="subtitle"> Steps to import data from Drivvo</p>
|
||||||
|
<ol>
|
||||||
|
<li>Export your data from Drivvo in the CSV format.</li>
|
||||||
|
<li>Select the vehicle the exported data is for. You may need to create the vehicle in Hammond first if you haven't already done so</li>
|
||||||
|
<li
|
||||||
|
>Make sure that the <u>Currency</u> and <u>Distance Unit</u> are set correctly in Hammond. Drivvo does not include this information in
|
||||||
|
their export, instead Hammond will use the values set for the user.</li
|
||||||
|
>
|
||||||
|
<li>Similiarly, make sure that the <u>Fuel Unit</u> and <u>Fuel Type</u> are correctly set in the Vehicle.</li>
|
||||||
|
<li>Once you have checked all these points, select the vehicle and import the CSV below.</li>
|
||||||
|
<li><b>Make sure that you do not import the file again as that will create repeat entries.</b></li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
><b>PS:</b> If you have <em>'income'</em> and <em>'trips'</em> in your export, they will not be imported to Hammond. The fields
|
||||||
|
<em>'Second fuel'</em> and <em>'Third fuel'</em> are are are also ignored as the use case for these is not understood by us. If you have a use
|
||||||
|
case for this, please open a issue on
|
||||||
|
<a href="https://github.com/akhilrex/hammond/issues">issue tracker</a>
|
||||||
|
</p>
|
||||||
|
<div class="section box">
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
<div class="column is-full"> <p class="subtitle">Choose the vehicle, then select the Drivvo CSV and press the import button.</p></div>
|
||||||
|
<div class="column is-full is-flex is-align-content-center">
|
||||||
|
<form @submit.prevent="importDrivvo">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<b-field label="Vehicle" label-position="on-border">
|
||||||
|
<b-select v-model="selectedVehicle" placeholder="Select Vehicle" required>
|
||||||
|
<option v-for="vehicle in myVehicles" :key="vehicle.id" :value="vehicle.id">{{ vehicle.nickname }}</option>
|
||||||
|
</b-select>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<b-field>
|
||||||
|
<b-tooltip label="Whether to import the location for fillups and services or not." multilined>
|
||||||
|
<b-checkbox v-model="importLocation">Import Location?</b-checkbox>
|
||||||
|
</b-tooltip>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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" :disabled="tryingToCreate" type="is-primary" class="control">
|
||||||
|
Import
|
||||||
|
</b-button>
|
||||||
|
</div></div
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
@@ -9,6 +9,19 @@ export default {
|
|||||||
meta: [{ name: 'description', content: 'The Import Fuelly page.' }],
|
meta: [{ name: 'description', content: 'The Import Fuelly page.' }],
|
||||||
},
|
},
|
||||||
components: { Layout },
|
components: { Layout },
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
file: null,
|
||||||
|
tryingToCreate: false,
|
||||||
|
errors: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState('utils', ['isMobile']),
|
...mapState('utils', ['isMobile']),
|
||||||
uploadButtonLabel() {
|
uploadButtonLabel() {
|
||||||
@@ -27,19 +40,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
props: {
|
|
||||||
user: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
file: null,
|
|
||||||
tryingToCreate: false,
|
|
||||||
errors: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
importFuelly() {
|
importFuelly() {
|
||||||
if (this.file == null) {
|
if (this.file == null) {
|
||||||
@@ -58,6 +58,7 @@ export default {
|
|||||||
duration: 3000,
|
duration: 3000,
|
||||||
})
|
})
|
||||||
this.file = null
|
this.file = null
|
||||||
|
setTimeout(() => this.$router.push({ name: 'home' }), 1000)
|
||||||
})
|
})
|
||||||
.catch((ex) => {
|
.catch((ex) => {
|
||||||
this.$buefy.toast.open({
|
this.$buefy.toast.open({
|
||||||
@@ -108,7 +109,7 @@ export default {
|
|||||||
<div class="columns"
|
<div class="columns"
|
||||||
><div class="column">
|
><div class="column">
|
||||||
<b-field class="file is-primary" :class="{ 'has-name': !!file }">
|
<b-field class="file is-primary" :class="{ 'has-name': !!file }">
|
||||||
<b-upload v-model="file" class="file-label" accept=".csv">
|
<b-upload v-model="file" class="file-label" accept=".csv" required>
|
||||||
<span class="file-cta">
|
<span class="file-cta">
|
||||||
<b-icon class="file-icon" icon="upload"></b-icon>
|
<b-icon class="file-icon" icon="upload"></b-icon>
|
||||||
<span class="file-label">{{ uploadButtonLabel }}</span>
|
<span class="file-label">{{ uploadButtonLabel }}</span>
|
||||||
@@ -120,7 +121,7 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<b-button tag="input" native-type="submit" :disabled="tryingToCreate" type="is-primary" :value="this.$t('uploadfile')" class="control">
|
<b-button tag="button" native-type="submit" :disabled="tryingToCreate" type="is-primary" class="control">
|
||||||
{{ $t('import') }}
|
{{ $t('import') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</div></div
|
</div></div
|
||||||
|
|||||||
@@ -26,11 +26,22 @@ export default {
|
|||||||
>
|
>
|
||||||
<br />
|
<br />
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="box column is-one-third" to="/import-fuelly">
|
<div class="column is-one-third">
|
||||||
<h1 class="title">Fuelly</h1>
|
<div class="box">
|
||||||
<p>{{ $t('importcsv', { 'name': 'Fuelly' }) }}</p>
|
<h1 class="title">Fuelly</h1>
|
||||||
<br />
|
<p>If you have been using Fuelly to store your vehicle data, export the CSV file from Fuelly and click here to import.</p>
|
||||||
<b-button type="is-primary" tag="router-link" to="/import/fuelly">{{ $t('import') }}</b-button>
|
<br />
|
||||||
|
<b-button type="is-primary" tag="router-link" to="/import/fuelly">{{ $t('import') }}</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-one-third" to="/import-fuelly">
|
||||||
|
<div class="box">
|
||||||
|
<h1 class="title">Drivvo</h1>
|
||||||
|
<p>{{ $t('importcsv', { 'name': 'Fuelly' }) }}</p>
|
||||||
|
<br />
|
||||||
|
<b-button type="is-primary" tag="router-link" to="/import/drivvo">{{ $t('import') }}</b-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ export default {
|
|||||||
{{ connectionError }}
|
{{ connectionError }}
|
||||||
</b-notification>
|
</b-notification>
|
||||||
|
|
||||||
<b-field addons :label="this.$t('mysqlconnstr')">
|
<b-field addons :label="$t('mysqlconnstr')">
|
||||||
<b-input v-model="url" required></b-input>
|
<b-input v-model="url" required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
@@ -200,20 +200,20 @@ export default {
|
|||||||
<div v-if="migrationMode === 'fresh'" class="box content">
|
<div v-if="migrationMode === 'fresh'" class="box content">
|
||||||
<h1 class="title">{{ $t('init.fresh.setupadminuser') }}</h1>
|
<h1 class="title">{{ $t('init.fresh.setupadminuser') }}</h1>
|
||||||
<form @submit.prevent="register">
|
<form @submit.prevent="register">
|
||||||
<b-field :label="this.$t('init.fresh.yourname')">
|
<b-field :label="$t('init.fresh.yourname')">
|
||||||
<b-input v-model="registerModel.name" required></b-input>
|
<b-input v-model="registerModel.name" required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('init.fresh.youremail')">
|
<b-field :label="$t('init.fresh.youremail')">
|
||||||
<b-input v-model="registerModel.email" type="email" required></b-input>
|
<b-input v-model="registerModel.email" type="email" required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('init.fresh.yourpassword')">
|
<b-field :label="$t('init.fresh.yourpassword')">
|
||||||
<b-input v-model="registerModel.password" type="password" required minlength="8" password-reveal></b-input>
|
<b-input v-model="registerModel.password" type="password" required minlength="8" password-reveal></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('currency')">
|
<b-field :label="$t('currency')">
|
||||||
<b-autocomplete
|
<b-autocomplete
|
||||||
v-model="registerModel.currency"
|
v-model="registerModel.currency"
|
||||||
:custom-formatter="formatCurrency"
|
:custom-formatter="formatCurrency"
|
||||||
:placeholder="this.$t('currency')"
|
:placeholder="$t('currency')"
|
||||||
:data="filteredCurrencyMasters"
|
:data="filteredCurrencyMasters"
|
||||||
:keep-first="true"
|
:keep-first="true"
|
||||||
:open-on-focus="true"
|
:open-on-focus="true"
|
||||||
@@ -221,8 +221,8 @@ export default {
|
|||||||
@select="(option) => (selected = option)"
|
@select="(option) => (selected = option)"
|
||||||
></b-autocomplete>
|
></b-autocomplete>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('distanceunit')">
|
<b-field :label="$t('distanceunit')">
|
||||||
<b-select v-model.number="registerModel.distanceUnit" :placeholder="this.$t('distanceunit')" required expanded>
|
<b-select v-model.number="registerModel.distanceUnit" :placeholder="$t('distanceunit')" required expanded>
|
||||||
<option v-for="(option, key) in distanceUnitMasters" :key="key" :value="key">
|
<option v-for="(option, key) in distanceUnitMasters" :key="key" :value="key">
|
||||||
{{ `${$t('unit.long.' + option.key)} (${$t('unit.short.' + option.key)})` }}
|
{{ `${$t('unit.long.' + option.key)} (${$t('unit.short.' + option.key)})` }}
|
||||||
</option>
|
</option>
|
||||||
@@ -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="input" :value="this.$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>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default {
|
|||||||
<b-field :label="$t('password')">
|
<b-field :label="$t('password')">
|
||||||
<b-input v-model="password" tag="b-input" name="password" type="password" :placeholder="placeholders.password" />
|
<b-input v-model="password" tag="b-input" name="password" type="password" :placeholder="placeholders.password" />
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-button tag="input" native-type="submit" :value="$t('login')" :disabled="tryingToLogIn" type="is-primary">
|
<b-button tag="button" native-type="submit" :disabled="tryingToLogIn" type="is-primary">
|
||||||
<BaseIcon v-if="tryingToLogIn" name="sync" spin />
|
<BaseIcon v-if="tryingToLogIn" name="sync" spin />
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ $t('login') }}
|
{{ $t('login') }}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -167,7 +185,7 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<br />
|
<br />
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-button tag="input" native-type="submit" :disabled="tryingToSave" type="is-primary" :value="$t('save')" expanded> </b-button>
|
<b-button tag="button" native-type="submit" :disabled="tryingToSave" type="is-primary" expanded> {{ $t('save') }} </b-button>
|
||||||
</b-field>
|
</b-field>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -185,7 +203,8 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<p v-if="!passwordValid" class="help is-danger">{{ $t('passworddontmatch') }}</p>
|
<p v-if="!passwordValid" class="help is-danger">{{ $t('passworddontmatch') }}</p>
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-button tag="input" native-type="submit" :disabled="!passwordValid" type="is-primary" :value="$t('changepassword')" expanded>
|
<b-button tag="button" native-type="submit" :disabled="!passwordValid" type="is-primary" expanded>
|
||||||
|
{{ $t('changepassword') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</b-field>
|
</b-field>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<form class="" @submit.prevent="saveSettings">
|
<form class="" @submit.prevent="saveSettings">
|
||||||
<b-field :label="this.$t('currency')">
|
<b-field :label="$t('currency')">
|
||||||
<b-select v-model="settingsModel.currency" :placeholder="this.$t('currency')" required expanded>
|
<b-select v-model="settingsModel.currency" :placeholder="$t('currency')" required expanded>
|
||||||
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
|
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
|
||||||
{{ `${option.namePlural} (${option.code})` }}
|
{{ `${option.namePlural} (${option.code})` }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('distanceunit')">
|
<b-field :label="$t('distanceunit')">
|
||||||
<b-select v-model.number="settingsModel.distanceUnit" :placeholder="this.$t('distanceunit')" required expanded>
|
<b-select v-model.number="settingsModel.distanceUnit" :placeholder="$t('distanceunit')" required expanded>
|
||||||
<option v-for="(option, key) in distanceUnitMasters" :key="key" :value="key">
|
<option v-for="(option, key) in distanceUnitMasters" :key="key" :value="key">
|
||||||
{{ `${$t('unit.long.' + option.key)} (${$t('unit.short.' + option.key)})` }}
|
{{ `${$t('unit.long.' + option.key)} (${$t('unit.short.' + option.key)})` }}
|
||||||
</option>
|
</option>
|
||||||
@@ -88,7 +88,7 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<br />
|
<br />
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-button tag="input" native-type="submit" :disabled="tryingToSave" type="is-primary" :value="this.$t('save')" expanded> </b-button>
|
<b-button tag="button" native-type="submit" :disabled="tryingToSave" type="is-primary" expanded> {{ $t('save') }}</b-button>
|
||||||
</b-field>
|
</b-field>
|
||||||
</form>
|
</form>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -138,13 +138,13 @@ export default {
|
|||||||
<div v-if="showUserForm" class="box content">
|
<div v-if="showUserForm" class="box content">
|
||||||
<h1 class="title">{{ $t('createnewuser') }}</h1>
|
<h1 class="title">{{ $t('createnewuser') }}</h1>
|
||||||
<form @submit.prevent="register">
|
<form @submit.prevent="register">
|
||||||
<b-field :label="this.$t('name')">
|
<b-field :label="$t('name')">
|
||||||
<b-input v-model="registerModel.name" required></b-input>
|
<b-input v-model="registerModel.name" required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('email')">
|
<b-field :label="$t('email')">
|
||||||
<b-input v-model="registerModel.email" type="email" required></b-input>
|
<b-input v-model="registerModel.email" type="email" required></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('password')">
|
<b-field :label="$t('password')">
|
||||||
<b-input
|
<b-input
|
||||||
v-model="registerModel.password"
|
v-model="registerModel.password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -153,24 +153,24 @@ export default {
|
|||||||
password-reveal
|
password-reveal
|
||||||
></b-input>
|
></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('role')">
|
<b-field :label="$t('role')">
|
||||||
<b-select v-model.number="registerModel.role" :placeholder="this.$t('placeholder')" required expanded>
|
<b-select v-model.number="registerModel.role" :placeholder="$t('role')" required expanded>
|
||||||
<option v-for="(option, key) in roleMasters" :key="key" :value="key">
|
<option v-for="(option, key) in roleMasters" :key="key" :value="key">
|
||||||
{{ `test` }}
|
{{ `${option.key}` }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('currency')">
|
<b-field :label="$t('currency')">
|
||||||
<b-select v-model="registerModel.currency" :placeholder="this.$t('currency')" required expanded>
|
<b-select v-model="registerModel.currency" :placeholder="$t('currency')" required expanded>
|
||||||
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
|
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
|
||||||
{{ `${option.namePlural} (${option.code})` }}
|
{{ `${option.namePlural} (${option.code})` }}
|
||||||
</option>
|
</option>
|
||||||
</b-select>
|
</b-select>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field :label="this.$t('distanceunit')">
|
<b-field :label="$t('distanceunit')">
|
||||||
<b-select
|
<b-select
|
||||||
v-model.number="registerModel.distanceUnit"
|
v-model.number="registerModel.distanceUnit"
|
||||||
:placeholder="this.$t('distanceunit')"
|
:placeholder="$t('distanceunit')"
|
||||||
required
|
required
|
||||||
expanded
|
expanded
|
||||||
>
|
>
|
||||||
@@ -181,28 +181,28 @@ export default {
|
|||||||
</b-field>
|
</b-field>
|
||||||
<br />
|
<br />
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<b-button type="is-primary" native-type="submit" tag="input" :value="this.$t('save')"></b-button>
|
<b-button type="is-primary" native-type="submit" tag="button">{{ $t('save') }}</b-button>
|
||||||
|
|
||||||
<b-button type="is-danger is-light" @click="resetUserForm">{{ $t('cancel') }}</b-button>
|
<b-button type="is-danger is-light" @click="resetUserForm">{{ $t('cancel') }}</b-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<b-table :data="users" hoverable mobile-cards detail-key="id" paginated per-page="10" :row-class="(row, index) => row.isDisabled && 'is-disabled'">
|
<b-table :data="users" hoverable mobile-cards detail-key="id" paginated per-page="10" :row-class="(row, index) => row.isDisabled && 'is-disabled'">
|
||||||
<b-table-column v-slot="props" field="name" :label="this.$t('name')">
|
<b-table-column v-slot="props" field="name" :label="$t('name')">
|
||||||
{{ `${props.row.name}` }} <template v-if="props.row.id === user.id">({{ $t('you') }})</template>
|
{{ `${props.row.name}` }} <template v-if="props.row.id === user.id">({{ $t('you') }})</template>
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="email" :label="this.$t('email')">
|
<b-table-column v-slot="props" field="email" :label="$t('email')">
|
||||||
{{ `${props.row.email}` }}
|
{{ `${props.row.email}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="role" :label="this.$t('role')">
|
<b-table-column v-slot="props" field="role" :label="$t('role')">
|
||||||
{{ `${$t('roles.' + props.row.roleDetail.key)}` }}
|
{{ `${$t('roles.' + props.row.roleDetail.key)}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="createdAt" :label="this.$t('created')" sortable date>
|
<b-table-column v-slot="props" field="createdAt" :label="$t('created')" sortable date>
|
||||||
{{ formatDate(props.row.createdAt) }}
|
{{ formatDate(props.row.createdAt) }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props">
|
<b-table-column v-slot="props">
|
||||||
<b-button type="is-success" v-if="props.row.isDisabled && props.row.roleDetail.key === 'USER'" @click="changeDisabledStatus(props.row.id, false)">{{ $t('enable') }}</b-button>
|
<b-button v-if="props.row.isDisabled && props.row.roleDetail.key === 'USER'" type="is-success" @click="changeDisabledStatus(props.row.id, false)">{{ $t('enable') }}</b-button>
|
||||||
<b-button type="is-danger" v-if="!props.row.isDisabled && props.row.roleDetail.key === 'USER'" @click="changeDisabledStatus(props.row.id, true)">{{ $t('disable') }}</b-button>
|
<b-button v-if="!props.row.isDisabled && props.row.roleDetail.key === 'USER'" type="is-danger" @click="changeDisabledStatus(props.row.id, true)">{{ $t('disable') }}</b-button>
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
</b-table>
|
</b-table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ export default {
|
|||||||
{ label: this.$t('alltime'), value: 'all_time' },
|
{ label: this.$t('alltime'), value: 'all_time' },
|
||||||
],
|
],
|
||||||
dateRangeOption: 'past_30_days',
|
dateRangeOption: 'past_30_days',
|
||||||
|
mileageOptions: [
|
||||||
|
{ label: 'L/100km', value: 'litre_100km' },
|
||||||
|
{ label: 'km/L', value: 'km_litre' },
|
||||||
|
{ label: 'mpg', value: 'mpg' },
|
||||||
|
],
|
||||||
|
mileageOption: 'litre_100km',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -86,7 +92,10 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: this.$t('avgfuelcost'),
|
label: this.$t('avgfuelcost'),
|
||||||
value: this.$t('per', {'0': this.formatCurrency(x.avgFuelPrice, x.currency), '1': this.$t('unit.short.' + this.vehicle.fuelUnitDetail.key)}),
|
value: this.$t('per', {
|
||||||
|
0: this.formatCurrency(x.avgFuelPrice, x.currency),
|
||||||
|
1: this.$t('unit.short.' + this.vehicle.fuelUnitDetail.key),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -303,15 +312,15 @@ export default {
|
|||||||
<div class="column is-one-half" :class="isMobile ? 'has-text-centered' : ''">
|
<div class="column is-one-half" :class="isMobile ? 'has-text-centered' : ''">
|
||||||
<p class="title">{{ vehicle.nickname }} - {{ vehicle.registration }}</p>
|
<p class="title">{{ vehicle.nickname }} - {{ vehicle.registration }}</p>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
{{ [vehicle.make, vehicle.model, this.$t('fuel.' + vehicle.fuelTypeDetail.key)].join(' | ') }}
|
{{ [vehicle.make, vehicle.model, $t('fuel.' + vehicle.fuelTypeDetail.key)].join(' | ') }}
|
||||||
|
|
||||||
<template v-if="users.length > 1">
|
<template v-if="users.length > 1">
|
||||||
| {{ $t("sharedwith") }} :
|
| {{ $t('sharedwith') }} :
|
||||||
{{
|
{{
|
||||||
users
|
users
|
||||||
.map((x) => {
|
.map((x) => {
|
||||||
if (x.userId === me.id) {
|
if (x.userId === me.id) {
|
||||||
return this.$t('you')
|
return $t('you')
|
||||||
} else {
|
} else {
|
||||||
return x.name
|
return x.name
|
||||||
}
|
}
|
||||||
@@ -322,8 +331,8 @@ export default {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div :class="(!isMobile ? 'has-text-right ' : '') + 'column is-one-half buttons'">
|
<div :class="(!isMobile ? 'has-text-right ' : '') + 'column is-one-half buttons'">
|
||||||
<b-button type="is-primary" tag="router-link" :to="`/vehicles/${vehicle.id}/fillup`">{{ this.$t('addfillup') }}</b-button>
|
<b-button type="is-primary" tag="router-link" :to="`/vehicles/${vehicle.id}/fillup`">{{ $t('addfillup') }}</b-button>
|
||||||
<b-button type="is-primary" tag="router-link" :to="`/vehicles/${vehicle.id}/expense`">{{ this.$t('addexpense') }}</b-button>
|
<b-button type="is-primary" tag="router-link" :to="`/vehicles/${vehicle.id}/expense`">{{ $t('addexpense') }}</b-button>
|
||||||
<b-button
|
<b-button
|
||||||
v-if="vehicle.isOwner"
|
v-if="vehicle.isOwner"
|
||||||
tag="router-link"
|
tag="router-link"
|
||||||
@@ -333,9 +342,8 @@ export default {
|
|||||||
props: { vehicle: vehicle },
|
props: { vehicle: vehicle },
|
||||||
params: { id: vehicle.id },
|
params: { id: vehicle.id },
|
||||||
}"
|
}"
|
||||||
>
|
><b-icon pack="fas" icon="edit" type="is-info"> </b-icon>
|
||||||
<b-icon pack="fas" icon="edit" type="is-info"> </b-icon
|
</b-button>
|
||||||
></b-button>
|
|
||||||
<b-button v-if="vehicle.isOwner" :title="$t('sharevehicle')" @click="showShareVehicleModal">
|
<b-button v-if="vehicle.isOwner" :title="$t('sharevehicle')" @click="showShareVehicleModal">
|
||||||
<b-icon pack="fas" icon="user-friends" type="is-info"> </b-icon
|
<b-icon pack="fas" icon="user-friends" type="is-info"> </b-icon
|
||||||
></b-button>
|
></b-button>
|
||||||
@@ -356,42 +364,42 @@ export default {
|
|||||||
<h1 class="title is-4">{{ $t('pastfillups') }}</h1>
|
<h1 class="title is-4">{{ $t('pastfillups') }}</h1>
|
||||||
|
|
||||||
<b-table :data="fillups" hoverable mobile-cards :detailed="isMobile" detail-key="id" paginated per-page="10">
|
<b-table :data="fillups" hoverable mobile-cards :detailed="isMobile" detail-key="id" paginated per-page="10">
|
||||||
<b-table-column v-slot="props" field="date" :label="this.$t('date')" :td-attrs="columnTdAttrs" sortable date>
|
<b-table-column v-slot="props" field="date" :label="$t('date')" :td-attrs="columnTdAttrs" sortable date>
|
||||||
{{ formatDate(props.row.date) }}
|
{{ formatDate(props.row.date) }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="fuelSubType" :label="this.$t('fuelsubtype')" :td-attrs="columnTdAttrs">
|
<b-table-column v-slot="props" field="fuelSubType" :label="$t('fuelsubtype')" :td-attrs="columnTdAttrs">
|
||||||
{{ props.row.fuelSubType }}
|
{{ props.row.fuelSubType }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="fuelQuantity" :label="this.$t('quantity')" :td-attrs="hiddenMobile" numeric>
|
<b-table-column v-slot="props" field="fuelQuantity" :label="$t('quantity')" :td-attrs="hiddenMobile" numeric>
|
||||||
{{ `${props.row.fuelQuantity} ${$t('unit.short.' + props.row.fuelUnitDetail.key)}` }}
|
{{ `${props.row.fuelQuantity} ${$t('unit.short.' + props.row.fuelUnitDetail.key)}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column
|
<b-table-column
|
||||||
v-slot="props"
|
v-slot="props"
|
||||||
field="perUnitPrice"
|
field="perUnitPrice"
|
||||||
:label="this.$t('per', { '0': this.$t('price'), '1': this.$t('unit.short.' + vehicle.fuelUnitDetail.key) })"
|
:label="$t('per', { '0': $t('price'), '1': $t('unit.short.' + vehicle.fuelUnitDetail.key) })"
|
||||||
:td-attrs="hiddenMobile"
|
:td-attrs="hiddenMobile"
|
||||||
numeric
|
numeric
|
||||||
sortable
|
sortable
|
||||||
>
|
>
|
||||||
{{ `${formatCurrency(props.row.perUnitPrice, props.row.currency)}` }}
|
{{ `${formatCurrency(props.row.perUnitPrice, props.row.currency)}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-if="isMobile" v-slot="props" field="totalAmount" :label="this.$t('total')" :td-attrs="hiddenDesktop" sortable numeric>
|
<b-table-column v-if="isMobile" v-slot="props" field="totalAmount" :label="$t('total')" :td-attrs="hiddenDesktop" sortable numeric>
|
||||||
{{ `${me.currency} ${props.row.totalAmount}` }} ({{ `${props.row.fuelQuantity} ${$t('unit.short.' + props.row.fuelUnitDetail.key)}` }} @
|
{{ `${me.currency} ${props.row.totalAmount}` }} ({{ `${props.row.fuelQuantity} ${$t('unit.short.' + props.row.fuelUnitDetail.key)}` }} @
|
||||||
{{ `${me.currency} ${props.row.perUnitPrice}` }})
|
{{ `${me.currency} ${props.row.perUnitPrice}` }})
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-if="!isMobile" v-slot="props" field="totalAmount" :label="this.$t('total')" :td-attrs="hiddenMobile" sortable numeric>
|
<b-table-column v-if="!isMobile" v-slot="props" field="totalAmount" :label="$t('total')" :td-attrs="hiddenMobile" sortable numeric>
|
||||||
{{ `${formatCurrency(props.row.totalAmount, props.row.currency)}` }}
|
{{ `${formatCurrency(props.row.totalAmount, props.row.currency)}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" width="20" field="isTankFull" :label="this.$t('fulltank')" :td-attrs="hiddenMobile">
|
<b-table-column v-slot="props" width="20" field="isTankFull" :label="$t('fulltank')" :td-attrs="hiddenMobile">
|
||||||
<b-icon pack="fas" :icon="props.row.isTankFull ? 'check' : 'times'" type="is-info"> </b-icon>
|
<b-icon pack="fas" :icon="props.row.isTankFull ? 'check' : 'times'" type="is-info"> </b-icon>
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="odoReading" :label="this.$t('odometer')" :td-attrs="hiddenMobile" numeric>
|
<b-table-column v-slot="props" field="odoReading" :label="$t('odometer')" :td-attrs="hiddenMobile" numeric>
|
||||||
{{ `${props.row.odoReading} ${$t('unit.short.' + me.distanceUnitDetail.key)}` }}
|
{{ `${props.row.odoReading} ${$t('unit.short.' + me.distanceUnitDetail.key)}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="fillingStation" :label="this.$t('gasstation')" :td-attrs="hiddenMobile">
|
<b-table-column v-slot="props" field="fillingStation" :label="$t('gasstation')" :td-attrs="hiddenMobile">
|
||||||
{{ `${props.row.fillingStation}` }}
|
{{ `${props.row.fillingStation}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="userId" :label="this.$t('by')" :td-attrs="hiddenMobile">
|
<b-table-column v-slot="props" field="userId" :label="$t('by')" :td-attrs="hiddenMobile">
|
||||||
{{ `${props.row.user.name}` }}
|
{{ `${props.row.user.name}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props">
|
<b-table-column v-slot="props">
|
||||||
@@ -406,7 +414,10 @@ export default {
|
|||||||
>
|
>
|
||||||
<b-icon pack="fas" icon="edit" type="is-info"> </b-icon
|
<b-icon pack="fas" icon="edit" type="is-info"> </b-icon
|
||||||
></b-button>
|
></b-button>
|
||||||
<b-button type="is-ghost" :title="$t('deletefillup')" @click="deleteFillup(props.row.id)">
|
<b-button
|
||||||
|
type="is-ghost"
|
||||||
|
:title="$t('deletefillup')"
|
||||||
|
@click="deleteFillup(props.row.id)">
|
||||||
<b-icon pack="fas" icon="trash" type="is-danger"> </b-icon
|
<b-icon pack="fas" icon="trash" type="is-danger"> </b-icon
|
||||||
></b-button>
|
></b-button>
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
@@ -421,22 +432,22 @@ export default {
|
|||||||
<h1 class="title is-4">{{ $t('expenses') }}</h1>
|
<h1 class="title is-4">{{ $t('expenses') }}</h1>
|
||||||
|
|
||||||
<b-table :data="expenses" hoverable mobile-cards paginated per-page="10">
|
<b-table :data="expenses" hoverable mobile-cards paginated per-page="10">
|
||||||
<b-table-column v-slot="props" field="date" :label="this.$t('date')" :td-attrs="columnTdAttrs" date>
|
<b-table-column v-slot="props" field="date" :label="$t('date')" :td-attrs="columnTdAttrs" date>
|
||||||
{{ formatDate(props.row.date) }}
|
{{ formatDate(props.row.date) }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="expenseType" :label="this.$t('expensetype')">
|
<b-table-column v-slot="props" field="expenseType" :label="$t('expensetype')">
|
||||||
{{ `${props.row.expenseType}` }}
|
{{ `${props.row.expenseType}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
|
|
||||||
<b-table-column v-slot="props" field="amount" :label="this.$t('total')" :td-attrs="hiddenMobile" sortable numeric>
|
<b-table-column v-slot="props" field="amount" :label="$t('total')" :td-attrs="hiddenMobile" sortable numeric>
|
||||||
{{ `${formatCurrency(props.row.amount, props.row.currency)}` }}
|
{{ `${formatCurrency(props.row.amount, props.row.currency)}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
|
|
||||||
<b-table-column v-slot="props" field="odoReading" :label="this.$t('odometer')" :td-attrs="columnTdAttrs" numeric>
|
<b-table-column v-slot="props" field="odoReading" :label="$t('odometer')" :td-attrs="columnTdAttrs" numeric>
|
||||||
{{ `${props.row.odoReading} ${$t('unit.short.' + me.distanceUnitDetail.key)}` }}
|
{{ `${props.row.odoReading} ${$t('unit.short.' + me.distanceUnitDetail.key)}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
|
|
||||||
<b-table-column v-slot="props" field="userId" :label="this.$t('by')" :td-attrs="columnTdAttrs">
|
<b-table-column v-slot="props" field="userId" :label="$t('by')" :td-attrs="columnTdAttrs">
|
||||||
{{ `${props.row.user.name}` }}
|
{{ `${props.row.user.name}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props">
|
<b-table-column v-slot="props">
|
||||||
@@ -461,7 +472,9 @@ export default {
|
|||||||
<br />
|
<br />
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-three-quarters"> <h1 class="title is-4">{{ $t('attachments') }}</h1></div>
|
<div class="column is-three-quarters">
|
||||||
|
<h1 class="title is-4">{{ $t('attachments') }}</h1></div
|
||||||
|
>
|
||||||
<div class="column buttons">
|
<div class="column buttons">
|
||||||
<b-button type="is-primary" @click="showAttachmentForm = true">
|
<b-button type="is-primary" @click="showAttachmentForm = true">
|
||||||
{{ $t('addattachment') }}
|
{{ $t('addattachment') }}
|
||||||
@@ -486,18 +499,18 @@ export default {
|
|||||||
</b-upload>
|
</b-upload>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-input v-model="title" required :placeholder="this.$t('labelforfile')"></b-input>
|
<b-input v-model="title" required :placeholder="$t('labelforfile')"></b-input>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field class="buttons">
|
<b-field class="buttons">
|
||||||
<b-button tag="input" native-type="submit" :disabled="tryingToUpload" type="is-primary" label="Upload File" value="Upload File">
|
<b-button tag="button" native-type="submit" :disabled="tryingToUpload" type="is-primary" label="Upload File" value="Upload File">
|
||||||
</b-button>
|
</b-button>
|
||||||
<b-button
|
<b-button
|
||||||
tag="input"
|
tag="button"
|
||||||
native-type="submit"
|
native-type="submit"
|
||||||
:disabled="tryingToUpload"
|
:disabled="tryingToUpload"
|
||||||
type="is-danger"
|
type="is-danger"
|
||||||
label="Upload File"
|
label="Cancel"
|
||||||
value="Cancel"
|
value="Cancel"
|
||||||
@click="showAttachmentForm = false"
|
@click="showAttachmentForm = false"
|
||||||
>
|
>
|
||||||
@@ -510,14 +523,14 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<b-table :data="attachments" hoverable mobile-cards>
|
<b-table :data="attachments" hoverable mobile-cards>
|
||||||
<b-table-column v-slot="props" field="title" :label="this.$t('title')" :td-attrs="columnTdAttrs">
|
<b-table-column v-slot="props" field="title" :label="$t('title')" :td-attrs="columnTdAttrs">
|
||||||
{{ `${props.row.title}` }}
|
{{ `${props.row.title}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
|
|
||||||
<b-table-column v-slot="props" field="originalName" :label="this.$t('name')" :td-attrs="columnTdAttrs">
|
<b-table-column v-slot="props" field="originalName" :label="$t('name')" :td-attrs="columnTdAttrs">
|
||||||
{{ `${props.row.originalName}` }}
|
{{ `${props.row.originalName}` }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
<b-table-column v-slot="props" field="id" :label="this.$t('download')" :td-attrs="columnTdAttrs">
|
<b-table-column v-slot="props" field="id" :label="$t('download')" :td-attrs="columnTdAttrs">
|
||||||
<b-button tag="a" :href="`/api/attachments/${props.row.id}/file?access_token=${currentUser.token}`" :download="props.row.originalName">
|
<b-button tag="a" :href="`/api/attachments/${props.row.id}/file?access_token=${currentUser.token}`" :download="props.row.originalName">
|
||||||
<b-icon type="is-primary" icon="download"></b-icon>
|
<b-icon type="is-primary" icon="download"></b-icon>
|
||||||
</b-button>
|
</b-button>
|
||||||
@@ -527,16 +540,29 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column" :class="isMobile ? 'has-text-centered' : ''"> <h1 class="title">{{ $t('statistics') }}</h1></div>
|
<div class="column" :class="isMobile ? 'has-text-centered' : ''">
|
||||||
<div class="column">
|
<h1 class="title">{{ $t('statistics') }}</h1></div
|
||||||
<b-select v-model="dateRangeOption" class="is-pulled-right is-medium">
|
|
||||||
<option v-for="option in dateRangeOptions" :key="option.value" :value="option.value">
|
|
||||||
{{ option.label }}
|
|
||||||
</option>
|
|
||||||
</b-select></div
|
|
||||||
>
|
>
|
||||||
|
<div class="column">
|
||||||
|
<div class="columns is-pulled-right is-medium">
|
||||||
|
<div class="column">
|
||||||
|
<b-select v-model="mileageOption">
|
||||||
|
<option v-for="option in mileageOptions" :key="option.value" :value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<b-select v-model="dateRangeOption">
|
||||||
|
<option v-for="option in dateRangeOptions" :key="option.value" :value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MileageChart :vehicle="vehicle" :since="getStartDate()" :user="me" :height="300" />
|
<MileageChart :vehicle="vehicle" :since="getStartDate()" :user="me" :height="300" :mileage-option="mileageOption" />
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
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
|
||||||
|
|||||||
14736
ui/yarn.lock
14736
ui/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user