Compare commits

...

4 Commits

Author SHA1 Message Date
Alf Sebastian Houge
302bdd2222 Implement switching language (but doesn't persist) 2023-07-18 22:48:36 +02:00
Alf Sebastian Houge
14968013dd Write and read language setting from backend 2023-02-26 13:54:55 +01:00
Alf Sebastian Houge
efa6aed8eb Add language masters 2023-02-26 13:45:17 +01:00
Alf Sebastian Houge
533c68ee09 Add db migration for supporting per user language setting 2023-02-26 13:44:58 +01:00
9 changed files with 124 additions and 39 deletions

View File

@@ -19,6 +19,7 @@ func RegisterAnonMasterConroller(router *gin.RouterGroup) {
"distanceUnits": db.DistanceUnitDetails, "distanceUnits": db.DistanceUnitDetails,
"roles": db.RoleDetails, "roles": db.RoleDetails,
"currencies": models.GetCurrencyMasterList(), "currencies": models.GetCurrencyMasterList(),
"languages": models.GetLanguageMastersList(),
}) })
}) })
} }
@@ -52,7 +53,7 @@ func udpateSettings(c *gin.Context) {
func udpateMySettings(c *gin.Context) { func udpateMySettings(c *gin.Context) {
var model models.UpdateSettingModel var model models.UpdateSettingModel
if err := c.ShouldBind(&model); err == nil { if err := c.ShouldBind(&model); err == nil {
err := service.UpdateUserSettings(c.MustGet("userId").(string), model.Currency, *model.DistanceUnit, model.DateFormat) err := service.UpdateUserSettings(c.MustGet("userId").(string), model.Currency, *model.DistanceUnit, model.DateFormat, model.Language)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, common.NewError("udpateMySettings", err)) c.JSON(http.StatusUnprocessableEntity, common.NewError("udpateMySettings", err))
return return

View File

@@ -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) {

View File

@@ -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() {

24
server/models/language.go Normal file
View 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",
},
}
}

View File

@@ -6,6 +6,7 @@ type UpdateSettingModel struct {
Currency string `json:"currency" form:"currency" query:"currency"` Currency string `json:"currency" form:"currency" query:"currency"`
DateFormat string `json:"dateFormat" form:"dateFormat" query:"dateFormat"` DateFormat string `json:"dateFormat" form:"dateFormat" query:"dateFormat"`
DistanceUnit *db.DistanceUnit `json:"distanceUnit" form:"distanceUnit" query:"distanceUnit" ` DistanceUnit *db.DistanceUnit `json:"distanceUnit" form:"distanceUnit" query:"distanceUnit" `
Language string `json:"language" form:"language" query:"language"`
} }
type ClarksonMigrationModel struct { type ClarksonMigrationModel struct {

View File

@@ -1,7 +1,9 @@
package service package service
import ( import (
"errors"
"hammond/db" "hammond/db"
"hammond/models"
) )
func CanInitializeSystem() (bool, error) { func CanInitializeSystem() (bool, error) {
@@ -14,15 +16,30 @@ func UpdateSettings(currency string, distanceUnit db.DistanceUnit) error {
setting.DistanceUnit = distanceUnit setting.DistanceUnit = distanceUnit
return db.UpdateSettings(setting) return db.UpdateSettings(setting)
} }
func UpdateUserSettings(userId, currency string, distanceUnit db.DistanceUnit, dateFormat string) error { func UpdateUserSettings(userId, currency string, distanceUnit db.DistanceUnit, dateFormat string, language string) error {
user, err := db.GetUserById(userId) user, err := db.GetUserById(userId)
if err != nil { if err != nil {
return err return err
} }
// TODO: Pull into function
languageExists := false
languages := models.GetLanguageMastersList();
for _, lang := range languages {
if (language == lang.Shorthand){
languageExists = true
}
}
if (!languageExists) {
return errors.New("Language not in masters list")
}
user.Currency = currency user.Currency = currency
user.DistanceUnit = distanceUnit user.DistanceUnit = distanceUnit
user.DateFormat = dateFormat user.DateFormat = dateFormat
user.Language = language
return db.UpdateUser(user) return db.UpdateUser(user)
} }

View File

@@ -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"

View 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
})
},
}

View File

@@ -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