mirror of
https://github.com/koel/koel
synced 2024-11-28 06:50:27 +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>
|
||||
|
||||
<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>
|
||||
Add</button>
|
||||
</div>
|
||||
|
@ -15,63 +15,50 @@
|
|||
|
||||
<div class="main-scroll-wrap">
|
||||
<div class="users">
|
||||
<form class="user-create user-item" v-if="creating" @submit.prevent="store">
|
||||
<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"/>
|
||||
|
||||
<user-item v-for="user in state.users" :user="user" @editUser="editUser"/>
|
||||
<article class="user-item" v-for="n in 6"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<edit-user-form ref="editUserForm"/>
|
||||
<add-user-form ref="addUserForm"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { clone } from 'lodash'
|
||||
import isMobile from 'ismobilejs'
|
||||
|
||||
import { userStore } from '../../../stores'
|
||||
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 {
|
||||
components: { userItem },
|
||||
components: { userItem, editUserForm, addUserForm },
|
||||
|
||||
data () {
|
||||
return {
|
||||
state: userStore.state,
|
||||
isPhone: isMobile.phone,
|
||||
showingControls: false,
|
||||
creating: false,
|
||||
newUser: {}
|
||||
showingControls: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Store the newly created user.
|
||||
* Open the "Add User" form.
|
||||
*/
|
||||
store () {
|
||||
userStore.store(this.newUser.name, this.newUser.email, this.newUser.password).then(u => {
|
||||
this.newUser = clone(userStore.stub)
|
||||
this.creating = false
|
||||
})
|
||||
addUser () {
|
||||
this.$refs.addUserForm.open()
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the "Edit User" form.
|
||||
*
|
||||
* @param {Object} user
|
||||
*/
|
||||
editUser (user) {
|
||||
this.$refs.editUserForm.open(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,36 +75,6 @@ export default {
|
|||
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 {
|
||||
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>
|
||||
<div id="editSongsOverlay" v-if="shown">
|
||||
<div id="editSongsOverlay" v-if="shown" class="overlay">
|
||||
<sound-bar v-if="loading"></sound-bar>
|
||||
<form v-else @submit.prevent="submit">
|
||||
<header>
|
||||
|
@ -330,66 +330,8 @@ export default {
|
|||
@import "../../../sass/partials/_mixins.scss";
|
||||
|
||||
#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 {
|
||||
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 {
|
||||
display: flex;
|
||||
background: #eee;
|
||||
border-radius: $borderRadius $borderRadius 0 0;
|
||||
|
||||
img {
|
||||
flex: 0 0 96px;
|
||||
}
|
||||
|
@ -398,12 +340,6 @@ export default {
|
|||
flex: 1;
|
||||
padding-left: 8px;
|
||||
|
||||
h1 {
|
||||
font-size: 1.8rem;
|
||||
line-height: 2.2rem;
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
|
||||
.mixed {
|
||||
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>
|
||||
<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">
|
||||
<sound-bar v-show="state.type === 'loading'"/>
|
||||
<i class="fa fa-exclamation-circle" v-show="state.type === 'error'"/>
|
||||
|
@ -85,15 +85,7 @@ export default {
|
|||
@import "../../../sass/partials/_mixins.scss";
|
||||
|
||||
#overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
|
||||
@include vertical-center();
|
||||
flex-direction: column;
|
||||
|
||||
.display {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<article class="user-item" :class="{ editing: editing, me: isCurrentUser }">
|
||||
<div class="info" v-if="!editing">
|
||||
<article class="user-item" :class="{ me: isCurrentUser }">
|
||||
<div class="info">
|
||||
<img :src="user.avatar" width="128" height="128">
|
||||
|
||||
<div class="right">
|
||||
|
@ -19,31 +19,10 @@
|
|||
</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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { clone, assign } from 'lodash'
|
||||
import swal from 'sweetalert'
|
||||
|
||||
import { userStore } from '../../stores'
|
||||
|
@ -54,9 +33,7 @@ export default {
|
|||
|
||||
data () {
|
||||
return {
|
||||
editing: false,
|
||||
confirmingDelete: false,
|
||||
cached: {}
|
||||
confirmingDelete: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -83,27 +60,7 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
// Keep a cached version of the user for rolling back.
|
||||
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
|
||||
})
|
||||
this.$emit('editUser', this.user)
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -171,6 +171,8 @@ export const userStore = {
|
|||
return new Promise((resolve, reject) => {
|
||||
http.put(`user/${user.id}`, { name, email, password }, () => {
|
||||
this.setAvatar(user)
|
||||
user.name = name
|
||||
user.email = email
|
||||
user.password = ''
|
||||
resolve(user)
|
||||
}, r => reject(r))
|
||||
|
|
|
@ -246,3 +246,74 @@ button, input[type="submit"], input[type="reset"], input[type="button"], .btn {
|
|||
margin-top: 16px;
|
||||
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()
|
||||
// Add new user
|
||||
->click('#usersWrapper .btn-add');
|
||||
$this->see('form.user-create')
|
||||
->typeIn('form.user-create input[name="name"]', 'Foo')
|
||||
->typeIn('form.user-create input[name="email"]', 'foo@koel.net')
|
||||
->typeIn('form.user-create input[name="password"]', 'SecureMuch')
|
||||
$this->see('form.user-add')
|
||||
->typeIn('form.user-add input[name="name"]', 'Foo')
|
||||
->typeIn('form.user-add input[name="email"]', 'foo@koel.net')
|
||||
->typeIn('form.user-add input[name="password"]', 'SecureMuch')
|
||||
->enter()
|
||||
->seeText('foo@koel.net', '#usersWrapper');
|
||||
|
||||
|
|
Loading…
Reference in a new issue