feat: find most popular ui

This commit is contained in:
Ibrahima G. Coulibaly
2024-07-09 18:42:00 +01:00
parent 41a5ff2774
commit f3e5c0dec7
4 changed files with 271 additions and 105 deletions

31
.idea/workspace.xml generated
View File

@@ -4,11 +4,10 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="chore: idea config">
<change afterPath="$PROJECT_DIR$/src/components/options/SelectWithDesc.tsx" afterDir="false" />
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: sort list">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/list/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/list/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/list/find-most-popular/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/list/find-most-popular/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/list/find-most-popular/service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/list/find-most-popular/service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/list/sort/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/list/sort/index.tsx" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@@ -204,15 +203,7 @@
<workItem from="1719475764139" duration="14903000" />
<workItem from="1719492452780" duration="8000" />
<workItem from="1719496624579" duration="6148000" />
<workItem from="1720542757452" duration="2683000" />
</task>
<task id="LOCAL-00039" summary="fix: build">
<option name="closed" value="true" />
<created>1719172147719</created>
<option name="number" value="00039" />
<option name="presentableId" value="LOCAL-00039" />
<option name="project" value="LOCAL" />
<updated>1719172147719</updated>
<workItem from="1720542757452" duration="4048000" />
</task>
<task id="LOCAL-00040" summary="chore: CODEOWNERS">
<option name="closed" value="true" />
@@ -598,7 +589,15 @@
<option name="project" value="LOCAL" />
<updated>1719600739052</updated>
</task>
<option name="localTasksCounter" value="88" />
<task id="LOCAL-00088" summary="feat: sort list">
<option name="closed" value="true" />
<created>1720545582958</created>
<option name="number" value="00088" />
<option name="presentableId" value="LOCAL-00088" />
<option name="project" value="LOCAL" />
<updated>1720545582958</updated>
</task>
<option name="localTasksCounter" value="89" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -642,7 +641,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="refactor: tool input and result" />
<MESSAGE value="feat: create transparent png" />
<MESSAGE value="fix: ToolFileInput.tsx" />
<MESSAGE value="fix: generate numbers" />
@@ -667,7 +665,8 @@
<MESSAGE value="fix: package.json" />
<MESSAGE value="feat: playwright report" />
<MESSAGE value="chore: idea config" />
<option name="LAST_COMMIT_MESSAGE" value="chore: idea config" />
<MESSAGE value="feat: sort list" />
<option name="LAST_COMMIT_MESSAGE" value="feat: sort list" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

View File

@@ -1,11 +1,172 @@
import { Box } from '@mui/material';
import React from 'react';
import React, { useState } from 'react';
import ToolTextInput from '../../../components/input/ToolTextInput';
import ToolTextResult from '../../../components/result/ToolTextResult';
import * as Yup from 'yup';
import ToolOptions from '../../../components/options/ToolOptions';
import {
DisplayFormat,
SortingMethod,
SplitOperatorType,
TopItemsList
} from './service';
import ToolInputAndResult from '../../../components/ToolInputAndResult';
import SimpleRadio from '../../../components/options/SimpleRadio';
import TextFieldWithDesc from '../../../components/options/TextFieldWithDesc';
import CheckboxWithDesc from '../../../components/options/CheckboxWithDesc';
import SelectWithDesc from '../../../components/options/SelectWithDesc';
const initialValues = {};
const initialValues = {
splitSeparatorType: 'symbol' as SplitOperatorType,
sortingMethod: 'alphabetic' as SortingMethod,
displayFormat: 'count' as DisplayFormat,
splitSeparator: ',',
deleteEmptyItems: false,
ignoreItemCase: false,
trimItems: false
};
const splitOperators: {
title: string;
description: string;
type: SplitOperatorType;
}[] = [
{
title: 'Use a Symbol for Splitting',
description: 'Delimit input list items with a character.',
type: 'symbol'
},
{
title: 'Use a Regex for Splitting',
type: 'regex',
description: 'Delimit input list items with a regular expression.'
}
];
export default function FindMostPopular() {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues, input: any) => {
const {
splitSeparatorType,
splitSeparator,
displayFormat,
sortingMethod,
deleteEmptyItems,
ignoreItemCase,
trimItems
} = optionsValues;
setResult(
TopItemsList(
splitSeparatorType,
sortingMethod,
displayFormat,
splitSeparator,
input,
deleteEmptyItems,
ignoreItemCase,
trimItems
)
);
};
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
});
export default function FindMostPopular() {
return <Box>Lorem ipsum</Box>;
return (
<Box>
<ToolInputAndResult
input={
<ToolTextInput
title={'Input list'}
value={input}
onChange={setInput}
/>
}
result={<ToolTextResult title={'Most popular items'} value={result} />}
/>
<ToolOptions
compute={compute}
getGroups={({ values, updateField }) => [
{
title: 'How to Extract List Items?',
component: (
<Box>
{splitOperators.map(({ title, description, type }) => (
<SimpleRadio
key={type}
onClick={() => updateField('splitSeparatorType', type)}
title={title}
description={description}
checked={values.splitSeparatorType === type}
/>
))}
<TextFieldWithDesc
description={'Set a delimiting symbol or regular expression.'}
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
/>
</Box>
)
},
{
title: 'Item comparison',
component: (
<Box>
<CheckboxWithDesc
title={'Remove empty items'}
description={'Ignore empty items from comparison.'}
checked={values.deleteEmptyItems}
onChange={(value) => updateField('deleteEmptyItems', value)}
/>
<CheckboxWithDesc
title={'Trim top list items'}
description={
'Remove leading and trailing spaces before comparing items'
}
checked={values.trimItems}
onChange={(value) => updateField('trimItems', value)}
/>
<CheckboxWithDesc
title={'Ignore Item Case'}
description={'Compare all list items in lowercase.'}
checked={values.ignoreItemCase}
onChange={(value) => updateField('ignoreItemCase', value)}
/>
</Box>
)
},
{
title: 'Top item output format',
component: (
<Box>
<SelectWithDesc
selected={values.displayFormat}
options={[
{ label: 'Show item percentage', value: 'percentage' },
{ label: 'Show item count', value: 'count' },
{ label: 'Show item total', value: 'total' }
]}
onChange={(value) => updateField('displayFormat', value)}
description={'How to display the most popular list items?'}
/>
<SelectWithDesc
selected={values.sortingMethod}
options={[
{ label: 'Sort Alphabetically', value: 'alphabetic' },
{ label: 'Sort by count', value: 'count' }
]}
onChange={(value) => updateField('sortingMethod', value)}
description={'Select a sorting method.'}
/>
</Box>
)
}
]}
initialValues={initialValues}
input={input}
validationSchema={validationSchema}
/>
</Box>
);
}

View File

@@ -3,7 +3,8 @@ export type DisplayFormat = 'count' | 'percentage' | 'total';
export type SortingMethod = 'count' | 'alphabetic';
// Function that takes the array as arg and returns a dict of element occurrences and handle the ignoreItemCase
function dictMaker(array: string[],
function dictMaker(
array: string[],
ignoreItemCase: boolean
): { [key: string]: number } {
const dict: { [key: string]: number } = {};
@@ -17,16 +18,18 @@ function dictMaker(array: string[],
// Function that sorts the dict created with dictMaker based on the chosen sorting method
function dictSorter(
dict: { [key: string]: number },
sortingMethod: SortingMethod,
sortingMethod: SortingMethod
): { [key: string]: number } {
let sortedArray: [string, number][];
switch (sortingMethod) {
case 'count':
sortedArray = Object.entries(dict).sort(([, countA], [, countB]) => countB - countA);
sortedArray = Object.entries(dict).sort(
([, countA], [, countB]) => countB - countA
);
break;
case 'alphabetic':
sortedArray = Object.entries(dict).sort(([keyA], [keyB]) => {
return keyA.localeCompare(keyB)
return keyA.localeCompare(keyB);
});
break;
default:
@@ -47,15 +50,17 @@ function displayFormater(
switch (displayFormat) {
case 'percentage':
Object.entries(dict).forEach(([key, value]) => {
formattedOutput.push(`${key}: ${value} (${((value / total) * 100).toFixed(2)}%)`);
formattedOutput.push(
`${key}: ${value} (${((value / total) * 100).toFixed(2)}%)`
);
});
break;
case "total":
case 'total':
Object.entries(dict).forEach(([key, value]) => {
formattedOutput.push(`${key}: ${value} (${value} / ${total})`);
});
break;
case "count":
case 'count':
Object.entries(dict).forEach(([key, value]) => {
formattedOutput.push(`${key}: ${value}`);
});
@@ -80,18 +85,20 @@ export function TopItemsList(
array = input.split(splitSeparator);
break;
case 'regex':
array = input.split(new RegExp(splitSeparator)).filter(item => item !== '');
array = input
.split(new RegExp(splitSeparator))
.filter((item) => item !== '');
break;
}
// Trim items if required
if (trimItems) {
array = array.map(item => item.trim());
array = array.map((item) => item.trim());
}
// Delete empty items after initial split
if (deleteEmptyItems) {
array = array.filter(item => item !== '');
array = array.filter((item) => item !== '');
}
// Transform the array into dict

View File

@@ -40,8 +40,7 @@ const splitOperators: {
export default function SplitText() {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
// const formRef = useRef<FormikProps<typeof initialValues>>(null);
const computeExternal = (optionsValues: typeof initialValues, input: any) => {
const compute = (optionsValues: typeof initialValues, input: any) => {
const {
splitSeparatorType,
joinSeparator,
@@ -82,7 +81,7 @@ export default function SplitText() {
result={<ToolTextResult title={'Sorted list'} value={result} />}
/>
<ToolOptions
compute={computeExternal}
compute={compute}
getGroups={({ values, updateField }) => [
{
title: 'Input item separator',