mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-18 13:39:31 +02:00
chore: tools by category
This commit is contained in:
24
.idea/workspace.xml
generated
24
.idea/workspace.xml
generated
@@ -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">
|
||||
|
@@ -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];
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
@@ -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
7
src/components/index.css
Normal file
@@ -0,0 +1,7 @@
|
||||
a {
|
||||
color: #1c76ce;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #030362
|
||||
}
|
@@ -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>
|
||||
|
@@ -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';
|
||||
|
@@ -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'],
|
||||
|
@@ -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>
|
||||
);
|
||||
|
@@ -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
3
src/utils/string.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function capitalizeFirstLetter(string: string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
Reference in New Issue
Block a user