Skip to content

Commit

Permalink
enhancement(AccountingCategoryForm): add validations (#11076)
Browse files Browse the repository at this point in the history
  • Loading branch information
Betree authored Mar 5, 2025
1 parent 0e49d3c commit 46312ba
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react';
import { useFormik } from 'formik';
import type { useFormik } from 'formik';
import { omit } from 'lodash';
import { defineMessages, useIntl } from 'react-intl';
import { z } from 'zod';

import {
AccountingCategoryAppliesTo,
Expand All @@ -10,11 +11,43 @@ import {
} from '../../../../lib/graphql/types/v2/schema';
import { i18nExpenseType } from '../../../../lib/i18n/expense';

import { useFormikZod } from '@/components/FormikZod';

import RichTextEditor from '../../../RichTextEditor';
import StyledInput from '../../../StyledInput';
import StyledInputField from '../../../StyledInputField';
import StyledSelect from '../../../StyledSelect';

const accountingCategoryFormSchema = z.object({
name: z.string().min(1).max(60),
friendlyName: z.string().max(60).optional(),
code: z.string().min(1).max(60),
appliesTo: z
.object({
value: z.nativeEnum(AccountingCategoryAppliesTo).nullable(),
label: z.string(),
})
.nullable(),
kind: z.object({
value: z.nativeEnum(AccountingCategoryKind),
label: z.string(),
}),
expensesTypes: z
.array(
z.object({
value: z.nativeEnum(ExpenseType),
label: z.string(),
}),
)
.optional()
.nullable(),
hostOnly: z.object({
value: z.boolean(),
label: z.string(),
}),
instructions: z.string().optional().nullable(),
});

type FormValues = {
name: string;
friendlyName?: string;
Expand All @@ -37,11 +70,13 @@ export type EditableAccountingCategoryFields =
| 'appliesTo';

type useAccountingCategoryFormikOptions = {
onSubmit: (values: FormValues) => void | Promise<void>;
onSubmit: (values) => void | Promise<void>;
initialValues: FormValues;
};

export function useAccountingCategoryFormik(opts: useAccountingCategoryFormikOptions) {
const formik = useFormik({
const formik = useFormikZod({
schema: accountingCategoryFormSchema,
initialValues: opts.initialValues,
onSubmit: opts.onSubmit,
});
Expand Down Expand Up @@ -136,12 +171,16 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
},
];

const getFieldError = field =>
(props.formik.submitCount || props.formik.touched[field]) && props.formik.errors[field];

return (
<React.Fragment>
<StyledInputField
required
name="code"
label={intl.formatMessage({ defaultMessage: 'Accounting code', id: 'tvVFNA' })}
error={getFieldError('code')}
mt={3}
>
<StyledInput
Expand All @@ -157,6 +196,7 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
name="name"
required
label={intl.formatMessage({ defaultMessage: 'Category name', id: 'kgVqk1' })}
error={getFieldError('name')}
mt={3}
>
<StyledInput
Expand All @@ -171,6 +211,7 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
<StyledInputField
required={false}
name="friendlyName"
error={getFieldError('friendlyName')}
label={intl.formatMessage({ id: 'AccountingCategory.friendlyName', defaultMessage: 'Friendly name' })}
mt={3}
>
Expand All @@ -187,6 +228,7 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
<StyledInputField
name="appliesTo"
required
error={getFieldError('appliesTo')}
label={intl.formatMessage({ defaultMessage: 'Applies To', id: 'M+BG8u' })}
mt={3}
>
Expand All @@ -210,6 +252,7 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
name="kind"
required
label={intl.formatMessage({ defaultMessage: 'Kind', id: 'Transaction.Kind' })}
error={getFieldError('kind')}
mt={3}
>
<StyledSelect
Expand All @@ -232,6 +275,7 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
<StyledInputField
name="hostOnly"
required
error={getFieldError('hostOnly')}
label={intl.formatMessage({ defaultMessage: 'Visible only to host admins', id: 'NvBPFR' })}
mt={3}
>
Expand All @@ -255,6 +299,7 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
<StyledInputField
name="expensesTypes"
required
error={getFieldError('expensesTypes')}
label={intl.formatMessage({ defaultMessage: 'Expense Types', id: 'D+aS5Z' })}
mt={3}
>
Expand All @@ -280,6 +325,7 @@ export function AccountingCategoryForm(props: AccountingCategoryFormProps) {
<StyledInputField
name="instructions"
required
error={getFieldError('instructions')}
label={intl.formatMessage({ defaultMessage: 'Instructions', id: 'sV2v5L' })}
mt={3}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { FormattedMessage, useIntl } from 'react-intl';
import type { AccountingCategory } from '../../../../lib/graphql/types/v2/schema';
import { AccountingCategoryAppliesTo, AccountingCategoryKind } from '../../../../lib/graphql/types/v2/schema';

import StyledButton from '../../../StyledButton';
import { Button } from '@/components/ui/Button';

import StyledModal, { ModalBody, ModalFooter, ModalHeader } from '../../../StyledModal';

import type { EditableAccountingCategoryFields } from './AccountingCategoryForm';
Expand Down Expand Up @@ -78,13 +79,13 @@ export function CreateAccountingCategoryModal(props: CreateAccountingCategoryMod
</ModalBody>
<ModalFooter showDivider={false}>
<form onSubmit={e => formik.handleSubmit(e)}>
<div className="flex justify-center gap-4">
<StyledButton type="submit" buttonStyle="primary">
<FormattedMessage defaultMessage="Create category" id="ZROXxK" />
</StyledButton>
<StyledButton onClick={props.onClose}>
<div className="flex justify-end gap-4 border-t-1 border-solid border-t-slate-100 pt-4">
<Button onClick={props.onClose} variant="outline">
<FormattedMessage id="actions.cancel" defaultMessage="Cancel" />
</StyledButton>
</Button>
<Button type="submit">
<FormattedMessage defaultMessage="Create category" id="ZROXxK" />
</Button>
</div>
</form>
</ModalFooter>
Expand Down

0 comments on commit 46312ba

Please sign in to comment.