chore: tools by category

This commit is contained in:
Ibrahima G. Coulibaly
2024-06-23 01:26:04 +01:00
parent 5ddfa1323b
commit bb3964eabc
11 changed files with 146 additions and 31 deletions

24
.idea/workspace.xml generated
View File

@@ -5,7 +5,17 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: deploy message"> <list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: deploy message">
<change beforePath="$PROJECT_DIR$/.github/workflows/ci.yml" beforeDir="false" afterPath="$PROJECT_DIR$/.github/workflows/ci.yml" afterDir="false" /> <change afterPath="$PROJECT_DIR$/src/components/index.css" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/utils/string.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/App.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/App.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolLayout.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolLayout.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/home/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/home/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/string/split/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/split/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/string/split/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/split/meta.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/tools/defineTool.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/defineTool.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/tools/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/index.ts" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -127,7 +137,7 @@
<workItem from="1719006951159" duration="2377000" /> <workItem from="1719006951159" duration="2377000" />
<workItem from="1719021128819" duration="3239000" /> <workItem from="1719021128819" duration="3239000" />
<workItem from="1719083989394" duration="7971000" /> <workItem from="1719083989394" duration="7971000" />
<workItem from="1719092003308" duration="4296000" /> <workItem from="1719092003308" duration="8256000" />
</task> </task>
<task id="LOCAL-00001" summary="feat: use vite and ts"> <task id="LOCAL-00001" summary="feat: use vite and ts">
<option name="closed" value="true" /> <option name="closed" value="true" />
@@ -329,7 +339,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1719096461432</updated> <updated>1719096461432</updated>
</task> </task>
<option name="localTasksCounter" value="26" /> <task id="LOCAL-00026" summary="fix: deploy message">
<option name="closed" value="true" />
<created>1719096551866</created>
<option name="number" value="00026" />
<option name="presentableId" value="LOCAL-00026" />
<option name="project" value="LOCAL" />
<updated>1719096551866</updated>
</task>
<option name="localTasksCounter" value="27" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">

View File

@@ -8,6 +8,7 @@ import theme from '../config/muiConfig';
import { CustomSnackBarProvider } from '../contexts/CustomSnackBarContext'; import { CustomSnackBarProvider } from '../contexts/CustomSnackBarContext';
import { SnackbarProvider } from 'notistack'; import { SnackbarProvider } from 'notistack';
import { tools } from '../tools'; import { tools } from '../tools';
import './index.css';
const AppRoutes = () => { const AppRoutes = () => {
const updatedRoutesConfig = [...routesConfig]; const updatedRoutesConfig = [...routesConfig];

View File

@@ -1,13 +1,17 @@
import { Box, Stack } from '@mui/material'; import { Box, Stack } from '@mui/material';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import textImage from '../assets/text.png';
interface ToolHeaderProps { interface ToolHeaderProps {
title: string; title: string;
description: string; description: string;
image?: string;
} }
export default function ToolHeader({ title, description }: ToolHeaderProps) { export default function ToolHeader({
image,
title,
description
}: ToolHeaderProps) {
return ( return (
<Stack direction={'row'} alignItems={'center'} spacing={2} mt={4}> <Stack direction={'row'} alignItems={'center'} spacing={2} mt={4}>
<Box> <Box>
@@ -16,7 +20,7 @@ export default function ToolHeader({ title, description }: ToolHeaderProps) {
</Typography> </Typography>
<Typography fontSize={20}>{description}</Typography> <Typography fontSize={20}>{description}</Typography>
</Box> </Box>
<img width={'20%'} src={textImage} /> {image && <img width={'250'} src={image} />}
</Stack> </Stack>
); );
} }

View File

@@ -6,10 +6,12 @@ import ToolHeader from './ToolHeader';
export default function ToolLayout({ export default function ToolLayout({
children, children,
title, title,
description description,
image
}: { }: {
title: string; title: string;
description: string; description: string;
image?: string;
children: ReactNode; children: ReactNode;
}) { }) {
return ( return (
@@ -23,7 +25,7 @@ export default function ToolLayout({
<title>{`${title} - Omni Tools`}</title> <title>{`${title} - Omni Tools`}</title>
</Helmet> </Helmet>
<Box width={'85%'}> <Box width={'85%'}>
<ToolHeader title={title} description={description} /> <ToolHeader title={title} description={description} image={image} />
{children} {children}
</Box> </Box>
</Box> </Box>

7
src/components/index.css Normal file
View File

@@ -0,0 +1,7 @@
a {
color: #1c76ce;
}
a:hover {
color: #030362
}

View File

@@ -1,11 +1,19 @@
import { Autocomplete, Box, Stack, TextField } from '@mui/material'; import {
Autocomplete,
Box,
Card,
CardContent,
Stack,
TextField
} from '@mui/material';
import Grid from '@mui/material/Grid'; import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import { useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { filterTools, tools } from '../../tools'; import { filterTools, getToolsByCategory, tools } from '../../tools';
import { useState } from 'react'; import { useState } from 'react';
import { DefinedTool } from '../../tools/defineTool'; import { DefinedTool } from '../../tools/defineTool';
import Button from '@mui/material/Button';
const exampleTools: { label: string; url: string }[] = [ const exampleTools: { label: string; url: string }[] = [
{ {
@@ -86,20 +94,21 @@ export default function Home() {
</Box> </Box>
)} )}
/> />
<Grid container spacing={1} mt={2}> <Grid container spacing={2} mt={2}>
{exampleTools.map((tool) => ( {exampleTools.map((tool) => (
<Grid <Grid
onClick={() => navigate(tool.url)} onClick={() => navigate(tool.url)}
item item
xs={4} xs={4}
key={tool.label} key={tool.label}
display="flex" >
flexDirection="row" <Box
justifyContent="center"
alignItems="center"
padding={2}
sx={{ sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1, borderWidth: 1,
padding: 1,
borderRadius: 3, borderRadius: 3,
borderColor: 'grey', borderColor: 'grey',
borderStyle: 'solid', borderStyle: 'solid',
@@ -107,6 +116,37 @@ export default function Home() {
}} }}
> >
<Typography>{tool.label}</Typography> <Typography>{tool.label}</Typography>
</Box>
</Grid>
))}
</Grid>
<Grid container mt={2} spacing={2}>
{getToolsByCategory().map((category) => (
<Grid key={category.type} item xs={6}>
<Card>
<CardContent>
<Link
style={{ fontSize: 20 }}
to={'/categories/' + category.type}
>
{category.title}
</Link>
<Typography sx={{ mt: 2 }}>{category.description}</Typography>
<Stack
mt={2}
direction={'row'}
justifyContent={'space-between'}
>
<Button
variant={'contained'}
>{`See all ${category.title}`}</Button>
<Button
onClick={() => navigate(category.example.path)}
variant={'outlined'}
>{`Try ${category.example.title}`}</Button>
</Stack>
</CardContent>
</Card>
</Grid> </Grid>
))} ))}
</Grid> </Grid>

View File

@@ -1,5 +1,3 @@
import ToolHeader from '../../../components/ToolHeader';
import ToolLayout from '../../../components/ToolLayout';
import { Box, Stack, TextField } from '@mui/material'; import { Box, Stack, TextField } from '@mui/material';
import Grid from '@mui/material/Grid'; import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';

View File

@@ -1,9 +1,11 @@
import { defineTool } from '../../../tools/defineTool'; import { defineTool } from '../../../tools/defineTool';
import { lazy } from 'react'; import { lazy } from 'react';
import image from '../../../assets/text.png';
export const tool = defineTool('string', { export const tool = defineTool('string', {
path: 'split', path: 'split',
name: 'Text splitter', name: 'Text splitter',
image: image,
description: description:
"World's simplest browser-based utility for splitting text. Load your text in the input form on the left and you'll automatically get pieces of this text on the right. Powerful, free, and fast. Load text get chunks.", "World's simplest browser-based utility for splitting text. Load your text in the input form on the left and you'll automatically get pieces of this text on the right. Powerful, free, and fast. Load text get chunks.",
keywords: ['text', 'split'], keywords: ['text', 'split'],

View File

@@ -5,14 +5,17 @@ interface ToolOptions {
path: string; path: string;
component: LazyExoticComponent<JSXElementConstructor<NonNullable<unknown>>>; component: LazyExoticComponent<JSXElementConstructor<NonNullable<unknown>>>;
keywords: string[]; keywords: string[];
image?: string;
name: string; name: string;
description: string; description: string;
} }
export interface DefinedTool { export interface DefinedTool {
type: string;
path: string; path: string;
name: string; name: string;
description: string; description: string;
image?: string;
keywords: string[]; keywords: string[];
component: () => JSX.Element; component: () => JSX.Element;
} }
@@ -21,16 +24,18 @@ export const defineTool = (
basePath: string, basePath: string,
options: ToolOptions options: ToolOptions
): DefinedTool => { ): DefinedTool => {
const { path, name, description, keywords, component } = options; const { image, path, name, description, keywords, component } = options;
const Component = component; const Component = component;
return { return {
type: basePath,
path: `${basePath}/${path}`, path: `${basePath}/${path}`,
name, name,
image,
description, description,
keywords, keywords,
component: () => { component: () => {
return ( return (
<ToolLayout title={name} description={description}> <ToolLayout title={name} description={description} image={image}>
<Component /> <Component />
</ToolLayout> </ToolLayout>
); );

View File

@@ -1,9 +1,21 @@
import { stringTools } from '../pages/string/stringTools'; import { stringTools } from '../pages/string/stringTools';
import { imageTools } from '../pages/images/imageTools'; import { imageTools } from '../pages/images/imageTools';
import { DefinedTool } from './defineTool'; import { DefinedTool } from './defineTool';
import { capitalizeFirstLetter } from '../utils/string';
export const tools: DefinedTool[] = [...stringTools, ...imageTools]; export const tools: DefinedTool[] = [...imageTools, ...stringTools];
const categoriesDescriptions: { type: string; value: string }[] = [
{
type: 'string',
value:
'Tools for working with text convert text to images, find and replace text, split text into fragments, join text lines, repeat text, and much more.'
},
{
type: 'png',
value:
'Tools for working with PNG images convert PNGs to JPGs, create transparent PNGs, change PNG colors, crop, rotate, resize PNGs, and much more.'
}
];
export const filterTools = ( export const filterTools = (
tools: DefinedTool[], tools: DefinedTool[],
query: string query: string
@@ -21,3 +33,26 @@ export const filterTools = (
) )
); );
}; };
export const getToolsByCategory = (): {
title: string;
description: string;
type: string;
example: { title: string; path: string };
}[] => {
const grouped: Partial<Record<string, DefinedTool[]>> = Object.groupBy(
tools,
({ type }) => type
);
return Object.entries(grouped).map(([type, tls]) => {
return {
title: `${capitalizeFirstLetter(type)} Tools`,
description:
categoriesDescriptions.find((desc) => desc.type === type)?.value ?? '',
type,
example: tls
? { title: tls[0].name, path: tls[0].path }
: { title: '', path: '' }
};
});
};

3
src/utils/string.ts Normal file
View File

@@ -0,0 +1,3 @@
export function capitalizeFirstLetter(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}