Compare commits

..

1 Commits

Author SHA1 Message Date
Alf Sebastian Houge
fb742f19a7 Change currency field from select to autocomplete 2022-04-17 18:52:19 +02:00
13 changed files with 87 additions and 78 deletions

View File

@@ -4,7 +4,7 @@ services:
image: akhilrex/hammond
container_name: hammond
environment:
- JWT_SECRET=somethingverystrong
- JWT_SECRET = somethingverystrong
volumes:
- /path/to/config:/config
- /path/to/data:/assets

View File

@@ -94,17 +94,17 @@ func userLogin(c *gin.Context) {
user, err := db.FindOneUser(&db.User{Email: loginRequest.Email})
if err != nil {
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("not Registered email or invalid password")))
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("Not Registered email or invalid password")))
return
}
if user.CheckPassword(loginRequest.Password) != nil {
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("not Registered email or invalid password")))
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("Not Registered email or invalid password")))
return
}
if user.IsDisabled {
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("your user has been disabled by the admin. Please contact them to get it re-enabled")))
c.JSON(http.StatusForbidden, common.NewError("login", errors.New("Your user has been disabled by the admin. Please contact them to get it re-enabled.")))
return
}
UpdateContextUserModel(c, user.ID)
@@ -170,16 +170,16 @@ func changePassword(c *gin.Context) {
user, err := service.GetUserById(c.GetString("userId"))
if err != nil {
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("not Registered email or invalid password")))
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("Not Registered email or invalid password")))
return
}
if user.CheckPassword(request.OldPassword) != nil {
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("incorrect old password")))
c.JSON(http.StatusForbidden, common.NewError("changePassword", errors.New("Incorrect old password")))
return
}
user.SetPassword(request.NewPassword)
success, _ := service.UpdatePassword(user.ID, request.NewPassword)
success, err := service.UpdatePassword(user.ID, request.NewPassword)
c.JSON(http.StatusOK, success)
}

View File

@@ -23,8 +23,8 @@ func stripBearerPrefixFromTokenString(tok string) (string, error) {
// Extract token from Authorization header
// Uses PostExtractionFilter to strip "TOKEN " prefix from header
var AuthorizationHeaderExtractor = &request.PostExtractionFilter{
Extractor: request.HeaderExtractor{"Authorization"},
Filter: stripBearerPrefixFromTokenString,
request.HeaderExtractor{"Authorization"},
stripBearerPrefixFromTokenString,
}
// Extractor for OAuth2 access tokens. Looks in 'Authorization'

View File

@@ -51,7 +51,7 @@ func migrate(c *gin.Context) {
canMigrate, _, _ := db.CanMigrate(request.Url)
if !canMigrate {
c.JSON(http.StatusBadRequest, fmt.Errorf("cannot migrate database. please check connection string"))
c.JSON(http.StatusBadRequest, fmt.Errorf("cannot migrate database. please check connection string."))
return
}

View File

@@ -397,7 +397,7 @@ func deleteVehicle(c *gin.Context) {
return
}
if !canDelete {
c.JSON(http.StatusUnprocessableEntity, common.NewError("shareVehicle", errors.New("you are not allowed to delete this vehicle")))
c.JSON(http.StatusUnprocessableEntity, common.NewError("shareVehicle", errors.New("You are not allowed to delete this vehicle.")))
return
}
err = service.DeleteVehicle(searchByIdQuery.Id)

View File

@@ -117,7 +117,7 @@ func UnshareVehicle(vehicleId, userId string) error {
return nil
}
if mapping.IsOwner {
return fmt.Errorf("cannot unshare owner")
return fmt.Errorf("Cannot unshare owner")
}
result := DB.Where("id=?", mapping.ID).Delete(&UserVehicle{})
return result.Error
@@ -332,7 +332,8 @@ func UnlockMissedJobs() {
if (job.Date == time.Time{}) {
continue
}
var duration = time.Duration(job.Duration)
var duration time.Duration
duration = time.Duration(job.Duration)
d := job.Date.Add(time.Minute * duration)
if d.Before(time.Now()) {
fmt.Println(job.Name + " is unlocked")

View File

@@ -3,6 +3,7 @@ package service
import (
"archive/tar"
"compress/gzip"
"errors"
"fmt"
"io"
"net/http"
@@ -125,14 +126,14 @@ func CreateBackup() (string, error) {
tarballFilePath := path.Join(folder, backupFileName)
file, err := os.Create(tarballFilePath)
if err != nil {
return "", fmt.Errorf("could not create tarball file '%s', got error '%s'", tarballFilePath, err.Error())
return "", errors.New(fmt.Sprintf("Could not create tarball file '%s', got error '%s'", tarballFilePath, err.Error()))
}
defer file.Close()
dbPath := path.Join(configPath, "hammond.db")
_, err = os.Stat(dbPath)
if err != nil {
return "", fmt.Errorf("could not find db file '%s', got error '%s'", dbPath, err.Error())
return "", errors.New(fmt.Sprintf("Could not find db file '%s', got error '%s'", dbPath, err.Error()))
}
gzipWriter := gzip.NewWriter(file)
defer gzipWriter.Close()
@@ -150,13 +151,13 @@ func CreateBackup() (string, error) {
func addFileToTarWriter(filePath string, tarWriter *tar.Writer) error {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("could not open file '%s', got error '%s'", filePath, err.Error())
return errors.New(fmt.Sprintf("Could not open file '%s', got error '%s'", filePath, err.Error()))
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
return fmt.Errorf("could not get stat for file '%s', got error '%s'", filePath, err.Error())
return errors.New(fmt.Sprintf("Could not get stat for file '%s', got error '%s'", filePath, err.Error()))
}
header := &tar.Header{
@@ -168,12 +169,12 @@ func addFileToTarWriter(filePath string, tarWriter *tar.Writer) error {
err = tarWriter.WriteHeader(header)
if err != nil {
return fmt.Errorf("could not write header for file '%s', got error '%s'", filePath, err.Error())
return errors.New(fmt.Sprintf("Could not write header for file '%s', got error '%s'", filePath, err.Error()))
}
_, err = io.Copy(tarWriter, file)
if err != nil {
return fmt.Errorf("could not copy the file '%s' data to the tarball, got error '%s'", filePath, err.Error())
return errors.New(fmt.Sprintf("Could not copy the file '%s' data to the tarball, got error '%s'", filePath, err.Error()))
}
return nil

View File

@@ -1,7 +1,6 @@
package service
import (
"sort"
"time"
"github.com/akhilrex/hammond/db"
@@ -16,9 +15,6 @@ func GetMileageByVehicleId(vehicleId string, since time.Time) (mileage []models.
fillups := make([]db.Fillup, len(*data))
copy(fillups, *data)
sort.Slice(fillups, func(i, j int) bool {
return fillups[i].OdoReading > fillups[j].OdoReading
})
var mileages []models.MileageModel

Binary file not shown.

Before

Width:  |  Height:  |  Size: 895 B

After

Width:  |  Height:  |  Size: 463 B

View File

@@ -5,7 +5,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="shortcut icon" href="<%= webpackConfig.output.publicPath %>hammond.png" />
<link rel="apple-touch-icon" href="<%= webpackConfig.output.publicPath %>touch-icon.png" />
<title><%= webpackConfig.name %></title>
</head>
<body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -21,13 +21,27 @@ export default {
email: '',
password: '',
distanceUnit: 1,
currency: 'INR',
currency: '',
},
}
},
computed: {
...mapGetters('auth', ['isInitialized']),
...mapState('vehicles', ['currencyMasters', 'distanceUnitMasters']),
filteredCurrencyMasters() {
return this.currencyMasters.filter((option) => {
return (
option.namePlural
.toString()
.toLowerCase()
.indexOf(this.registerModel.currency.toLowerCase()) >= 0 ||
option.code
.toString()
.toLowerCase()
.indexOf(this.registerModel.currency.toLowerCase()) >= 0
)
})
},
},
mounted() {
store.dispatch('vehicles/fetchMasters').then((data) => {})
@@ -139,6 +153,9 @@ export default {
})
.finally(() => (this.isWorking = false))
},
formatCurrency(option) {
return `${option.namePlural} (${option.code})`
},
},
}
</script>
@@ -148,15 +165,10 @@ export default {
<div v-if="!migrationMode" class="box">
<h1 class="title">Migrate from Clarkson</h1>
<p>
If you have an existing Clarkson deployment and you want to migrate your data from that,
press the following button.
If you have an existing Clarkson deployment and you want to migrate your data from that, press the following button.
</p>
<br />
<b-field>
<b-button type="is-primary" @click="migrationMode = 'clarkson'"
>Migrate from Clarkson</b-button
></b-field
>
<b-field> <b-button type="is-primary" @click="migrationMode = 'clarkson'">Migrate from Clarkson</b-button></b-field>
</div>
<div v-if="!migrationMode" class="box">
<h1 class="title">Fresh Install</h1>
@@ -170,21 +182,12 @@ export default {
</div>
<div v-if="migrationMode === 'clarkson'" class="box content">
<h1 class="title">Migrate from Clarkson</h1>
<p>You need to make sure that this deployment of Hammond can access the MySQL database used by Clarkson.</p>
<p>If that is not directly possible, you can make a copy of that database somewhere accessible from this instance.</p>
<p>Once that is done, enter the connection string to the MySQL instance in the following format.</p>
<p
>You need to make sure that this deployment of Hammond can access the MySQL database used by
Clarkson.</p
>
<p
>If that is not directly possible, you can make a copy of that database somewhere accessible
from this instance.</p
>
<p
>Once that is done, enter the connection string to the MySQL instance in the following
format.</p
>
<p
>All the users imported from Clarkson will have their username as their email in Clarkson
database and pasword set to <span class="" style="font-weight:bold">hammond</span></p
>All the users imported from Clarkson will have their username as their email in Clarkson database and pasword set to
<span class="" style="font-weight:bold">hammond</span></p
>
<code>
user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
@@ -200,15 +203,8 @@ export default {
</b-field>
<div class="buttons">
<b-button
v-if="!testSuccess"
type="is-primary"
:disabled="isWorking"
@click="testConnection"
>Test Connection</b-button
><b-button v-if="testSuccess" type="is-success" :disabled="isWorking" @click="migrate"
>Migrate</b-button
>
<b-button v-if="!testSuccess" type="is-primary" :disabled="isWorking" @click="testConnection">Test Connection</b-button
><b-button v-if="testSuccess" type="is-success" :disabled="isWorking" @click="migrate">Migrate</b-button>
<b-button type="is-danger is-light" @click="resetMigrationMode">Cancel</b-button>
</div>
</div>
@@ -222,28 +218,22 @@ export default {
<b-input v-model="registerModel.email" type="email" required></b-input>
</b-field>
<b-field label="Your Password">
<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 label="Currency">
<b-select v-model="registerModel.currency" placeholder="Currency" required expanded>
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
{{ `${option.namePlural} (${option.code})` }}
</option>
</b-select>
<b-autocomplete
v-model="registerModel.currency"
:custom-formatter="formatCurrency"
placeholder="Currency"
:data="filteredCurrencyMasters"
:keep-first="true"
:open-on-focus="true"
required
@select="(option) => (selected = option)"
></b-autocomplete>
</b-field>
<b-field label="Distance Unit">
<b-select
v-model.number="registerModel.distanceUnit"
placeholder="Distance Unit"
required
expanded
>
<b-select v-model.number="registerModel.distanceUnit" placeholder="Distance Unit" required expanded>
<option v-for="(option, key) in distanceUnitMasters" :key="key" :value="key">
{{ `${option.long} (${option.short})` }}
</option>

View File

@@ -44,6 +44,20 @@ export default {
return this.changePassModel.new === this.changePassModel.renew
},
filteredCurrencyMasters() {
return this.currencyMasters.filter((option) => {
return (
option.namePlural
.toString()
.toLowerCase()
.indexOf(this.settingsModel.currency.toLowerCase()) >= 0 ||
option.code
.toString()
.toLowerCase()
.indexOf(this.settingsModel.currency.toLowerCase()) >= 0
)
})
},
},
methods: {
changePassword() {
@@ -109,6 +123,9 @@ export default {
this.tryingToSave = false
})
},
formatCurrency(option) {
return `${option.namePlural} (${option.code})`
},
},
}
</script>
@@ -123,11 +140,16 @@ export default {
These will be used as default values whenever you create a new fillup or expense.
</h1>
<b-field label="Currency">
<b-select v-model="settingsModel.currency" placeholder="Currency" required expanded>
<option v-for="option in currencyMasters" :key="option.code" :value="option.code">
{{ `${option.namePlural} (${option.code})` }}
</option>
</b-select>
<b-autocomplete
v-model="settingsModel.currency"
:custom-formatter="formatCurrency"
placeholder="Currency"
:data="filteredCurrencyMasters"
:keep-first="true"
:open-on-focus="true"
required
@select="(option) => (selected = option)"
></b-autocomplete>
</b-field>
<b-field label="Distance Unit">
<b-select v-model.number="settingsModel.distanceUnit" placeholder="Distance Unit" required expanded>