From 5526af924f0aeae8a60bbf6a7c9cc8be5a08cac5 Mon Sep 17 00:00:00 2001 From: Kuan Wee Loong Date: Fri, 19 Apr 2024 14:56:39 +0800 Subject: [PATCH] feat: round number to decimal places --- .../apps/formatter/actions/numbers/index.ts | 5 +- .../round-number-decimal-places/fields.ts | 66 +++++++++++++++++++ .../round-number-decimal-places/index.ts | 19 ++++++ .../transform-data.ts | 49 ++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/fields.ts create mode 100644 packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/index.ts create mode 100644 packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/transform-data.ts diff --git a/packages/backend/src/apps/formatter/actions/numbers/index.ts b/packages/backend/src/apps/formatter/actions/numbers/index.ts index 28870a61f..bda11829b 100644 --- a/packages/backend/src/apps/formatter/actions/numbers/index.ts +++ b/packages/backend/src/apps/formatter/actions/numbers/index.ts @@ -4,6 +4,7 @@ import { fieldSchema as selectTransformFieldSchema } from '../../common/select-t import { setUpActionFields } from '../../common/set-up-action-fields' import { spec as performCalculation } from './transforms/perform-calculation' +import { spec as roundNumberDecimalPlaces } from './transforms/round-number-decimal-places' const action: IRawAction = { name: 'Numbers', @@ -11,7 +12,7 @@ const action: IRawAction = { description: 'Manipulate numbers in your data', arguments: setUpActionFields({ commonFields: [], - transforms: [performCalculation], + transforms: [performCalculation, roundNumberDecimalPlaces], }), async run($) { @@ -20,6 +21,8 @@ const action: IRawAction = { switch (transformId) { case performCalculation.id: return await performCalculation.transformData($) + case roundNumberDecimalPlaces.id: + return await roundNumberDecimalPlaces.transformData($) default: throw new Error(`Unknown number transform: '${transformId}'`) } diff --git a/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/fields.ts b/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/fields.ts new file mode 100644 index 000000000..31e6b8ea7 --- /dev/null +++ b/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/fields.ts @@ -0,0 +1,66 @@ +import z from 'zod' + +import { numericStringSchema } from '@/apps/formatter/common/numeric-string-schema' +import { type TransformSpec } from '@/apps/formatter/common/transform-spec' +import { ensureZodEnumValue, ensureZodObjectKey } from '@/helpers/zod-utils' + +const opTypeEnum = z.enum(['roundDown', 'roundUp', 'roundOff']) + +export const fieldSchema = z + .object({ + roundNumberValue: numericStringSchema, + roundNumberOp: opTypeEnum, + roundNumberToDecimalPlaces: numericStringSchema.pipe( + z + .number() + .int('Enter whole numbers without decimals') + .nonnegative('Enter a number greater than 0'), + ), + }) + .transform((params) => ({ + value: params.roundNumberValue, + op: params.roundNumberOp, + toDecimalPlaces: params.roundNumberToDecimalPlaces, + })) + +export const fields = [ + { + label: 'What number do you want to round?', + key: ensureZodObjectKey(fieldSchema.sourceType(), 'roundNumberValue'), + type: 'string' as const, + required: true, + variables: true, + }, + { + label: 'What kind of rounding do you want?', + key: ensureZodObjectKey(fieldSchema.sourceType(), 'roundNumberOp'), + type: 'dropdown' as const, + required: true, + variables: false, + showOptionValue: false, + options: [ + { + label: 'Round Down', + value: ensureZodEnumValue(opTypeEnum, 'roundDown'), + }, + { + label: 'Round Up', + value: ensureZodEnumValue(opTypeEnum, 'roundUp'), + }, + { + label: 'Round Off', + value: ensureZodEnumValue(opTypeEnum, 'roundOff'), + }, + ], + }, + { + label: 'How many decimal places do you want to round to?', + key: ensureZodObjectKey( + fieldSchema.sourceType(), + 'roundNumberToDecimalPlaces', + ), + type: 'string' as const, + required: true, + variables: true, + }, +] satisfies TransformSpec['fields'] diff --git a/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/index.ts b/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/index.ts new file mode 100644 index 000000000..e912bc8ff --- /dev/null +++ b/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/index.ts @@ -0,0 +1,19 @@ +import type { TransformSpec } from '@/apps/formatter/common/transform-spec' + +import { fields } from './fields' +import { transformData } from './transform-data' + +const id = 'roundNumberDecimalPlaces' satisfies TransformSpec['id'] + +const dropdownConfig = { + label: 'Round number to decimal places', + description: + 'Round down, round up, or round off a number to the decimal places you want', +} satisfies TransformSpec['dropdownConfig'] + +export const spec = { + id, + dropdownConfig, + fields, + transformData, +} satisfies TransformSpec diff --git a/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/transform-data.ts b/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/transform-data.ts new file mode 100644 index 000000000..98c03e721 --- /dev/null +++ b/packages/backend/src/apps/formatter/actions/numbers/transforms/round-number-decimal-places/transform-data.ts @@ -0,0 +1,49 @@ +import Big from 'big.js' +import { ZodError } from 'zod' + +import type { TransformSpec } from '@/apps/formatter/common/transform-spec' +import StepError, { GenericSolution } from '@/errors/step' +import { firstZodParseError } from '@/helpers/zod-utils' + +import { fieldSchema } from './fields' + +export const transformData: TransformSpec['transformData'] = async ($) => { + try { + const { value, op, toDecimalPlaces } = fieldSchema.parse($.step.parameters) + + let result = '' + switch (op) { + case 'roundOff': + result = new Big(value).toFixed(toDecimalPlaces) + break + case 'roundUp': + result = new Big(value).toFixed(toDecimalPlaces, Big.roundUp) + break + case 'roundDown': + result = new Big(value).toFixed(toDecimalPlaces, Big.roundDown) + break + } + + $.setActionItem({ + raw: { + result, + }, + }) + } catch (error) { + if (error instanceof ZodError) { + throw new StepError( + `Configuration problem: '${firstZodParseError(error)}'`, + GenericSolution.ReconfigureInvalidField, + $.step.position, + $.app.name, + ) + } else { + throw new StepError( + `Error performing rounding: '${error.message}'`, + 'Ensure that you have entered valid numbers.', + $.step.position, + $.app.name, + ) + } + } +}