Listify
Project Summary
Have you ever had a situation when you are going shopping or you just found an interesting food or drink recipe and definitely would like take a note, but you do not have any notepad or something like that at hand? Oh boy, I believe you have encountered this kind of situation.
Listify
is a smart application that helps you keep track of your favorite food and drink recipes and ingredients in general. You will be able to create a grocery list, proceed to its details, and then create a more detailed list of items you find interesting to keep an eye on
Well, there is good news for you. Motivation
The main goal of this project is to keep practicing and improving skills, to make hands dirty in some new development frameworks, libraries, and tools, and eventually create some awesome and useful application
Getting Started
Step-by-step instructions on how to locally run the application
HTTPS
or SSH
. You will need node
and npm
installed globally on your machine.
Clone down this repository via In order to run the application you will need to follow the next steps:
-
Runnpm run packages-install
in order to install all project dependencies withinroot
directory package.json as well as withinclient
folder.npm run packages-install
-
Inroot
directory runnpm run server
in order to run a servernpm run server
-
Inroot
directory runnpm run client
in order to run the project itselfnpm run client
-
The application will be available onhttp://localhost:5500
Features
Detailed information about what features the application is rich in
Listify
application are the following:
The main features of -
User Authentication flow that consists of abilities to Sign-In via email/password and Google, Sign-Up via email/password and Google, Forgot password, Reset Password
-
Ability to create a shopping list with a specific name and currency.
-
Ability to delete a specific shopping list.
-
Ability to see the total price of all items within a specific shopping list and information on how many items were purchased and how many need to be bought via tooltip.
-
Ability to create/update/delete a specific product item within a specific shopping list
-
Ability to select individual product item (selected item appears at the bottom of the list and is sorted by their name) or select all product items at once.
-
Ability to see all items that should be bought or have been already purchased based on selected product items within a widget.
-
Ability to create a copy of a particular shopping list with all product items.
-
Ability to delete a particular shopping list with all product items.
-
Ability to update user name, picture, change password and apply some specific user preferences such as default currency or ability to calculate products price by their quantity within user's Profile, etc.
API
General information about existing application's API endpoints
/api/users
)
User (GET: /api/me - Creates a current, registered Firebase user in MongoDB or updates it.
Example of returned data:
POST: /api/users/profile - Updates User's information, such as name, picture, etc.
Example of returned data:
/api/shopping-lists
)
Shopping Lists (GET: /api/shopping-lists - Gets all availble shopping lists from database
Example of returned data:
[
{
"_id": "63347da0b7fcd218f1024cab",
"user": "qnjPpkmaWlUsCb5FqcbllcxYW7v1",
"name": "Alex Smith",
"currency": "â‚Ł",
"shoppingListItems": [
{
"name": "New",
"quantity": 2.5,
"units": "L",
"price": 14,
"isChecked": true,
"_id": "63347da9b7fcd218f1024cad",
"updatedAt": "2022-11-10T18:22:46.639Z",
"createdAt": "2022-11-10T18:22:45.299Z"
},
{
"name": "Product-1",
"quantity": 1,
"units": "L",
"price": 5,
"isChecked": true,
"_id": "6337f44583d311f66fb8b49e",
"updatedAt": "2022-11-10T18:22:49.603Z",
"createdAt": "2022-11-10T18:22:45.299Z"
},
{
"name": "new Product-2",
"quantity": 2,
"units": "L",
"price": 10,
"isChecked": false,
"_id": "633c708240ef8be1a1e502de",
"updatedAt": "2022-11-10T18:23:06.310Z",
"createdAt": "2022-11-10T18:22:45.299Z"
}
],
"createdAt": "2022-09-28T17:00:16.790Z",
"updatedAt": "2022-11-10T18:23:06.310Z",
"__v": 0
},
]
POST: /api/shopping-lists - Creates a new shopping list
Example of returned data:
DELETE: /api/shopping-lists/:id - Deletes a particular shopping list by its ID
Example of returned data:
"6370d06590f20bef1d2af00c" - id of deleted shopping list
/api/shopping-lists
)
Shopping List Details (PUT: /api/shopping-lists/:id/create-product-item - Creates a new product item within a specific shopping list
Example of returned data:
DELETE: /api/shopping-lists/:id/delete-product-item - Deletes a new product item within a specific shopping list
Example of returned data:
PUT: /api/shopping-lists/:id/select-product-item - Selects a particular product item
Example of returned data:
PUT: /api/shopping-lists/:id/edit-product-item - Updates a particular product item
Example of returned data:
PUT: /api/shopping-lists/:id/select-all-product-items - Selects all product items
Example of returned data:
[
{
name: 'Another test product',
quantity: 0,
units: '',
price: 0,
isChecked: true,
_id: '6371071ac4722436c000cd23',
updatedAt: '2022-11-13T15:07:18.123Z',
createdAt: '2022-11-13T15:07:18.123Z'
},
{
name: 'Updated product item',
quantity: 2,
units: 'L',
price: 12,
isChecked: true,
_id: '6370d5a190f20bef1d2af03e',
updatedAt: '2022-11-13T15:07:18.123Z',
createdAt: '2022-11-13T15:07:18.123Z'
}
]
Products views
Visual presentation of Listify application
Shopping lists/Shopping list details/Profile pages
Sign-In/Sign-Up/Forgot-password/Reset-password pages
Modals
Tools/libraries/frameworks used
Client
Vite.
This project was bootstrapped with a help of a new beast in the neighborhood -State Management
Zustand.
As for state management perspectives, the choice fell onclient/src/app/modules/shopping-lists/shopping-lists.store.ts
, client/src/app/modules/aauth/auth.store.ts
, etc).
There is no global store within the application. Hence, particular modules have their own store configurations (Example of store configuration
// create a store itself
export const useAuthStore = create<AuthStoreState & AuthStoreActions>()(
// devtools allows to create a Redux like Devtools in browser
devtools(
// set function allows to merges state
(set) => ({
...initialState,
setUser: (payload) => {
return set((state) => ({ ...state, user: payload }), false, 'setUser');
},
setUserLoadingStatus: (payload) =>
set((state) => ({ ...state, userLoadingStatus: payload }), false, 'setUserLoadingStatus'),
setUpdateUser: (payload) => {
return set(
produce((state) => ({ user: { ...state.user, ...payload } })),
false,
'setUpdateUser'
);
},
reset: () => set({ ...initialState, userLoadingStatus: 'idle' }, false, 'resetAuthStore'),
}),
{ name: 'AuthStore' }
)
);
Example of binding React component with the store
const Profile = (): ReactElement => {
const user = useAuthStore((state) => state.user);
return <h1>Hello {user.name}</h1>
}
React dependency
.
Example of the binding store without export const validateUserAction = async (): Promise<void> => {
const setUser = useAuthStore.getState().setUser;
// the rest of the code
};
React UI component library
Material UI library was chosen
For the sake of simplicity, speeding up the application development and for more practice perspectives, theForms
The application uses:
-
Formik for forms management.
-
Formik-Mui for Material UI components.
The 3rd-party binding -
Yup for forms validation
Example of Formik usage
interface AuthSignInFormProps {
initialValues: SignInFormInitialValues;
validationSchema: SchemaOf<SignInFormInitialValues, never>;
onSubmit: (values: SignInFormInitialValues, actions: FormikHelpers<SignInFormInitialValues>) => Promise<void>;
}
const SignInForm = ({ initialValues, validationSema, onSubmit }: AuthSignInFormProps): ReactElement => {
return (
<Formik initialValues={initialValues} onSubmit={onSubmit} validationSchema={dtFormikExampleValidationSchema}>
{({ submitForm, isSubmitting }) => (
<Form>
<Field component={TextField} name='email' type='email' label='Email' />
<Field component={TextField} type='password' label='Password' name='password' />
<Button variant='contained' color='primary' disabled={isSubmitting} onClick={submitForm}>
Submit
</Button>
</Form>
)}
</Formik>
);
};
Notifications
The application uses:
-
React-Toastify for displaying any notification messages on UI
Usage:
-
In order to start usingreact-toastify
just importtoastService
fromclient/src/services/toast.service.ts
and then call needed method
toastService.success("Success message");
toastService.info("Info message");
toastService.warn("Warn message");
toastService.error("Error message");
Authentication flow
Firebase flow, because it provides a wide range of useful methods to deal with user authentication
Aplication usersIcons
React Iconsthroughout different parts of the application.
Aplication usesLoader Spinner
React Loader Spinner package
Aplication usesTests
General information about application testing flow, coverage, tests running instructions, etc.
The application uses the following testing libraries/frameworks:
-
React Testing Library for testing React components
-
Vitest framework that allows closely work with Vite and acts as a substitute for Jest
In order to run tests, you need to proceed with the following command within the client directory:
cd client
----
npm run test
client
directory:
In order to check application's tests coverage, you need to proceed with the following command within the cd client
----
npm run coverage
client
directory:
In order to open Vitest testing interface in the browser and run tests there, you need to proceed with the following command within the cd client
----
npm run test:ui