Skip to content

Commit

Permalink
Single dialog button with tabs for note input (#112)
Browse files Browse the repository at this point in the history
* Added input method tabs for NoteInputDialog

* Hotfix eslint

microsoft/vscode-eslint#1856

* Refactor NoteInputDialog in a new component

* Add reusable AddOrEditNoteFlow component

* Setup tests and dsl for AddOrEditNoteFlow

* Fixed act warning in AddOrEditNoteFlow tests

* Moved tests from NoteInputDialog to AddOrEditNoteFlow

* Use AddOrEditNoteFlow in EditNote

* Added aider

* Added reset for add/edit note mutations

* Remove old NoteInputDialog

* Upload and preview photo in AddOrEditNoteFlow

* Merge repeated note props into FormValues prop

* Hide tabs for edit note dialog

* Implement recognize note in "From photo" tab

* Remove unused components and hooks

* Use type alias for productAutocompleteInput

* Fix dialog and tab paddings

* WIP: Fix clear note form after successful edit

* Fixed focus trap issue

* Fixed paddings
  • Loading branch information
pkirilin authored Jul 7, 2024
1 parent b8245f8 commit 0f6f112
Show file tree
Hide file tree
Showing 41 changed files with 1,384 additions and 1,366 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
.env
!tests/.env
.aider*
4 changes: 2 additions & 2 deletions src/frontend/.pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,10 @@
},
"engines": {
"node": ">=18.6"
},
"dependenciesMeta": {
"eslint-plugin-prettier@5.1.3": {
"unplugged": true
}
}
}
1 change: 1 addition & 0 deletions src/frontend/src/entities/note/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './groupByMealType';
export * from './mealsHelpers';
export * from './useNotes';
export * from './useNextDisplayOrder';
export * from './useFormValues';
25 changes: 25 additions & 0 deletions src/frontend/src/entities/note/lib/useFormValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useCallback, useMemo, useState } from 'react';
import { type FormValues } from '../model';

interface Result {
values: FormValues;
setValues: (values: FormValues) => void;
clearValues: () => void;
}

export const useFormValues = (initialValues: FormValues): Result => {
const [values, setValues] = useState<FormValues>(initialValues);

const clearValues = useCallback(() => {
setValues(initialValues);
}, [initialValues]);

return useMemo<Result>(
() => ({
values,
setValues,
clearValues,
}),
[clearValues, values],
);
};
7 changes: 7 additions & 0 deletions src/frontend/src/entities/note/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ export interface NoteItem {
productDefaultQuantity: number;
calories: number;
}

export interface FormValues {
pageId: number;
mealType: MealType;
displayOrder: number;
quantity: number;
}
4 changes: 2 additions & 2 deletions src/frontend/src/entities/product/ui/ProductInputForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
validateDefaultQuantity,
} from '../model';

export interface ProductInputFormProps {
interface Props {
id: string;
values: FormValues;
touched?: boolean;
Expand All @@ -24,7 +24,7 @@ export interface ProductInputFormProps {
onSubmitDisabledChange: (disabled: boolean) => void;
}

export const ProductInputForm: FC<ProductInputFormProps> = ({
export const ProductInputForm: FC<Props> = ({
id,
values,
touched = false,
Expand Down
3 changes: 1 addition & 2 deletions src/frontend/src/features/note/addEdit/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from './types';
export * from './mapping';
export * from './useAddProductIfNotExists';
export * from './useNoteDialog';
export * from './useProductDialog';
export * from './useRecognizeNotes';
15 changes: 15 additions & 0 deletions src/frontend/src/features/note/addEdit/lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { type productLib, type productModel } from '@/entities/product';
import { type Note } from '../model';

export interface RenderContentProps {
submitLoading: boolean;
submitDisabled: boolean;
productAutocompleteInput: productLib.AutocompleteInput;
productAutocompleteData: productLib.AutocompleteData;
productFormValues: productModel.FormValues;
onClose: () => void;
onSubmit: (note: Note) => Promise<void>;
onSubmitDisabledChange: (disabled: boolean) => void;
onProductChange: (value: productModel.AutocompleteOption | null) => void;
onProductFormValuesChange: (values: productModel.FormValues) => void;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { store } from '@/app/store';
import { type CreateProductRequest, productApi, type productModel } from '@/entities/product';

type AddProductIfNotExistsFn = (product: productModel.AutocompleteOption) => Promise<number>;

const mapToCreateProductRequest = ({
name,
caloriesCost,
Expand All @@ -15,30 +12,28 @@ const mapToCreateProductRequest = ({
categoryId: category?.id ?? 0,
});

export const addProductIfNotExists: AddProductIfNotExistsFn = async product => {
if (!product.freeSolo) {
return product.id;
}

const request = mapToCreateProductRequest(product);

const { id } = await store
.dispatch(productApi.endpoints.createProduct.initiate(request))
.unwrap();
type AddProductIfNotExistsFn = (product: productModel.AutocompleteOption) => Promise<number>;

return id;
};
interface Result {
sendRequest: AddProductIfNotExistsFn;
isLoading: boolean;
}

export const useAddProductIfNotExists = (): AddProductIfNotExistsFn => {
const [createProduct] = productApi.useCreateProductMutation();
export const useAddProductIfNotExists = (): Result => {
const [addProduct, { isLoading }] = productApi.useCreateProductMutation();

return async product => {
const sendRequest: AddProductIfNotExistsFn = async product => {
if (!product.freeSolo) {
return product.id;
}

const request = mapToCreateProductRequest(product);
const { id } = await createProduct(request).unwrap();
const { id } = await addProduct(request).unwrap();
return id;
};

return {
sendRequest,
isLoading,
};
};
99 changes: 0 additions & 99 deletions src/frontend/src/features/note/addEdit/lib/useNoteDialog.tsx

This file was deleted.

59 changes: 0 additions & 59 deletions src/frontend/src/features/note/addEdit/lib/useProductDialog.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import { parseClientError, type ClientError } from '@/shared/api';

type FetchFn = (file: File) => Promise<void>;

export const EMPTY_RECOGNIZE_NOTES_RESULT: RecognizeNotesResult = {
notes: [],
isLoading: false,
isSuccess: false,
};

export interface RecognizeNotesResult {
notes: RecognizeNoteItem[];
isLoading: boolean;
Expand Down
15 changes: 1 addition & 14 deletions src/frontend/src/features/note/addEdit/model.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
import { type ReactElement } from 'react';
import { type noteModel } from '@/entities/note';
import { type productModel } from '@/entities/product';

export type DialogStateType = 'note' | 'product';

export interface DialogState {
type: DialogStateType;
title: string;
submitText: string;
submitLoading: boolean;
submitDisabled: boolean;
cancelDisabled: boolean;
formId: string;
content: ReactElement;
onClose: () => void;
}
export type InputMethod = 'fromInput' | 'fromPhoto';

export interface Note {
mealType: noteModel.MealType;
Expand Down
Loading

0 comments on commit 0f6f112

Please sign in to comment.