Bookmark group deletion update; Icon updates

This commit is contained in:
Sean Edmunds
2025-04-07 17:29:54 -05:00
parent b9d6ec1f3a
commit a03b87245f
6 changed files with 63 additions and 25 deletions

View File

@@ -27,10 +27,6 @@ sudo pm2 serve ./dist --no-daemon --spa --port 3000
sudo pm2 start server.cjs sudo pm2 start server.cjs
# TODO # TODO
- Reassign/delete bookmarks when group is deleted
- Find way to list all available icons in dropdown
- Expand settings search to fields other than name - Expand settings search to fields other than name
- ? Image uploads for backgrounds ? - ? Image uploads for backgrounds ?

View File

@@ -1,10 +1,14 @@
import { Icon } from '@iconify/react';
import { Box, Stack, Text } from '@mantine/core'; import { Box, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useState } from 'react'; import { useState } from 'react';
import * as fa6 from 'react-icons/fa6';
import * as si from 'react-icons/si';
import * as vsc from 'react-icons/vsc';
import { stackClasses, textClasses } from 'styles'; import { stackClasses, textClasses } from 'styles';
function AppItem({ url, icon, name }: any) { function AppItem({ url, icon, name }: any) {
const allIcons: any = { ...fa6, ...vsc, ...si };
const Icon = allIcons[icon];
const { isMobile } = useCommonHooks(); const { isMobile } = useCommonHooks();
const [textDecoration, setTextDecoration] = useState<string>('none'); const [textDecoration, setTextDecoration] = useState<string>('none');
const [hover, setHover] = useState<boolean>(false); const [hover, setHover] = useState<boolean>(false);
@@ -45,7 +49,7 @@ function AppItem({ url, icon, name }: any) {
paddingTop: isMobile ? '0' : '15px', paddingTop: isMobile ? '0' : '15px',
}} }}
> >
<Icon icon={`mdi:${icon}`} height={32} width={32} /> {Icon && <Icon style={{ height: '32px', width: '32px' }} />}
</Stack> </Stack>
<Stack <Stack
style={{ style={{

View File

@@ -1,11 +1,11 @@
import { Icon } from '@iconify/react';
import { ActionIcon, Box, Stack, Text, Tooltip } from '@mantine/core'; import { ActionIcon, Box, Stack, Text, Tooltip } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useState } from 'react'; import { useState } from 'react';
import { FiAlertCircle } from 'react-icons/fi'; import { FiAlertCircle } from 'react-icons/fi';
import { actionIconClasses, boxClasses, textClasses } from 'styles'; import { actionIconClasses, boxClasses, textClasses } from 'styles';
function AppSettingsItem({ url, icon, name, enabled }: any) { function AppSettingsItem({ url, icon, name, enabled, allIcons }: any) {
const Icon = allIcons[icon];
const [textDecoration, setTextDecoration] = useState<string>('none'); const [textDecoration, setTextDecoration] = useState<string>('none');
const [hover, setHover] = useState<boolean>(false); const [hover, setHover] = useState<boolean>(false);
const { isMobile, theme } = useCommonHooks(); const { isMobile, theme } = useCommonHooks();
@@ -58,7 +58,7 @@ function AppSettingsItem({ url, icon, name, enabled }: any) {
paddingTop: isMobile ? '0' : '32px', paddingTop: isMobile ? '0' : '32px',
}} }}
> >
<Icon icon={`mdi:${icon}`} height={64} width={64} /> {Icon && <Icon style={{ height: '64px', width: '64px' }} />}
</Stack> </Stack>
<Stack <Stack
style={{ style={{

View File

@@ -8,6 +8,7 @@ import {
GridCol, GridCol,
Group, Group,
Popover, Popover,
Select,
Stack, Stack,
Text, Text,
TextInput, TextInput,
@@ -15,6 +16,9 @@ import {
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import * as fa6 from 'react-icons/fa6';
import * as si from 'react-icons/si';
import * as vsc from 'react-icons/vsc';
import { textInputClasses } from 'styles'; import { textInputClasses } from 'styles';
import type { Application } from 'types'; import type { Application } from 'types';
import AddItemModal from '../Shared/AddItemModal'; import AddItemModal from '../Shared/AddItemModal';
@@ -23,6 +27,7 @@ import AppSettingsItem from './AppSettingsItem';
import DebouncedApplication from './DebouncedApplication'; import DebouncedApplication from './DebouncedApplication';
const ApplicationSettings = () => { const ApplicationSettings = () => {
const allIcons: any = { ...fa6, ...vsc, ...si };
const defaultApplication: Application = { const defaultApplication: Application = {
id: null, id: null,
name: '', name: '',
@@ -157,18 +162,26 @@ const ApplicationSettings = () => {
/> />
</Group> </Group>
<Group justify='center'> <Group justify='center'>
<TextInput <Select
label={'Icon'} label={'Icon'}
className={textInputClasses.settingsItem} className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses} classNames={textInputClasses}
defaultValue={newApplication.icon} data={Object.keys(allIcons).map((icon) => {
onChange={(event) => { return { value: icon, label: icon };
setNewApplication({ })}
...newApplication, comboboxProps={{
icon: event.target.value, zIndex: 1000,
});
}} }}
renderOption={(item: any) => {
const Icon = allIcons[item.option.value];
return (
<Group>
{item.option.value}
<Icon />
</Group>
);
}}
searchable
/> />
<TextInput <TextInput
label={'Filter'} label={'Filter'}
@@ -347,6 +360,7 @@ const ApplicationSettings = () => {
icon={application.icon} icon={application.icon}
name={application.name} name={application.name}
enabled={application.enabled} enabled={application.enabled}
allIcons={allIcons}
/> />
</Box> </Box>
</Popover.Target> </Popover.Target>
@@ -354,6 +368,7 @@ const ApplicationSettings = () => {
<DebouncedApplication <DebouncedApplication
application={application} application={application}
setIsOpen={setIsOpen} setIsOpen={setIsOpen}
allIcons={allIcons}
/> />
</Popover.Dropdown> </Popover.Dropdown>
</Popover> </Popover>

View File

@@ -3,6 +3,7 @@ import {
Button, Button,
Card, Card,
Group, Group,
Select,
Stack, Stack,
Switch, Switch,
Text, Text,
@@ -20,7 +21,8 @@ import DeleteItemModal from '../Shared/DeleteItemModal';
function DebouncedApplication({ function DebouncedApplication({
application, application,
setIsOpen, setIsOpen,
}: { application: Application; setIsOpen: any }) { allIcons,
}: { application: Application; setIsOpen: any; allIcons: any }) {
const { theme, handleDelete } = useCommonHooks(); const { theme, handleDelete } = useCommonHooks();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false); const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
@@ -169,18 +171,35 @@ function DebouncedApplication({
/> />
</Group> </Group>
<Group w={'100%'} justify='center'> <Group w={'100%'} justify='center'>
<TextInput <Select
label={'Icon'} label={'Icon'}
className={textInputClasses.settingsItem} className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses} classNames={textInputClasses}
data={Object.keys(allIcons).map((icon) => {
return { value: icon, label: icon };
})}
defaultValue={app.icon} defaultValue={app.icon}
onChange={(event) => { comboboxProps={{
zIndex: 3000,
}}
onChange={(event: any) => {
setUpdate({ setUpdate({
...app, ...app,
icon: event.target.value, icon: event,
}); });
}} }}
renderOption={(item: any) => {
const Icon = allIcons[item.option.value];
return (
<Group>
{item.option.value}
<Icon />
</Group>
);
}}
searchable
onDropdownOpen={() => setIsOpen(true)}
onDropdownClose={() => setIsOpen(false)}
/> />
<TextInput <TextInput
label={'Filter'} label={'Filter'}

View File

@@ -95,7 +95,7 @@ function DebouncedGroupSettingsItem({
handleClose={closeModal} handleClose={closeModal}
title='Delete message' title='Delete message'
body={ body={
<Stack gap={0} w={'100%'} h={'150px'} align='center'> <Stack gap={0} w={'100%'} h={'175px'} align='center'>
<Text>Are you sure you want to delete this bookmark group?</Text> <Text>Are you sure you want to delete this bookmark group?</Text>
<Text <Text
style={{ style={{
@@ -108,12 +108,16 @@ function DebouncedGroupSettingsItem({
color: 'var(--color-background)', color: 'var(--color-background)',
}} }}
>{`"${group.categoryName}"`}</Text> >{`"${group.categoryName}"`}</Text>
<Text style={{ fontWeight: 'bold', textTransform: 'uppercase' }}>
This will also delete all bookmarks in this group!
</Text>
<Text c='red'>This action cannot be undone!</Text> <Text c='red'>This action cannot be undone!</Text>
<Group pos={'absolute'} bottom={8}> <Group pos={'absolute'} bottom={8}>
<Button <Button
color={'red'} color={'red'}
styles={{ label: { color: 'black' } }} styles={{ label: { color: 'black' } }}
onClick={() => { onClick={() => {
handleDelete(bookmarks, 'api/links');
handleDelete(group, 'api/categories'); handleDelete(group, 'api/categories');
closeModal(); closeModal();
}} }}
@@ -121,8 +125,8 @@ function DebouncedGroupSettingsItem({
Delete Delete
</Button> </Button>
<Button <Button
color={'grey'} color={'var(--color-text-acc)'}
styles={{ label: { color: 'black' } }} styles={{ label: { color: 'var(--color-text-pri)' } }}
onClick={() => { onClick={() => {
closeModal(); closeModal();
}} }}