mirror of
https://github.com/koel/koel
synced 2025-02-17 22:08:28 +00:00
Revamp user add/edit forms
This commit is contained in:
parent
30237d27ad
commit
4595f0bff4
9 changed files with 242 additions and 189 deletions
|
@ -7,7 +7,7 @@
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="buttons" v-show="!isPhone || showingControls">
|
<div class="buttons" v-show="!isPhone || showingControls">
|
||||||
<button class="btn btn-green btn-add" @click="creating = !creating">
|
<button class="btn btn-green btn-add" @click="addUser">
|
||||||
<i class="fa fa-plus"></i>
|
<i class="fa fa-plus"></i>
|
||||||
Add</button>
|
Add</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,63 +15,50 @@
|
||||||
|
|
||||||
<div class="main-scroll-wrap">
|
<div class="main-scroll-wrap">
|
||||||
<div class="users">
|
<div class="users">
|
||||||
<form class="user-create user-item" v-if="creating" @submit.prevent="store">
|
<user-item v-for="user in state.users" :user="user" @editUser="editUser"/>
|
||||||
<div class="input-row">
|
|
||||||
<label>Name</label>
|
|
||||||
<input type="text" name="name" v-model="newUser.name" required v-koel-focus>
|
|
||||||
</div>
|
|
||||||
<div class="input-row">
|
|
||||||
<label>Email</label>
|
|
||||||
<input type="email" name="email" v-model="newUser.email" required>
|
|
||||||
</div>
|
|
||||||
<div class="input-row">
|
|
||||||
<label>Password</label>
|
|
||||||
<input type="password" name="password" v-model="newUser.password" required>
|
|
||||||
</div>
|
|
||||||
<div class="input-row">
|
|
||||||
<label></label>
|
|
||||||
<button class="btn btn-green btn-create">Create</button>
|
|
||||||
<button class="btn btn-red btn-cancel" @click.prevent="creating = false">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<user-item v-for="user in state.users" :user="user"/>
|
|
||||||
|
|
||||||
<article class="user-item" v-for="n in 6"/>
|
<article class="user-item" v-for="n in 6"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<edit-user-form ref="editUserForm"/>
|
||||||
|
<add-user-form ref="addUserForm"/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { clone } from 'lodash'
|
|
||||||
import isMobile from 'ismobilejs'
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
import { userStore } from '../../../stores'
|
import { userStore } from '../../../stores'
|
||||||
import userItem from '../../shared/user-item.vue'
|
import userItem from '../../shared/user-item.vue'
|
||||||
|
import editUserForm from '../../modals/edit-user-form.vue'
|
||||||
|
import addUserForm from '../../modals/add-user-form.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { userItem },
|
components: { userItem, editUserForm, addUserForm },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
state: userStore.state,
|
state: userStore.state,
|
||||||
isPhone: isMobile.phone,
|
isPhone: isMobile.phone,
|
||||||
showingControls: false,
|
showingControls: false
|
||||||
creating: false,
|
|
||||||
newUser: {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
* Store the newly created user.
|
* Open the "Add User" form.
|
||||||
*/
|
*/
|
||||||
store () {
|
addUser () {
|
||||||
userStore.store(this.newUser.name, this.newUser.email, this.newUser.password).then(u => {
|
this.$refs.addUserForm.open()
|
||||||
this.newUser = clone(userStore.stub)
|
},
|
||||||
this.creating = false
|
|
||||||
})
|
/**
|
||||||
|
* Open the "Edit User" form.
|
||||||
|
*
|
||||||
|
* @param {Object} user
|
||||||
|
*/
|
||||||
|
editUser (user) {
|
||||||
|
this.$refs.editUserForm.open(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,36 +75,6 @@ export default {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
|
||||||
padding: 8px 16px;
|
|
||||||
background-size: 30px 30px;
|
|
||||||
background-image: linear-gradient(
|
|
||||||
-45deg,
|
|
||||||
rgba(black, 0.3) 25%,
|
|
||||||
transparent 25%,
|
|
||||||
transparent 50%,
|
|
||||||
rgba(black, 0.3) 50%,
|
|
||||||
rgba(black, 0.3) 75%,
|
|
||||||
transparent 75%,
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
|
|
||||||
.input-row {
|
|
||||||
display: flex;
|
|
||||||
margin: 6px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
flex: 0 0 128px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
button {
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
|
|
66
resources/assets/js/components/modals/add-user-form.vue
Normal file
66
resources/assets/js/components/modals/add-user-form.vue
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<div class="overlay" v-if="newUser">
|
||||||
|
<sound-bar v-if="loading"/>
|
||||||
|
<form class="user-add" @submit.prevent="submit" v-else>
|
||||||
|
<header>
|
||||||
|
<h1>Add New User</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Name</label>
|
||||||
|
<input type="text" name="name" v-model="newUser.name" required v-koel-focus>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Email</label>
|
||||||
|
<input type="email" name="email" v-model="newUser.email" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Password</label>
|
||||||
|
<input type="password" name="password" v-model="newUser.password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<button class="btn btn-green btn-add">Save</button>
|
||||||
|
<button class="btn btn-white btn-cancel" @click.prevent="cancel">Cancel</button>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</overlay>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { clone } from 'lodash'
|
||||||
|
import soundBar from '../shared/sound-bar.vue'
|
||||||
|
import { userStore } from '../../stores'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'modals--add-user-form',
|
||||||
|
components: { soundBar },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
newUser: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
open () {
|
||||||
|
this.newUser = clone(userStore.stub)
|
||||||
|
},
|
||||||
|
|
||||||
|
submit () {
|
||||||
|
this.loading = true
|
||||||
|
userStore.store(this.newUser.name, this.newUser.email, this.newUser.password).then(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.newUser = null
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel () {
|
||||||
|
this.newUser = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="editSongsOverlay" v-if="shown">
|
<div id="editSongsOverlay" v-if="shown" class="overlay">
|
||||||
<sound-bar v-if="loading"></sound-bar>
|
<sound-bar v-if="loading"></sound-bar>
|
||||||
<form v-else @submit.prevent="submit">
|
<form v-else @submit.prevent="submit">
|
||||||
<header>
|
<header>
|
||||||
|
@ -330,66 +330,8 @@ export default {
|
||||||
@import "../../../sass/partials/_mixins.scss";
|
@import "../../../sass/partials/_mixins.scss";
|
||||||
|
|
||||||
#editSongsOverlay {
|
#editSongsOverlay {
|
||||||
z-index: 9999;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: rgba(0, 0, 0, .7);
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
@include vertical-center();
|
|
||||||
|
|
||||||
$borderRadius: 5px;
|
|
||||||
|
|
||||||
form {
|
form {
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 460px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: $borderRadius;
|
|
||||||
color: #333;
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
> header, > div, > footer {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"], input[type="number"], textarea {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border-color: $colorOrange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
color: #f00;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
min-height: 192px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> header {
|
> header {
|
||||||
display: flex;
|
|
||||||
background: #eee;
|
|
||||||
border-radius: $borderRadius $borderRadius 0 0;
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
flex: 0 0 96px;
|
flex: 0 0 96px;
|
||||||
}
|
}
|
||||||
|
@ -398,12 +340,6 @@ export default {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 1.8rem;
|
|
||||||
line-height: 2.2rem;
|
|
||||||
margin-bottom: .3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mixed {
|
.mixed {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
}
|
}
|
||||||
|
|
72
resources/assets/js/components/modals/edit-user-form.vue
Normal file
72
resources/assets/js/components/modals/edit-user-form.vue
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<div class="overlay" v-if="copiedUser">
|
||||||
|
<sound-bar v-if="loading"/>
|
||||||
|
<form class="user-edit" @submit.prevent="submit" v-else>
|
||||||
|
<header>
|
||||||
|
<h1>Edit User</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Name</label>
|
||||||
|
<input type="text" name="name" v-model="copiedUser.name" required v-koel-focus>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Email</label>
|
||||||
|
<input type="email" name="email" v-model="copiedUser.email" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Password</label>
|
||||||
|
<input type="password" name="password" v-model="copiedUser.password" placeholder="Leave blank for no changes">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<button class="btn btn-green btn-update">Update</button>
|
||||||
|
<button class="btn btn-white btn-cancel" @click.prevent="cancel">Cancel</button>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</overlay>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { clone } from 'lodash'
|
||||||
|
import soundBar from '../shared/sound-bar.vue'
|
||||||
|
import { userStore } from '../../stores'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'modals--edit-user-form',
|
||||||
|
components: { soundBar },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
user: null,
|
||||||
|
loading: false,
|
||||||
|
// We work on a cloned version of the user
|
||||||
|
copiedUser: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
open (user) {
|
||||||
|
this.copiedUser = clone(user)
|
||||||
|
// Keep a reference
|
||||||
|
this.user = user
|
||||||
|
},
|
||||||
|
|
||||||
|
submit () {
|
||||||
|
this.loading = true
|
||||||
|
userStore.update(this.user, this.copiedUser.name, this.copiedUser.email, this.copiedUser.password)
|
||||||
|
.then(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.copiedUser = null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel () {
|
||||||
|
this.copiedUser = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="overlay" v-show="state.showing" :class="state.type">
|
<div id="overlay" v-show="state.showing" class="overlay" :class="state.type">
|
||||||
<div class="display">
|
<div class="display">
|
||||||
<sound-bar v-show="state.type === 'loading'"/>
|
<sound-bar v-show="state.type === 'loading'"/>
|
||||||
<i class="fa fa-exclamation-circle" v-show="state.type === 'error'"/>
|
<i class="fa fa-exclamation-circle" v-show="state.type === 'error'"/>
|
||||||
|
@ -85,15 +85,7 @@ export default {
|
||||||
@import "../../../sass/partials/_mixins.scss";
|
@import "../../../sass/partials/_mixins.scss";
|
||||||
|
|
||||||
#overlay {
|
#overlay {
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 9999;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 1);
|
background-color: rgba(0, 0, 0, 1);
|
||||||
|
|
||||||
@include vertical-center();
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.display {
|
.display {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<article class="user-item" :class="{ editing: editing, me: isCurrentUser }">
|
<article class="user-item" :class="{ me: isCurrentUser }">
|
||||||
<div class="info" v-if="!editing">
|
<div class="info">
|
||||||
<img :src="user.avatar" width="128" height="128">
|
<img :src="user.avatar" width="128" height="128">
|
||||||
|
|
||||||
<div class="right">
|
<div class="right">
|
||||||
|
@ -19,31 +19,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="user-edit" @submit.prevent="update" v-else>
|
|
||||||
<div class="input-row">
|
|
||||||
<label>Name</label>
|
|
||||||
<input type="text" name="name" v-model="user.name" required v-koel-focus>
|
|
||||||
</div>
|
|
||||||
<div class="input-row">
|
|
||||||
<label>Email</label>
|
|
||||||
<input type="email" name="email" v-model="user.email" required>
|
|
||||||
</div>
|
|
||||||
<div class="input-row">
|
|
||||||
<label>Password</label>
|
|
||||||
<input type="password" name="password" v-model="user.password" placeholder="Leave blank for no changes">
|
|
||||||
</div>
|
|
||||||
<div class="input-row">
|
|
||||||
<label></label>
|
|
||||||
<button class="btn btn-green btn-update">Update</button>
|
|
||||||
<button class="btn btn-red btn-cancel" @click.prevent="cancelEdit">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</article>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { clone, assign } from 'lodash'
|
|
||||||
import swal from 'sweetalert'
|
import swal from 'sweetalert'
|
||||||
|
|
||||||
import { userStore } from '../../stores'
|
import { userStore } from '../../stores'
|
||||||
|
@ -54,9 +33,7 @@ export default {
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
editing: false,
|
confirmingDelete: false
|
||||||
confirmingDelete: false,
|
|
||||||
cached: {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -83,27 +60,7 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep a cached version of the user for rolling back.
|
this.$emit('editUser', this.user)
|
||||||
this.cached = clone(this.user)
|
|
||||||
this.editing = true
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel editing a user.
|
|
||||||
*/
|
|
||||||
cancelEdit () {
|
|
||||||
// Restore the original user's properties
|
|
||||||
assign(this.user, this.cached)
|
|
||||||
this.editing = false
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the edited user.
|
|
||||||
*/
|
|
||||||
update () {
|
|
||||||
userStore.update(this.user, this.user.name, this.user.email, this.user.password).then(u => {
|
|
||||||
this.editing = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -171,6 +171,8 @@ export const userStore = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.put(`user/${user.id}`, { name, email, password }, () => {
|
http.put(`user/${user.id}`, { name, email, password }, () => {
|
||||||
this.setAvatar(user)
|
this.setAvatar(user)
|
||||||
|
user.name = name
|
||||||
|
user.email = email
|
||||||
user.password = ''
|
user.password = ''
|
||||||
resolve(user)
|
resolve(user)
|
||||||
}, r => reject(r))
|
}, r => reject(r))
|
||||||
|
|
|
@ -246,3 +246,74 @@ button, input[type="submit"], input[type="reset"], input[type="button"], .btn {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: rgba(0, 0, 0, .7);
|
||||||
|
|
||||||
|
@include vertical-center();
|
||||||
|
|
||||||
|
form {
|
||||||
|
$borderRadius: 5px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 460px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: $borderRadius;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> header, > div, > footer {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"], input[type="number"], input[type="email"], input[type="password"],
|
||||||
|
input[type="url"], textarea {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: $colorOrange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
min-height: 192px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> header {
|
||||||
|
display: flex;
|
||||||
|
background: #eee;
|
||||||
|
border-radius: $borderRadius $borderRadius 0 0;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
line-height: 2.2rem;
|
||||||
|
margin-bottom: .3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,10 +21,10 @@ class UsersScreenTest extends TestCase
|
||||||
->back()
|
->back()
|
||||||
// Add new user
|
// Add new user
|
||||||
->click('#usersWrapper .btn-add');
|
->click('#usersWrapper .btn-add');
|
||||||
$this->see('form.user-create')
|
$this->see('form.user-add')
|
||||||
->typeIn('form.user-create input[name="name"]', 'Foo')
|
->typeIn('form.user-add input[name="name"]', 'Foo')
|
||||||
->typeIn('form.user-create input[name="email"]', 'foo@koel.net')
|
->typeIn('form.user-add input[name="email"]', 'foo@koel.net')
|
||||||
->typeIn('form.user-create input[name="password"]', 'SecureMuch')
|
->typeIn('form.user-add input[name="password"]', 'SecureMuch')
|
||||||
->enter()
|
->enter()
|
||||||
->seeText('foo@koel.net', '#usersWrapper');
|
->seeText('foo@koel.net', '#usersWrapper');
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue