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.
Well, there is good news for you. 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
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
Step-by-step instructions on how to locally run the application
1) Clone down this repository via HTTPS
or SSH
. You will need node
and npm
installed globally on your machine.
2) Create a .env
file separately for root
and client
directories and set the following environment variables:
env
file forroot
directory
NODE_ENV=development
PORT=8080
MONGODB_URI="mongodb+srv://your_connection_with_mongoDB"
FIREBASE_PROJECT_ID="your_firebase_project_id"
FIREBASE_KEY_ID="your_firebase_key_id"
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY---your_key_id-----END PRIVATE KEY-----\n"
FIREBASE_CLIENT_EMAIL="your_firebase_client_email"
FIREBASE_CLIENT_ID="your_firebase_client_id"
FIREBASE_AUTH_URI="your_firebase_auth_uri"
FIREBASE_TOKEN_URI="your_firebase_token_uri"
FIREBASE_AUTH_PROVIDER_X509_CERT_URL="your_firebase_auth_provider_cert_url"
FIREBASE_CLIENT_X509_CERT_URL="your_firebase_client_cert_url"
env
file forclient
directory
VITE_FIREBASE_API_KEY=your_firebase_api_key
VITE_AUTH_DOMAIN=yor_firebase_auth_domain
VITE_PROJECT_ID=your_firebase_project_id
VITE_STORAGE_BUCKET=your_firebase_storage_bucket
VITE_MESSAGING_SENDER_ID=your_firebase_sender_id
VITE_MESSAGING_APP_ID=1:your_firebase_messaging_app_id
VITE_API_BASE_URL=your_base_url
VITE_NODE_ENV=development
-
Run
npm run packages-install
in order to install all project dependencies withinroot
directory package.json as well as withinclient
folder.npm run packages-install
-
npm run server
-
npm run client
Detailed information about what features the application is rich in
General information about existing application's API endpoints
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:
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
PUT: /api/shopping-lists/select-all-shopping-lists - Selects all shopping lists for the further deletion
{
"_id": "658166208bcf2cdf0c607ac4",
"user": "Xhp3G87YBhaq29QWOWi0WYExxJp1",
"name": "Terra Store",
"currency": "$",
"shoppingListItems": [],
"isFavorite": false,
"isChecked": true,
"createdAt": "2023-12-19T09:45:04.269Z",
"updatedAt": "2023-12-19T10:22:19.224Z",
"__v": 0
}
PUT: /api/shopping-lists/delete-all-shopping-lists - Delete all selected shopping lists altogether at once
{
"success": true,
"message": "All shopping lists have been deleted successfully",
"data": []
}
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'
}
]
PUT: /api/shopping-lists/:id/add-to-favorites - Adds to or remove a specific shopping-list from favorites
Example of returned data:
{
"_id": "656b2103c7ed1d8b57e15168",
"user": "Xhp3G87YBhaq29QWOWi0WYExxJp1",
"name": "Cosmopolitan Cocktail",
"currency": "$",
"shoppingListItems": [],
"isFavorite": true,
"createdAt": "2023-12-02T12:20:19.458Z",
"updatedAt": "2023-12-02T12:41:48.016Z",
"__v": 0
}
PUT: /api/shopping-lists/:id/edit-shopping-list - Edits a particular shopping list's name
Example of returned data:
{
"_id": "656b20b9c7ed1d8b57e15166",
"user": "Xhp3G87YBhaq29QWOWi0WYExxJp1",
"name": "Shepherd's pie",
"currency": "$",
"shoppingListItems": [],
"isFavorite": true,
"createdAt": "2023-12-02T12:19:05.416Z",
"updatedAt": "2023-12-02T12:47:52.872Z",
"__v": 0
}
PUT: /api/shopping-lists/:id/select-all-product-items - Selects all product items
Example of returned data:
{
"_id": "6581760b8bcf2cdf0c607af0",
"user": "Xhp3G87YBhaq29QWOWi0WYExxJp1",
"name": "Terra store",
"currency": "$",
"shoppingListItems": [
{
"name": "Yogurt",
"quantity": 1,
"units": "L",
"price": 2,
"isChecked": true,
"_id": "658176548bcf2cdf0c607af2",
"createdAt": "2023-12-19T11:32:23.800Z",
"updatedAt": "2023-12-19T11:32:23.800Z"
},
{
"name": "Milk",
"quantity": 1,
"units": "L",
"price": 4,
"isChecked": true,
"_id": "658176608bcf2cdf0c607af8",
"createdAt": "2023-12-19T11:32:23.800Z",
"updatedAt": "2023-12-19T11:32:23.800Z"
}
],
"isFavorite": false,
"isChecked": false,
"createdAt": "2023-12-19T10:52:59.489Z",
"updatedAt": "2023-12-19T11:32:23.800Z",
"__v": 0
}
PUT: /api/shopping-lists/:id/delete-all-product-items - Deletes all selected product items
Example of returned data:
{
"success": true,
"message": "All product items have been deleted successfully",
"data": {
"_id": "6581760b8bcf2cdf0c607af0",
"user": "Xhp3G87YBhaq29QWOWi0WYExxJp1",
"name": "Terra store",
"currency": "$",
"shoppingListItems": [],
"isFavorite": false,
"isChecked": false,
"createdAt": "2023-12-19T10:52:59.489Z",
"updatedAt": "2023-12-19T11:37:09.763Z",
"__v": 0
}
}
Visual presentation of Listify application
Client
This project was bootstrapped with the help of a new beast in the neighborhood - Vite.
As for state management perspectives, the choice fell on Zustand.
There is no global store within the application. Hence, particular modules have their own store configurations (client/src/app/modules/shopping-lists/shopping-lists.store.ts
, client/src/app/modules/aauth/auth.store.ts
, etc).
// 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' }
)
);
const Profile = (): ReactElement => {
const user = useAuthStore((state) => state.user);
return <h1>Hello {user.name}</h1>
}
export const validateUserAction = async (): Promise<void> => {
const setUser = useAuthStore.getState().setUser;
// the rest of the code
};
For the sake of simplicity, speeding up the application development and for more practice perspectives, the Material UI library was chosen
-
Formik for forms management.
-
The 3rd-party binding Formik-Mui for Material UI components.
-
Yup for forms validation
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>
);
};
-
React-Toastify for displaying any notification messages on UI
toastService.success("Success message");
toastService.info("Info message");
toastService.warn("Warn message");
toastService.error("Error message");
Aplication users Firebase flow, because it provides a wide range of useful methods to deal with user authentication
Aplication uses React Iconsthroughout different parts of the application.
Aplication uses React Loader Spinner package
General information about application testing flow, coverage, tests running instructions, etc.
-
React Testing Library for testing React components
cd client
----
npm run test
In order to check application's tests coverage, you need to proceed with the following command within the client
directory:
cd client
----
npm run coverage
In order to open Vitest testing interface in the browser and run tests there, you need to proceed with the following command within the client
directory:
cd client
----
npm run test:ui