New settings pages
This commit is contained in:
@@ -1,61 +0,0 @@
|
|||||||
import type { MRT_ColumnDef } from 'mantine-react-table';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
export function PersonalizationColumns() {
|
|
||||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
accessorFn: (row: any) => {
|
|
||||||
if (row.firstName) {
|
|
||||||
return `${row.firstName}`;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
id: 'firstName',
|
|
||||||
header: 'First Name',
|
|
||||||
size: 50,
|
|
||||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
|
||||||
Header: ({ column }) => column.columnDef.header,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorFn: (row: any) => {
|
|
||||||
if (row.lastName) {
|
|
||||||
return `${row.lastName}`;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
id: 'lastName',
|
|
||||||
header: 'Last Name',
|
|
||||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
|
||||||
Header: ({ column }) => column.columnDef.header,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorFn: (row: any) => {
|
|
||||||
if (row.email) {
|
|
||||||
return `${row.email}`;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
id: 'email',
|
|
||||||
header: 'Email',
|
|
||||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
|
||||||
Header: ({ column }) => column.columnDef.header,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorFn: (row: any) => {
|
|
||||||
if (row.physicalAddress) {
|
|
||||||
return `${row.physicalAddress}`;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
id: 'physicalAddress',
|
|
||||||
header: 'Address',
|
|
||||||
Cell: ({ renderedCellValue }) => renderedCellValue,
|
|
||||||
Header: ({ column }) => column.columnDef.header,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
return columns;
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
import { Divider, Group, Modal, Stack } from '@mantine/core';
|
import { Divider, Modal, Stack } from '@mantine/core';
|
||||||
import { useCommonHooks } from 'hooks';
|
import { useCommonHooks } from 'hooks';
|
||||||
import { modalClasses } from 'styles';
|
import { modalClasses } from 'styles';
|
||||||
import HintItem from './HintItem';
|
import HelpSection from './HelpSection';
|
||||||
|
|
||||||
export function HelpModal({
|
export function HelpModal({
|
||||||
|
setSearchValue,
|
||||||
|
setProviderLabel,
|
||||||
|
setProviderURL,
|
||||||
providerModalOpened,
|
providerModalOpened,
|
||||||
closeProviderModal,
|
closeProviderModal,
|
||||||
applicationData,
|
applicationData,
|
||||||
providerData,
|
providerData,
|
||||||
}: any) {
|
}: any) {
|
||||||
const { theme, setSearchValue, setProviderLabel, setProviderURL } = useCommonHooks();
|
const { theme } = useCommonHooks();
|
||||||
return (
|
return (
|
||||||
<Modal.Root
|
<Modal.Root
|
||||||
size={'60%'}
|
size={'60%'}
|
||||||
@@ -17,10 +20,10 @@ export function HelpModal({
|
|||||||
classNames={modalClasses}
|
classNames={modalClasses}
|
||||||
opened={providerModalOpened}
|
opened={providerModalOpened}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
closeProviderModal();
|
|
||||||
setSearchValue('');
|
setSearchValue('');
|
||||||
setProviderLabel('Google Search');
|
setProviderLabel('Google Search');
|
||||||
setProviderURL('https://www.google.com/search?q=');
|
setProviderURL('https://www.google.com/search?q=');
|
||||||
|
closeProviderModal();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
'--color-text-pri': theme.primaryColor,
|
'--color-text-pri': theme.primaryColor,
|
||||||
@@ -41,67 +44,25 @@ export function HelpModal({
|
|||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Body pb={'2rem'}>
|
<Modal.Body pb={'2rem'}>
|
||||||
<Stack gap={0} w={'100%'} align='center'>
|
<Stack gap={0} w={'100%'} align='center'>
|
||||||
<h3
|
<HelpSection
|
||||||
style={{
|
name={'Search Providers'}
|
||||||
zIndex: 0,
|
items={providerData.map((provider: any) => {
|
||||||
padding: '8px',
|
return {
|
||||||
marginTop: 0,
|
name: provider.name,
|
||||||
marginBottom: -32,
|
command: provider.prefix,
|
||||||
borderRadius: '8px',
|
};
|
||||||
color: theme.backgroundColor,
|
})}
|
||||||
backgroundColor: theme.borderColor,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Search Providers
|
|
||||||
</h3>
|
|
||||||
<Divider py={8} color={theme.accentColor} />
|
|
||||||
<Group
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
backgroundColor: theme.searchColor,
|
|
||||||
borderRadius: '8px',
|
|
||||||
padding: '24px',
|
|
||||||
justifyItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{providerData?.map((provider: any) => (
|
|
||||||
<HintItem
|
|
||||||
key={provider.prefix}
|
|
||||||
command={provider.prefix}
|
|
||||||
item={provider.name}
|
|
||||||
/>
|
/>
|
||||||
))}
|
<Divider my={8} color={theme.accentColor} />
|
||||||
</Group>
|
<HelpSection
|
||||||
<h3
|
name={'Available Applications'}
|
||||||
style={{
|
items={applicationData.map((application: any) => {
|
||||||
zIndex: 0,
|
return {
|
||||||
padding: '8px',
|
name: application.url,
|
||||||
marginBottom: -32,
|
command: `!${application.baseUrl.substring(1).toLowerCase()}`,
|
||||||
borderRadius: '8px',
|
};
|
||||||
color: theme.backgroundColor,
|
})}
|
||||||
backgroundColor: theme.borderColor,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Available Applications
|
|
||||||
</h3>
|
|
||||||
<Divider py={8} color={theme.accentColor} />
|
|
||||||
<Group
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
backgroundColor: theme.searchColor,
|
|
||||||
borderRadius: '8px',
|
|
||||||
padding: '32px',
|
|
||||||
justifyItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{applicationData?.map((application: any) => (
|
|
||||||
<HintItem
|
|
||||||
key={application.id}
|
|
||||||
command={`!${application.baseUrl.substr(1).toLowerCase()}`}
|
|
||||||
item={application.url}
|
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
|
|||||||
42
src/components/HelpModal/HelpSection.tsx
Normal file
42
src/components/HelpModal/HelpSection.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Divider, Group } from '@mantine/core';
|
||||||
|
import { useCommonHooks } from 'hooks';
|
||||||
|
import HintItem from './HintItem';
|
||||||
|
|
||||||
|
function HelpSection({ name, items }: any) {
|
||||||
|
const { theme } = useCommonHooks();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h3
|
||||||
|
style={{
|
||||||
|
zIndex: 0,
|
||||||
|
padding: '8px',
|
||||||
|
marginTop: 0,
|
||||||
|
marginBottom: -32,
|
||||||
|
borderRadius: '8px',
|
||||||
|
color: theme.backgroundColor,
|
||||||
|
backgroundColor: theme.borderColor,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</h3>
|
||||||
|
<Divider py={8} color={theme.accentColor} />
|
||||||
|
<Group
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: theme.searchColor,
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '24px',
|
||||||
|
justifyItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{items
|
||||||
|
?.sort((a: any, b: any) => (a.command > b.command ? 1 : -1))
|
||||||
|
.map((item: any) => (
|
||||||
|
<HintItem key={item.command} command={item.command} name={item.name} />
|
||||||
|
))}
|
||||||
|
</Group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HelpSection;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Group, Text } from '@mantine/core';
|
import { Group, Text } from '@mantine/core';
|
||||||
|
|
||||||
function HintItem({ command, item }: any) {
|
function HintItem({ command, name }: any) {
|
||||||
return (
|
return (
|
||||||
<Group
|
<Group
|
||||||
gap={0}
|
gap={0}
|
||||||
@@ -31,7 +31,7 @@ function HintItem({ command, item }: any) {
|
|||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
|||||||
120
src/components/Shared/DebouncedMessage.tsx
Normal file
120
src/components/Shared/DebouncedMessage.tsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Group,
|
||||||
|
Modal,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
Tooltip,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
|
import { IconTrash } from '@tabler/icons-react';
|
||||||
|
import { useCommonHooks } from 'hooks';
|
||||||
|
import { cardClasses, modalClasses, textInputClasses } from 'styles';
|
||||||
|
|
||||||
|
function DebouncedMessage({ label, message, setMessage, handleDelete }: any) {
|
||||||
|
const { theme } = useCommonHooks();
|
||||||
|
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||||
|
return (
|
||||||
|
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
|
||||||
|
<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%'} h={'150px'} align='center'>
|
||||||
|
<Text>Are you sure you want to delete this message?</Text>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: '20px',
|
||||||
|
padding: '8px',
|
||||||
|
margin: '8px',
|
||||||
|
borderRadius: '10px',
|
||||||
|
backgroundColor: 'var(--color-text-acc)',
|
||||||
|
color: 'var(--color-background)',
|
||||||
|
}}
|
||||||
|
>{`"${message.message}"`}</Text>
|
||||||
|
<Text c='red'>This action cannot be undone!</Text>
|
||||||
|
<Group pos={'absolute'} bottom={8}>
|
||||||
|
<Button
|
||||||
|
color={'red'}
|
||||||
|
styles={{ label: { color: 'black' } }}
|
||||||
|
onClick={() => {
|
||||||
|
handleDelete(message);
|
||||||
|
closeModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color={'grey'}
|
||||||
|
styles={{ label: { color: 'black' } }}
|
||||||
|
onClick={() => {
|
||||||
|
closeModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal.Content>
|
||||||
|
</Modal.Root>
|
||||||
|
<Tooltip
|
||||||
|
color={theme.primaryColor}
|
||||||
|
bd={`solid 1px ${theme.borderColor}`}
|
||||||
|
label={'Delete'}
|
||||||
|
>
|
||||||
|
<ActionIcon
|
||||||
|
pos={'absolute'}
|
||||||
|
right={16}
|
||||||
|
color='red'
|
||||||
|
onClick={() => {
|
||||||
|
openModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconTrash />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<TextInput
|
||||||
|
label={label}
|
||||||
|
className={textInputClasses.settingsItem}
|
||||||
|
pt={16}
|
||||||
|
classNames={textInputClasses}
|
||||||
|
defaultValue={message.message}
|
||||||
|
onChange={(event) => {
|
||||||
|
setMessage({
|
||||||
|
...message,
|
||||||
|
message: event.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DebouncedMessage;
|
||||||
@@ -7,6 +7,5 @@ export { ApplicationColumns } from './DataTable/columns/ApplicationColumns';
|
|||||||
export { BookmarkColumns } from './DataTable/columns/BookmarkColumns';
|
export { BookmarkColumns } from './DataTable/columns/BookmarkColumns';
|
||||||
export { CategoryColumns } from './DataTable/columns/CategoryColumns';
|
export { CategoryColumns } from './DataTable/columns/CategoryColumns';
|
||||||
export { MessagesColumns } from './DataTable/columns/MessagesColumns';
|
export { MessagesColumns } from './DataTable/columns/MessagesColumns';
|
||||||
export { PersonalizationColumns } from './DataTable/columns/PersonalizationColumns';
|
|
||||||
export { SearchProviderColumns } from './DataTable/columns/SearchProviderColumns';
|
export { SearchProviderColumns } from './DataTable/columns/SearchProviderColumns';
|
||||||
export { WolClientColumns } from './DataTable/columns/WolClientColumns';
|
export { WolClientColumns } from './DataTable/columns/WolClientColumns';
|
||||||
|
|||||||
@@ -240,8 +240,12 @@ export default function Search({
|
|||||||
|
|
||||||
const optionsFilter: OptionsFilter = ({ options, search }) => {
|
const optionsFilter: OptionsFilter = ({ options, search }) => {
|
||||||
const splittedSearch = search.toLowerCase().trim().split(' ');
|
const splittedSearch = search.toLowerCase().trim().split(' ');
|
||||||
if (splittedSearch[0] === '!') {
|
if (splittedSearch[0].startsWith('!')) {
|
||||||
return apps.map((app: any) => {
|
return apps
|
||||||
|
.filter((app: any) =>
|
||||||
|
app.name.toLowerCase().includes(splittedSearch[0].substring(1).toLowerCase()),
|
||||||
|
)
|
||||||
|
.map((app: any) => {
|
||||||
return {
|
return {
|
||||||
label: app.name,
|
label: app.name,
|
||||||
value: app.baseUrl,
|
value: app.baseUrl,
|
||||||
@@ -469,6 +473,9 @@ export default function Search({
|
|||||||
onKeyUp={handleKeyUp}
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
/>
|
||||||
<HelpModal
|
<HelpModal
|
||||||
|
setSearchValue={setSearchValue}
|
||||||
|
setProviderLabel={setProviderLabel}
|
||||||
|
setProviderURL={setProviderURL}
|
||||||
providerModalOpened={providerModalOpened}
|
providerModalOpened={providerModalOpened}
|
||||||
applicationData={applicationData}
|
applicationData={applicationData}
|
||||||
providerData={providerData}
|
providerData={providerData}
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ import {
|
|||||||
} from 'react-icons/fi';
|
} from 'react-icons/fi';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { actionIconClasses, tabClasses, textClasses } from 'styles';
|
import { actionIconClasses, tabClasses, textClasses } from 'styles';
|
||||||
import Applications from './components/Applications';
|
import Applications from './components/Applications/Applications';
|
||||||
import Bookmarks from './components/Bookmarks';
|
import Bookmarks from './components/Bookmarks/Bookmarks';
|
||||||
import Categories from './components/Categories';
|
import Categories from './components/Categories/Categories';
|
||||||
import Messages from './components/Messages';
|
import Messages from './components/Messages/Messages';
|
||||||
import Personalization from './components/Personalization';
|
import Personalization from './components/Personalization/Personalization';
|
||||||
import SearchProviders from './components/SearchProviders';
|
import SearchProviders from './components/Search/SearchProviders';
|
||||||
import ThemePlaylists from './components/ThemePlaylists';
|
import ThemePlaylists from './components/Theming/ThemePlaylists';
|
||||||
import Themes from './components/Themes';
|
import Themes from './components/Theming/Themes';
|
||||||
import WolClients from './components/WolClients';
|
import WolClients from './components/WoL/WolClients';
|
||||||
|
|
||||||
export function Settings() {
|
export function Settings() {
|
||||||
const { isMobile } = useCommonHooks();
|
const { isMobile } = useCommonHooks();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Stack, Text } from '@mantine/core';
|
|||||||
import { CategoryColumns, DataTable } from 'components';
|
import { CategoryColumns, DataTable } from 'components';
|
||||||
import { useQueryAPI } from 'hooks';
|
import { useQueryAPI } from 'hooks';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { textClasses } from '../../../styles';
|
import { textClasses } from '../../../../styles';
|
||||||
const CategorySettings = () => {
|
const CategorySettings = () => {
|
||||||
const endpoint = 'api/categories';
|
const endpoint = 'api/categories';
|
||||||
const {
|
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,
|
createTheme,
|
||||||
allThemes,
|
allThemes,
|
||||||
}: any) {
|
}: any) {
|
||||||
const { data: themePlaylists, isFetched: isThemePlaylistsFetched } = useQueryAPI({
|
const { data: themePlaylists } = useQueryAPI({
|
||||||
endpoint: 'api/themePlaylists',
|
endpoint: 'api/themePlaylists',
|
||||||
});
|
});
|
||||||
const [hover, setHover] = useState(false);
|
const [hover, setHover] = useState(false);
|
||||||
7
src/styles/Card.module.css
Normal file
7
src/styles/Card.module.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.settingsItem {
|
||||||
|
&.root {
|
||||||
|
color: var(--color-text-pri);
|
||||||
|
background-color: transparent;
|
||||||
|
backdrop-filter: blur(10px) brightness(.75) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settingsItem {
|
||||||
|
&.root {
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background-color: var(--color-search-background);
|
||||||
|
color: var(--color-text-pri);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.section {
|
||||||
|
color: var(--color-border);
|
||||||
|
width: auto;
|
||||||
|
padding-left: .5rem;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
&.root {
|
&.root {
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import actionIconClasses from './ActionIcon.module.css';
|
import actionIconClasses from './ActionIcon.module.css';
|
||||||
import buttonClasses from './Button.module.css';
|
import buttonClasses from './Button.module.css';
|
||||||
|
import cardClasses from './Card.module.css';
|
||||||
import menuClasses from './Menu.module.css';
|
import menuClasses from './Menu.module.css';
|
||||||
import modalClasses from './Modal.module.css';
|
import modalClasses from './Modal.module.css';
|
||||||
import multiSelectClasses from './MultiSelect.module.css';
|
import multiSelectClasses from './MultiSelect.module.css';
|
||||||
@@ -13,6 +14,7 @@ import textInputClasses from './TextInput.module.css';
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
actionIconClasses,
|
actionIconClasses,
|
||||||
|
cardClasses,
|
||||||
buttonClasses,
|
buttonClasses,
|
||||||
menuClasses,
|
menuClasses,
|
||||||
modalClasses,
|
modalClasses,
|
||||||
|
|||||||
Reference in New Issue
Block a user