This commit is contained in:
Sean Edmunds
2024-08-20 16:28:46 -05:00
parent 8152d3fe73
commit 7990f164d6
39 changed files with 824 additions and 772 deletions

View File

@@ -2,17 +2,14 @@ module.exports = {
root: true, root: true,
env: { browser: true, es2020: true }, env: { browser: true, es2020: true },
extends: [ extends: [
"eslint:recommended", 'eslint:recommended',
"plugin:@typescript-eslint/recommended", 'plugin:@typescript-eslint/recommended',
"plugin:react-hooks/recommended", 'plugin:react-hooks/recommended',
], ],
ignorePatterns: ["dist", ".eslintrc.cjs"], ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: "@typescript-eslint/parser", parser: '@typescript-eslint/parser',
plugins: ["react-refresh"], plugins: ['react-refresh'],
rules: { rules: {
"react-refresh/only-export-components": [ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
"warn",
{ allowConstantExport: true },
],
}, },
}; };

52
biome.json Normal file
View File

@@ -0,0 +1,52 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"organizeImports": {
"enabled": true
},
"files": {
"ignore": ["./node_modules/", "./dist/", "./.nx/"],
"ignoreUnknown": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"a11y": {
"noSvgWithoutTitle": "off",
"all": true
},
"complexity": {
"noUselessTypeConstraint": "off",
"recommended": true
},
"suspicious": {
"noExplicitAny": "off",
"noConsoleLog": "error",
"noImplicitAnyLet": "off",
"recommended": true
},
"correctness": {
"useExhaustiveDependencies": "off",
"noUnusedImports": "error",
"recommended": true
},
"performance": {
"noDelete": "off",
"recommended": true
}
}
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 90
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "single"
}
}
}

View File

@@ -1,36 +1,36 @@
import Homepage from "./pages/homepage/Homepage";
import { import {
MantineProvider,
DEFAULT_THEME, DEFAULT_THEME,
MantineProvider,
createTheme, createTheme,
mergeMantineTheme, mergeMantineTheme,
} from "@mantine/core"; } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';
import { ModalsProvider } from '@mantine/modals';
import { useEffect } from 'react';
import { import {
Route, Route,
RouterProvider,
createBrowserRouter, createBrowserRouter,
createRoutesFromElements, createRoutesFromElements,
RouterProvider, } from 'react-router-dom';
} from "react-router-dom"; import ThemeProvider from './components/ThemeProvider';
import Settings from "./pages/settings/Settings"; import { useQueryAPI } from './hooks/useAPI';
import { ModalsProvider } from "@mantine/modals"; import Homepage from './pages/homepage/Homepage';
import { useLocalStorage } from "@mantine/hooks"; import Settings from './pages/settings/Settings';
import { useEffect, useState } from "react"; import '@mantine/core/styles.css'; //import Mantine V7 styles needed by MRT
import { useQueryAPI } from "./hooks/useAPI"; import '@mantine/dates/styles.css'; //if using mantine date picker features
import ThemeProvider from "./components/ThemeProvider"; import 'mantine-react-table/styles.css'; //import MRT styles
import "@mantine/core/styles.css"; //import Mantine V7 styles needed by MRT import { useAtom } from 'jotai';
import "@mantine/dates/styles.css"; //if using mantine date picker features import { appTheme } from './contexts/theme';
import "mantine-react-table/styles.css"; //import MRT styles
import { appTheme } from "./contexts/theme";
import { useAtom } from "jotai";
function App() { function App() {
const { data, isFetched } = useQueryAPI({ const { data, isFetched } = useQueryAPI({
endpoint: "api/themes", endpoint: 'api/themes',
}); });
const [themeName, setThemeName] = useLocalStorage<any>({ const [themeName, setThemeName] = useLocalStorage<any>({
key: "theme", key: 'theme',
defaultValue: "blackboard", defaultValue: 'blackboard',
}); });
const [theme, setTheme] = useAtom(appTheme); const [theme, setTheme] = useAtom(appTheme);
@@ -45,23 +45,18 @@ function App() {
const router = createBrowserRouter( const router = createBrowserRouter(
createRoutesFromElements( createRoutesFromElements(
<Route> <Route>
<Route path="/trigger" element={<Homepage theme={theme} />} /> <Route path='/trigger' element={<Homepage theme={theme} />} />
<Route <Route path='/trigger/settings' element={<Settings theme={theme} />} />
path="/trigger/settings"
element={<Settings theme={theme} />}
/>
</Route>, </Route>,
), ),
); );
const overrides = createTheme({ const overrides = createTheme({
white: theme?.colorbackground, white: theme?.colorbackground,
fontFamily: fontFamily: '-apple-system,Jost,BlinkMacSystemFont,Helvetica Neue,Roboto,sans-serif',
"-apple-system,Jost,BlinkMacSystemFont,Helvetica Neue,Roboto,sans-serif",
}); });
const mantineTheme = mergeMantineTheme(DEFAULT_THEME, overrides); const mantineTheme = mergeMantineTheme(DEFAULT_THEME, overrides);
return ( return (
<> <>
{theme && ( {theme && (
@@ -74,8 +69,6 @@ function App() {
</MantineProvider> </MantineProvider>
)} )}
</> </>
); );
} }

View File

@@ -1,19 +1,19 @@
import { import {
type MRT_Row,
type MRT_TableOptions,
MantineReactTable, MantineReactTable,
MRT_Row,
MRT_TableOptions,
useMantineReactTable, useMantineReactTable,
} from "mantine-react-table"; } from 'mantine-react-table';
import { memo } from "react"; import { memo } from 'react';
import "../dataTableStyles.css"; import '../dataTableStyles.css';
import { ActionIcon, Button, Flex, Text } from "@mantine/core"; import { ActionIcon, Button, Flex, Text } from '@mantine/core';
import { FiPlus } from "react-icons/fi"; import { modals } from '@mantine/modals';
import { IconEdit, IconTrash } from "@tabler/icons-react"; import { IconEdit, IconTrash } from '@tabler/icons-react';
import { useMutationAPI } from "../hooks/useAPI"; import { useAtom } from 'jotai';
import { modals } from "@mantine/modals"; import { FiPlus } from 'react-icons/fi';
import { actionIconClasses, buttonClasses } from "../styles"; import { appTheme } from '../contexts/theme';
import { appTheme } from "../contexts/theme"; import { useMutationAPI } from '../hooks/useAPI';
import { useAtom } from "jotai"; import { actionIconClasses, buttonClasses } from '../styles';
export function DataTableComponent({ export function DataTableComponent({
tableName, tableName,
@@ -34,29 +34,28 @@ export function DataTableComponent({
const { mutateAsync: createRow } = useMutationAPI(); const { mutateAsync: createRow } = useMutationAPI();
const { mutateAsync: editRow } = useMutationAPI(); const { mutateAsync: editRow } = useMutationAPI();
const { mutateAsync: deleteRow } = useMutationAPI(); const { mutateAsync: deleteRow } = useMutationAPI();
const handleCreateRow: MRT_TableOptions<any>["onCreatingRowSave"] = async ({ const handleCreateRow: MRT_TableOptions<any>['onCreatingRowSave'] = async ({
values, values,
exitCreatingMode, exitCreatingMode,
}) => { }) => {
createRow({ method: "POST", endpoint: endpoint, body: values }); createRow({ method: 'POST', endpoint: endpoint, body: values });
exitCreatingMode(); exitCreatingMode();
}; };
const handleEditRow: MRT_TableOptions<any>["onEditingRowSave"] = async ({ const handleEditRow: MRT_TableOptions<any>['onEditingRowSave'] = async ({
values, values,
table, table,
}) => { }) => {
editRow({ method: "PATCH", endpoint: endpoint, body: values }); editRow({ method: 'PATCH', endpoint: endpoint, body: values });
table.setEditingRow(null); table.setEditingRow(null);
}; };
console.log(theme)
const openDeleteConfirmModal = (row: MRT_Row<any>) => const openDeleteConfirmModal = (row: MRT_Row<any>) =>
modals.openConfirmModal({ modals.openConfirmModal({
title: "Are you sure you want to delete this user?", title: 'Are you sure you want to delete this user?',
children: <Text>Are you sure?</Text>, children: <Text>Are you sure?</Text>,
labels: { confirm: "Delete", cancel: "Cancel" }, labels: { confirm: 'Delete', cancel: 'Cancel' },
confirmProps: { color: "red" }, confirmProps: { color: 'red' },
onConfirm: () => onConfirm: () =>
deleteRow({ method: "DELETE", endpoint: endpoint, body: row.original }), deleteRow({ method: 'DELETE', endpoint: endpoint, body: row.original }),
}); });
const table = useMantineReactTable({ const table = useMantineReactTable({
columns: columns, columns: columns,
@@ -65,8 +64,8 @@ console.log(theme)
enablePagination: false, enablePagination: false,
enableRowVirtualization: true, enableRowVirtualization: true,
enableEditing: true, enableEditing: true,
createDisplayMode: "modal", createDisplayMode: 'modal',
editDisplayMode: "row", editDisplayMode: 'row',
enableBottomToolbar: false, enableBottomToolbar: false,
enableToolbarInternalActions: false, enableToolbarInternalActions: false,
onCreatingRowSave: handleCreateRow, onCreatingRowSave: handleCreateRow,
@@ -84,11 +83,15 @@ console.log(theme)
}, },
mantinePaperProps: { className: 'root' }, mantinePaperProps: { className: 'root' },
renderRowActions: ({ row, table }) => ( renderRowActions: ({ row, table }) => (
<Flex gap="md"> <Flex gap='md'>
<ActionIcon className={actionIconClasses.editButton} classNames={actionIconClasses} onClick={() => table.setEditingRow(row)}> <ActionIcon
className={actionIconClasses.editButton}
classNames={actionIconClasses}
onClick={() => table.setEditingRow(row)}
>
<IconEdit /> <IconEdit />
</ActionIcon> </ActionIcon>
<ActionIcon color="red" onClick={() => openDeleteConfirmModal(row)}> <ActionIcon color='red' onClick={() => openDeleteConfirmModal(row)}>
<IconTrash /> <IconTrash />
</ActionIcon> </ActionIcon>
</Flex> </Flex>
@@ -100,7 +103,12 @@ console.log(theme)
}, },
renderTopToolbarCustomActions: ({ table }) => { renderTopToolbarCustomActions: ({ table }) => {
return ( return (
<Button className={buttonClasses.tableButton} classNames={buttonClasses} disabled={tableName === "Personalization"} onClick={() => table.setCreatingRow(true)}> <Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
disabled={tableName === 'Personalization'}
onClick={() => table.setCreatingRow(true)}
>
<FiPlus /> <FiPlus />
</Button> </Button>
); );

View File

@@ -1,5 +1,5 @@
import type { MRT_ColumnDef } from "mantine-react-table"; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from "react"; import { useMemo } from 'react';
export default function ApplicationColumns() { export default function ApplicationColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
@@ -9,14 +9,14 @@ export default function ApplicationColumns() {
if (row.id) { if (row.id) {
return `${row.id}`; return `${row.id}`;
} }
return ""; return '';
}, },
enableEditing: false, enableEditing: false,
id: "id", id: 'id',
header: "ID", header: 'ID',
size: 50, size: 50,
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "id", className: 'id',
id: `${cell.getValue()}-id`, id: `${cell.getValue()}-id`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -27,12 +27,12 @@ export default function ApplicationColumns() {
if (row.name) { if (row.name) {
return `${row.name}`; return `${row.name}`;
} }
return ""; return '';
}, },
id: "name", id: 'name',
header: "Name", header: 'Name',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "name", className: 'name',
id: `${cell.getValue()}-name`, id: `${cell.getValue()}-name`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -43,12 +43,12 @@ export default function ApplicationColumns() {
if (row.url) { if (row.url) {
return `${row.url}`; return `${row.url}`;
} }
return ""; return '';
}, },
id: "url", id: 'url',
header: "Url", header: 'Url',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "url", className: 'url',
id: `${cell.getValue()}-url`, id: `${cell.getValue()}-url`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -59,12 +59,12 @@ export default function ApplicationColumns() {
if (row.icon) { if (row.icon) {
return `${row.icon}`; return `${row.icon}`;
} }
return ""; return '';
}, },
id: "icon", id: 'icon',
header: "Icon", header: 'Icon',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "icon", className: 'icon',
id: `${cell.getValue()}-icon`, id: `${cell.getValue()}-icon`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -75,12 +75,12 @@ export default function ApplicationColumns() {
if (row.urlbase) { if (row.urlbase) {
return `${row.urlbase}`; return `${row.urlbase}`;
} }
return ""; return '';
}, },
id: "urlbase", id: 'urlbase',
header: "Base Url", header: 'Base Url',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "urlbase", className: 'urlbase',
id: `${cell.getValue()}-urlbase`, id: `${cell.getValue()}-urlbase`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -91,12 +91,12 @@ export default function ApplicationColumns() {
if (row.category) { if (row.category) {
return `${row.category}`; return `${row.category}`;
} }
return ""; return '';
}, },
id: "category", id: 'category',
header: "Category", header: 'Category',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "category", className: 'category',
id: `${cell.getValue()}-category`, id: `${cell.getValue()}-category`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,

View File

@@ -1,5 +1,5 @@
import type { MRT_ColumnDef } from "mantine-react-table"; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from "react"; import { useMemo } from 'react';
export default function ApplicationColumns(groups: any, groupData: any) { export default function ApplicationColumns(groups: any, groupData: any) {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
@@ -9,14 +9,14 @@ export default function ApplicationColumns(groups: any, groupData: any) {
if (row.id) { if (row.id) {
return `${row.id}`; return `${row.id}`;
} }
return ""; return '';
}, },
enableEditing: false, enableEditing: false,
id: "id", id: 'id',
header: "ID", header: 'ID',
size: 50, size: 50,
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "id", className: 'id',
id: `${cell.getValue()}-id`, id: `${cell.getValue()}-id`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -27,12 +27,12 @@ export default function ApplicationColumns(groups: any, groupData: any) {
if (row.name) { if (row.name) {
return `${row.name}`; return `${row.name}`;
} }
return ""; return '';
}, },
id: "name", id: 'name',
header: "Name", header: 'Name',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "name", className: 'name',
id: `${cell.getValue()}-name`, id: `${cell.getValue()}-name`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -43,12 +43,12 @@ export default function ApplicationColumns(groups: any, groupData: any) {
if (row.url) { if (row.url) {
return `${row.url}`; return `${row.url}`;
} }
return ""; return '';
}, },
id: "url", id: 'url',
header: "Url", header: 'Url',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "url", className: 'url',
id: `${cell.getValue()}-url`, id: `${cell.getValue()}-url`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -60,14 +60,14 @@ export default function ApplicationColumns(groups: any, groupData: any) {
if (index !== null && index !== undefined) { if (index !== null && index !== undefined) {
return `${index}`; return `${index}`;
} }
return "N/A"; return 'N/A';
}, },
id: "category", id: 'category',
header: "Category", header: 'Category',
editVariant: "select", editVariant: 'select',
mantineEditSelectProps: { data: groups }, mantineEditSelectProps: { data: groups },
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "category", className: 'category',
id: `${cell.getValue()}-category`, id: `${cell.getValue()}-category`,
}), }),
Cell: ({ renderedCellValue, row }) => Cell: ({ renderedCellValue, row }) =>

View File

@@ -1,5 +1,5 @@
import type { MRT_ColumnDef } from "mantine-react-table"; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from "react"; import { useMemo } from 'react';
export default function CategoryColumns() { export default function CategoryColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
@@ -9,11 +9,11 @@ export default function CategoryColumns() {
if (row.id) { if (row.id) {
return `${row.id}`; return `${row.id}`;
} }
return ""; return '';
}, },
enableEditing: false, enableEditing: false,
id: "id", id: 'id',
header: "ID", header: 'ID',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
@@ -23,10 +23,10 @@ export default function CategoryColumns() {
if (row.category_name) { if (row.category_name) {
return `${row.category_name}`; return `${row.category_name}`;
} }
return ""; return '';
}, },
id: "category_name", id: 'category_name',
header: "Name", header: 'Name',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
@@ -36,11 +36,11 @@ export default function CategoryColumns() {
if (row.category_index > -1) { if (row.category_index > -1) {
return `${row.category_index}`; return `${row.category_index}`;
} }
return ""; return '';
}, },
enableEditing: false, enableEditing: false,
id: "category_index", id: 'category_index',
header: "Index", header: 'Index',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
@@ -50,10 +50,10 @@ export default function CategoryColumns() {
if (row.column_filter) { if (row.column_filter) {
return `${row.column_filter}`; return `${row.column_filter}`;
} }
return ""; return '';
}, },
id: "column_filter", id: 'column_filter',
header: "Filter Name", header: 'Filter Name',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,

View File

@@ -1,5 +1,5 @@
import type { MRT_ColumnDef } from "mantine-react-table"; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from "react"; import { useMemo } from 'react';
export default function MessagesColumns() { export default function MessagesColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
@@ -9,14 +9,14 @@ export default function MessagesColumns() {
if (row.id) { if (row.id) {
return `${row.id}`; return `${row.id}`;
} }
return ""; return '';
}, },
enableEditing: false, enableEditing: false,
id: "id", id: 'id',
header: "ID", header: 'ID',
size: 50, size: 50,
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "id", className: 'id',
id: `${cell.getValue()}-id`, id: `${cell.getValue()}-id`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -27,12 +27,12 @@ export default function MessagesColumns() {
if (row.message) { if (row.message) {
return `${row.message}`; return `${row.message}`;
} }
return ""; return '';
}, },
id: "message", id: 'message',
header: "Message", header: 'Message',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "message", className: 'message',
id: `${cell.getValue()}-message`, id: `${cell.getValue()}-message`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -43,12 +43,12 @@ export default function MessagesColumns() {
if (row.easteregg) { if (row.easteregg) {
return `${row.easteregg}`; return `${row.easteregg}`;
} }
return ""; return '';
}, },
id: "easteregg", id: 'easteregg',
header: "Easter Egg?", header: 'Easter Egg?',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "easteregg", className: 'easteregg',
id: `${cell.getValue()}-easteregg`, id: `${cell.getValue()}-easteregg`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,

View File

@@ -1,5 +1,5 @@
import type { MRT_ColumnDef } from "mantine-react-table"; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from "react"; import { useMemo } from 'react';
export default function PersonalizationColumns() { export default function PersonalizationColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
@@ -9,13 +9,13 @@ export default function PersonalizationColumns() {
if (row.firstname) { if (row.firstname) {
return `${row.firstname}`; return `${row.firstname}`;
} }
return ""; return '';
}, },
id: "firstname", id: 'firstname',
header: "First Name", header: 'First Name',
size: 50, size: 50,
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "firstname", className: 'firstname',
id: `${cell.getValue()}-firstname`, id: `${cell.getValue()}-firstname`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -26,12 +26,12 @@ export default function PersonalizationColumns() {
if (row.lastname) { if (row.lastname) {
return `${row.lastname}`; return `${row.lastname}`;
} }
return ""; return '';
}, },
id: "lastname", id: 'lastname',
header: "Last Name", header: 'Last Name',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "lastname", className: 'lastname',
id: `${cell.getValue()}-lastname`, id: `${cell.getValue()}-lastname`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
@@ -42,12 +42,12 @@ export default function PersonalizationColumns() {
if (row.email) { if (row.email) {
return `${row.email}`; return `${row.email}`;
} }
return ""; return '';
}, },
id: "email", id: 'email',
header: "Email", header: 'Email',
mantineCopyButtonProps: ({ cell, row }) => ({ mantineCopyButtonProps: ({ cell, row }) => ({
className: "email", className: 'email',
id: `${cell.getValue()}-email`, id: `${cell.getValue()}-email`,
}), }),
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,

View File

@@ -1,5 +1,5 @@
import type { MRT_ColumnDef } from "mantine-react-table"; import type { MRT_ColumnDef } from 'mantine-react-table';
import { useMemo } from "react"; import { useMemo } from 'react';
export default function SearchProviderColumns() { export default function SearchProviderColumns() {
const columns = useMemo<MRT_ColumnDef<any>[]>( const columns = useMemo<MRT_ColumnDef<any>[]>(
@@ -9,11 +9,11 @@ export default function SearchProviderColumns() {
if (row.id) { if (row.id) {
return `${row.id}`; return `${row.id}`;
} }
return ""; return '';
}, },
enableEditing: false, enableEditing: false,
id: "id", id: 'id',
header: "ID", header: 'ID',
size: 50, size: 50,
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
@@ -23,10 +23,10 @@ export default function SearchProviderColumns() {
if (row.name) { if (row.name) {
return `${row.name}`; return `${row.name}`;
} }
return ""; return '';
}, },
id: "name", id: 'name',
header: "Name", header: 'Name',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
@@ -35,10 +35,10 @@ export default function SearchProviderColumns() {
if (row.url) { if (row.url) {
return `${row.url}`; return `${row.url}`;
} }
return ""; return '';
}, },
id: "url", id: 'url',
header: "Url", header: 'Url',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },
@@ -47,10 +47,10 @@ export default function SearchProviderColumns() {
if (row.prefix) { if (row.prefix) {
return `${row.prefix}`; return `${row.prefix}`;
} }
return ""; return '';
}, },
id: "prefix", id: 'prefix',
header: "Prefix", header: 'Prefix',
Cell: ({ renderedCellValue, row }) => renderedCellValue, Cell: ({ renderedCellValue, row }) => renderedCellValue,
Header: ({ column }) => column.columnDef.header, Header: ({ column }) => column.columnDef.header,
}, },

View File

@@ -1,4 +1,4 @@
import { Box } from "@mantine/core"; import { Box } from '@mantine/core';
export default function ThemeProvider({ export default function ThemeProvider({
theme, theme,
@@ -7,11 +7,12 @@ export default function ThemeProvider({
return ( return (
<Box <Box
style={{ style={{
"--color-text-pri": theme.colortextpri, '--color-text-pri': theme.colortextpri,
"--color-text-acc": theme.colortextacc, '--color-text-acc': theme.colortextacc,
"--color-background": theme.colorbackground, '--color-background': theme.colorbackground,
"--color-border": theme.colorborder, '--color-border': theme.colorborder,
"--color-search-background": theme.colorsearchbackground, '--color-search-background': theme.colorsearchbackground,
'--image-background': `url(${theme.imagebackground})`,
}} }}
> >
{children} {children}

View File

@@ -1,10 +1,10 @@
import { Stack, Text } from "@mantine/core"; import { Stack, Text } from '@mantine/core';
import { stackClasses } from "../styles"; import { stackClasses } from '../styles';
export function ToastError({ err }: { err: any }) { export function ToastError({ err }: { err: any }) {
const errorResponse = JSON.parse(err.message); const errorResponse = JSON.parse(err.message);
return ( return (
<Stack className={stackClasses.VStack} style={{ alignItems: "flex-start" }}> <Stack className={stackClasses.VStack} style={{ alignItems: 'flex-start' }}>
<Text> <Text>
Status: {errorResponse.status} {errorResponse.statusText} Status: {errorResponse.status} {errorResponse.statusText}
</Text> </Text>

View File

@@ -1,3 +1,3 @@
import { atom } from "jotai"; import { atom } from 'jotai';
export const HomepageFilters = atom([{ value: "All", label: "All" }]); export const HomepageFilters = atom([{ value: 'All', label: 'All' }]);

View File

@@ -1,5 +1,5 @@
import { atom } from "jotai"; import { atom } from 'jotai';
const appTheme = atom<any>({}); const appTheme = atom<any>({});
export { appTheme } export { appTheme };

View File

@@ -1 +1 @@
export { useQueryAPI } from "./useAPI"; export { useQueryAPI } from './useAPI';

View File

@@ -1,25 +1,25 @@
import { import {
useInfiniteQuery,
useMutation,
useQuery, useQuery,
useQueryClient, useQueryClient,
useMutation, } from '@tanstack/react-query';
useInfiniteQuery, import { useCallback } from 'react';
} from "@tanstack/react-query"; import { toast } from 'react-toastify';
import { useCallback } from "react";
import { toast } from "react-toastify";
export function useQueryAPI({ endpoint }: { endpoint: string }) { export function useQueryAPI({ endpoint }: { endpoint: string }) {
const callEndpoint = async (endpoint: string) => { const callEndpoint = async (endpoint: string) => {
const url = `http://localhost:5000/trigger/${endpoint}`; const url = `http://localhost:5000/trigger/${endpoint}`;
const options: object = { const options: object = {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*", 'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Headers": "*", 'Access-Control-Allow-Headers': '*',
"Access-Control-Allow-Methods": "POST, GET, OPTIONS", 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
"Access-Control-Allow-Credentials": true, 'Access-Control-Allow-Credentials': true,
"Sec-Fetch-Mode": "no-cors", 'Sec-Fetch-Mode': 'no-cors',
"Sec-Fetch-Site": "same-site", 'Sec-Fetch-Site': 'same-site',
}, },
}; };
@@ -31,7 +31,7 @@ export function useQueryAPI({ endpoint }: { endpoint: string }) {
}; };
return useQuery({ return useQuery({
queryKey: ["GET", endpoint], queryKey: ['GET', endpoint],
queryFn: () => callEndpoint(endpoint), queryFn: () => callEndpoint(endpoint),
}); });
} }
@@ -46,13 +46,13 @@ export function useMutationAPI() {
const options: any = { const options: any = {
method: method, method: method,
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*", 'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Headers": "*", 'Access-Control-Allow-Headers': '*',
"Access-Control-Allow-Methods": "POST, GET, OPTIONS", 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
"Access-Control-Allow-Credentials": true, 'Access-Control-Allow-Credentials': true,
"Sec-Fetch-Mode": "no-cors", 'Sec-Fetch-Mode': 'no-cors',
"Sec-Fetch-Site": "same-site", 'Sec-Fetch-Site': 'same-site',
}, },
body: JSON.stringify(body), body: JSON.stringify(body),
}; };
@@ -60,7 +60,7 @@ export function useMutationAPI() {
const response = await fetch(url, options); const response = await fetch(url, options);
if (!response.ok) { if (!response.ok) {
const responseData = await response.json(); const responseData = await response.json();
if (typeof responseData.detail !== "string") { if (typeof responseData.detail !== 'string') {
const errorResponse = JSON.stringify({ const errorResponse = JSON.stringify({
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
@@ -73,7 +73,7 @@ export function useMutationAPI() {
throw new Error(errorResponse); throw new Error(errorResponse);
} }
if (typeof responseData.detail === "object") { if (typeof responseData.detail === 'object') {
const errorResponse = JSON.stringify({ const errorResponse = JSON.stringify({
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
@@ -110,17 +110,17 @@ export function useMutationAPI() {
}) => callEndpoint(method, endpoint, body), }) => callEndpoint(method, endpoint, body),
onMutate: () => {}, onMutate: () => {},
onSuccess: (data, variables) => { onSuccess: (data, variables) => {
queryClient.invalidateQueries({ queryKey: ["GET", variables.endpoint] }); queryClient.invalidateQueries({ queryKey: ['GET', variables.endpoint] });
if (variables.endpoint.includes("/")) { if (variables.endpoint.includes('/')) {
if ((variables.endpoint.match(/\//g) || []).length > 2) { if ((variables.endpoint.match(/\//g) || []).length > 2) {
const GetEndpoint = variables.endpoint.split("/"); const GetEndpoint = variables.endpoint.split('/');
const Endpoint = `${GetEndpoint[1]}/${GetEndpoint[2]}`; const Endpoint = `${GetEndpoint[1]}/${GetEndpoint[2]}`;
queryClient.invalidateQueries({ queryKey: ["GET", `/${Endpoint}`] }); queryClient.invalidateQueries({ queryKey: ['GET', `/${Endpoint}`] });
} else { } else {
const GetEndpoint = variables.endpoint.split("/")[1]; const GetEndpoint = variables.endpoint.split('/')[1];
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["GET", `/${GetEndpoint}`], queryKey: ['GET', `/${GetEndpoint}`],
}); });
} }
} }
@@ -139,20 +139,20 @@ export function useFileUploadAPI() {
const options = { const options = {
method: method, method: method,
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*", 'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Headers": "*", 'Access-Control-Allow-Headers': '*',
"Access-Control-Allow-Methods": "POST, GET, OPTIONS", 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
"Access-Control-Allow-Credentials": true, 'Access-Control-Allow-Credentials': true,
"Sec-Fetch-Mode": "no-cors", 'Sec-Fetch-Mode': 'no-cors',
"Sec-Fetch-Site": "same-site", 'Sec-Fetch-Site': 'same-site',
}, },
body: body ?? undefined, body: body ?? undefined,
}; };
const response = await fetch(url, options as unknown as Body); const response = await fetch(url, options as unknown as Body);
if (!response.ok) { if (!response.ok) {
const responseData = await response.json(); const responseData = await response.json();
if (typeof responseData.detail !== "string") { if (typeof responseData.detail !== 'string') {
const errorResponse = JSON.stringify({ const errorResponse = JSON.stringify({
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
@@ -192,10 +192,10 @@ export function useFileUploadAPI() {
}) => callEndpoint(method, endpoint, body), }) => callEndpoint(method, endpoint, body),
onMutate: () => {}, onMutate: () => {},
onSuccess: (variables) => { onSuccess: (variables) => {
queryClient.invalidateQueries({ queryKey: ["GET", variables.endpoint] }); queryClient.invalidateQueries({ queryKey: ['GET', variables.endpoint] });
if (variables.endpoint?.includes("/")) { if (variables.endpoint?.includes('/')) {
const GetEndpoint = variables.endpoint.split("/")[1]; const GetEndpoint = variables.endpoint.split('/')[1];
queryClient.invalidateQueries({ queryKey: ["GET", `/${GetEndpoint}`] }); queryClient.invalidateQueries({ queryKey: ['GET', `/${GetEndpoint}`] });
} }
}, },
}); });
@@ -210,7 +210,7 @@ export function useFetchAllRecordsAPI({ endpoint }: { endpoint: string }) {
const url = new URL(`http://localhost:5000/trigger/${endpoint}`); const url = new URL(`http://localhost:5000/trigger/${endpoint}`);
// Check and append custom query parameters from the endpoint // Check and append custom query parameters from the endpoint
const customParams = new URLSearchParams(endpoint.split("?")[1]); const customParams = new URLSearchParams(endpoint.split('?')[1]);
for (const [key, value] of customParams) { for (const [key, value] of customParams) {
if (!url.searchParams.has(key)) { if (!url.searchParams.has(key)) {
url.searchParams.append(key, value); url.searchParams.append(key, value);
@@ -218,19 +218,19 @@ export function useFetchAllRecordsAPI({ endpoint }: { endpoint: string }) {
} }
// Add 'page' and 'size' parameters // Add 'page' and 'size' parameters
url.searchParams.set("page", page.toString()); // Use 'set' to ensure uniqueness url.searchParams.set('page', page.toString()); // Use 'set' to ensure uniqueness
url.searchParams.set("size", pageSize.toString()); url.searchParams.set('size', pageSize.toString());
const options: any = { const options: any = {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*", 'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Headers": "*", 'Access-Control-Allow-Headers': '*',
"Access-Control-Allow-Methods": "POST, GET, OPTIONS", 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
"Access-Control-Allow-Credentials": true, 'Access-Control-Allow-Credentials': true,
"Sec-Fetch-Mode": "no-cors", 'Sec-Fetch-Mode': 'no-cors',
"Sec-Fetch-Site": "same-site", 'Sec-Fetch-Site': 'same-site',
}, },
}; };
@@ -295,18 +295,18 @@ export function useFetchAllRecordsAPI({ endpoint }: { endpoint: string }) {
return allItems; return allItems;
} catch (error) { } catch (error) {
// Handling errors in fetching the first page // Handling errors in fetching the first page
console.error("Error fetching the first page:", error); console.error('Error fetching the first page:', error);
return []; // Return empty array on error return []; // Return empty array on error
} }
}; };
const invalidate = useCallback(() => { const invalidate = useCallback(() => {
queryClient.invalidateQueries({ queryKey: ["GET", endpoint] }); queryClient.invalidateQueries({ queryKey: ['GET', endpoint] });
}, [queryClient, endpoint]); }, [queryClient, endpoint]);
return { return {
...useQuery({ ...useQuery({
queryKey: ["GET", endpoint], queryKey: ['GET', endpoint],
queryFn: callEndpoint, queryFn: callEndpoint,
}), }),
invalidate, invalidate,
@@ -323,31 +323,31 @@ export function useDownloadFile() {
async ({ endpoint, file_name }: DownloadFileOptions) => { async ({ endpoint, file_name }: DownloadFileOptions) => {
const url = `http://localhost:5000/trigger/${endpoint}`; const url = `http://localhost:5000/trigger/${endpoint}`;
const options: any = { const options: any = {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*", 'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Headers": "*", 'Access-Control-Allow-Headers': '*',
"Access-Control-Allow-Methods": "POST, GET, OPTIONS", 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
"Access-Control-Allow-Credentials": true, 'Access-Control-Allow-Credentials': true,
"Sec-Fetch-Mode": "no-cors", 'Sec-Fetch-Mode': 'no-cors',
"Sec-Fetch-Site": "same-site", 'Sec-Fetch-Site': 'same-site',
}, },
}; };
const toastID = toast.loading("Processing..."); const toastID = toast.loading('Processing...');
try { try {
const response = await fetch(url, options); const response = await fetch(url, options);
if (!response.ok) { if (!response.ok) {
throw new Error("Network response was not ok"); throw new Error('Network response was not ok');
} }
const blob = await response.blob(); const blob = await response.blob();
if (blob.type === "application/json") { if (blob.type === 'application/json') {
const blob_text = await blob.text(); const blob_text = await blob.text();
const cleaned_text = blob_text.replace(/"/g, ""); const cleaned_text = blob_text.replace(/"/g, '');
toast.update(toastID, { toast.update(toastID, {
render: cleaned_text, render: cleaned_text,
type: "info", type: 'info',
isLoading: false, isLoading: false,
autoClose: 5000, autoClose: 5000,
closeButton: null, closeButton: null,
@@ -355,11 +355,11 @@ export function useDownloadFile() {
return; return;
} }
if (typeof window !== "undefined") { if (typeof window !== 'undefined') {
const blobURL = window.URL.createObjectURL(blob); const blobURL = window.URL.createObjectURL(blob);
const a = document.createElement("a"); const a = document.createElement('a');
a.style.display = "none"; a.style.display = 'none';
a.href = blobURL; a.href = blobURL;
a.download = file_name; a.download = file_name;
@@ -369,17 +369,17 @@ export function useDownloadFile() {
document.body.removeChild(a); document.body.removeChild(a);
toast.update(toastID, { toast.update(toastID, {
render: `${file_name} Downloaded`, render: `${file_name} Downloaded`,
type: "success", type: 'success',
isLoading: false, isLoading: false,
autoClose: 5000, autoClose: 5000,
closeButton: null, closeButton: null,
}); });
} }
} catch (error) { } catch (error) {
console.error("Download failed:", error); console.error('Download failed:', error);
toast.update(toastID, { toast.update(toastID, {
render: `Error Downloading ${file_name}. Please try again.`, render: `Error Downloading ${file_name}. Please try again.`,
type: "error", type: 'error',
isLoading: false, isLoading: false,
autoClose: 5000, autoClose: 5000,
closeButton: null, closeButton: null,
@@ -399,27 +399,27 @@ export const useFetchInfiniteQuery = ({
endpoint: string; endpoint: string;
pageSize?: number; pageSize?: number;
}) => { }) => {
const queryKey = ["GET", endpoint]; const queryKey = ['GET', endpoint];
const fetchInfiniteQuery = async (endpoint: string) => { const fetchInfiniteQuery = async (endpoint: string) => {
const url = `http://localhost:5000/trigger/${endpoint}`; const url = `http://localhost:5000/trigger/${endpoint}`;
const requestOptions: any = { const requestOptions: any = {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*", 'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Headers": "*", 'Access-Control-Allow-Headers': '*',
"Access-Control-Allow-Methods": "POST, GET, OPTIONS", 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
"Access-Control-Allow-Credentials": true, 'Access-Control-Allow-Credentials': true,
"Sec-Fetch-Mode": "no-cors", 'Sec-Fetch-Mode': 'no-cors',
"Sec-Fetch-Site": "same-site", 'Sec-Fetch-Site': 'same-site',
}, },
}; };
try { try {
const response = await fetch(url, requestOptions); const response = await fetch(url, requestOptions);
return await response.json(); return await response.json();
} catch (error) { } catch (error) {
console.error("Error fetching transactions:", error); console.error('Error fetching transactions:', error);
throw error; throw error;
} }
}; };
@@ -431,14 +431,8 @@ export const useFetchInfiniteQuery = ({
} }
return allPages.length + 1; return allPages.length + 1;
}; };
const { const { data, isLoading, isFetchingNextPage, fetchNextPage, isError, isFetching } =
data, useInfiniteQuery({
isLoading,
isFetchingNextPage,
fetchNextPage,
isError,
isFetching,
} = useInfiniteQuery({
queryKey, queryKey,
queryFn, queryFn,
getNextPageParam, getNextPageParam,

View File

@@ -1,5 +1,9 @@
@import url('https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100;0,200;0,400;1,100;1,200&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100;0,200;0,400;1,100;1,200&display=swap');
.root {
background-image: var(--image-background);
}
.mrt-edit-action-buttons { .mrt-edit-action-buttons {
.mantine-Button-root[data-variant="subtle"] { .mantine-Button-root[data-variant="subtle"] {
color: var(--color-text-pri) !important; color: var(--color-text-pri) !important;

View File

@@ -1,7 +1,7 @@
import React from "react"; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ReactDOM from "react-dom/client"; import React from 'react';
import App from "./App.tsx"; import ReactDOM from 'react-dom/client';
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import App from './App.tsx';
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
@@ -12,7 +12,7 @@ const queryClient = new QueryClient({
}, },
}); });
ReactDOM.createRoot(document.getElementById("root")!).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<App /> <App />

View File

@@ -1,31 +1,31 @@
import { Stack } from "@mantine/core"; import { Stack } from '@mantine/core';
import stackClasses from "../../styles/Stack.module.css"; import { useEffect, useState } from 'react';
import { useQueryAPI } from "../../hooks/useAPI"; import { useQueryAPI } from '../../hooks/useAPI';
import Search from "./components/Search"; import stackClasses from '../../styles/Stack.module.css';
import Header from "./components/Header"; import Applications from './components/Applications';
import Applications from "./components/Applications"; import Bookmarks from './components/Bookmarks';
import Bookmarks from "./components/Bookmarks"; import Header from './components/Header';
import SettingsButton from "./components/SettingsButton"; import Search from './components/Search';
import { useEffect, useState } from "react"; import SettingsButton from './components/SettingsButton';
import "../../index.css"; import '../../index.css';
import { useLocalStorage } from "@mantine/hooks"; import { useLocalStorage } from '@mantine/hooks';
export default function Homepage({ theme }: any) { export default function Homepage({ theme }: any) {
const { data, isFetching } = useQueryAPI({ endpoint: "api/messages" }); const { data, isFetching } = useQueryAPI({ endpoint: 'api/messages' });
const [displayedGreeting, setDisplayedGreeting] = useState(""); const [displayedGreeting, setDisplayedGreeting] = useState('');
const [contentType, setContentType] = useLocalStorage<any>({ const [contentType, setContentType] = useLocalStorage<any>({
key: "contenttype", key: 'contenttype',
defaultValue: "All", defaultValue: 'All',
}); });
function date() { function date() {
const currentDate = new Date(); const currentDate = new Date();
const dateOptions: Intl.DateTimeFormatOptions = { const dateOptions: Intl.DateTimeFormatOptions = {
weekday: "long", weekday: 'long',
year: "numeric", year: 'numeric',
month: "long", month: 'long',
day: "numeric", day: 'numeric',
}; };
const date = currentDate.toLocaleDateString("en-GB", dateOptions); const date = currentDate.toLocaleDateString('en-GB', dateOptions);
return date; return date;
} }
function greet(list: any) { function greet(list: any) {
@@ -35,7 +35,7 @@ export default function Homepage({ theme }: any) {
let timeBased: any = []; let timeBased: any = [];
let easterEggs: any = []; let easterEggs: any = [];
for (const l of list) { for (const l of list) {
if (l.easteregg === "N") { if (l.easteregg === 'N') {
timeBased = timeBased.concat(l); timeBased = timeBased.concat(l);
} else { } else {
easterEggs = easterEggs.concat(l); easterEggs = easterEggs.concat(l);
@@ -54,7 +54,7 @@ export default function Homepage({ theme }: any) {
}, [isFetching]); }, [isFetching]);
return ( return (
<Stack gap={0} className={stackClasses.containerStack} justify="center"> <Stack gap={0} className={stackClasses.containerStack} justify='center'>
<Search /> <Search />
<Header date={date()} greet={displayedGreeting} /> <Header date={date()} greet={displayedGreeting} />
<Applications contentType={contentType} /> <Applications contentType={contentType} />

View File

@@ -1,37 +1,37 @@
import { Icon } from "@iconify/react"; import { Icon } from '@iconify/react';
import { Box, Stack, Text } from "@mantine/core"; import { Box, Stack, Text } from '@mantine/core';
import { useState } from "react"; import { useState } from 'react';
import stacks from "../../../styles/Stack.module.css"; import stacks from '../../../styles/Stack.module.css';
import texts from "../../../styles/Text.module.css"; import texts from '../../../styles/Text.module.css';
function AppItem({ url, icon, name }: any) { function AppItem({ url, icon, name }: any) {
const [textDecoration, setTextDecoration] = useState<string>("none"); const [textDecoration, setTextDecoration] = useState<string>('none');
return ( return (
<Box <Box
component="a" component='a'
href={`https://${url}`} href={`https://${url}`}
onMouseOver={() => setTextDecoration("underline")} onMouseOver={() => setTextDecoration('underline')}
onMouseOut={() => setTextDecoration("none")} onMouseOut={() => setTextDecoration('none')}
className={stacks.appItemBox} className={stacks.appItemBox}
> >
<Stack <Stack
style={{ style={{
height: "64px", height: '64px',
marginRight: "1em", marginRight: '1em',
marginLeft: "1em", marginLeft: '1em',
paddingTop: "15px", paddingTop: '15px',
}} }}
> >
<Icon icon={`mdi:${icon}`} height={32} width={32} /> <Icon icon={`mdi:${icon}`} height={32} width={32} />
</Stack> </Stack>
<Stack <Stack
style={{ style={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
justifyContent: "center", justifyContent: 'center',
flex: 1, flex: 1,
overflow: "hidden", overflow: 'hidden',
gap: 0, gap: 0,
}} }}
> >

View File

@@ -1,11 +1,11 @@
import { Stack, Text } from "@mantine/core"; import { Stack, Text } from '@mantine/core';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useQueryAPI } from '../../../hooks/useAPI';
import texts from "../../../styles/Text.module.css"; import texts from '../../../styles/Text.module.css';
import AppItem from "./AppItem"; import AppItem from './AppItem';
function Applications({ contentType }: any) { function Applications({ contentType }: any) {
const { data, isFetching } = useQueryAPI({ endpoint: "api/apps" }); const { data, isFetching } = useQueryAPI({ endpoint: 'api/apps' });
const [applications, setApplications] = useState([]); const [applications, setApplications] = useState([]);
useEffect(() => { useEffect(() => {
if (!isFetching) { if (!isFetching) {
@@ -13,26 +13,26 @@ function Applications({ contentType }: any) {
} }
}, [isFetching]); }, [isFetching]);
return ( return (
<Stack id="apps" style={{ gap: 0 }}> <Stack id='apps' style={{ gap: 0 }}>
<Text className={texts.sectionTitle} classNames={texts}> <Text className={texts.sectionTitle} classNames={texts}>
Applications Applications
</Text> </Text>
<Stack <Stack
style={{ style={{
background: "var(--color-background)", background: 'var(--color-background)',
gap: ".25rem", gap: '.25rem',
backgroundSize: "cover", backgroundSize: 'cover',
borderBottom: "0px solid", borderBottom: '0px solid',
display: "grid", display: 'grid',
gridColumnGap: "5px", gridColumnGap: '5px',
gridRowGap: "5px", gridRowGap: '5px',
gridTemplateColumns: "1fr 1fr 1fr 1fr 1fr 1fr", gridTemplateColumns: '1fr 1fr 1fr 1fr 1fr 1fr',
gridTemplateRows: "64px", gridTemplateRows: '64px',
paddingBottom: "var(--module-spacing)", paddingBottom: 'var(--module-spacing)',
}} }}
> >
{applications?.map((item: any) => { {applications?.map((item: any) => {
if (item.category === contentType || contentType === "All") if (item.category === contentType || contentType === 'All')
return ( return (
<AppItem <AppItem
key={item.name} key={item.name}

View File

@@ -1,15 +1,15 @@
import { Stack, Text } from "@mantine/core"; import { Stack, Text } from '@mantine/core';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useQueryAPI } from '../../../hooks/useAPI';
import texts from "../../../styles/Text.module.css"; import texts from '../../../styles/Text.module.css';
import LinkGroups from "./LinkGroups"; import LinkGroups from './LinkGroups';
function Links({ contentType }: any) { function Links({ contentType }: any) {
const { data: linkData, isFetched: linksIsFetched } = useQueryAPI({ const { data: linkData, isFetched: linksIsFetched } = useQueryAPI({
endpoint: "api/links", endpoint: 'api/links',
}); });
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({ const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
endpoint: "api/categories", endpoint: 'api/categories',
}); });
const [bookmarks, setBookmarks] = useState([]); const [bookmarks, setBookmarks] = useState([]);
const [categories, setCategories] = useState([]); const [categories, setCategories] = useState([]);
@@ -24,32 +24,28 @@ function Links({ contentType }: any) {
return ( return (
<> <>
{groupsIsFetched && linksIsFetched && categories?.length > 0 && ( {groupsIsFetched && linksIsFetched && categories?.length > 0 && (
<Stack id="links" style={{ gap: 0 }}> <Stack id='links' style={{ gap: 0 }}>
<Text className={texts.sectionTitle} classNames={texts}> <Text className={texts.sectionTitle} classNames={texts}>
Bookmarks Bookmarks
</Text> </Text>
<Stack <Stack
style={{ style={{
display: "grid", display: 'grid',
flexWrap: "nowrap", flexWrap: 'nowrap',
gridColumnGap: "20px", gridColumnGap: '20px',
gridRowGap: "0px", gridRowGap: '0px',
gridTemplateColumns: "1fr 1fr 1fr 1fr 1fr", gridTemplateColumns: '1fr 1fr 1fr 1fr 1fr',
gridTemplateRows: "auto", gridTemplateRows: 'auto',
}} }}
> >
{categories.map((category: any) => { {categories.map((category: any) => {
if ( if (category.column_filter === contentType || contentType === 'All') {
category.column_filter === contentType ||
contentType === "All"
) {
return ( return (
<LinkGroups <LinkGroups
key={category.id} key={category.id}
category={category.category_name} category={category.category_name}
links={bookmarks.filter( links={bookmarks.filter(
(bookmark: any) => (bookmark: any) => bookmark.category === category.category_index,
bookmark.category === category.category_index,
)} )}
/> />
); );

View File

@@ -1,6 +1,6 @@
import { Stack, Text } from "@mantine/core"; import { Stack, Text } from '@mantine/core';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { textClasses } from "../../../styles"; import { textClasses } from '../../../styles';
function Header({ greet, date }: any) { function Header({ greet, date }: any) {
const [time, setTime] = useState(new Date()); const [time, setTime] = useState(new Date());
@@ -22,18 +22,18 @@ function Header({ greet, date }: any) {
return ( return (
<Stack <Stack
style={{ style={{
borderBottom: "0px solid", borderBottom: '0px solid',
zIndex: "1", zIndex: '1',
clear: "both", clear: 'both',
display: "flex", display: 'flex',
flexDirection: "row", flexDirection: 'row',
gap: 0, gap: 0,
}} }}
> >
<Stack <Stack
style={{ style={{
display: "grid", display: 'grid',
width: "100%", width: '100%',
}} }}
> >
<Text className={textClasses.greeting} classNames={textClasses}> <Text className={textClasses.greeting} classNames={textClasses}>
@@ -42,27 +42,27 @@ function Header({ greet, date }: any) {
</Stack> </Stack>
<Stack <Stack
style={{ style={{
display: "flex", display: 'flex',
float: "right", float: 'right',
justifyContent: "right", justifyContent: 'right',
width: "auto", width: 'auto',
gap: 0, gap: 0,
}} }}
> >
<Text <Text
className={textClasses.dateTime} className={textClasses.dateTime}
classNames={textClasses} classNames={textClasses}
style={{ width: "max-content" }} style={{ width: 'max-content' }}
> >
{date} {date}
</Text> </Text>
<Text <Text
component="h2" component='h2'
className={textClasses.dateTime} className={textClasses.dateTime}
classNames={textClasses} classNames={textClasses}
style={{ style={{
justifyContent: "right", justifyContent: 'right',
display: "grid", display: 'grid',
}} }}
> >
{`${ {`${
@@ -71,7 +71,7 @@ function Header({ greet, date }: any) {
: time.getHours() > 12 : time.getHours() > 12
? time.getHours() - 12 ? time.getHours() - 12
: time.getHours() : time.getHours()
}:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? "PM" : "AM"}`} }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`}
</Text> </Text>
</Stack> </Stack>
</Stack> </Stack>

View File

@@ -1,14 +1,14 @@
import { Stack, Text } from "@mantine/core"; import { Stack, Text } from '@mantine/core';
import texts from "../../../styles/Text.module.css"; import texts from '../../../styles/Text.module.css';
import LinkItem from "./LinkItem"; import LinkItem from './LinkItem';
function LinkGroups({ links, category }: any) { function LinkGroups({ links, category }: any) {
return ( return (
<Stack <Stack
style={{ style={{
marginBottom: "0em", marginBottom: '0em',
display: "block", display: 'block',
lineHeight: "1.5rem", lineHeight: '1.5rem',
}} }}
> >
<Text className={texts.linkCategory} classNames={texts}> <Text className={texts.linkCategory} classNames={texts}>

View File

@@ -1,14 +1,9 @@
import { Text } from "@mantine/core"; import { Text } from '@mantine/core';
import texts from "../../../styles/Text.module.css"; import texts from '../../../styles/Text.module.css';
function LinkItem({ link }: any) { function LinkItem({ link }: any) {
return ( return (
<Text <Text className={texts.linkItem} classNames={texts} component='a' href={link.url}>
className={texts.linkItem}
classNames={texts}
component="a"
href={link.url}
>
{link.name} {link.name}
</Text> </Text>
); );

View File

@@ -1,9 +1,9 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import Messages from "../../../components/Messages"; import Messages from '../../../components/Messages';
import useQueryAPI from "../../../hooks/useAPI"; import useQueryAPI from '../../../hooks/useAPI';
function MessageInfo() { function MessageInfo() {
const messageData = useQueryAPI({ endpoint: "api/messages" }); const messageData = useQueryAPI({ endpoint: 'api/messages' });
const [greetings, setGreetings] = useState([]); const [greetings, setGreetings] = useState([]);
useEffect(() => { useEffect(() => {
@@ -15,7 +15,7 @@ function MessageInfo() {
return ( return (
<> <>
{greetings && ( {greetings && (
<div id="messageForm"> <div id='messageForm'>
{greetings.map((greeting, index) => ( {greetings.map((greeting, index) => (
<Messages <Messages
key={greeting.id} key={greeting.id}

View File

@@ -1,50 +1,47 @@
import { Stack, TextInput } from "@mantine/core"; import { Stack, TextInput } from '@mantine/core';
import { textInputClasses } from "../../../styles"; import { useState } from 'react';
import { useQueryAPI } from "../../../hooks"; import { useQueryAPI } from '../../../hooks';
import { useState } from "react"; import { textInputClasses } from '../../../styles';
export default function Search() { export default function Search() {
const { data: providers } = useQueryAPI({ endpoint: "api/providers" }); const { data: providers } = useQueryAPI({ endpoint: 'api/providers' });
const [providerLabel, setProviderLabel] = useState("Google Search"); const [providerLabel, setProviderLabel] = useState('Google Search');
const [providerURL, setProviderURL] = useState( const [providerURL, setProviderURL] = useState('https://www.google.com/search?q=');
"https://www.google.com/search?q=", const [value, setValue] = useState('');
);
const [value, setValue] = useState("");
const handleKeyPress = (item: any) => { const handleKeyPress = (item: any) => {
let option = '' let option = '';
let subtext = '' let subtext = '';
if (item.target.value.startsWith("/")) { if (item.target.value.startsWith('/')) {
option = option =
item.target.value.substr(1, item.target.value.indexOf(" ") - 1) || item.target.value.substr(1, item.target.value.indexOf(' ') - 1) ||
item.target.value.substr(1); item.target.value.substr(1);
subtext = item.target.value.substr(2 + option.length); subtext = item.target.value.substr(2 + option.length);
} else { } else {
subtext = item.target.value; subtext = item.target.value;
} }
if (item.key === "Enter") { if (item.key === 'Enter') {
if (item.target.value.startsWith("/")) { if (item.target.value.startsWith('/')) {
open(`${providerURL}${subtext}`, "_self"); open(`${providerURL}${subtext}`, '_self');
} else if (validURL(item.target.value)) { } else if (validURL(item.target.value)) {
if (containsProtocol(item.target.value)) if (containsProtocol(item.target.value)) window.location = item.target.value;
window.location = item.target.value; else open(`https://${item.target.value}`, '_self');
else open(`https://${item.target.value}`, "_self");
} else { } else {
open(`${providerURL}${subtext}`, "_self"); open(`${providerURL}${subtext}`, '_self');
} }
} }
}; };
function validURL(str: any) { function validURL(str: any) {
const pattern = new RegExp( const pattern = new RegExp(
"^(https?:\\/\\/)?" + // protocol '^(https?:\\/\\/)?' + // protocol
"((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
"((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
"(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
"(\\?[;&a-z\\d%_.~+=-]*)?" + // query string '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
"(\\#[-a-z\\d_]*)?$", '(\\#[-a-z\\d_]*)?$',
"i", 'i',
); // fragment locator ); // fragment locator
return !!pattern.test(str); return !!pattern.test(str);
} }
@@ -56,37 +53,37 @@ export default function Search() {
return ( return (
<Stack <Stack
style={{ style={{
height: "100px", height: '100px',
backgroundColor: "transparent", backgroundColor: 'transparent',
textAlign: "center", textAlign: 'center',
position: "relative", position: 'relative',
padding: ".3em", padding: '.3em',
display: "inline-block", display: 'inline-block',
gap: 0, gap: 0,
}} }}
> >
<TextInput <TextInput
className={textInputClasses.search} className={textInputClasses.search}
classNames={textInputClasses} classNames={textInputClasses}
spellCheck="true" spellCheck='true'
leftSection={providerLabel} leftSection={providerLabel}
value={value} value={value}
data-active={value !== ''} data-active={value !== ''}
onChange={(item: any) => { onChange={(item: any) => {
setValue(item.currentTarget.value) setValue(item.currentTarget.value);
if (item.target.value.startsWith("/")) { if (item.target.value.startsWith('/')) {
const provider = providers?.find( const provider = providers?.find(
(p: any) => p.prefix === item.target.value.split(" ")[0], (p: any) => p.prefix === item.target.value.split(' ')[0],
) );
setProviderLabel(provider?.name || "Invalid Search"); setProviderLabel(provider?.name || 'Invalid Search');
setProviderURL(provider?.url); setProviderURL(provider?.url);
} else { } else {
setProviderLabel("Google Search"); setProviderLabel('Google Search');
setProviderURL("https://www.google.com/search?q="); setProviderURL('https://www.google.com/search?q=');
} }
}} }}
onKeyUp={(item: any) => { onKeyUp={(item: any) => {
if(providerLabel !== "Invalid Search"){ if (providerLabel !== 'Invalid Search') {
handleKeyPress(item); handleKeyPress(item);
} }
}} }}

View File

@@ -1,17 +1,15 @@
import { Button, Menu, NavLink, Select } from "@mantine/core"; import { Button, Menu, NavLink, Select } from '@mantine/core';
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { FiMenu, FiMonitor } from "react-icons/fi"; import { FiMenu, FiMonitor } from 'react-icons/fi';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useQueryAPI } from '../../../hooks/useAPI';
import button from "../../../styles/Button.module.css"; import button from '../../../styles/Button.module.css';
import menu from "../../../styles/Menu.module.css"; import menu from '../../../styles/Menu.module.css';
import navLink from "../../../styles/NavLink.module.css"; import navLink from '../../../styles/NavLink.module.css';
import selectClasses from "../../../styles/Select.module.css"; import selectClasses from '../../../styles/Select.module.css';
function SettingsButton({ contentType, setContentType, theme }: any) { function SettingsButton({ contentType, setContentType, theme }: any) {
const { data, isFetched } = useQueryAPI({ endpoint: "api/appCategories" }); const { data, isFetched } = useQueryAPI({ endpoint: 'api/appCategories' });
const [categories, setCategories] = useState([ const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]);
{ value: "All", label: "All" },
]);
useEffect(() => { useEffect(() => {
if (data) { if (data) {
@@ -19,20 +17,20 @@ function SettingsButton({ contentType, setContentType, theme }: any) {
data.map((category: any) => { data.map((category: any) => {
return { value: category.category, label: category.category }; return { value: category.category, label: category.category };
}) || []; }) || [];
list.unshift({ value: "All", label: "All" }); list.unshift({ value: 'All', label: 'All' });
setCategories(list); setCategories(list);
} }
}, [data, isFetched]); }, [data, isFetched]);
function runWakeCommand() { function runWakeCommand() {
const requestOptions = { const requestOptions = {
method: "GET", method: 'GET',
headers: { "Content-Type": "application/json" }, headers: { 'Content-Type': 'application/json' },
}; };
fetch("/trigger/api/wakeDakota", requestOptions); fetch('/trigger/api/wakeDakota', requestOptions);
} }
return ( return (
<Menu withinPortal={false} withArrow shadow="md" position="top"> <Menu withinPortal={false} withArrow shadow='md' position='top'>
<Menu.Target> <Menu.Target>
<Button className={button.settingsButton} classNames={button}> <Button className={button.settingsButton} classNames={button}>
<FiMenu /> <FiMenu />
@@ -40,19 +38,19 @@ function SettingsButton({ contentType, setContentType, theme }: any) {
</Menu.Target> </Menu.Target>
<Menu.Dropdown className={menu.dropdown}> <Menu.Dropdown className={menu.dropdown}>
<NavLink <NavLink
label="Settings" label='Settings'
component="a" component='a'
href="/trigger/settings" href='/trigger/settings'
className={navLink.defaultNavLink} className={navLink.defaultNavLink}
classNames={navLink} classNames={navLink}
/> />
<NavLink <NavLink
label="Quick Actions" label='Quick Actions'
className={navLink.defaultNavLink} className={navLink.defaultNavLink}
classNames={navLink} classNames={navLink}
> >
<NavLink <NavLink
label="Wake Dakota" label='Wake Dakota'
leftSection={<FiMonitor />} leftSection={<FiMonitor />}
onClick={() => runWakeCommand()} onClick={() => runWakeCommand()}
className={navLink.defaultNavLink} className={navLink.defaultNavLink}
@@ -60,7 +58,7 @@ function SettingsButton({ contentType, setContentType, theme }: any) {
/> />
</NavLink> </NavLink>
<Select <Select
label="Homepage Filter" label='Homepage Filter'
data={categories ?? []} data={categories ?? []}
defaultValue={contentType} defaultValue={contentType}
withCheckIcon={false} withCheckIcon={false}

View File

@@ -1,24 +1,24 @@
import { ActionIcon, Group, Stack, Tabs, Text } from "@mantine/core"; import { ActionIcon, Group, Stack, Tabs, Text } from '@mantine/core';
import tabClasses from "../../styles/Tabs.module.css"; import { FiArrowLeftCircle } from 'react-icons/fi';
import Personalization from "./components/Personalization"; import { actionIconClasses, textClasses } from '../../styles';
import { FiArrowLeftCircle } from "react-icons/fi"; import tabClasses from '../../styles/Tabs.module.css';
import { actionIconClasses, textClasses } from "../../styles"; import Applications from './components/Applications';
import Messages from "./components/Messages"; import Bookmarks from './components/Bookmarks';
import Applications from "./components/Applications"; import Categories from './components/Categories';
import Bookmarks from "./components/Bookmarks"; import Messages from './components/Messages';
import Categories from "./components/Categories"; import Personalization from './components/Personalization';
import SearchProviders from "./components/SearchProviders"; import SearchProviders from './components/SearchProviders';
import Themes from "./components/Themes"; import Themes from './components/Themes';
function Settings({ theme }: any) { function Settings({ theme }: any) {
return ( return (
<Stack h={"100vh"} px={"2rem"} bg={"var(--color-background)"}> <Stack h={'100vh'} px={'2rem'} bg={'var(--color-background)'}>
<Group gap={0}> <Group gap={0}>
<ActionIcon <ActionIcon
className={actionIconClasses.backButton} className={actionIconClasses.backButton}
classNames={actionIconClasses} classNames={actionIconClasses}
component="a" component='a'
href="/trigger" href='/trigger'
> >
<FiArrowLeftCircle /> <FiArrowLeftCircle />
</ActionIcon> </ActionIcon>
@@ -27,39 +27,39 @@ function Settings({ theme }: any) {
</Text> </Text>
</Group> </Group>
<Tabs <Tabs
orientation="vertical" orientation='vertical'
defaultValue={"personalization"} defaultValue={'personalization'}
className={tabClasses.settingsTabs} className={tabClasses.settingsTabs}
classNames={tabClasses} classNames={tabClasses}
> >
<Tabs.List> <Tabs.List>
<Tabs.Tab value="personalization">Personal Info</Tabs.Tab> <Tabs.Tab value='personalization'>Personal Info</Tabs.Tab>
<Tabs.Tab value="messages">Messages</Tabs.Tab> <Tabs.Tab value='messages'>Messages</Tabs.Tab>
<Tabs.Tab value="applications">Applications</Tabs.Tab> <Tabs.Tab value='applications'>Applications</Tabs.Tab>
<Tabs.Tab value="bookmarks">Bookmarks</Tabs.Tab> <Tabs.Tab value='bookmarks'>Bookmarks</Tabs.Tab>
<Tabs.Tab value="bookmarksCategories">Categories</Tabs.Tab> <Tabs.Tab value='bookmarksCategories'>Categories</Tabs.Tab>
<Tabs.Tab value="themes">Themes</Tabs.Tab> <Tabs.Tab value='themes'>Themes</Tabs.Tab>
<Tabs.Tab value="providers">Search Providers</Tabs.Tab> <Tabs.Tab value='providers'>Search Providers</Tabs.Tab>
</Tabs.List> </Tabs.List>
<Tabs.Panel value="personalization"> <Tabs.Panel value='personalization'>
<Personalization /> <Personalization />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value="messages"> <Tabs.Panel value='messages'>
<Messages /> <Messages />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value="applications"> <Tabs.Panel value='applications'>
<Applications /> <Applications />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value="bookmarks"> <Tabs.Panel value='bookmarks'>
<Bookmarks /> <Bookmarks />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value="bookmarksCategories"> <Tabs.Panel value='bookmarksCategories'>
<Categories /> <Categories />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value="themes"> <Tabs.Panel value='themes'>
<Themes theme={theme} /> <Themes theme={theme} />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value="providers"> <Tabs.Panel value='providers'>
<SearchProviders /> <SearchProviders />
</Tabs.Panel> </Tabs.Panel>
</Tabs> </Tabs>

View File

@@ -1,12 +1,12 @@
import { useEffect, useState } from "react"; import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useEffect, useState } from 'react';
import { DataTable } from "../../../DataTable/DataTable"; import { DataTable } from '../../../DataTable/DataTable';
import ApplicationColumns from "../../../DataTable/columns/ApplicationColumns"; import ApplicationColumns from '../../../DataTable/columns/ApplicationColumns';
import { Stack, Text } from "@mantine/core"; import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from "../../../styles"; import { textClasses } from '../../../styles';
const ApplicationSettings = () => { const ApplicationSettings = () => {
const endpoint = "api/apps"; const endpoint = 'api/apps';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
@@ -24,7 +24,7 @@ const ApplicationSettings = () => {
Applications Applications
</Text> </Text>
<DataTable <DataTable
tableName="Applications" tableName='Applications'
columns={ApplicationColumns()} columns={ApplicationColumns()}
tableData={applicationData} tableData={applicationData}
isFetching={isFetching} isFetching={isFetching}

View File

@@ -1,11 +1,11 @@
import { useEffect, useState } from "react"; import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useEffect, useState } from 'react';
import { DataTable } from "../../../DataTable/DataTable"; import { DataTable } from '../../../DataTable/DataTable';
import { Stack, Text } from "@mantine/core"; import BookmarkColumns from '../../../DataTable/columns/BookmarkColumns';
import BookmarkColumns from "../../../DataTable/columns/BookmarkColumns"; import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from "../../../styles"; import { textClasses } from '../../../styles';
const BookmarkSettings = () => { const BookmarkSettings = () => {
const endpoint = "api/links"; const endpoint = 'api/links';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
@@ -14,7 +14,7 @@ const BookmarkSettings = () => {
isFetching: groupDataisFetching, isFetching: groupDataisFetching,
isLoading: groupDataisLoading, isLoading: groupDataisLoading,
isFetched: groupDataisFetched, isFetched: groupDataisFetched,
} = useQueryAPI({ endpoint: "api/categories" }); } = useQueryAPI({ endpoint: 'api/categories' });
const [bookmarkData, setBookmarkData] = useState<any>([]); const [bookmarkData, setBookmarkData] = useState<any>([]);
const [groups, setGroups] = useState([]); const [groups, setGroups] = useState([]);
const [fullyFetched, setFullyFetched] = useState(false); const [fullyFetched, setFullyFetched] = useState(false);
@@ -41,7 +41,7 @@ const BookmarkSettings = () => {
Bookmarks Bookmarks
</Text> </Text>
<DataTable <DataTable
tableName="Applications" tableName='Applications'
columns={BookmarkColumns(groups, groupData)} columns={BookmarkColumns(groups, groupData)}
tableData={bookmarkData} tableData={bookmarkData}
isFetching={isFetching} isFetching={isFetching}

View File

@@ -1,11 +1,11 @@
import { useEffect, useState } from "react"; import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useEffect, useState } from 'react';
import { DataTable } from "../../../DataTable/DataTable"; import { DataTable } from '../../../DataTable/DataTable';
import CategoryColumns from "../../../DataTable/columns/CategoryColumns"; import CategoryColumns from '../../../DataTable/columns/CategoryColumns';
import { Stack, Text } from "@mantine/core"; import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from "../../../styles"; import { textClasses } from '../../../styles';
const CategorySettings = () => { const CategorySettings = () => {
const endpoint = "api/categories"; const endpoint = 'api/categories';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
@@ -23,7 +23,7 @@ const CategorySettings = () => {
Categories Categories
</Text> </Text>
<DataTable <DataTable
tableName="Categories" tableName='Categories'
columns={CategoryColumns()} columns={CategoryColumns()}
tableData={categoryData} tableData={categoryData}
isFetching={isFetching} isFetching={isFetching}

View File

@@ -1,11 +1,11 @@
import { useEffect, useState } from "react"; import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useEffect, useState } from 'react';
import { DataTable } from "../../../DataTable/DataTable"; import { DataTable } from '../../../DataTable/DataTable';
import MessagesColumns from "../../../DataTable/columns/MessagesColumns"; import MessagesColumns from '../../../DataTable/columns/MessagesColumns';
import { Stack, Text } from "@mantine/core"; import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from "../../../styles"; import { textClasses } from '../../../styles';
const MessageSettings = () => { const MessageSettings = () => {
const endpoint = "api/messages"; const endpoint = 'api/messages';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
@@ -23,7 +23,7 @@ const MessageSettings = () => {
Messages Messages
</Text> </Text>
<DataTable <DataTable
tableName="Messages" tableName='Messages'
columns={MessagesColumns()} columns={MessagesColumns()}
tableData={messageData} tableData={messageData}
isFetching={isFetching} isFetching={isFetching}

View File

@@ -1,11 +1,11 @@
import { useEffect, useState } from "react"; import { Text } from '@mantine/core';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useEffect, useState } from 'react';
import { DataTable } from "../../../DataTable/DataTable"; import { DataTable } from '../../../DataTable/DataTable';
import PersonalizationColumns from "../../../DataTable/columns/PersonalizationColumns"; import PersonalizationColumns from '../../../DataTable/columns/PersonalizationColumns';
import { textClasses } from "../../../styles"; import { useQueryAPI } from '../../../hooks/useAPI';
import { Text } from "@mantine/core"; import { textClasses } from '../../../styles';
const PersonalizationSettings = () => { const PersonalizationSettings = () => {
const endpoint = "api/personaldata"; const endpoint = 'api/personaldata';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
@@ -23,7 +23,7 @@ const PersonalizationSettings = () => {
Personalization Personalization
</Text> </Text>
<DataTable <DataTable
tableName="Personalization" tableName='Personalization'
columns={PersonalizationColumns()} columns={PersonalizationColumns()}
tableData={personalData} tableData={personalData}
isFetching={isFetching} isFetching={isFetching}

View File

@@ -1,11 +1,11 @@
import { useEffect, useState } from "react"; import { Stack, Text } from '@mantine/core';
import { useQueryAPI } from "../../../hooks/useAPI"; import { useEffect, useState } from 'react';
import { DataTable } from "../../../DataTable/DataTable"; import { DataTable } from '../../../DataTable/DataTable';
import SearchProviderColumns from "../../../DataTable/columns/SearchProviderColumns"; import SearchProviderColumns from '../../../DataTable/columns/SearchProviderColumns';
import { Stack, Text } from "@mantine/core"; import { useQueryAPI } from '../../../hooks/useAPI';
import { textClasses } from "../../../styles"; import { textClasses } from '../../../styles';
const SearchProviderSettings = () => { const SearchProviderSettings = () => {
const endpoint = "api/providers"; const endpoint = 'api/providers';
const { data, isFetching, isLoading, isFetched } = useQueryAPI({ const { data, isFetching, isLoading, isFetched } = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
@@ -23,7 +23,7 @@ const SearchProviderSettings = () => {
Search Providers Search Providers
</Text> </Text>
<DataTable <DataTable
tableName="Categories" tableName='Categories'
columns={SearchProviderColumns()} columns={SearchProviderColumns()}
tableData={providerData} tableData={providerData}
isFetching={isFetching} isFetching={isFetching}

View File

@@ -1,40 +1,40 @@
import { Icon } from "@iconify/react/dist/iconify.js"; import { Icon } from '@iconify/react/dist/iconify.js';
import { Stack, Group, Text, Box } from "@mantine/core"; import { Box, Group, Stack, Text } from '@mantine/core';
import { useState } from "react"; import { useState } from 'react';
import { stackClasses } from "../../../styles"; import { stackClasses } from '../../../styles';
export default function ThemeModalContent({ selectedTheme }: any) { export default function ThemeModalContent({ selectedTheme }: any) {
const ExampleApp = (previewTheme: any) => { const ExampleApp = (previewTheme: any) => {
const [textDecoration, setTextDecoration] = useState<string>("none"); const [textDecoration, setTextDecoration] = useState<string>('none');
return ( return (
<Box <Box
component="a" component='a'
onMouseOver={() => setTextDecoration("underline")} onMouseOver={() => setTextDecoration('underline')}
onMouseOut={() => setTextDecoration("none")} onMouseOut={() => setTextDecoration('none')}
className={stackClasses.appItemBox} className={stackClasses.appItemBox}
> >
<Stack <Stack
style={{ style={{
height: "64px", height: '64px',
marginRight: "1em", marginRight: '1em',
marginLeft: "1em", marginLeft: '1em',
paddingTop: "15px", paddingTop: '15px',
}} }}
> >
<Icon <Icon
style={{ color: previewTheme.theme.colortextpri }} style={{ color: previewTheme.theme.colortextpri }}
icon={`mdi:check`} icon={'mdi:check'}
height={32} height={32}
width={32} width={32}
/> />
</Stack> </Stack>
<Stack <Stack
style={{ style={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
justifyContent: "center", justifyContent: 'center',
flex: 1, flex: 1,
overflow: "hidden", overflow: 'hidden',
gap: 0, gap: 0,
}} }}
> >
@@ -42,28 +42,28 @@ export default function ThemeModalContent({ selectedTheme }: any) {
style={{ style={{
fontWeight: 400, fontWeight: 400,
fontSize: 11, fontSize: 11,
fontStyle: "normal", fontStyle: 'normal',
color: previewTheme.theme.colortextpri, color: previewTheme.theme.colortextpri,
fontFamily: fontFamily:
"-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important", '-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important',
margin: 0, margin: 0,
}} }}
> >
{"Example App"} {'Example App'}
</Text> </Text>
<Text <Text
style={{ style={{
fontWeight: 400, fontWeight: 400,
fontSize: 11, fontSize: 11,
fontStyle: "italic", fontStyle: 'italic',
color: previewTheme.theme.colortextacc, color: previewTheme.theme.colortextacc,
fontFamily: fontFamily:
"-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important", '-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important',
margin: 0, margin: 0,
textDecoration: textDecoration, textDecoration: textDecoration,
}} }}
> >
{"https://www.example.com"} {'https://www.example.com'}
</Text> </Text>
</Stack> </Stack>
</Box> </Box>
@@ -72,31 +72,31 @@ export default function ThemeModalContent({ selectedTheme }: any) {
return ( return (
<Stack <Stack
style={{ style={{
width: "100%", width: '100%',
backgroundColor: selectedTheme.colorbackground, backgroundColor: selectedTheme.colorbackground,
borderRadius: "10px", borderRadius: '10px',
}} }}
> >
<Group <Group
justify="center" justify='center'
style={{ style={{
color: selectedTheme.colortextpri, color: selectedTheme.colortextpri,
paddingTop: "1rem", paddingTop: '1rem',
}} }}
> >
{selectedTheme.name} {selectedTheme.name}
</Group> </Group>
<Text <Text
pl={"1.5rem"} pl={'1.5rem'}
style={{ style={{
textDecoration: "underline", textDecoration: 'underline',
fontSize: "20px", fontSize: '20px',
fontWeight: 200, fontWeight: 200,
fontStyle: "italic", fontStyle: 'italic',
height: "10px", height: '10px',
textTransform: "uppercase", textTransform: 'uppercase',
marginTop: "20px", marginTop: '20px',
marginBottom: "20px", marginBottom: '20px',
color: selectedTheme.colortextpri, color: selectedTheme.colortextpri,
}} }}
> >
@@ -105,18 +105,18 @@ export default function ThemeModalContent({ selectedTheme }: any) {
<Stack <Stack
style={{ style={{
background: selectedTheme.colorbackground, background: selectedTheme.colorbackground,
gap: ".25rem", gap: '.25rem',
backgroundSize: "cover", backgroundSize: 'cover',
borderBottom: "0px solid", borderBottom: '0px solid',
display: "grid", display: 'grid',
gridColumnGap: "5px", gridColumnGap: '5px',
gridRowGap: "5px", gridRowGap: '5px',
gridTemplateColumns: "1fr 1fr 1fr", gridTemplateColumns: '1fr 1fr 1fr',
gridTemplateRows: "64px", gridTemplateRows: '64px',
padding: "1rem", padding: '1rem',
paddingTop: "0rem", paddingTop: '0rem',
justifyContent: "center", justifyContent: 'center',
alignContent: "center", alignContent: 'center',
}} }}
> >
<ExampleApp theme={selectedTheme} /> <ExampleApp theme={selectedTheme} />
@@ -124,16 +124,16 @@ export default function ThemeModalContent({ selectedTheme }: any) {
<ExampleApp theme={selectedTheme} /> <ExampleApp theme={selectedTheme} />
</Stack> </Stack>
<Text <Text
pl={"1.5rem"} pl={'1.5rem'}
style={{ style={{
textDecoration: "underline", textDecoration: 'underline',
fontSize: "20px", fontSize: '20px',
fontWeight: 200, fontWeight: 200,
fontStyle: "italic", fontStyle: 'italic',
height: "10px", height: '10px',
textTransform: "uppercase", textTransform: 'uppercase',
marginTop: "20px", marginTop: '20px',
marginBottom: "20px", marginBottom: '20px',
color: selectedTheme.colortextpri, color: selectedTheme.colortextpri,
}} }}
> >
@@ -141,107 +141,107 @@ export default function ThemeModalContent({ selectedTheme }: any) {
</Text> </Text>
<Stack <Stack
style={{ style={{
display: "flex", display: 'flex',
flexDirection: "row", flexDirection: 'row',
flexWrap: "nowrap", flexWrap: 'nowrap',
gridColumnGap: "20px", gridColumnGap: '20px',
gridRowGap: "0px", gridRowGap: '0px',
gridTemplateColumns: "1fr 1fr 1fr 1fr 1fr", gridTemplateColumns: '1fr 1fr 1fr 1fr 1fr',
gridTemplateRows: "auto", gridTemplateRows: 'auto',
}} }}
> >
<Stack <Stack
style={{ style={{
width: "100%", width: '100%',
marginBottom: "0em", marginBottom: '0em',
display: "block", display: 'block',
lineHeight: "1.5rem", lineHeight: '1.5rem',
padding: "1rem", padding: '1rem',
paddingTop: "0rem", paddingTop: '0rem',
}} }}
> >
<Text <Text
style={{ style={{
fontSize: "14px", fontSize: '14px',
fontWeight: 400, fontWeight: 400,
height: "10px", height: '10px',
width: "auto", width: 'auto',
color: selectedTheme.colortextacc, color: selectedTheme.colortextacc,
textTransform: "uppercase", textTransform: 'uppercase',
marginTop: "20px", marginTop: '20px',
marginBottom: "20px", marginBottom: '20px',
}} }}
> >
Example Category Example Category
</Text> </Text>
<Text <Text
style={{ style={{
display: "block", display: 'block',
fontSize: "14px", fontSize: '14px',
lineHeight: 2, lineHeight: 2,
color: selectedTheme.colortextpri, color: selectedTheme.colortextpri,
}} }}
component="a" component='a'
> >
{"Link 1"} {'Link 1'}
</Text> </Text>
<Text <Text
style={{ style={{
display: "block", display: 'block',
fontSize: "14px", fontSize: '14px',
lineHeight: 2, lineHeight: 2,
color: selectedTheme.colortextpri, color: selectedTheme.colortextpri,
}} }}
component="a" component='a'
> >
{"Link 2"} {'Link 2'}
</Text> </Text>
</Stack> </Stack>
<Stack <Stack
style={{ style={{
width: "100%", width: '100%',
marginBottom: "0em", marginBottom: '0em',
display: "block", display: 'block',
lineHeight: "1.5rem", lineHeight: '1.5rem',
padding: "1rem", padding: '1rem',
paddingTop: "0rem", paddingTop: '0rem',
}} }}
> >
<Text <Text
style={{ style={{
fontSize: "14px", fontSize: '14px',
fontWeight: 400, fontWeight: 400,
height: "10px", height: '10px',
width: "auto", width: 'auto',
color: selectedTheme.colortextacc, color: selectedTheme.colortextacc,
textTransform: "uppercase", textTransform: 'uppercase',
marginTop: "20px", marginTop: '20px',
marginBottom: "20px", marginBottom: '20px',
}} }}
> >
Example Category Example Category
</Text> </Text>
<Text <Text
style={{ style={{
display: "block", display: 'block',
fontSize: "14px", fontSize: '14px',
lineHeight: 2, lineHeight: 2,
color: selectedTheme.colortextpri, color: selectedTheme.colortextpri,
}} }}
component="a" component='a'
> >
{"Link 1"} {'Link 1'}
</Text> </Text>
<Text <Text
style={{ style={{
display: "block", display: 'block',
fontSize: "14px", fontSize: '14px',
lineHeight: 2, lineHeight: 2,
color: selectedTheme.colortextpri, color: selectedTheme.colortextpri,
}} }}
component="a" component='a'
> >
{"Link 2"} {'Link 2'}
</Text> </Text>
</Stack> </Stack>
</Stack> </Stack>

View File

@@ -1,5 +1,3 @@
import { useEffect, useState } from "react";
import { useMutationAPI, useQueryAPI } from "../../../hooks/useAPI";
import { import {
ActionIcon, ActionIcon,
Button, Button,
@@ -12,33 +10,37 @@ import {
Stack, Stack,
Text, Text,
TextInput, TextInput,
} from "@mantine/core"; } from '@mantine/core';
import { FiEdit, FiPlus, FiSave, FiTrash, FiX } from "react-icons/fi"; import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { useDisclosure, useLocalStorage } from "@mantine/hooks"; import { useEffect, useState } from 'react';
import "@mantine/core/styles/ModalBase.css"; import { FiEdit, FiPlus, FiSave, FiTrash, FiX } from 'react-icons/fi';
import "@mantine/core/styles/Modal.css"; import { useMutationAPI, useQueryAPI } from '../../../hooks/useAPI';
import { actionIconClasses, buttonClasses, modalClasses, textClasses } from "../../../styles"; import '@mantine/core/styles/ModalBase.css';
import ThemeModalContent from "./ThemeModalContent"; import '@mantine/core/styles/Modal.css';
import { modals } from "@mantine/modals"; import { modals } from '@mantine/modals';
import {
actionIconClasses,
buttonClasses,
modalClasses,
textClasses,
} from '../../../styles';
import ThemeModalContent from './ThemeModalContent';
function ThemesSettings({ theme }: any) { function ThemesSettings({ theme }: any) {
const [opened, { open, close }] = useDisclosure(false); const [opened, { open, close }] = useDisclosure(false);
const [pickerOpen, { open: openPicker, close: closePicker }] = const [pickerOpen, { open: openPicker, close: closePicker }] = useDisclosure(false);
useDisclosure(false); const [colorId, setColorId] = useState<string>('');
const [colorId, setColorId] = useState<string>(""); const [colorValue, setColorValue] = useState('#000000');
const [colorValue, setColorValue] = useState("#000000");
const [value, setValue] = useLocalStorage<any>({ const [value, setValue] = useLocalStorage<any>({
key: "theme", key: 'theme',
defaultValue: {}, defaultValue: {},
}); });
const { data, isFetched } = useQueryAPI({ const { data, isFetched } = useQueryAPI({
endpoint: "api/themes", endpoint: 'api/themes',
}); });
const [themesList, setThemesList] = useState([]); const [themesList, setThemesList] = useState([]);
const [selectedTheme, setSelectedTheme] = useState<any>({}); const [selectedTheme, setSelectedTheme] = useState<any>({});
const { mutateAsync: createTheme, isPending: isCreatingTheme } = const { mutateAsync: createTheme, isPending: isCreatingTheme } = useMutationAPI();
useMutationAPI(); const { mutateAsync: deleteTheme, isPending: isDeletingTheme } = useMutationAPI();
const { mutateAsync: deleteTheme, isPending: isDeletingTheme } =
useMutationAPI();
useEffect(() => { useEffect(() => {
if (isFetched) { if (isFetched) {
setThemesList(data); setThemesList(data);
@@ -57,8 +59,8 @@ function ThemesSettings({ theme }: any) {
const handleSaveTheme = () => { const handleSaveTheme = () => {
createTheme({ createTheme({
method: selectedTheme.id === undefined ? "POST" : "PATCH", method: selectedTheme.id === undefined ? 'POST' : 'PATCH',
endpoint: "api/themes", endpoint: 'api/themes',
body: selectedTheme, body: selectedTheme,
}); });
close(); close();
@@ -66,11 +68,11 @@ function ThemesSettings({ theme }: any) {
const openDeleteConfirmModal = (theme: any) => const openDeleteConfirmModal = (theme: any) =>
modals.openConfirmModal({ modals.openConfirmModal({
title: "Are you sure you want to delete this theme?", title: 'Are you sure you want to delete this theme?',
labels: { confirm: "Delete", cancel: "Cancel" }, labels: { confirm: 'Delete', cancel: 'Cancel' },
confirmProps: { color: "red" }, confirmProps: { color: 'red' },
onConfirm: () => onConfirm: () =>
deleteTheme({ method: "DELETE", endpoint: "api/themes", body: theme }), deleteTheme({ method: 'DELETE', endpoint: 'api/themes', body: theme }),
}); });
const handleDeleteTheme = (theme: any) => { const handleDeleteTheme = (theme: any) => {
@@ -81,7 +83,7 @@ function ThemesSettings({ theme }: any) {
return ( return (
<> <>
<Modal.Root <Modal.Root
size={"50%"} size={'50%'}
className={modalClasses.editThemeModal} className={modalClasses.editThemeModal}
classNames={modalClasses} classNames={modalClasses}
opened={opened} opened={opened}
@@ -93,135 +95,153 @@ function ThemesSettings({ theme }: any) {
<Modal.Title>Edit Theme</Modal.Title> <Modal.Title>Edit Theme</Modal.Title>
<Modal.CloseButton /> <Modal.CloseButton />
</Modal.Header> </Modal.Header>
<Modal.Body p={"2rem"}> <Modal.Body p={'2rem'}>
<ThemeModalContent selectedTheme={selectedTheme} /> <ThemeModalContent selectedTheme={selectedTheme} />
<Divider color="black" my="sm" /> <Divider color='black' my='sm' />
<TextInput <TextInput
label="Theme Name" label='Theme Name'
value={selectedTheme.name} value={selectedTheme.name}
onChange={(e) => onChange={(e) =>
setSelectedTheme({ ...selectedTheme, name: e.target.value }) setSelectedTheme({ ...selectedTheme, name: e.target.value })
} }
styles={{ styles={{
label: { label: {
color: "black", color: 'black',
}, },
input: { input: {
color: "black", color: 'black',
backgroundColor: "white", backgroundColor: 'white',
}, },
}} }}
/> />
<Divider color="black" my="sm" /> <Divider color='black' my='sm' />
<Group w={"100%"} h={"100%"} gap={0}> <Group w={'100%'} h={'100%'} gap={0}>
<Stack w={"50%"}> <Stack w={'50%'}>
<Group c={"black"} justify="space-between"> <Group c={'black'} justify='space-between'>
<Text>Primary Color</Text> <Text>Primary Color</Text>
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
setColorId("primary"); setColorId('primary');
openPicker(); openPicker();
setColorValue(selectedTheme.colortextpri); setColorValue(selectedTheme.colortextpri);
}} }}
radius={"sm"} radius={'sm'}
w={"100px"} w={'100px'}
color={selectedTheme.colortextpri} color={selectedTheme.colortextpri}
style={{ cursor: "pointer" }} style={{ cursor: 'pointer' }}
/> />
</Group> </Group>
<Group c={"black"} justify="space-between"> <Group c={'black'} justify='space-between'>
<Text>Accent Color</Text> <Text>Accent Color</Text>
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
setColorId("accent"); setColorId('accent');
openPicker(); openPicker();
setColorValue(selectedTheme.colortextacc); setColorValue(selectedTheme.colortextacc);
}} }}
radius={"sm"} radius={'sm'}
w={"100px"} w={'100px'}
color={selectedTheme.colortextacc} color={selectedTheme.colortextacc}
style={{ cursor: "pointer" }} style={{ cursor: 'pointer' }}
/> />
</Group> </Group>
<Group c={"black"} justify="space-between"> <Group c={'black'} justify='space-between'>
<Text>Background Color</Text> <Text>Background Color</Text>
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
setColorId("background"); setColorId('background');
openPicker(); openPicker();
setColorValue(selectedTheme.colorbackground); setColorValue(selectedTheme.colorbackground);
}} }}
radius={"sm"} radius={'sm'}
w={"100px"} w={'100px'}
color={selectedTheme.colorbackground} color={selectedTheme.colorbackground}
style={{ cursor: "pointer" }} style={{ cursor: 'pointer' }}
/> />
</Group> </Group>
<Group c={"black"} justify="space-between"> <Group c={'black'} justify='space-between'>
<Text>Border Color</Text> <Text>Border Color</Text>
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
setColorId("border"); setColorId('border');
openPicker(); openPicker();
setColorValue(selectedTheme.colorborder); setColorValue(selectedTheme.colorborder);
}} }}
radius={"sm"} radius={'sm'}
w={"100px"} w={'100px'}
color={selectedTheme.colorborder} color={selectedTheme.colorborder}
style={{ cursor: "pointer" }} style={{ cursor: 'pointer' }}
/> />
</Group> </Group>
<Group c={"black"} justify="space-between"> <Group c={'black'} justify='space-between'>
<Text>Search Background Color</Text> <Text>Search Background Color</Text>
<ColorSwatch <ColorSwatch
onClick={() => { onClick={() => {
setColorId("searchbackground"); setColorId('searchbackground');
openPicker(); openPicker();
setColorValue(selectedTheme.colorsearchbackground); setColorValue(selectedTheme.colorsearchbackground);
}} }}
radius={"sm"} radius={'sm'}
w={"100px"} w={'100px'}
color={selectedTheme.colorsearchbackground} color={selectedTheme.colorsearchbackground}
style={{ cursor: "pointer" }} style={{ cursor: 'pointer' }}
/>
</Group>
<Group c={'black'} justify='space-between'>
<TextInput
label='Background Image Path'
value={selectedTheme.imagebackground}
onChange={(e) =>
setSelectedTheme({ ...selectedTheme, imagebackground: e.target.value })
}
styles={{
label: {
color: 'black',
},
input: {
color: 'black',
backgroundColor: 'white',
},
}}
/> />
</Group> </Group>
</Stack> </Stack>
<Stack h={"100%"} w={"50%"} align="center" justify="center"> <Stack h={'100%'} w={'50%'} align='center' justify='center'>
{pickerOpen ? ( {pickerOpen ? (
<> <>
<ColorPicker value={colorValue} onChange={setColorValue} /> <ColorPicker value={colorValue} onChange={setColorValue} />
<Group w={"75%"} justify="space-between"> <Group w={'75%'} justify='space-between'>
<Text c={"black"}>{colorValue}</Text> <Text c={'black'}>{colorValue}</Text>
<Group> <Group>
<Button <Button
color={"green"} color={'green'}
onClick={() => { onClick={() => {
switch (colorId) { switch (colorId) {
case "primary": case 'primary':
setSelectedTheme({ setSelectedTheme({
...selectedTheme, ...selectedTheme,
colortextpri: colorValue, colortextpri: colorValue,
}); });
break; break;
case "accent": case 'accent':
setSelectedTheme({ setSelectedTheme({
...selectedTheme, ...selectedTheme,
colortextacc: colorValue, colortextacc: colorValue,
}); });
break; break;
case "background": case 'background':
setSelectedTheme({ setSelectedTheme({
...selectedTheme, ...selectedTheme,
colorbackground: colorValue, colorbackground: colorValue,
}); });
break; break;
case "border": case 'border':
setSelectedTheme({ setSelectedTheme({
...selectedTheme, ...selectedTheme,
colorborder: colorValue, colorborder: colorValue,
}); });
break; break;
case "searchbackground": case 'searchbackground':
setSelectedTheme({ setSelectedTheme({
...selectedTheme, ...selectedTheme,
colorsearchbackground: colorValue, colorsearchbackground: colorValue,
@@ -233,7 +253,7 @@ function ThemesSettings({ theme }: any) {
> >
<FiSave /> <FiSave />
</Button> </Button>
<Button color={"red"} onClick={closePicker}> <Button color={'red'} onClick={closePicker}>
<FiX /> <FiX />
</Button> </Button>
</Group> </Group>
@@ -241,34 +261,31 @@ function ThemesSettings({ theme }: any) {
</> </>
) : ( ) : (
<Card <Card
bg={"#0002"} bg={'#0002'}
h={"100%"} h={'100%'}
w={"90%"} w={'90%'}
style={{ alignItems: "center" }} style={{ alignItems: 'center' }}
> >
<Text c="black">Pick a swatch to edit the color.</Text> <Text c='black'>Pick a swatch to edit the color.</Text>
</Card> </Card>
)} )}
</Stack> </Stack>
</Group> </Group>
<Divider color="black" my="sm" /> <Divider color='black' my='sm' />
<Group> <Group>
<Button color={"green"} onClick={handleSaveTheme}> <Button color={'green'} onClick={handleSaveTheme}>
Save Save
</Button> </Button>
<Button color={"red"} onClick={close}> <Button color={'red'} onClick={close}>
Cancel Cancel
</Button> </Button>
</Group> </Group>
</Modal.Body> </Modal.Body>
</Modal.Content> </Modal.Content>
</Modal.Root> </Modal.Root>
<Stack h={"100%"} w={"100%"}> <Stack h={'100%'} w={'100%'}>
<Group> <Group>
<Text <Text className={textClasses.settingsSubTitle} classNames={textClasses}>
className={textClasses.settingsSubTitle}
classNames={textClasses}
>
Themes Themes
</Text> </Text>
<Button <Button
@@ -277,13 +294,13 @@ function ThemesSettings({ theme }: any) {
onClick={() => { onClick={() => {
setSelectedTheme({ setSelectedTheme({
id: undefined, id: undefined,
name: "default", name: 'default',
imagebackground: "none", imagebackground: 'none',
colorbackground: "#EEEEEE", colorbackground: '#EEEEEE',
colorsearchbackground: "#00000033", colorsearchbackground: '#00000033',
colortextpri: "#222222", colortextpri: '#222222',
colortextacc: "#5c5c5c", colortextacc: '#5c5c5c',
colorborder: "#5c5c5c", colorborder: '#5c5c5c',
}); });
open(); open();
}} }}
@@ -291,13 +308,13 @@ function ThemesSettings({ theme }: any) {
<FiPlus /> <FiPlus />
</Button> </Button>
</Group> </Group>
<Group justify="center"> <Group justify='center'>
{themesList.map((theme: any) => ( {themesList.map((theme: any) => (
<Stack key={theme.name} w={"300px"}> <Stack key={theme.name} w={'300px'}>
<Group gap={0} justify="space-between"> <Group gap={0} justify='space-between'>
<Button <Button
w={"100%"} w={'100%'}
h={"75px"} h={'75px'}
style={{ style={{
backgroundColor: theme.colorbackground, backgroundColor: theme.colorbackground,
border: `solid ${theme.colorborder}`, border: `solid ${theme.colorborder}`,
@@ -311,8 +328,8 @@ function ThemesSettings({ theme }: any) {
{theme.name} {theme.name}
</Button> </Button>
</Group> </Group>
<Group justify="space-between" gap={0}> <Group justify='space-between' gap={0}>
<Group className="theme-palette"> <Group className='theme-palette'>
<ActionIcon <ActionIcon
className={actionIconClasses.editButton} className={actionIconClasses.editButton}
classNames={actionIconClasses} classNames={actionIconClasses}
@@ -331,7 +348,7 @@ function ThemesSettings({ theme }: any) {
classNames={actionIconClasses} classNames={actionIconClasses}
onClick={() => handleDeleteTheme(theme)} onClick={() => handleDeleteTheme(theme)}
> >
<FiTrash color="red" /> <FiTrash color='red' />
</ActionIcon> </ActionIcon>
</Group> </Group>
</Group> </Group>

View File

@@ -1,14 +1,14 @@
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 menuClasses from "./Menu.module.css"; import menuClasses from './Menu.module.css';
import modalClasses from "./Modal.module.css"; import modalClasses from './Modal.module.css';
import navLinkClasses from "./NavLink.module.css"; import navLinkClasses from './NavLink.module.css';
import popoverClasses from "./Popover.module.css"; import popoverClasses from './Popover.module.css';
import selectClasses from "./Select.module.css"; import selectClasses from './Select.module.css';
import stackClasses from "./Stack.module.css"; import stackClasses from './Stack.module.css';
import tabClasses from "./Tabs.module.css"; import tabClasses from './Tabs.module.css';
import textClasses from "./Text.module.css"; import textClasses from './Text.module.css';
import textInputClasses from "./TextInput.module.css"; import textInputClasses from './TextInput.module.css';
export { export {
actionIconClasses, actionIconClasses,

View File

@@ -1,15 +1,15 @@
import react from "@vitejs/plugin-react"; import react from '@vitejs/plugin-react';
import { defineConfig } from "vite"; import { defineConfig } from 'vite';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
server: { server: {
port: 3000, port: 3000,
host: "0.0.0.0", host: '0.0.0.0',
proxy: { proxy: {
"/api": { '/api': {
target: "http://localhost:5000/trigger", target: 'http://localhost:5000/trigger',
changeOrigin: false, changeOrigin: false,
secure: false, secure: false,
}, },
@@ -17,10 +17,10 @@ export default defineConfig({
}, },
preview: { preview: {
port: 3000, port: 3000,
host: "0.0.0.0", host: '0.0.0.0',
proxy: { proxy: {
api: { api: {
target: "http://localhost:5000", target: 'http://localhost:5000',
changeOrigin: false, changeOrigin: false,
secure: false, secure: false,
}, },