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 name="ChangeListManager">
<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>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -127,7 +137,7 @@
<workItem from="1719006951159" duration="2377000" />
<workItem from="1719021128819" duration="3239000" />
<workItem from="1719083989394" duration="7971000" />
<workItem from="1719092003308" duration="4296000" />
<workItem from="1719092003308" duration="8256000" />
</task>
<task id="LOCAL-00001" summary="feat: use vite and ts">
<option name="closed" value="true" />
@@ -329,7 +339,15 @@
<option name="project" value="LOCAL" />
<updated>1719096461432</updated>
</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 />
</component>
<component name="TypeScriptGeneratedFilesManager">

View File

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

View File

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

View File

@@ -6,10 +6,12 @@ import ToolHeader from './ToolHeader';
export default function ToolLayout({
children,
title,
description
description,
image
}: {
title: string;
description: string;
image?: string;
children: ReactNode;
}) {
return (
@@ -23,7 +25,7 @@ export default function ToolLayout({
<title>{`${title} - Omni Tools`}</title>
</Helmet>
<Box width={'85%'}>
<ToolHeader title={title} description={description} />
<ToolHeader title={title} description={description} image={image} />
{children}
</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 Typography from '@mui/material/Typography';
import SearchIcon from '@mui/icons-material/Search';
import { useNavigate } from 'react-router-dom';
import { filterTools, tools } from '../../tools';
import { Link, useNavigate } from 'react-router-dom';
import { filterTools, getToolsByCategory, tools } from '../../tools';
import { useState } from 'react';
import { DefinedTool } from '../../tools/defineTool';
import Button from '@mui/material/Button';
const exampleTools: { label: string; url: string }[] = [
{
@@ -86,27 +94,59 @@ export default function Home() {
</Box>
)}
/>
<Grid container spacing={1} mt={2}>
<Grid container spacing={2} mt={2}>
{exampleTools.map((tool) => (
<Grid
onClick={() => navigate(tool.url)}
item
xs={4}
key={tool.label}
display="flex"
flexDirection="row"
justifyContent="center"
alignItems="center"
padding={2}
sx={{
borderWidth: 1,
borderRadius: 3,
borderColor: 'grey',
borderStyle: 'solid',
cursor: 'pointer'
}}
>
<Typography>{tool.label}</Typography>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
padding: 1,
borderRadius: 3,
borderColor: 'grey',
borderStyle: 'solid',
cursor: 'pointer'
}}
>
<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>

View File

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

View File

@@ -1,9 +1,11 @@
import { defineTool } from '../../../tools/defineTool';
import { lazy } from 'react';
import image from '../../../assets/text.png';
export const tool = defineTool('string', {
path: 'split',
name: 'Text splitter',
image: image,
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.",
keywords: ['text', 'split'],

View File

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

View File

@@ -1,9 +1,21 @@
import { stringTools } from '../pages/string/stringTools';
import { imageTools } from '../pages/images/imageTools';
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 = (
tools: DefinedTool[],
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);
}