feat: arithmetic sequence

This commit is contained in:
Ibrahima G. Coulibaly
2025-03-08 07:32:20 +00:00
parent f678c76200
commit a4895d6721
11 changed files with 249 additions and 48 deletions

View File

@@ -1 +1,2 @@
db/
db/
docs

44
.idea/workspace.xml generated
View File

@@ -4,17 +4,21 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: minify json">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: stringify json">
<change afterPath="$PROJECT_DIR$/.codebuddy/docs/Generate an Arithmetic Progression - Online Number Tools.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.codebuddy/docs/Stringify JSON - Online JSON Tools.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/json/stringify/index.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/json/stringify/meta.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/json/stringify/service.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/arithmetic-sequence.service.test.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/index.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/meta.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.codebuddy/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.codebuddy/.gitignore" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/json/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/json/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/json/minify/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/json/minify/meta.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/json/prettify/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/json/prettify/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils/string.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils/string.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tsconfig.json" beforeDir="false" afterPath="$PROJECT_DIR$/tsconfig.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolContent.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolContent.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/examples/ExampleCard.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/examples/ExampleCard.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/examples/ToolExamples.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/examples/ToolExamples.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/json/stringify/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/json/stringify/meta.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/number/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/number/index.ts" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -337,14 +341,6 @@
<workItem from="1740923024259" duration="23000" />
<workItem from="1740933006573" duration="3679000" />
</task>
<task id="LOCAL-00095" summary="feat: self host">
<option name="closed" value="true" />
<created>1720665220407</created>
<option name="number" value="00095" />
<option name="presentableId" value="LOCAL-00095" />
<option name="project" value="LOCAL" />
<updated>1720665220408</updated>
</task>
<task id="LOCAL-00096" summary="chore: format number">
<option name="closed" value="true" />
<created>1720730102816</created>
@@ -729,7 +725,15 @@
<option name="project" value="LOCAL" />
<updated>1741416193639</updated>
</task>
<option name="localTasksCounter" value="144" />
<task id="LOCAL-00144" summary="feat: stringify json">
<option name="closed" value="true" />
<created>1741417920442</created>
<option name="number" value="00144" />
<option name="presentableId" value="LOCAL-00144" />
<option name="project" value="LOCAL" />
<updated>1741417920442</updated>
</task>
<option name="localTasksCounter" value="145" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -788,7 +792,6 @@
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="fix: readme" />
<MESSAGE value="fix: broken links" />
<MESSAGE value="chore: style buttons" />
<MESSAGE value="chore: style" />
@@ -813,7 +816,8 @@
<MESSAGE value="fix: replace text service" />
<MESSAGE value="chore: smooth scroll for use this tool and examles" />
<MESSAGE value="feat: minify json" />
<option name="LAST_COMMIT_MESSAGE" value="feat: minify json" />
<MESSAGE value="feat: stringify json" />
<option name="LAST_COMMIT_MESSAGE" value="feat: stringify json" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

View File

@@ -10,7 +10,7 @@ import ToolExamples, {
} from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
interface ToolContentPropsBase<T, I> extends ToolComponentProps {
interface ToolContentProps<T, I> extends ToolComponentProps {
// Input/Output components
inputComponent: ReactNode;
resultComponent: ReactNode;
@@ -29,28 +29,15 @@ interface ToolContentPropsBase<T, I> extends ToolComponentProps {
};
// Input value to pass to the compute function
input: I;
input?: I;
exampleCards?: CardExampleType<T>[];
setInput?: React.Dispatch<React.SetStateAction<I>>;
// Validation schema (optional)
validationSchema?: any;
}
interface ToolContentPropsWithExamples<T, I>
extends ToolContentPropsBase<T, I> {
exampleCards: CardExampleType<T>[];
setInput: React.Dispatch<React.SetStateAction<I>>;
}
interface ToolContentPropsWithoutExamples<T, I>
extends ToolContentPropsBase<T, I> {
exampleCards?: never;
setInput?: never;
}
type ToolContentProps<T, I> =
| ToolContentPropsWithExamples<T, I>
| ToolContentPropsWithoutExamples<T, I>;
export default function ToolContent<T extends FormikValues, I>({
title,
inputComponent,

View File

@@ -14,10 +14,10 @@ import { GetGroupsType } from '@components/options/ToolOptions';
export interface ExampleCardProps<T> {
title: string;
description: string;
sampleText: string;
sampleText?: string;
sampleResult: string;
sampleOptions: T;
changeInputResult: (newInput: string, newOptions: T) => void;
changeInputResult: (newInput: string | undefined, newOptions: T) => void;
getGroups: GetGroupsType<T> | null;
}

View File

@@ -15,7 +15,7 @@ export interface ExampleProps<T> {
exampleCards: CardExampleType<T>[];
getGroups: GetGroupsType<T> | null;
formRef: React.RefObject<FormikProps<T>>;
setInput: React.Dispatch<React.SetStateAction<any>>;
setInput?: React.Dispatch<React.SetStateAction<any>>;
}
export default function ToolExamples<T>({
@@ -26,8 +26,8 @@ export default function ToolExamples<T>({
formRef,
setInput
}: ExampleProps<T>) {
function changeInputResult(newInput: string, newOptions: T) {
setInput(newInput);
function changeInputResult(newInput: string | undefined, newOptions: T) {
setInput?.(newInput);
formRef.current?.setValues(newOptions);
const toolsElement = document.getElementById('tool');
if (toolsElement) {

View File

@@ -4,9 +4,18 @@ import { lazy } from 'react';
export const tool = defineTool('json', {
name: 'Stringify JSON',
path: 'stringify',
icon: 'lets-icons:json-format-light',
description: 'Convert JavaScript objects and arrays into their JSON string representation. Options include custom indentation and HTML character escaping for web-safe JSON strings.',
icon: 'ant-design:field-string-outlined',
description:
'Convert JavaScript objects and arrays into their JSON string representation. Options include custom indentation and HTML character escaping for web-safe JSON strings.',
shortDescription: 'Convert JavaScript objects to JSON strings',
keywords: ['stringify', 'serialize', 'convert', 'object', 'array', 'json', 'string'],
keywords: [
'stringify',
'serialize',
'convert',
'object',
'array',
'json',
'string'
],
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,29 @@
import { describe, expect, it } from 'vitest';
import { generateArithmeticSequence } from './service';
describe('generateArithmeticSequence', () => {
it('should generate basic arithmetic sequence', () => {
const result = generateArithmeticSequence(1, 2, 5, ', ');
expect(result).toBe('1, 3, 5, 7, 9');
});
it('should handle negative first term', () => {
const result = generateArithmeticSequence(-5, 2, 5, ' ');
expect(result).toBe('-5 -3 -1 1 3');
});
it('should handle negative common difference', () => {
const result = generateArithmeticSequence(10, -2, 5, ',');
expect(result).toBe('10,8,6,4,2');
});
it('should handle decimal numbers', () => {
const result = generateArithmeticSequence(1.5, 0.5, 4, ' ');
expect(result).toBe('1.5 2 2.5 3');
});
it('should handle single term sequence', () => {
const result = generateArithmeticSequence(1, 2, 1, ',');
expect(result).toBe('1');
});
});

View File

@@ -0,0 +1,136 @@
import { Box } from '@mui/material';
import React, { useState } from 'react';
import ToolContent from '@components/ToolContent';
import ToolTextResult from '@components/result/ToolTextResult';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { generateArithmeticSequence } from './service';
import * as Yup from 'yup';
import { CardExampleType } from '@components/examples/ToolExamples';
type InitialValuesType = {
firstTerm: string;
commonDifference: string;
numberOfTerms: string;
separator: string;
};
const initialValues: InitialValuesType = {
firstTerm: '1',
commonDifference: '2',
numberOfTerms: '10',
separator: ', '
};
const validationSchema = Yup.object({
firstTerm: Yup.number().required('First term is required'),
commonDifference: Yup.number().required('Common difference is required'),
numberOfTerms: Yup.number()
.min(1, 'Must generate at least 1 term')
.max(1000, 'Maximum 1000 terms allowed')
.required('Number of terms is required'),
separator: Yup.string().required('Separator is required')
});
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Basic Arithmetic Sequence',
description:
'Generate a sequence starting at 1, increasing by 2, for 5 terms',
sampleOptions: {
firstTerm: '1',
commonDifference: '2',
numberOfTerms: '5',
separator: ', '
},
sampleResult: '1, 3, 5, 7, 9'
},
{
title: 'Negative Sequence',
description: 'Generate a decreasing sequence starting at 10',
sampleOptions: {
firstTerm: '10',
commonDifference: '-3',
numberOfTerms: '4',
separator: ' → '
},
sampleResult: '10 → 7 → 4 → 1'
},
{
title: 'Decimal Sequence',
description: 'Generate a sequence with decimal numbers',
sampleOptions: {
firstTerm: '0.5',
commonDifference: '0.5',
numberOfTerms: '6',
separator: ' '
},
sampleResult: '0.5 1 1.5 2 2.5 3'
}
];
export default function ArithmeticSequence() {
const [result, setResult] = useState<string>('');
return (
<ToolContent
inputComponent={null}
resultComponent={
<ToolTextResult title="Generated Sequence" value={result} />
}
initialValues={initialValues}
validationSchema={validationSchema}
exampleCards={exampleCards}
toolInfo={{
title: 'What is an Arithmetic Sequence?',
description:
'An arithmetic sequence is a sequence of numbers where the difference between each consecutive term is constant. This constant difference is called the common difference. Given the first term (a₁) and the common difference (d), each term can be found by adding the common difference to the previous term.'
}}
getGroups={({ values, updateField }) => [
{
title: 'Sequence Parameters',
component: (
<Box>
<TextFieldWithDesc
description="First term of the sequence (a₁)"
value={values.firstTerm}
onOwnChange={(val) => updateField('firstTerm', val)}
type="number"
/>
<TextFieldWithDesc
description="Common difference between terms (d)"
value={values.commonDifference}
onOwnChange={(val) => updateField('commonDifference', val)}
type="number"
/>
<TextFieldWithDesc
description="Number of terms to generate (n)"
value={values.numberOfTerms}
onOwnChange={(val) => updateField('numberOfTerms', val)}
type="number"
/>
</Box>
)
},
{
title: 'Output Format',
component: (
<TextFieldWithDesc
description="Separator between terms"
value={values.separator}
onOwnChange={(val) => updateField('separator', val)}
/>
)
}
]}
compute={(values) => {
const sequence = generateArithmeticSequence(
Number(values.firstTerm),
Number(values.commonDifference),
Number(values.numberOfTerms),
values.separator
);
setResult(sequence);
}}
/>
);
}

View File

@@ -0,0 +1,21 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('number', {
name: 'Generate Arithmetic Sequence',
path: 'arithmetic-sequence',
icon: 'ic:sharp-plus',
description:
'Generate an arithmetic sequence by specifying the first term (a₁), common difference (d), and number of terms (n). The tool creates a sequence where each number differs from the previous by a constant difference.',
shortDescription:
'Generate a sequence where each term differs by a constant value.',
keywords: [
'arithmetic',
'sequence',
'progression',
'numbers',
'series',
'generate'
],
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,13 @@
export function generateArithmeticSequence(
firstTerm: number,
commonDifference: number,
numberOfTerms: number,
separator: string
): string {
const sequence: number[] = [];
for (let i = 0; i < numberOfTerms; i++) {
const term = firstTerm + i * commonDifference;
sequence.push(term);
}
return sequence.join(separator);
}

View File

@@ -1,4 +1,5 @@
import { tool as numberSum } from './sum/meta';
import { tool as numberGenerate } from './generate/meta';
import { tool as numberArithmeticSequence } from './arithmetic-sequence/meta';
export const numberTools = [numberSum, numberGenerate];
export const numberTools = [numberSum, numberGenerate, numberArithmeticSequence];