Invoice Manager is an
- Repository: https://github.com/princemuel/invoicemanager
- Live Site: https://invoicemanager.vercel.app
- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- CSS Grid
- Mobile-first workflow
- React - The JS library for web and native user interfaces
- Remix - The full stack React web framework that uses web standards
- Tailwind CSS - A utility-first CSS framework packed with classes that can be composed to build any design, directly in your markup
- I faced a challenge where I desired real-time synchronization between the user's input in the price and quantity fields, with an immediate computation of the result. Although the code I wrote was somewhat intricate, I successfully implemented it by leveraging React's useEffect to trigger the component's re-render when the inputs (price and quantity) changed. While I acknowledge that opting for a readonly input to register changes could have been a simpler approach, I intentionally chose the approach I took to experiment with achieving the same result in a different manner. Additionally, I took precautions to simplify the variable names and the code for better understanding.
// The Real Time Input Sync Code: It renders the total in an <output></output>
useEffect(() => {
const subscription = watch((_, { name, type }) => {
const value = getValues();
if (type === "change" && name) {
if (name.endsWith("quantity") || name.endsWith("price")) {
type FieldValueType = FieldPathValue<typeof value, typeof name>;
const { items } = value;
const [, indexString, fieldName] = name.split(".");
const index = parseInt(indexString);
const fieldValue: FieldValueType = get(value, name);
if (fieldValue) {
if (fieldName === "quantity")
setValue(
`items.${index}.total`,
approximate(calculateTotal(fieldValue, items[index].price)),
);
else if (fieldName === "price")
setValue(
`items.${index}.total`,
approximate(calculateTotal(items[index].quantity, fieldValue)),
);
}
}
}
});
return () => {
subscription.unsubscribe();
};
}, [getValues, setValue, watch]);
- I wrote a function calculateTotal that calculates the total value based on its input
Use Case:
- Total of 2 numbers
- Total an array of items based on the total of each item
- Total an array of items based on the price and quantity in each item object
export function calculateTotal<T extends FirstArg>(
a?: T,
b?: T extends number ? NonNullable<T>
: T extends (infer _)[] ? "total"
: never,
) {
// Safely parses a value to a number and guards against NaN and negative zero.
function const numberGuard = (value: any, defaultValue: number = 0): number => {
const parsed = Number(value);
return Number.isNaN(parsed) || Object.is(parsed, -0) ? defaultValue : parsed;
};
if (Array.isArray(a)) {
return a.reduce((acc, item) => {
const { total = 0, quantity = 0, price = 0 } = item;
return b === "total" ? acc + total : acc + quantity * price;
}, 0);
}
// bailout since the function expects 2 number params, or an array params
return numberGuard(a) * numberGuard(b);
}
calculateTotal(10, 5); // 50
calculateTotal([{ total: 100 }, { total: 200 }], "total"); // 300
calculateTotal([{ quantity: 3, price: 20 }, { quantity: 2, price: 15 }]); // 90
calculateTotal([{ total: 100 }, { quantity: 2, price: 30 }, { total: 50 }],'total'); // 210
Use this section to outline areas that you want to continue focusing on in future projects. These could be concepts you're still not completely comfortable with or techniques you found useful that you want to refine and perfect.
- Example - This helped me for XYZ reason. I really liked this pattern and will use it going forward.
- Example - This is an amazing article which helped me finally understand XYZ. I'd recommend it to anyone still learning this concept.
To contribute to this project please check out the contribution guidelines.
Before cloning/forking this project, make sure you have the following tools installed:
From your terminal:
Clone the project
# Choose one method out of these 3 c
git clone [email protected]:princemuel/invoicemanager.git # clone with git history
git clone [email protected]:your_username/invoicemanager.git # if you forked the project
npx degit [email protected]:princemuel/invoicemanager # clone without git history
Navigate to the project directory
cd invoicemanager
Install the Dependencies
# This project uses pnpm but you can use your favorite package manager.
# Just make sure to remove the lockfile before installation is not using pnpm
pnpm install
This starts your app in development mode, rebuilding assets on file changes.
pnpm run dev # replace package manager name with your chosen package manager
First, build your app for production:
pnpm run build # replace package manager name with your chosen package manager
Then run the app in production mode:
pnpm start # replace package manager name with your chosen package manager
Now you'll need to pick a host to deploy it to.
If you're familiar with deploying Node.JS applications, the built-in Remix app server is production-ready.
Make sure to deploy the output of remix build
build/
public/build/
- Twitter - @iamprincemuel
- LinkedIn - @princemuel