1
Fork 0

Add user management in settings

This commit is contained in:
viktorstrate 2019-07-31 16:23:25 +02:00
parent e0f6d8fa0b
commit ab278a5a99
8 changed files with 416 additions and 6 deletions

View File

@ -56,7 +56,6 @@ const schema = makeAugmentedSchema({
hasRole: true,
},
mutation: false,
query: true,
query: {
exclude: [
'ScannerResult',

View File

@ -1,6 +1,6 @@
import { neo4jgraphql } from 'neo4j-graphql-js'
import jwt from 'jsonwebtoken'
import { registerUser } from './users'
import { registerUser, authorizeUser } from './users'
async function initialSetup(driver) {
const session = driver.session()
@ -59,10 +59,12 @@ const Mutation = {
session.close()
const token = (await authorizeUser(root, args, ctx, info)).token
return {
success: true,
status: 'Initial setup successful',
token: userResult.token,
token,
}
},
}

View File

@ -84,9 +84,30 @@ const Mutation = {
token: token,
}
},
async updateUser(root, args, ctx, info) {
if (args.rootPath) {
if (!(await fs.exists(args.rootPath))) {
throw Error('New root path not found in server filesystem')
}
}
return neo4jgraphql(root, args, ctx, info)
},
async createUser(root, args, ctx, info) {
if (args.rootPath) {
if (!(await fs.exists(args.rootPath))) {
throw Error('Root path not found in server filesystem')
}
}
args.id = uuid()
return neo4jgraphql(root, args, ctx, info)
},
}
export const registerUser = Mutation.registerUser
export const authorizeUser = Mutation.authorizeUser
const Query = {
myUser(root, args, ctx, info) {

View File

@ -106,6 +106,12 @@ type Mutation {
password: String!
rootPath: String!
): AuthorizeResult @neo4j_ignore
updateUser(id: ID!, username: String, rootPath: String, admin: Boolean): User
@hasRole(roles: [admin])
createUser(id: ID, username: String, rootPath: String, admin: Boolean): User
@hasRole(roles: [admin])
deleteUser(id: ID!): User @hasRole(roles: [admin])
}
type Query {

View File

@ -0,0 +1,104 @@
import React, { useState } from 'react'
import { Mutation } from 'react-apollo'
import { Table, Button, Input, Checkbox } from 'semantic-ui-react'
import gql from 'graphql-tag'
const createUserMutation = gql`
mutation createUser($username: String, $rootPath: String, $admin: Boolean) {
createUser(username: $username, rootPath: $rootPath, admin: $admin) {
id
username
rootPath
admin
}
}
`
const initialState = {
username: '',
rootPath: '',
admin: false,
}
const AddUserRow = ({ setShow, show, onUserAdded }) => {
const [state, setState] = useState(initialState)
function updateInput(event, key) {
setState({
...state,
[key]: event.target.value,
})
}
if (!show) {
return null
}
return (
<Mutation
mutation={createUserMutation}
onCompleted={data => {
onUserAdded()
}}
>
{(createUser, { loading, data }) => (
<Table.Row>
<Table.Cell>
<Input
placeholder="Username"
value={state.username}
onChange={e => updateInput(e, 'username')}
/>
</Table.Cell>
<Table.Cell>
<Input
placeholder="/path/to/photos"
value={state.rootPath}
onChange={e => updateInput(e, 'rootPath')}
/>
</Table.Cell>
<Table.Cell>
<Checkbox
toggle
checked={state.admin}
onChange={(e, data) => {
console.log(data)
setState({
...state,
admin: data.checked,
})
}}
/>
</Table.Cell>
<Table.Cell>
<Button.Group>
<Button negative onClick={e => setShow(false)}>
Cancel
</Button>
<Button
type="submit"
loading={loading}
disabled={loading}
positive
onClick={() => {
createUser({
variables: {
username: state.username,
rootPath: state.rootPath,
admin: state.admin,
},
})
setState(initialState)
}}
>
Add User
</Button>
</Button.Group>
</Table.Cell>
</Table.Row>
)}
</Mutation>
)
}
export default AddUserRow

View File

@ -4,6 +4,7 @@ import Layout from '../../Layout'
import { Button, Icon } from 'semantic-ui-react'
import { Mutation } from 'react-apollo'
import gql from 'graphql-tag'
import UsersTable from './UsersTable'
const scanMutation = gql`
mutation scanAllMutation {
@ -18,7 +19,7 @@ const SettingsPage = () => (
<Layout>
<h1>Settings</h1>
<Mutation mutation={scanMutation}>
{(scan, { data }) => (
{(scan, { data, called }) => (
<>
<h2>Scanner</h2>
<Button
@ -27,15 +28,15 @@ const SettingsPage = () => (
onClick={() => {
scan()
}}
disabled={data && data.scanAll && data.scanAll.success}
disabled={called}
>
<Icon name="sync" />
Scan All
</Button>
<p>Scan for new images for all users</p>
</>
)}
</Mutation>
<UsersTable />
</Layout>
)

View File

@ -0,0 +1,197 @@
import React, { useState } from 'react'
import { Mutation } from 'react-apollo'
import { Table, Icon, Button, Input, Checkbox, Modal } from 'semantic-ui-react'
import gql from 'graphql-tag'
const updateUserMutation = gql`
mutation updateUser(
$id: ID!
$username: String
$rootPath: String
$admin: Boolean
) {
updateUser(
id: $id
username: $username
rootPath: $rootPath
admin: $admin
) {
id
username
rootPath
admin
}
}
`
const deleteUserMutation = gql`
mutation deleteUser($id: ID!) {
deleteUser(id: $id) {
id
username
}
}
`
const UserRow = ({ user, refetchUsers }) => {
const [state, setState] = useState({
...user,
editing: false,
})
const [showComfirmDelete, setConfirmDelete] = useState(false)
function updateInput(event, key) {
setState({
...state,
[key]: event.target.value,
})
}
if (state.editing) {
return (
<Mutation
mutation={updateUserMutation}
onCompleted={data => {
setState({
...data.updateUser,
editing: false,
})
refetchUsers()
}}
>
{(updateUser, { loading, data }) => (
<Table.Row>
<Table.Cell>
<Input
placeholder={user.username}
value={state.username}
onChange={e => updateInput(e, 'username')}
/>
</Table.Cell>
<Table.Cell>
<Input
placeholder={user.rootPath}
value={state.rootPath}
onChange={e => updateInput(e, 'rootPath')}
/>
</Table.Cell>
<Table.Cell>
<Checkbox
toggle
checked={state.admin}
onChange={(e, data) => {
setState({
...state,
admin: data.checked,
})
}}
/>
</Table.Cell>
<Table.Cell>
<Button.Group>
<Button
negative
onClick={e =>
setState({
...state.oldState,
})
}
>
Cancel
</Button>
<Button
loading={loading}
disabled={loading}
positive
onClick={() =>
updateUser({
variables: {
id: user.id,
username: state.username,
rootPath: state.rootPath,
admin: state.admin,
},
})
}
>
Save
</Button>
</Button.Group>
</Table.Cell>
</Table.Row>
)}
</Mutation>
)
}
return (
<Mutation
mutation={deleteUserMutation}
onCompleted={() => {
refetchUsers()
}}
>
{(deleteUser, { loading, data }) => (
<Table.Row>
<Table.Cell>{user.username}</Table.Cell>
<Table.Cell>{user.rootPath}</Table.Cell>
<Table.Cell>
{user.admin ? <Icon name="checkmark" size="large" /> : null}
</Table.Cell>
<Table.Cell>
<Button.Group>
<Button
onClick={() => {
setState({ ...state, editing: true, oldState: state })
}}
>
<Icon name="edit" />
Edit
</Button>
<Button
negative
onClick={() => {
setConfirmDelete(true)
}}
>
<Icon name="delete" />
Delete
</Button>
<Modal open={showComfirmDelete}>
<Modal.Header>Delete user</Modal.Header>
<Modal.Content>
<p>
{`Are you sure, you want to delete `}
<b>{user.username}</b>?
</p>
<p>{`This action cannot be undone`}</p>
</Modal.Content>
<Modal.Actions>
<Button onClick={() => setConfirmDelete(false)}>
Cancel
</Button>
<Button
negative
onClick={() => {
setConfirmDelete(false)
deleteUser({
variables: {
id: user.id,
},
})
}}
>
Delete {user.username}
</Button>
</Modal.Actions>
</Modal>
</Button.Group>
</Table.Cell>
</Table.Row>
)}
</Mutation>
)
}
export default UserRow

View File

@ -0,0 +1,80 @@
import React, { useState } from 'react'
import { Table, Loader, Button } from 'semantic-ui-react'
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
import UserRow from './UserRow'
import AddUserRow from './AddUserRow'
const usersQuery = gql`
query settingsUsersQuery {
User {
id
username
rootPath
admin
}
}
`
const UsersTable = () => {
const [showAddUser, setShowAddUser] = useState(false)
return (
<Query query={usersQuery}>
{({ loading, error, data, refetch }) => {
let userRows = []
if (data && data.User) {
userRows = data.User.map(user => (
<UserRow user={user} refetchUsers={refetch} key={user.id} />
))
}
return (
<div style={{ marginTop: 24 }}>
<h2>Users</h2>
<Loader active={loading} />
<Table celled>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Username</Table.HeaderCell>
<Table.HeaderCell>Photo path</Table.HeaderCell>
<Table.HeaderCell>Admin</Table.HeaderCell>
<Table.HeaderCell>Action</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{userRows}
<AddUserRow
show={showAddUser}
setShow={setShowAddUser}
onUserAdded={() => {
setShowAddUser(false)
refetch()
}}
/>
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan="4">
<Button
positive
floated="right"
onClick={e => setShowAddUser(true)}
>
New user
</Button>
</Table.HeaderCell>
</Table.Row>
</Table.Footer>
</Table>
</div>
)
}}
</Query>
)
}
export default UsersTable