feat(ui): language switcher

This commit is contained in:
Denis Gukov 2023-09-17 14:57:57 +02:00
parent 41c60b9eae
commit 985f3f3a03
6 changed files with 304 additions and 82 deletions

View File

@ -24,9 +24,112 @@ func GetProjects(w http.ResponseWriter, r *http.Request) {
helpers.WriteJSON(w, http.StatusOK, projects)
}
func createDemoProject(projectID int, store db.Store) (err error) {
var noneKey db.AccessKey
var demoRepo db.Repository
var emptyEnv db.Environment
var buildInv db.Inventory
var devInv db.Inventory
var prodInv db.Inventory
noneKey, err = store.CreateAccessKey(db.AccessKey{
Name: "None",
})
if err != nil {
return
}
demoRepo, err = store.CreateRepository(db.Repository{
Name: "Demo Project",
ProjectID: projectID,
GitURL: "https://github.com/semaphoreui/demo-project.git",
GitBranch: "main",
SSHKeyID: noneKey.ID,
})
if err != nil {
return
}
emptyEnv, err = store.CreateEnvironment(db.Environment{
Name: "Empty",
ProjectID: projectID,
JSON: "{}",
})
if err != nil {
return
}
buildInv, err = store.CreateInventory(db.Inventory{
Name: "Build",
ProjectID: projectID,
Inventory: "[builder]\nlocalhost",
Type: "static",
})
if err != nil {
return
}
devInv, err = store.CreateInventory(db.Inventory{
ProjectID: projectID,
})
if err != nil {
return
}
prodInv, err = store.CreateInventory(db.Inventory{
ProjectID: projectID,
})
if err != nil {
return
}
_, err = store.CreateTemplate(db.Template{
Name: "Build",
Playbook: "build.yml",
ProjectID: projectID,
InventoryID: buildInv.ID,
EnvironmentID: &emptyEnv.ID,
RepositoryID: demoRepo.ID,
})
if err != nil {
return
}
_, err = store.CreateTemplate(db.Template{
Name: "Deploy to Dev",
Playbook: "deploy.yml",
ProjectID: projectID,
InventoryID: devInv.ID,
EnvironmentID: &emptyEnv.ID,
RepositoryID: demoRepo.ID,
})
if err != nil {
return
}
_, err = store.CreateTemplate(db.Template{
Name: "Deploy to Production",
Playbook: "deploy.yml",
ProjectID: projectID,
InventoryID: prodInv.ID,
EnvironmentID: &emptyEnv.ID,
RepositoryID: demoRepo.ID,
})
return
}
// AddProject adds a new project to the database
func AddProject(w http.ResponseWriter, r *http.Request) {
var body db.Project
user := context.Get(r, "user").(*db.User)
@ -36,25 +139,43 @@ func AddProject(w http.ResponseWriter, r *http.Request) {
return
}
if !helpers.Bind(w, r, &body) {
var bodyWithDemo struct {
db.Project
Demo bool `json:"demo"`
}
if !helpers.Bind(w, r, &bodyWithDemo) {
return
}
body, err := helpers.Store(r).CreateProject(body)
body := bodyWithDemo.Project
store := helpers.Store(r)
body, err := store.CreateProject(body)
if err != nil {
helpers.WriteError(w, err)
return
}
_, err = helpers.Store(r).CreateProjectUser(db.ProjectUser{ProjectID: body.ID, UserID: user.ID, Role: db.ProjectOwner})
_, err = store.CreateProjectUser(db.ProjectUser{ProjectID: body.ID, UserID: user.ID, Role: db.ProjectOwner})
if err != nil {
helpers.WriteError(w, err)
return
}
if bodyWithDemo.Demo {
err = createDemoProject(body.ID, store)
if err != nil {
helpers.WriteError(w, err)
return
}
}
desc := "Project Created"
oType := db.EventProject
_, err = helpers.Store(r).CreateEvent(db.Event{
_, err = store.CreateEvent(db.Event{
UserID: &user.ID,
ProjectID: &body.ID,
Description: &desc,

View File

@ -261,17 +261,52 @@
</v-list>
<template v-slot:append>
<v-menu top max-width="235" nudge-top="12">
<template v-slot:activator="{ on, attrs }">
<v-list class="pa-0">
<v-list-item>
<v-switch
v-model="darkMode"
inset
:label="$t('darkMode')"
persistent-hint
></v-switch>
</v-list-item>
<v-list class="pa-0">
<v-list-item>
<v-switch
v-model="darkMode"
inset
:label="$t('darkMode')"
persistent-hint
></v-switch>
<v-spacer />
<v-menu top min-width="150" max-width="235" nudge-top="12" :position-x="50" absolute>
<template v-slot:activator="{on, attrs}">
<v-btn
icon
x-large
v-bind="attrs"
v-on="on"
>
<span style="font-size: 30px;">{{ lang.flag }}</span>
</v-btn>
</template>
<v-list dense>
<v-list-item
v-for="lang in languages"
:key="lang.id"
@click="selectLanguage(lang.id)"
>
<v-list-item-icon>
{{ lang.flag }}
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ lang.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</v-list-item>
<v-menu top max-width="235" nudge-top="12">
<template v-slot:activator="{ on, attrs }">
<v-list-item
key="project"
v-bind="attrs"
@ -285,72 +320,43 @@
<v-list-item-title>{{ user.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
<v-list>
<v-list-item key="users" to="/users" v-if="user.admin">
<v-list-item-icon>
<v-icon>mdi-account-multiple</v-icon>
</v-list-item-icon>
<v-list-item-content>
{{ $t('users') }}
</v-list-item-content>
</v-list-item>
<v-list-item key="edit" @click="userDialog = true">
<v-list-item-icon>
<v-icon>mdi-pencil</v-icon>
</v-list-item-icon>
<v-list-item-content>
{{ $t('editAccount') }}
</v-list-item-content>
</v-list-item>
<v-list-item key="sign_out" @click="signOut()">
<v-list-item-icon>
<v-icon>mdi-exit-to-app</v-icon>
</v-list-item-icon>
<v-list-item-content>
{{ $t('signOut') }}
</v-list-item-content>
</v-list-item>
</v-list>
</template>
</v-menu>
<v-list>
<v-list-item key="users" to="/users" v-if="user.admin">
<v-list-item-icon>
<v-icon>mdi-account-multiple</v-icon>
</v-list-item-icon>
</v-list>
<v-list-item-content>
{{ $t('users') }}
</v-list-item-content>
</v-list-item>
<v-list-item key="edit" @click="userDialog = true">
<v-list-item-icon>
<v-icon>mdi-pencil</v-icon>
</v-list-item-icon>
<v-list-item-content>
{{ $t('editAccount') }}
</v-list-item-content>
</v-list-item>
<!--
<v-list-item key="password" @click="passwordDialog = true">
<v-list-item-icon>
<v-icon>mdi-information</v-icon>
</v-list-item-icon>
<v-list-item-content>
System Information
</v-list-item-content>
</v-list-item>
<v-list-item key="password" @click="passwordDialog = true">
<v-list-item-icon>
<v-icon>mdi-tune</v-icon>
</v-list-item-icon>
<v-list-item-content>
System Settings
</v-list-item-content>
</v-list-item>
<v-list-item key="password" @click="passwordDialog = true">-->
<!-- <v-list-item-icon>-->
<!-- <v-icon>mdi-lock</v-icon>-->
<!-- </v-list-item-icon>-->
<!-- <v-list-item-content>-->
<!-- Change Password-->
<!-- </v-list-item-content>-->
<!-- </v-list-item>-->
<v-list-item key="sign_out" @click="signOut()">
<v-list-item-icon>
<v-icon>mdi-exit-to-app</v-icon>
</v-list-item-icon>
<v-list-item-content>
{{ $t('signOut') }}
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-navigation-drawer>
@ -546,6 +552,49 @@ const PROJECT_COLORS = [
'green',
];
const LANGUAGES = {
en: {
flag: '🇺🇸',
title: 'English',
},
ru: {
flag: '🇷🇺',
title: 'Russian',
},
de: {
flag: '🇩🇪',
title: 'German',
},
zh: {
flag: '🇨🇳',
title: 'Chinese',
},
fr: {
flag: '🇫🇷',
title: 'French',
},
pt: {
flag: '🇵🇹',
title: 'Portuguese',
},
};
function getLangInfo(locale) {
let res = LANGUAGES[locale];
if (!res) {
res = LANGUAGES.en;
}
return res;
}
function getSystemLang() {
const locale = navigator.language.split('-')[0];
return getLangInfo(locale || 'en');
}
export default {
name: 'App',
components: {
@ -574,6 +623,17 @@ export default {
task: null,
template: null,
darkMode: false,
languages: [
{
id: '',
flag: getSystemLang().flag,
title: 'System',
},
...Object.keys(LANGUAGES).map((lang) => ({
id: lang,
...LANGUAGES[lang],
})),
],
};
},
@ -608,6 +668,17 @@ export default {
},
computed: {
lang() {
const locale = localStorage.getItem('lang');
if (!locale) {
return getSystemLang();
}
return getLangInfo(locale || 'en');
},
projectId() {
return parseInt(this.$route.params.projectId, 10) || null;
},
@ -768,6 +839,12 @@ export default {
},
methods: {
selectLanguage(lang) {
localStorage.setItem('lang', lang);
window.location.reload();
},
async onTaskLogDialogClosed() {
const query = { ...this.$route.query, t: undefined };
await this.$router.replace({ query });

View File

@ -48,6 +48,9 @@
import ItemFormBase from '@/components/ItemFormBase';
export default {
props: {
demoProject: Boolean,
},
mixins: [ItemFormBase],
methods: {
getItemsUrl() {
@ -56,6 +59,9 @@ export default {
getSingleItemUrl() {
return `/api/project/${this.itemId}`;
},
beforeSave() {
this.item.demo = this.demoProject;
},
},
};
</script>

View File

@ -3,7 +3,13 @@ import VueI18n from 'vue-i18n';
import { messages } from '../lang';
Vue.use(VueI18n);
const locale = navigator.language.split('-')[0];
let locale = localStorage.getItem('lang');
if (!locale) {
locale = navigator.language.split('-')[0];
}
export default new VueI18n({
fallbackLocale: 'en',
locale,

View File

@ -12,7 +12,8 @@
v-if="can(USER_PERMISSIONS.updateProject)"
key="settings"
:to="`/project/${projectId}/settings`"
>{{ $t('settings') }}
>
{{ $t('settings') }}
</v-tab>
</v-tabs>

View File

@ -8,10 +8,14 @@
<div class="project-settings-form">
<div style="height: 300px;">
<ProjectForm item-id="new" ref="editForm" @save="onSave"/>
<ProjectForm item-id="new" ref="editForm" @save="onSave" :demo-project="true" />
</div>
<div class="text-right">
<v-btn
color="secondary" class="mr-3" @click="createDemoProject()"
>Create Demo Project</v-btn>
<v-btn color="primary" @click="createProject()">{{ $t('create') }}</v-btn>
</div>
</div>
@ -29,6 +33,7 @@ export default {
components: { ProjectForm },
data() {
return {
demoProject: false,
};
},
@ -45,6 +50,12 @@ export default {
},
async createProject() {
this.demoProject = false;
await this.$refs.editForm.save();
},
async createDemoProject() {
this.demoProject = true;
await this.$refs.editForm.save();
},
},