diff --git a/ui/src/Pages/SettingsPage/SettingsPage.js b/ui/src/Pages/SettingsPage/SettingsPage.js
index abd73e05..2c815f2e 100644
--- a/ui/src/Pages/SettingsPage/SettingsPage.js
+++ b/ui/src/Pages/SettingsPage/SettingsPage.js
@@ -4,7 +4,7 @@ import styled from 'styled-components'
import Layout from '../../Layout'
import ScannerSection from './ScannerSection'
-import UsersTable from './UsersTable'
+import UsersTable from './Users/UsersTable'
export const SectionTitle = styled.h2`
margin-top: ${({ nospace }) => (nospace ? '0' : '1.4em')} !important;
diff --git a/ui/src/Pages/SettingsPage/UserRow.js b/ui/src/Pages/SettingsPage/UserRow.js
deleted file mode 100644
index 0c807e1d..00000000
--- a/ui/src/Pages/SettingsPage/UserRow.js
+++ /dev/null
@@ -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 (
-
- Change password
-
-
- Change password for {user.username}
-
-
-
- setPasswordInput(e.target.value)}
- type="password"
- />
-
-
-
-
-
-
-
-
- )
-}
-
-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 (
-
-
- updateInput(e, 'username')}
- />
-
-
- updateInput(e, 'rootPath')}
- />
-
-
- {
- setState({
- ...state,
- admin: data.checked,
- })
- }}
- />
-
-
-
-
-
-
-
-
- )
- }
-
- return (
-
- {user.username}
- {user.rootPath}
-
- {user.admin ? : null}
-
-
-
-
-
-
- setChangePassword(false)}
- />
-
-
- Delete user
-
-
- {`Are you sure, you want to delete `}
- {user.username}?
-
- {`This action cannot be undone`}
-
-
-
-
-
-
-
-
-
- )
-}
-
-UserRow.propTypes = {
- user: PropTypes.object.isRequired,
- refetchUsers: PropTypes.func.isRequired,
-}
-
-export default UserRow
diff --git a/ui/src/Pages/SettingsPage/AddUserRow.js b/ui/src/Pages/SettingsPage/Users/AddUserRow.js
similarity index 100%
rename from ui/src/Pages/SettingsPage/AddUserRow.js
rename to ui/src/Pages/SettingsPage/Users/AddUserRow.js
diff --git a/ui/src/Pages/SettingsPage/Users/EditUserRow.js b/ui/src/Pages/SettingsPage/Users/EditUserRow.js
new file mode 100644
index 00000000..ac21e939
--- /dev/null
+++ b/ui/src/Pages/SettingsPage/Users/EditUserRow.js
@@ -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 }) => (
+
+ {filePath}
+
+
+)
+
+EditRootPath.propTypes = {
+ filePath: PropTypes.string.isRequired,
+ removePath: PropTypes.func.isRequired,
+}
+
+const NewRootPathInput = styled(Input)`
+ width: 100%;
+ margin-top: 24px;
+`
+
+const EditNewRootPath = ({ state, updateInput }) => (
+
+ updateInput(e, 'rootPath')}
+ action={{
+ positive: true,
+ icon: 'add',
+ content: 'Add',
+ }}
+ />
+
+)
+
+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 => (
+ {}}
+ />
+ ))
+
+ return (
+
+ {editRows}
+
+
+ )
+}
+
+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 (
+
+
+ updateInput(e, 'username')}
+ />
+
+
+
+
+
+ {
+ setState({
+ ...state,
+ admin: data.checked,
+ })
+ }}
+ />
+
+
+
+
+
+
+
+
+ )
+}
+
+EditUserRow.propTypes = UserRowProps
+
+export default EditUserRow
diff --git a/ui/src/Pages/SettingsPage/Users/UserChangePassword.js b/ui/src/Pages/SettingsPage/Users/UserChangePassword.js
new file mode 100644
index 00000000..39d9b9a8
--- /dev/null
+++ b/ui/src/Pages/SettingsPage/Users/UserChangePassword.js
@@ -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 (
+
+ Change password
+
+
+ Change password for {user.username}
+
+
+
+ setPasswordInput(e.target.value)}
+ type="password"
+ />
+
+
+
+
+
+
+
+
+ )
+}
+
+ChangePasswordModal.propTypes = {
+ onClose: PropTypes.func,
+ user: PropTypes.object.isRequired,
+}
+
+export default ChangePasswordModal
diff --git a/ui/src/Pages/SettingsPage/Users/UserRow.js b/ui/src/Pages/SettingsPage/Users/UserRow.js
new file mode 100644
index 00000000..7a19b14d
--- /dev/null
+++ b/ui/src/Pages/SettingsPage/Users/UserRow.js
@@ -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
+ }
+
+ return
+}
+
+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
diff --git a/ui/src/Pages/SettingsPage/UsersTable.js b/ui/src/Pages/SettingsPage/Users/UsersTable.js
similarity index 96%
rename from ui/src/Pages/SettingsPage/UsersTable.js
rename to ui/src/Pages/SettingsPage/Users/UsersTable.js
index 9c86aa09..fa628d99 100644
--- a/ui/src/Pages/SettingsPage/UsersTable.js
+++ b/ui/src/Pages/SettingsPage/Users/UsersTable.js
@@ -4,7 +4,7 @@ import { Table, Loader, Button, Icon } from 'semantic-ui-react'
import { useQuery, gql } from '@apollo/client'
import UserRow from './UserRow'
import AddUserRow from './AddUserRow'
-import { SectionTitle } from './SettingsPage'
+import { SectionTitle } from '../SettingsPage'
const USERS_QUERY = gql`
query settingsUsersQuery {
@@ -13,7 +13,7 @@ const USERS_QUERY = gql`
username
# rootPath
admin
- albums {
+ rootAlbums {
id
filePath
}
diff --git a/ui/src/Pages/SettingsPage/Users/ViewUserRow.js b/ui/src/Pages/SettingsPage/Users/ViewUserRow.js
new file mode 100644
index 00000000..d90432ac
--- /dev/null
+++ b/ui/src/Pages/SettingsPage/Users/ViewUserRow.js
@@ -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 = (
+
+ {user.rootAlbums.map(album => (
+ {album.filePath}
+ ))}
+
+ )
+
+ return (
+
+ {user.username}
+ {paths}
+
+ {user.admin ? : null}
+
+
+
+
+
+
+ setChangePassword(false)}
+ />
+
+
+ Delete user
+
+
+ {`Are you sure, you want to delete `}
+ {user.username}?
+
+ {`This action cannot be undone`}
+
+
+
+
+
+
+
+
+
+ )
+}
+
+ViewUserRow.propTypes = UserRowProps
+
+export default ViewUserRow