mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 21:09:05 +00:00
Add UI for multi root users
This commit is contained in:
@@ -4,7 +4,7 @@ import styled from 'styled-components'
|
|||||||
import Layout from '../../Layout'
|
import Layout from '../../Layout'
|
||||||
|
|
||||||
import ScannerSection from './ScannerSection'
|
import ScannerSection from './ScannerSection'
|
||||||
import UsersTable from './UsersTable'
|
import UsersTable from './Users/UsersTable'
|
||||||
|
|
||||||
export const SectionTitle = styled.h2`
|
export const SectionTitle = styled.h2`
|
||||||
margin-top: ${({ nospace }) => (nospace ? '0' : '1.4em')} !important;
|
margin-top: ${({ nospace }) => (nospace ? '0' : '1.4em')} !important;
|
||||||
|
|||||||
@@ -1,298 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import React, { useState } from 'react'
|
|
||||||
import { gql, useMutation } from '@apollo/client'
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Checkbox,
|
|
||||||
Form,
|
|
||||||
Icon,
|
|
||||||
Input,
|
|
||||||
Modal,
|
|
||||||
Table,
|
|
||||||
} from 'semantic-ui-react'
|
|
||||||
|
|
||||||
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 changeUserPasswordMutation = gql`
|
|
||||||
mutation changeUserPassword($userId: ID!, $password: String!) {
|
|
||||||
updateUser(id: $userId, password: $password) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const scanUserMutation = gql`
|
|
||||||
mutation scanUser($userId: ID!) {
|
|
||||||
scanUser(userId: $userId) {
|
|
||||||
success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const ChangePasswordModal = ({ onClose, user, ...props }) => {
|
|
||||||
const [passwordInput, setPasswordInput] = useState('')
|
|
||||||
|
|
||||||
const [changePassword] = useMutation(changeUserPasswordMutation, {
|
|
||||||
onCompleted: () => {
|
|
||||||
onClose && onClose()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal {...props}>
|
|
||||||
<Modal.Header>Change password</Modal.Header>
|
|
||||||
<Modal.Content>
|
|
||||||
<p>
|
|
||||||
Change password for <b>{user.username}</b>
|
|
||||||
</p>
|
|
||||||
<Form>
|
|
||||||
<Form.Field>
|
|
||||||
<label>New password</label>
|
|
||||||
<Input
|
|
||||||
placeholder="password"
|
|
||||||
onChange={e => setPasswordInput(e.target.value)}
|
|
||||||
type="password"
|
|
||||||
/>
|
|
||||||
</Form.Field>
|
|
||||||
</Form>
|
|
||||||
</Modal.Content>
|
|
||||||
<Modal.Actions>
|
|
||||||
<Button onClick={() => onClose && onClose()}>Cancel</Button>
|
|
||||||
<Button
|
|
||||||
positive
|
|
||||||
onClick={() => {
|
|
||||||
changePassword({
|
|
||||||
variables: {
|
|
||||||
userId: user.id,
|
|
||||||
password: passwordInput,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Change password
|
|
||||||
</Button>
|
|
||||||
</Modal.Actions>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangePasswordModal.propTypes = {
|
|
||||||
onClose: PropTypes.func,
|
|
||||||
user: PropTypes.object.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserRow = ({ user, refetchUsers }) => {
|
|
||||||
const [state, setState] = useState({
|
|
||||||
...user,
|
|
||||||
editing: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const [showConfirmDelete, setConfirmDelete] = useState(false)
|
|
||||||
const [showChangePassword, setChangePassword] = useState(false)
|
|
||||||
|
|
||||||
function updateInput(event, key) {
|
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
[key]: event.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const [updateUser, { loading: updateUserLoading }] = useMutation(
|
|
||||||
updateUserMutation,
|
|
||||||
{
|
|
||||||
onCompleted: data => {
|
|
||||||
setState({
|
|
||||||
...data.updateUser,
|
|
||||||
editing: false,
|
|
||||||
})
|
|
||||||
refetchUsers()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const [deleteUser] = useMutation(deleteUserMutation, {
|
|
||||||
onCompleted: () => {
|
|
||||||
refetchUsers()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const [scanUser, { called: scanUserCalled }] = useMutation(scanUserMutation, {
|
|
||||||
onCompleted: () => {
|
|
||||||
refetchUsers()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (state.editing) {
|
|
||||||
return (
|
|
||||||
<Table.Row>
|
|
||||||
<Table.Cell>
|
|
||||||
<Input
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
placeholder={user.username}
|
|
||||||
value={state.username}
|
|
||||||
onChange={e => updateInput(e, 'username')}
|
|
||||||
/>
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell>
|
|
||||||
<Input
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
placeholder={user.rootPath}
|
|
||||||
value={state.rootPath}
|
|
||||||
onChange={e => updateInput(e, 'rootPath')}
|
|
||||||
/>
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell>
|
|
||||||
<Checkbox
|
|
||||||
toggle
|
|
||||||
checked={state.admin}
|
|
||||||
onChange={(_, data) => {
|
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
admin: data.checked,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Table.Cell>
|
|
||||||
<Table.Cell>
|
|
||||||
<Button.Group>
|
|
||||||
<Button
|
|
||||||
negative
|
|
||||||
onClick={() =>
|
|
||||||
setState({
|
|
||||||
...state.oldState,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
loading={updateUserLoading}
|
|
||||||
disabled={updateUserLoading}
|
|
||||||
positive
|
|
||||||
onClick={() =>
|
|
||||||
updateUser({
|
|
||||||
variables: {
|
|
||||||
id: user.id,
|
|
||||||
username: state.username,
|
|
||||||
rootPath: state.rootPath,
|
|
||||||
admin: state.admin,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</Button.Group>
|
|
||||||
</Table.Cell>
|
|
||||||
</Table.Row>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<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
|
|
||||||
disabled={scanUserCalled}
|
|
||||||
onClick={() => scanUser({ variables: { userId: user.id } })}
|
|
||||||
>
|
|
||||||
<Icon name="sync" />
|
|
||||||
Scan
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => setChangePassword(true)}>
|
|
||||||
<Icon name="key" />
|
|
||||||
Change password
|
|
||||||
</Button>
|
|
||||||
<ChangePasswordModal
|
|
||||||
user={user}
|
|
||||||
open={showChangePassword}
|
|
||||||
onClose={() => setChangePassword(false)}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
negative
|
|
||||||
onClick={() => {
|
|
||||||
setConfirmDelete(true)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name="delete" />
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
<Modal open={showConfirmDelete}>
|
|
||||||
<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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
UserRow.propTypes = {
|
|
||||||
user: PropTypes.object.isRequired,
|
|
||||||
refetchUsers: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UserRow
|
|
||||||
158
ui/src/Pages/SettingsPage/Users/EditUserRow.js
Normal file
158
ui/src/Pages/SettingsPage/Users/EditUserRow.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Button, Checkbox, Icon, Input, Table } from 'semantic-ui-react'
|
||||||
|
import { UserRowProps } from './UserRow'
|
||||||
|
|
||||||
|
const RootPathListItem = styled.li`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const EditRootPath = ({ filePath, removePath }) => (
|
||||||
|
<RootPathListItem>
|
||||||
|
<span>{filePath}</span>
|
||||||
|
<Button negative onClick={() => removePath()}>
|
||||||
|
<Icon name="remove" />
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
</RootPathListItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
EditRootPath.propTypes = {
|
||||||
|
filePath: PropTypes.string.isRequired,
|
||||||
|
removePath: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NewRootPathInput = styled(Input)`
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 24px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const EditNewRootPath = ({ state, updateInput }) => (
|
||||||
|
<li>
|
||||||
|
<NewRootPathInput
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
value={state.rootPath}
|
||||||
|
onChange={e => updateInput(e, 'rootPath')}
|
||||||
|
action={{
|
||||||
|
positive: true,
|
||||||
|
icon: 'add',
|
||||||
|
content: 'Add',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
|
||||||
|
EditNewRootPath.propTypes = {
|
||||||
|
state: PropTypes.object.isRequired,
|
||||||
|
updateInput: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
const RootPathList = styled.ul`
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
`
|
||||||
|
|
||||||
|
const EditRootPaths = ({ user, state, updateInput }) => {
|
||||||
|
const editRows = user.rootAlbums.map(album => (
|
||||||
|
<EditRootPath
|
||||||
|
key={album.id}
|
||||||
|
filePath={album.filePath}
|
||||||
|
removePath={() => {}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RootPathList>
|
||||||
|
{editRows}
|
||||||
|
<EditNewRootPath state={state} updateInput={updateInput} />
|
||||||
|
</RootPathList>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
EditRootPaths.propTypes = {
|
||||||
|
updateInput: PropTypes.func.isRequired,
|
||||||
|
user: PropTypes.object.isRequired,
|
||||||
|
state: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditUserRow = ({
|
||||||
|
user,
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
updateUser,
|
||||||
|
updateUserLoading,
|
||||||
|
}) => {
|
||||||
|
function updateInput(event, key) {
|
||||||
|
setState(state => ({
|
||||||
|
...state,
|
||||||
|
[key]: event.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table.Row>
|
||||||
|
<Table.Cell>
|
||||||
|
<Input
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder={user.username}
|
||||||
|
value={state.username}
|
||||||
|
onChange={e => updateInput(e, 'username')}
|
||||||
|
/>
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
<EditRootPaths user={user} state={state} updateInput={updateInput} />
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
<Checkbox
|
||||||
|
toggle
|
||||||
|
checked={state.admin}
|
||||||
|
onChange={(_, data) => {
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
admin: data.checked,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
<Button.Group>
|
||||||
|
<Button
|
||||||
|
negative
|
||||||
|
onClick={() =>
|
||||||
|
setState({
|
||||||
|
...state.oldState,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
loading={updateUserLoading}
|
||||||
|
disabled={updateUserLoading}
|
||||||
|
positive
|
||||||
|
onClick={() =>
|
||||||
|
updateUser({
|
||||||
|
variables: {
|
||||||
|
id: user.id,
|
||||||
|
username: state.username,
|
||||||
|
rootPath: state.rootPath,
|
||||||
|
admin: state.admin,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Button.Group>
|
||||||
|
</Table.Cell>
|
||||||
|
</Table.Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
EditUserRow.propTypes = UserRowProps
|
||||||
|
|
||||||
|
export default EditUserRow
|
||||||
66
ui/src/Pages/SettingsPage/Users/UserChangePassword.js
Normal file
66
ui/src/Pages/SettingsPage/Users/UserChangePassword.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { gql, useMutation } from '@apollo/client'
|
||||||
|
import { Button, Form, Input, Modal } from 'semantic-ui-react'
|
||||||
|
|
||||||
|
const changeUserPasswordMutation = gql`
|
||||||
|
mutation changeUserPassword($userId: ID!, $password: String!) {
|
||||||
|
updateUser(id: $userId, password: $password) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ChangePasswordModal = ({ onClose, user, ...props }) => {
|
||||||
|
const [passwordInput, setPasswordInput] = useState('')
|
||||||
|
|
||||||
|
const [changePassword] = useMutation(changeUserPasswordMutation, {
|
||||||
|
onCompleted: () => {
|
||||||
|
onClose && onClose()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal {...props}>
|
||||||
|
<Modal.Header>Change password</Modal.Header>
|
||||||
|
<Modal.Content>
|
||||||
|
<p>
|
||||||
|
Change password for <b>{user.username}</b>
|
||||||
|
</p>
|
||||||
|
<Form>
|
||||||
|
<Form.Field>
|
||||||
|
<label>New password</label>
|
||||||
|
<Input
|
||||||
|
placeholder="password"
|
||||||
|
onChange={e => setPasswordInput(e.target.value)}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</Form.Field>
|
||||||
|
</Form>
|
||||||
|
</Modal.Content>
|
||||||
|
<Modal.Actions>
|
||||||
|
<Button onClick={() => onClose && onClose()}>Cancel</Button>
|
||||||
|
<Button
|
||||||
|
positive
|
||||||
|
onClick={() => {
|
||||||
|
changePassword({
|
||||||
|
variables: {
|
||||||
|
userId: user.id,
|
||||||
|
password: passwordInput,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Change password
|
||||||
|
</Button>
|
||||||
|
</Modal.Actions>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangePasswordModal.propTypes = {
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
user: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChangePasswordModal
|
||||||
121
ui/src/Pages/SettingsPage/Users/UserRow.js
Normal file
121
ui/src/Pages/SettingsPage/Users/UserRow.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { gql, useMutation } from '@apollo/client'
|
||||||
|
import EditUserRow from './EditUserRow'
|
||||||
|
import ViewUserRow from './ViewUserRow'
|
||||||
|
|
||||||
|
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 scanUserMutation = gql`
|
||||||
|
mutation scanUser($userId: ID!) {
|
||||||
|
scanUser(userId: $userId) {
|
||||||
|
success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const UserRow = ({ user, refetchUsers }) => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
...user,
|
||||||
|
editing: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [showConfirmDelete, setConfirmDelete] = useState(false)
|
||||||
|
const [showChangePassword, setChangePassword] = useState(false)
|
||||||
|
|
||||||
|
const [updateUser, { loading: updateUserLoading }] = useMutation(
|
||||||
|
updateUserMutation,
|
||||||
|
{
|
||||||
|
onCompleted: data => {
|
||||||
|
setState({
|
||||||
|
...data.updateUser,
|
||||||
|
editing: false,
|
||||||
|
})
|
||||||
|
refetchUsers()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const [deleteUser] = useMutation(deleteUserMutation, {
|
||||||
|
onCompleted: () => {
|
||||||
|
refetchUsers()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [scanUser, { called: scanUserCalled }] = useMutation(scanUserMutation, {
|
||||||
|
onCompleted: () => {
|
||||||
|
refetchUsers()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
user,
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
scanUser,
|
||||||
|
updateUser,
|
||||||
|
updateUserLoading,
|
||||||
|
deleteUser,
|
||||||
|
setChangePassword,
|
||||||
|
setConfirmDelete,
|
||||||
|
scanUserCalled,
|
||||||
|
showChangePassword,
|
||||||
|
showConfirmDelete,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.editing) {
|
||||||
|
return <EditUserRow {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ViewUserRow {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
UserRow.propTypes = {
|
||||||
|
user: PropTypes.object.isRequired,
|
||||||
|
refetchUsers: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserRowProps = {
|
||||||
|
user: PropTypes.object.isRequired,
|
||||||
|
state: PropTypes.object.isRequired,
|
||||||
|
setState: PropTypes.func.isRequired,
|
||||||
|
scanUser: PropTypes.func.isRequired,
|
||||||
|
updateUser: PropTypes.func.isRequired,
|
||||||
|
updateUserLoading: PropTypes.bool.isRequired,
|
||||||
|
deleteUser: PropTypes.func.isRequired,
|
||||||
|
setChangePassword: PropTypes.func.isRequired,
|
||||||
|
setConfirmDelete: PropTypes.func.isRequired,
|
||||||
|
scanUserCalled: PropTypes.func.isRequired,
|
||||||
|
showChangePassword: PropTypes.func.isRequired,
|
||||||
|
showConfirmDelete: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserRow
|
||||||
@@ -4,7 +4,7 @@ import { Table, Loader, Button, Icon } from 'semantic-ui-react'
|
|||||||
import { useQuery, gql } from '@apollo/client'
|
import { useQuery, gql } from '@apollo/client'
|
||||||
import UserRow from './UserRow'
|
import UserRow from './UserRow'
|
||||||
import AddUserRow from './AddUserRow'
|
import AddUserRow from './AddUserRow'
|
||||||
import { SectionTitle } from './SettingsPage'
|
import { SectionTitle } from '../SettingsPage'
|
||||||
|
|
||||||
const USERS_QUERY = gql`
|
const USERS_QUERY = gql`
|
||||||
query settingsUsersQuery {
|
query settingsUsersQuery {
|
||||||
@@ -13,7 +13,7 @@ const USERS_QUERY = gql`
|
|||||||
username
|
username
|
||||||
# rootPath
|
# rootPath
|
||||||
admin
|
admin
|
||||||
albums {
|
rootAlbums {
|
||||||
id
|
id
|
||||||
filePath
|
filePath
|
||||||
}
|
}
|
||||||
109
ui/src/Pages/SettingsPage/Users/ViewUserRow.js
Normal file
109
ui/src/Pages/SettingsPage/Users/ViewUserRow.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Button, Icon, Table, Modal } from 'semantic-ui-react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import ChangePasswordModal from './UserChangePassword'
|
||||||
|
import { UserRowProps } from './UserRow'
|
||||||
|
|
||||||
|
const PathList = styled.ul`
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 12px;
|
||||||
|
list-style: none;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ViewUserRow = ({
|
||||||
|
user,
|
||||||
|
state,
|
||||||
|
setState,
|
||||||
|
scanUser,
|
||||||
|
deleteUser,
|
||||||
|
setChangePassword,
|
||||||
|
setConfirmDelete,
|
||||||
|
scanUserCalled,
|
||||||
|
showChangePassword,
|
||||||
|
showConfirmDelete,
|
||||||
|
}) => {
|
||||||
|
const paths = (
|
||||||
|
<PathList>
|
||||||
|
{user.rootAlbums.map(album => (
|
||||||
|
<li key={album.id}>{album.filePath}</li>
|
||||||
|
))}
|
||||||
|
</PathList>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table.Row>
|
||||||
|
<Table.Cell>{user.username}</Table.Cell>
|
||||||
|
<Table.Cell>{paths}</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
|
||||||
|
disabled={scanUserCalled}
|
||||||
|
onClick={() => scanUser({ variables: { userId: user.id } })}
|
||||||
|
>
|
||||||
|
<Icon name="sync" />
|
||||||
|
Scan
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => setChangePassword(true)}>
|
||||||
|
<Icon name="key" />
|
||||||
|
Change password
|
||||||
|
</Button>
|
||||||
|
<ChangePasswordModal
|
||||||
|
user={user}
|
||||||
|
open={showChangePassword}
|
||||||
|
onClose={() => setChangePassword(false)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
negative
|
||||||
|
onClick={() => {
|
||||||
|
setConfirmDelete(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="delete" />
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
<Modal open={showConfirmDelete}>
|
||||||
|
<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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewUserRow.propTypes = UserRowProps
|
||||||
|
|
||||||
|
export default ViewUserRow
|
||||||
Reference in New Issue
Block a user