Skip to content

Commit

Permalink
feat: add ability to create users
Browse files Browse the repository at this point in the history
  • Loading branch information
bayang committed Apr 15, 2022
1 parent b06ba68 commit 631e386
Show file tree
Hide file tree
Showing 18 changed files with 652 additions and 87 deletions.
466 changes: 393 additions & 73 deletions src/jelu-ui/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/jelu-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"tsc": "vue-tsc --noEmit"
},
"dependencies": {
"@formkit/i18n": "^1.0.0-beta.7-c3de75f",
"@formkit/tailwindcss": "^1.0.0-beta.7-c3de75f",
"@formkit/vue": "^1.0.0-beta.7-c3de75f",
"@mdi/font": "6.6.96",
"@oruga-ui/oruga-next": "0.5.5",
"@saekitominaga/isbn-verify": "^1.4.1",
Expand Down
4 changes: 4 additions & 0 deletions src/jelu-ui/src/components/AdminBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const items = ref([{ name:t('settings.profile'), tooltip:t('settings.my_profile'
{ name:t('settings.imports'), icon:"bxs-file-plus", href:"/profile/imports", tooltip: t('settings.csv_import') },
])
if (store.getters.isAdmin) {
items.value.push({ name:t('settings.users'), icon:"bxs-user-plus", href:"/profile/admin/users", tooltip: t('settings.users_management') })
}
const isOpened = ref(false)
const sideBarWidth = ref(175)
Expand Down
95 changes: 95 additions & 0 deletions src/jelu-ui/src/components/AdminUsers.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<script setup lang="ts">
import { useTitle } from '@vueuse/core'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
import dataService from "../services/DataService"
import { key } from '../store'
import { setErrors } from '@formkit/vue'
import { ObjectUtils } from "../utils/ObjectUtils";
import { useProgrammatic } from "@oruga-ui/oruga-next";
const { t } = useI18n({
inheritLocale: true,
useScope: 'global'
})
useTitle('Jelu | Users admin')
const store = useStore(key)
const { oruga } = useProgrammatic()
const form = ref({'login' : '', 'password' : '', 'admin': false})
async function createUser(user: any) {
console.log("create user")
console.log(user)
try {
await dataService.createUser({"login" : user.login, "password": user.password, "isAdmin" : user.admin})
ObjectUtils.toast(oruga, "success", t('admin_user.user_saved', {name : user.login}), 4000)
} catch (err: any) {
setErrors('create-user-form', [], err.message)
}
}
</script>

<template>
<div class="grid grid-cols-1 justify-center justify-items-center justify-self-center">
<h1 class="typewriter text-2xl mb-3 capitalize">
{{ t('admin_user.create_user') }} :
</h1>
<div class="flex flex-row justify-center basis-10/12 sm:basis-1/3">
<div class="">
<FormKit
id="create-user-form"
v-slot="{ state: { valid } }"
v-model="form"
type="form"
:actions="false"
message-class="text-error-content"
messages-class="alert alert-error mt-2"
@submit="createUser"
>
<FormKit
type="text"
name="login"
:label="t('admin_user.login')"
placeholder="joe123"
validation="required|length:3"
/>
<FormKit
type="password"
name="password"
:label="t('admin_user.password')"
validation="required|length:3"
:placeholder="t('admin_user.password')"
/>
<FormKit
type="password"
name="password_confirm"
:label="t('admin_user.password_confirm')"
:placeholder="t('admin_user.password_confirm')"
validation="required|confirm"
/>
<FormKit
type="checkbox"
:help="t('admin_user.admin_help')"
:label="t('admin_user.admin')"
name="admin"
/>
<FormKit
type="submit"
:disabled="!valid"
input-class="btn-accent"
>
{{ t('admin_user.create_user') }}
</FormKit>
</FormKit>
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
</style>
2 changes: 1 addition & 1 deletion src/jelu-ui/src/components/AuthorBooks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ getBooks()
/>
</template>

<style lang="scss" scoped>
<style scoped>
label {
margin: 0 0.5em;
Expand Down
2 changes: 1 addition & 1 deletion src/jelu-ui/src/components/Imports.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const importFile = async () => {
</script>

<template>
<div class="grid grid-cols-1 justify-center justify-items-center justify-self-center columns is-centered">
<div class="grid grid-cols-1 justify-center justify-items-center justify-self-center">
<h1 class="text-2xl typewriter w-11/12 sm:w-8/12 pb-4 capitalize">
{{ t('csv_import.import_csv') }}
</h1>
Expand Down
2 changes: 1 addition & 1 deletion src/jelu-ui/src/components/SearchResultsDisplay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ if (titleQuery.value != null ||
/>
</template>

<style lang="scss" scoped>
<style scoped>
label {
margin: 0 0.5em;
Expand Down
5 changes: 3 additions & 2 deletions src/jelu-ui/src/components/UserSettings.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { useTitle } from '@vueuse/core'
import { onMounted, watch } from 'vue'
import Avatar from 'vue-avatar-sdh'
import { inject, onMounted, watch } from 'vue'
import { useStore } from 'vuex'
import { key } from '../store'
import { themeChange } from 'theme-change'
Expand All @@ -15,6 +14,7 @@ const { t, locale, availableLocales } = useI18n({
inheritLocale: true,
useScope: 'global'
})
const formKitConfig: any = inject(Symbol.for('FormKitConfig'))
availableLocales.forEach(locale => {
console.log(`${locale} locale `)
Expand Down Expand Up @@ -61,6 +61,7 @@ watch(() => locale.value,(newValue, oldValue) => {
console.log('locale changed: ' + newValue + " " + oldValue)
const storedLanguage = useLocalStorage("jelu_language", oldValue)
storedLanguage.value = newValue
formKitConfig.locale = newValue
})
</script>
Expand Down
73 changes: 73 additions & 0 deletions src/jelu-ui/src/formkit-theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Create some re-useable definitions because
// many input types are identical in how
// we want to style them.
const textClassification = {
label: 'label label-text font-semibold formkit-invalid:text-error capitalize',
wrapper: `
form-control
`,
input: 'input input-primary',
}
const boxClassification = {
outer: ' mt-2',
inner: 'self-center flex items-center content-center',
input: 'checkbox checkbox-primary',
fieldset: 'max-w-md border border-gray-400 rounded-md px-2 pb-1',
legend: 'font-bold text-sm',
wrapper: 'flex items-center content-center cursor-pointer mb-0',
label: 'label label-text mt-1 mx-1 capitalize self-center'
}
const buttonClassification = {
input: 'btn'
}

// export our definitions using our above
// templates and declare one-offs and
// overrides as needed.
export default {
// the global key will apply to all inputs
global: {
help: 'label label-text mt-0',
message: 'text-error'
},
button: buttonClassification,
color: {
label: 'block mb-1 font-bold text-sm',
input: 'w-16 h-8 appearance-none cursor-pointer border border-gray-300 rounded-md mb-2 p-1'
},
date: textClassification,
'datetime-local': textClassification,
checkbox: boxClassification,
email: textClassification,
file: {
label: 'block mb-1 font-bold text-sm',
inner: 'max-w-md cursor-pointer',
input: 'text-gray-600 text-sm mb-1 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:bg-blue-500 file:text-white hover:file:bg-blue-600',
noFiles: 'block text-gray-800 text-sm mb-1',
fileItem: 'block flex text-gray-800 text-sm mb-1',
removeFiles: 'ml-auto text-blue-500 text-sm'
},
month: textClassification,
number: textClassification,
password: textClassification,
radio: {
...boxClassification,
input: boxClassification.input.replace('rounded-sm', 'rounded-full'),
},
range: {
inner: 'max-w-md',
input: 'form-range appearance-none w-full h-2 p-0 bg-gray-200 rounded-full focus:outline-none focus:ring-0 focus:shadow-none'
},
search: textClassification,
select: textClassification,
submit: buttonClassification,
tel: textClassification,
text: textClassification,
textarea: {
...textClassification,
input: 'block w-full h-32 px-3 border-none text-base text-gray-700 placeholder-gray-400 focus:shadow-outline',
},
time: textClassification,
url: textClassification,
week: textClassification,
}
13 changes: 12 additions & 1 deletion src/jelu-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@
"csv_import" : "Csv import",
"authors" : "Authors",
"username" : "username",
"role" : "role | roles"
"role" : "role | roles",
"users" : "Users",
"users_management" : "Users management"
},
"user" : {
"log_first" : "Please log in first"
Expand Down Expand Up @@ -207,6 +209,15 @@
"password_not_match" : "password fields do not match",
"login_length" : "login must be 3 chars long minimum",
"password_length" : "password must be 3 chars long minimum"
},
"admin_user" : {
"create_user" : "create user",
"password_confirm" : "confirm password",
"password" : "password",
"admin" : "admin",
"admin_help" : "user should have admin rights ?",
"user_saved" : "User saved : {name}",
"login" : "login"
}
}

13 changes: 12 additions & 1 deletion src/jelu-ui/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@
"csv_import" : "Import Csv",
"authors" : "Auteurs",
"username" : "nom d'utilisateur",
"role" : "rôle | rôles"
"role" : "rôle | rôles",
"users" : "Utilisateurs",
"users_management" : "gestion des utilisateurs"
},
"user" : {
"log_first" : "Veuillez vous identifier"
Expand Down Expand Up @@ -208,5 +210,14 @@
"password_not_match" : "les mots de passe ne sont pas identiques",
"login_length" : "l'identifiant doit avoir une taille de 3 caractères au moins",
"password_length" : "le mot de passe doit avoir une taille de 3 caractères au moins"
},
"admin_user" : {
"create_user" : "créer utilisateur",
"password_confirm" : "confirmer mot de passe",
"password" : "mot de passe",
"admin" : "admin",
"admin_help" : "Créer utilisateur avec droits admin ?",
"user_saved" : "Utilisateur sauvegardé : {name}",
"login" : "identifiant"
}
}
13 changes: 13 additions & 0 deletions src/jelu-ui/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import SidebarMenu from 'vuejs-sidebar-menu'
import router from './router'
import store, { key } from './store'
import VueSplide from '@splidejs/vue-splide';
import { plugin, defaultConfig } from '@formkit/vue'
import { ar, hr, cs, da, nl, fi, fy, he, id, it, ko, fa, pl, pt, ru, es, tr, vi, de, fr, zh } from '@formkit/i18n'
import { generateClasses } from '@formkit/tailwindcss'
import formkitTheme from './formkit-theme'

import './assets/style.css'

Expand Down Expand Up @@ -58,4 +62,13 @@ createApp(App)
})
.use(SidebarMenu)
.use(VueSplide)
.use(plugin, defaultConfig({
config : {
classes: generateClasses(formkitTheme)
},
// Define additional locales
locales: { ar, hr, cs, da, nl, fi, fy, he, id, it, ko, fa, pl, pt, ru, es, tr, vi, de, fr, zh },
// Define the active locale
locale: storedLanguage.value,
}))
.mount('#app')
5 changes: 5 additions & 0 deletions src/jelu-ui/src/model/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ export interface UserAuthentication {
user: User,
token?: string
}
export interface CreateUser {
login: string,
password: string,
isAdmin: boolean
}
8 changes: 8 additions & 0 deletions src/jelu-ui/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ const isLogged = () => {
}
}

const isAdmin = () => {
if (!store.getters.isAdmin) {
console.log("is not admin")
return false
}
}

const router = createRouter({
history: createWebHistory(),
linkActiveClass: 'is-active',
Expand Down Expand Up @@ -80,6 +87,7 @@ const router = createRouter({
children: [
{ path : 'me', component: () => import(/* webpackChunkName: "recommend" */ './components/ProfilePage.vue')},
{ path : 'admin/authors', component: () => import(/* webpackChunkName: "recommend" */ './components/AdminAuthors.vue')},
{ path : 'admin/users', beforeEnter: [isAdmin], component: () => import(/* webpackChunkName: "recommend" */ './components/AdminUsers.vue')},
{ path: 'imports', component: () => import(/* webpackChunkName: "recommend" */ './components/Imports.vue')},
{ path: 'settings', component: () => import(/* webpackChunkName: "recommend" */ './components/UserSettings.vue')},

Expand Down
Loading

0 comments on commit 631e386

Please sign in to comment.