New settings pages
This commit is contained in:
@@ -13,15 +13,15 @@ import {
|
||||
} from 'react-icons/fi';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { actionIconClasses, tabClasses, textClasses } from 'styles';
|
||||
import Applications from './components/Applications';
|
||||
import Bookmarks from './components/Bookmarks';
|
||||
import Categories from './components/Categories';
|
||||
import Messages from './components/Messages';
|
||||
import Personalization from './components/Personalization';
|
||||
import SearchProviders from './components/SearchProviders';
|
||||
import ThemePlaylists from './components/ThemePlaylists';
|
||||
import Themes from './components/Themes';
|
||||
import WolClients from './components/WolClients';
|
||||
import Applications from './components/Applications/Applications';
|
||||
import Bookmarks from './components/Bookmarks/Bookmarks';
|
||||
import Categories from './components/Categories/Categories';
|
||||
import Messages from './components/Messages/Messages';
|
||||
import Personalization from './components/Personalization/Personalization';
|
||||
import SearchProviders from './components/Search/SearchProviders';
|
||||
import ThemePlaylists from './components/Theming/ThemePlaylists';
|
||||
import Themes from './components/Theming/Themes';
|
||||
import WolClients from './components/WoL/WolClients';
|
||||
|
||||
export function Settings() {
|
||||
const { isMobile } = useCommonHooks();
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Stack, Text } from '@mantine/core';
|
||||
import { CategoryColumns, DataTable } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { textClasses } from '../../../styles';
|
||||
import { textClasses } from '../../../../styles';
|
||||
const CategorySettings = () => {
|
||||
const endpoint = 'api/categories';
|
||||
const {
|
||||
@@ -1,40 +0,0 @@
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { DataTable, MessagesColumns } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { textClasses } from 'styles';
|
||||
const MessageSettings = () => {
|
||||
const endpoint = 'api/messages';
|
||||
const {
|
||||
data: messageData,
|
||||
isFetching: isMessagesFetching,
|
||||
isFetched: isMessagesFetched,
|
||||
isLoading: isMessagesLoading,
|
||||
} = useQueryAPI({
|
||||
endpoint: 'api/messages',
|
||||
});
|
||||
const [messages, setMessages] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (messageData && isMessagesFetched) {
|
||||
setMessages(messageData);
|
||||
}
|
||||
}, [messageData, isMessagesFetched]);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
|
||||
Messages
|
||||
</Text>
|
||||
<DataTable
|
||||
tableName='Messages'
|
||||
columns={MessagesColumns()}
|
||||
tableData={messages}
|
||||
isFetching={isMessagesFetching}
|
||||
isLoading={isMessagesLoading}
|
||||
endpoint={endpoint}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default MessageSettings;
|
||||
154
src/pages/settings/components/Messages/Messages.tsx
Normal file
154
src/pages/settings/components/Messages/Messages.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
} from '@mantine/core';
|
||||
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
||||
import DebouncedMessage from 'components/Shared/DebouncedMessage';
|
||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { FiPlus } from 'react-icons/fi';
|
||||
import { buttonClasses, modalClasses, textClasses, textInputClasses } from 'styles';
|
||||
const MessageSettings = () => {
|
||||
const endpoint = 'api/messages';
|
||||
const {
|
||||
data: messageData,
|
||||
isFetching: isMessagesFetching,
|
||||
isFetched: isMessagesFetched,
|
||||
isLoading: isMessagesLoading,
|
||||
} = useQueryAPI({
|
||||
endpoint: 'api/messages',
|
||||
});
|
||||
const { theme } = useCommonHooks();
|
||||
const { mutateAsync } = useMutationAPI();
|
||||
const [messages, setMessages] = useState<any>([]);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [newMessageError, setNewMessageError] = useState<string>('');
|
||||
const [message, setMessage] = useState({});
|
||||
const [debounced] = useDebouncedValue(message, 1000);
|
||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||
|
||||
const handleDelete = (message: any) => {
|
||||
mutateAsync({ method: 'DELETE', endpoint: endpoint, body: message });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (messageData && isMessagesFetched) {
|
||||
setMessages(messageData);
|
||||
}
|
||||
}, [messageData, isMessagesFetched]);
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(debounced).length) {
|
||||
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Modal.Root
|
||||
size={'60%'}
|
||||
className={modalClasses.helpModal}
|
||||
classNames={modalClasses}
|
||||
opened={modalOpened}
|
||||
onClose={() => {
|
||||
closeModal();
|
||||
}}
|
||||
style={{
|
||||
'--color-text-pri': theme.primaryColor,
|
||||
'--color-text-acc': theme.accentColor,
|
||||
'--color-background': theme.backgroundColor,
|
||||
'--color-border': theme.borderColor,
|
||||
'--color-search-background': theme.searchColor,
|
||||
'--image-background': `url(${theme.backgroundImage})`,
|
||||
}}
|
||||
closeOnEscape
|
||||
closeOnClickOutside
|
||||
>
|
||||
<Modal.Overlay />
|
||||
<Modal.Content>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Add a new message</Modal.Title>
|
||||
<Modal.CloseButton />
|
||||
</Modal.Header>
|
||||
<Modal.Body pb={'2rem'}>
|
||||
<Stack gap={0} w={'100%'} align='center'>
|
||||
<TextInput
|
||||
autoFocus
|
||||
w={'75%'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
onChange={(event) => {
|
||||
setNewMessage(event.target.value);
|
||||
}}
|
||||
error={newMessageError}
|
||||
/>
|
||||
<Divider color='black' my='sm' />
|
||||
<Group pos={'absolute'} bottom={8}>
|
||||
<Button
|
||||
color={'green'}
|
||||
styles={{ label: { color: 'black' } }}
|
||||
onClick={() => {
|
||||
if (newMessage) {
|
||||
mutateAsync({
|
||||
method: 'POST',
|
||||
endpoint: endpoint,
|
||||
body: { message: newMessage },
|
||||
});
|
||||
closeModal();
|
||||
} else {
|
||||
setNewMessageError('Please enter a message');
|
||||
}
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
color={'red'}
|
||||
styles={{ label: { color: 'black' } }}
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal.Body>
|
||||
</Modal.Content>
|
||||
</Modal.Root>
|
||||
<Group align='center'>
|
||||
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
|
||||
Messages
|
||||
</Text>
|
||||
<Button
|
||||
mt={'1.5rem'}
|
||||
className={buttonClasses.tableButton}
|
||||
classNames={buttonClasses}
|
||||
onClick={openModal}
|
||||
>
|
||||
<FiPlus />
|
||||
</Button>
|
||||
</Group>
|
||||
<Grid>
|
||||
{messages?.map((message: any) => (
|
||||
<GridCol span={6} key={message.id}>
|
||||
<DebouncedMessage
|
||||
label={`ID# ${message.id}`}
|
||||
message={message}
|
||||
setMessage={setMessage}
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
</GridCol>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default MessageSettings;
|
||||
@@ -1,40 +0,0 @@
|
||||
import { Text } from '@mantine/core';
|
||||
import { DataTable, PersonalizationColumns } from 'components';
|
||||
import { useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { textClasses } from 'styles';
|
||||
const PersonalizationSettings = () => {
|
||||
const endpoint = 'api/personalData';
|
||||
const {
|
||||
data: personalData,
|
||||
isFetched: personalIsFetched,
|
||||
isFetching: personalIsFetching,
|
||||
isLoading: personalIsLoading,
|
||||
} = useQueryAPI({
|
||||
endpoint: endpoint,
|
||||
});
|
||||
const [personal, setPersonal] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (personalData && personalIsFetched) {
|
||||
setPersonal(personalData);
|
||||
}
|
||||
}, [personalData, personalIsFetched]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
|
||||
Personalization
|
||||
</Text>
|
||||
<DataTable
|
||||
tableName='Personalization'
|
||||
columns={PersonalizationColumns()}
|
||||
tableData={personal}
|
||||
isFetching={personalIsFetching}
|
||||
isLoading={personalIsLoading}
|
||||
endpoint={endpoint}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PersonalizationSettings;
|
||||
@@ -0,0 +1,128 @@
|
||||
import { Card, Grid, GridCol, Overlay, Stack, Text, TextInput } from '@mantine/core';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { useMutationAPI, useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { cardClasses, textClasses, textInputClasses } from 'styles';
|
||||
const PersonalizationSettings = () => {
|
||||
const endpoint = 'api/personalData';
|
||||
const {
|
||||
data: personalData,
|
||||
isFetched: personalIsFetched,
|
||||
isFetching: personalIsFetching,
|
||||
} = useQueryAPI({
|
||||
endpoint: endpoint,
|
||||
});
|
||||
const [personal, setPersonal] = useState<any>({});
|
||||
const [update, setUpdate] = useState({});
|
||||
const [debounced] = useDebouncedValue(update, 1000);
|
||||
const { mutateAsync: editRow } = useMutationAPI();
|
||||
useEffect(() => {
|
||||
if (personalData && personalIsFetched) {
|
||||
setPersonal(personalData[0]);
|
||||
}
|
||||
}, [personalData, personalIsFetched]);
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(debounced).length) {
|
||||
setPersonal(update);
|
||||
editRow({ method: 'PATCH', endpoint: endpoint, body: debounced });
|
||||
setUpdate({});
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
useEffect(() => {
|
||||
if (update !== personal && Object.keys(update).length) {
|
||||
setPersonal(update);
|
||||
}
|
||||
}, [update]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
|
||||
Personalization
|
||||
</Text>
|
||||
<Stack w={'100%'} align='center'>
|
||||
<Grid w={'75%'} pos={'relative'}>
|
||||
{personalIsFetching && (
|
||||
<Overlay
|
||||
style={{
|
||||
borderRadius: '10px',
|
||||
margin: '-8px',
|
||||
backdropFilter: 'blur(10px) brightness(1.2)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<GridCol span={4}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
|
||||
<TextInput
|
||||
label={'First Name'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.firstName}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
firstName: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={4}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
|
||||
<TextInput
|
||||
label={'Last Name'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.lastName}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
lastName: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={4}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
|
||||
<TextInput
|
||||
label={'Email'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.email}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
email: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={12}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
|
||||
<TextInput
|
||||
label={'Physical Address'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.physicalAddress}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
physicalAddress: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PersonalizationSettings;
|
||||
@@ -11,7 +11,7 @@ function ThemeCard({
|
||||
createTheme,
|
||||
allThemes,
|
||||
}: any) {
|
||||
const { data: themePlaylists, isFetched: isThemePlaylistsFetched } = useQueryAPI({
|
||||
const { data: themePlaylists } = useQueryAPI({
|
||||
endpoint: 'api/themePlaylists',
|
||||
});
|
||||
const [hover, setHover] = useState(false);
|
||||
Reference in New Issue
Block a user