mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-11-13 19:42:38 +05:30
Merge 166771d638 into 3b702b260c
This commit is contained in:
commit
03f73c58c4
8 changed files with 232 additions and 1 deletions
|
|
@ -50,9 +50,9 @@ export default function ToolTextInput({
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(event) => onChange(event.target.value)}
|
onChange={(event) => onChange(event.target.value)}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
placeholder={placeholder}
|
||||||
multiline
|
multiline
|
||||||
rows={10}
|
rows={10}
|
||||||
placeholder={placeholder}
|
|
||||||
sx={{
|
sx={{
|
||||||
'&.MuiTextField-root': {
|
'&.MuiTextField-root': {
|
||||||
backgroundColor: 'background.paper'
|
backgroundColor: 'background.paper'
|
||||||
|
|
|
||||||
60
src/components/result/ValidatedToolResult.tsx
Normal file
60
src/components/result/ValidatedToolResult.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Alert } from '@mui/material';
|
||||||
|
|
||||||
|
interface ValidatedToolResultProps {
|
||||||
|
isValid: boolean | null;
|
||||||
|
hasInteracted: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ValidatedToolResult: React.FC<ValidatedToolResultProps> = ({
|
||||||
|
isValid,
|
||||||
|
hasInteracted,
|
||||||
|
errorMessage = 'Invalid input.',
|
||||||
|
children
|
||||||
|
}) => (
|
||||||
|
<div style={{ position: 'relative', minHeight: 80 }}>
|
||||||
|
{hasInteracted && isValid === false && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
zIndex: 2,
|
||||||
|
pointerEvents: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
background: 'transparent'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Alert
|
||||||
|
severity="error"
|
||||||
|
style={{
|
||||||
|
width: '80%',
|
||||||
|
opacity: 0.85,
|
||||||
|
textAlign: 'center',
|
||||||
|
pointerEvents: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{errorMessage}
|
||||||
|
</Alert>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
filter: hasInteracted && isValid === false ? 'blur(1px)' : 'none',
|
||||||
|
transition: 'filter 0.2s'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{hasInteracted && isValid === false
|
||||||
|
? React.cloneElement(children as React.ReactElement, { value: '' })
|
||||||
|
: children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ValidatedToolResult;
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { expect, describe, it } from 'vitest';
|
||||||
|
import { epochToDate, dateToEpoch, main } from './service';
|
||||||
|
|
||||||
|
describe('epoch-converter service', () => {
|
||||||
|
it('converts epoch (seconds) to date', () => {
|
||||||
|
expect(epochToDate('1609459200')).toBe('Fri, 01 Jan 2021 00:00:00 GMT');
|
||||||
|
});
|
||||||
|
it('converts epoch (milliseconds) to date', () => {
|
||||||
|
expect(epochToDate('1609459200000')).toBe('Fri, 01 Jan 2021 00:00:00 GMT');
|
||||||
|
});
|
||||||
|
it('returns error for invalid epoch', () => {
|
||||||
|
expect(epochToDate('notanumber')).toMatch(/Invalid epoch/);
|
||||||
|
});
|
||||||
|
it('converts date string to epoch', () => {
|
||||||
|
expect(dateToEpoch('2021-01-01T00:00:00Z')).toBe('1609459200');
|
||||||
|
});
|
||||||
|
it('returns error for invalid date string', () => {
|
||||||
|
expect(dateToEpoch('notadate')).toMatch(/Invalid date/);
|
||||||
|
});
|
||||||
|
it('main: detects and converts epoch', () => {
|
||||||
|
expect(main('1609459200', {})).toBe('Fri, 01 Jan 2021 00:00:00 GMT');
|
||||||
|
});
|
||||||
|
it('main: detects and converts date', () => {
|
||||||
|
expect(main('2021-01-01T00:00:00Z', {})).toBe('1609459200');
|
||||||
|
});
|
||||||
|
it('main: returns error for invalid input', () => {
|
||||||
|
expect(main('notadate', {})).toMatch(/Invalid date/);
|
||||||
|
expect(main('notanumber', {})).toMatch(/Invalid date/);
|
||||||
|
});
|
||||||
|
});
|
||||||
87
src/pages/tools/time/epoch-converter/index.tsx
Normal file
87
src/pages/tools/time/epoch-converter/index.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { Alert, Button, Stack } from '@mui/material';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import ToolContent from '@components/ToolContent';
|
||||||
|
import { ToolComponentProps } from '@tools/defineTool';
|
||||||
|
import ToolTextInput from '@components/input/ToolTextInput';
|
||||||
|
import ToolTextResult from '@components/result/ToolTextResult';
|
||||||
|
import { CardExampleType } from '@components/examples/ToolExamples';
|
||||||
|
import { main } from './service';
|
||||||
|
import { InitialValuesType } from './types';
|
||||||
|
import ValidatedToolResult from '@components/result/ValidatedToolResult';
|
||||||
|
|
||||||
|
const initialValues: InitialValuesType = {};
|
||||||
|
|
||||||
|
const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||||
|
{
|
||||||
|
title: 'Epoch to Date (seconds)',
|
||||||
|
description: 'Convert Unix timestamp (seconds) to date',
|
||||||
|
sampleText: '1609459200',
|
||||||
|
sampleResult: 'Fri, 01 Jan 2021 00:00:00 GMT',
|
||||||
|
sampleOptions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Epoch to Date (milliseconds)',
|
||||||
|
description: 'Convert Unix timestamp (milliseconds) to date',
|
||||||
|
sampleText: '1609459200000',
|
||||||
|
sampleResult: 'Fri, 01 Jan 2021 00:00:00 GMT',
|
||||||
|
sampleOptions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Date to Epoch',
|
||||||
|
description: 'Convert date string to Unix timestamp (seconds)',
|
||||||
|
sampleText: '2021-01-01T00:00:00Z',
|
||||||
|
sampleResult: '1609459200',
|
||||||
|
sampleOptions: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function EpochConverter({
|
||||||
|
title,
|
||||||
|
longDescription
|
||||||
|
}: ToolComponentProps) {
|
||||||
|
const [input, setInput] = useState<string>('');
|
||||||
|
const [result, setResult] = useState<string>('');
|
||||||
|
const [hasInteracted, setHasInteracted] = useState<boolean>(false);
|
||||||
|
const [isValid, setIsValid] = useState<boolean | null>(null);
|
||||||
|
|
||||||
|
const compute = (_values: InitialValuesType, input: string) => {
|
||||||
|
const output = main(input, {});
|
||||||
|
const invalid = output.startsWith('Invalid');
|
||||||
|
setIsValid(!invalid);
|
||||||
|
setResult(output);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (val: string) => {
|
||||||
|
if (!hasInteracted) setHasInteracted(true);
|
||||||
|
setInput(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolContent
|
||||||
|
title={title}
|
||||||
|
input={input}
|
||||||
|
inputComponent={
|
||||||
|
<ToolTextInput
|
||||||
|
value={input}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter epoch timestamp or date string (e.g. 1609459200 or 2021-01-01T00:00:00Z)"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
resultComponent={
|
||||||
|
<ValidatedToolResult
|
||||||
|
isValid={isValid}
|
||||||
|
hasInteracted={hasInteracted}
|
||||||
|
errorMessage="Invalid input. Please enter a valid epoch timestamp or date string."
|
||||||
|
>
|
||||||
|
<ToolTextResult value={result} />
|
||||||
|
</ValidatedToolResult>
|
||||||
|
}
|
||||||
|
initialValues={initialValues}
|
||||||
|
exampleCards={exampleCards}
|
||||||
|
getGroups={null}
|
||||||
|
setInput={setInput}
|
||||||
|
compute={compute}
|
||||||
|
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
src/pages/tools/time/epoch-converter/meta.ts
Normal file
23
src/pages/tools/time/epoch-converter/meta.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { defineTool } from '@tools/defineTool';
|
||||||
|
import { lazy } from 'react';
|
||||||
|
|
||||||
|
export const tool = defineTool('time', {
|
||||||
|
name: 'Epoch Converter',
|
||||||
|
path: 'epoch-converter',
|
||||||
|
icon: 'mdi:clock-time-four-outline',
|
||||||
|
description:
|
||||||
|
'Convert Unix epoch timestamps to human-readable dates and vice versa.',
|
||||||
|
shortDescription: 'Convert between Unix timestamps and dates.',
|
||||||
|
keywords: [
|
||||||
|
'epoch',
|
||||||
|
'converter',
|
||||||
|
'timestamp',
|
||||||
|
'date',
|
||||||
|
'unix',
|
||||||
|
'time',
|
||||||
|
'convert'
|
||||||
|
],
|
||||||
|
longDescription:
|
||||||
|
'Enter a Unix timestamp (in seconds or milliseconds) to get a human-readable date, or enter a date string (e.g., 2021-01-01T00:00:00Z) to get the corresponding Unix timestamp. Useful for developers, sysadmins, and anyone working with time data.',
|
||||||
|
component: lazy(() => import('./index'))
|
||||||
|
});
|
||||||
26
src/pages/tools/time/epoch-converter/service.ts
Normal file
26
src/pages/tools/time/epoch-converter/service.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { InitialValuesType } from './types';
|
||||||
|
|
||||||
|
export function epochToDate(input: string): string {
|
||||||
|
const num = Number(input);
|
||||||
|
if (isNaN(num)) return 'Invalid epoch timestamp.';
|
||||||
|
// Support both seconds and milliseconds
|
||||||
|
const date = new Date(num > 1e12 ? num : num * 1000);
|
||||||
|
if (isNaN(date.getTime())) return 'Invalid epoch timestamp.';
|
||||||
|
return date.toUTCString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dateToEpoch(input: string): string {
|
||||||
|
const date = new Date(input);
|
||||||
|
if (isNaN(date.getTime())) return 'Invalid date string.';
|
||||||
|
return Math.floor(date.getTime() / 1000).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main(input: string, _options: any): string {
|
||||||
|
if (!input.trim()) return '';
|
||||||
|
// If input is a number, treat as epoch
|
||||||
|
if (/^-?\d+(\.\d+)?$/.test(input.trim())) {
|
||||||
|
return epochToDate(input.trim());
|
||||||
|
}
|
||||||
|
// Otherwise, treat as date string
|
||||||
|
return dateToEpoch(input.trim());
|
||||||
|
}
|
||||||
3
src/pages/tools/time/epoch-converter/types.ts
Normal file
3
src/pages/tools/time/epoch-converter/types.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export type InitialValuesType = {
|
||||||
|
// splitSeparator: string;
|
||||||
|
};
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { tool as timeEpochConverter } from './epoch-converter/meta';
|
||||||
import { tool as timeCrontabGuru } from './crontab-guru/meta';
|
import { tool as timeCrontabGuru } from './crontab-guru/meta';
|
||||||
import { tool as timeBetweenDates } from './time-between-dates/meta';
|
import { tool as timeBetweenDates } from './time-between-dates/meta';
|
||||||
import { tool as daysDoHours } from './convert-days-to-hours/meta';
|
import { tool as daysDoHours } from './convert-days-to-hours/meta';
|
||||||
|
|
@ -14,6 +15,7 @@ export const timeTools = [
|
||||||
convertTimetoSeconds,
|
convertTimetoSeconds,
|
||||||
truncateClockTime,
|
truncateClockTime,
|
||||||
timeBetweenDates,
|
timeBetweenDates,
|
||||||
|
timeEpochConverter,
|
||||||
timeCrontabGuru,
|
timeCrontabGuru,
|
||||||
checkLeapYear
|
checkLeapYear
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue