{"id":21667074,"url":"https://github.com/princemuel/invoicemanager","last_synced_at":"2025-04-12T01:22:53.797Z","repository":{"id":64886290,"uuid":"559737687","full_name":"princemuel/invoicemanager","owner":"princemuel","description":"An app to track and simplify your invoice workflow","archived":false,"fork":false,"pushed_at":"2024-06-21T07:57:07.000Z","size":4262,"stargazers_count":10,"open_issues_count":6,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T21:07:36.028Z","etag":null,"topics":["clerkauth","ecommerce","headlessui","invoice-generator","react-hook-form","reactjs","remix-run","tailwindcss","typescript"],"latest_commit_sha":null,"homepage":"https://invoicemanager.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/princemuel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-31T01:36:43.000Z","updated_at":"2024-08-13T00:11:03.000Z","dependencies_parsed_at":"2023-10-26T12:32:35.102Z","dependency_job_id":"68c2432f-7531-427d-badb-777c5cca0df1","html_url":"https://github.com/princemuel/invoicemanager","commit_stats":null,"previous_names":["princemuel/invoicetracker"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/princemuel%2Finvoicemanager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/princemuel%2Finvoicemanager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/princemuel%2Finvoicemanager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/princemuel%2Finvoicemanager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/princemuel","download_url":"https://codeload.github.com/princemuel/invoicemanager/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248502154,"owners_count":21114751,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clerkauth","ecommerce","headlessui","invoice-generator","react-hook-form","reactjs","remix-run","tailwindcss","typescript"],"created_at":"2024-11-25T11:32:56.755Z","updated_at":"2025-04-12T01:22:53.776Z","avatar_url":"https://github.com/princemuel.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Welcome to Invoice Manager\n\nInvoice Manager is an\n\n## Table of contents\n\n- [Welcome to Invoice Manager](#welcome-to-invoice-manager)\n  - [Table of contents](#table-of-contents)\n  - [Overview](#overview)\n    - [Screenshot](#screenshot)\n    - [Links](#links)\n    - [Built with](#built-with)\n    - [What I learned](#what-i-learned)\n    - [Continued development](#continued-development)\n    - [Useful resources](#useful-resources)\n  - [Contribution](#contribution)\n    - [Prerequisites](#prerequisites)\n    - [Installation](#installation)\n    - [Deployment](#deployment)\n    - [Do It Yourself](#do-it-yourself)\n  - [Author](#author)\n  - [Acknowledgments](#acknowledgments)\n\n## Overview\n\n### Screenshot\n\n![Invoice Manager](./preview.jpg)\n\n### Links\n\n- Repository: [https://github.com/princemuel/invoicemanager](https://github.com/princemuel/invoicemanager)\n- Live Site: [https://invoicemanager.vercel.app](https://invoicemanager.vercel.app/)\n\n### Built with\n\n- Semantic HTML5 markup\n- CSS custom properties\n- Flexbox\n- CSS Grid\n- Mobile-first workflow\n- [React](https://react.dev/) - The JS library for web and native user interfaces\n- [Remix](https://remix.run/docs) - The full stack React web framework that uses web standards\n- [Tailwind CSS](https://tailwindcss.com/) - A utility-first CSS framework packed with classes that can be composed to build any design, directly in your markup\n\n### What I learned\n\n1. 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.\n\n```tsx\n// The Real Time Input Sync Code: It renders the total in an \u003coutput\u003e\u003c/output\u003e\nuseEffect(() =\u003e {\n  const subscription = watch((_, { name, type }) =\u003e {\n    const value = getValues();\n\n    if (type === \"change\" \u0026\u0026 name) {\n      if (name.endsWith(\"quantity\") || name.endsWith(\"price\")) {\n        type FieldValueType = FieldPathValue\u003ctypeof value, typeof name\u003e;\n\n        const { items } = value;\n        const [, indexString, fieldName] = name.split(\".\");\n        const index = parseInt(indexString);\n\n        const fieldValue: FieldValueType = get(value, name);\n\n        if (fieldValue) {\n          if (fieldName === \"quantity\")\n            setValue(\n              `items.${index}.total`,\n              approximate(calculateTotal(fieldValue, items[index].price)),\n            );\n          else if (fieldName === \"price\")\n            setValue(\n              `items.${index}.total`,\n              approximate(calculateTotal(items[index].quantity, fieldValue)),\n            );\n        }\n      }\n    }\n  });\n\n  return () =\u003e {\n    subscription.unsubscribe();\n  };\n}, [getValues, setValue, watch]);\n```\n\n2. I wrote a function calculateTotal that calculates the total value based on its input\n\nUse Case:\n\n- Total of 2 numbers\n- Total an array of items based on the total of each item\n- Total an array of items based on the price and quantity in each item object\n\n```tsx\nexport function calculateTotal\u003cT extends FirstArg\u003e(\n  a?: T,\n  b?: T extends number ? NonNullable\u003cT\u003e\n  : T extends (infer _)[] ? \"total\"\n  : never,\n) {\n  // Safely parses a value to a number and guards against NaN and negative zero.\n  function const numberGuard = (value: any, defaultValue: number = 0): number =\u003e {\n    const parsed = Number(value);\n    return Number.isNaN(parsed) || Object.is(parsed, -0) ? defaultValue : parsed;\n  };\n\n  if (Array.isArray(a)) {\n    return a.reduce((acc, item) =\u003e {\n      const { total = 0, quantity = 0, price = 0 } = item;\n      return b === \"total\" ? acc + total : acc + quantity * price;\n    }, 0);\n  }\n\n  // bailout since the function expects 2 number params, or an array params\n  return numberGuard(a) * numberGuard(b);\n}\n\ncalculateTotal(10, 5);  // 50\ncalculateTotal([{ total: 100 }, { total: 200 }], \"total\"); // 300\ncalculateTotal([{ quantity: 3, price: 20 }, { quantity: 2, price: 15 }]); // 90\ncalculateTotal([{ total: 100 }, { quantity: 2, price: 30 }, { total: 50 }],'total'); // 210\n\n```\n\n### Continued development\n\nUse 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.\n\n### Useful resources\n\n- [Example](https://www.example.com) - This helped me for XYZ reason. I really liked this pattern and will use it going forward.\n- [Example](https://www.example.com) - This is an amazing article which helped me finally understand XYZ. I'd recommend it to anyone still learning this concept.\n\n## Contribution\n\nTo contribute to this project please check out the contribution guidelines.\n\n### Prerequisites\n\nBefore cloning/forking this project, make sure you have the following tools installed:\n\n- [Git](https://git-scm.com/downloads)\n- [Node.JS](https://nodejs.org/en/download/)\n\n### Installation\n\nFrom your terminal:\n\nClone the project\n\n```sh\n# Choose one method out of these 3 c\ngit clone git@github.com:princemuel/invoicemanager.git # clone with git history\n\ngit clone git@github.com:your_username/invoicemanager.git # if you forked the project\n\nnpx degit git@github.com:princemuel/invoicemanager # clone without git history\n```\n\nNavigate to the project directory\n\n```sh\ncd invoicemanager\n```\n\nInstall the Dependencies\n\n```sh\n# This project uses pnpm but you can use your favorite package manager.\n# Just make sure to remove the lockfile before installation is not using pnpm\npnpm install\n```\n\nThis starts your app in development mode, rebuilding assets on file changes.\n\n```sh\npnpm run dev # replace package manager name with your chosen package manager\n```\n\n### Deployment\n\nFirst, build your app for production:\n\n```sh\npnpm run build # replace package manager name with your chosen package manager\n```\n\nThen run the app in production mode:\n\n```sh\npnpm start # replace package manager name with your chosen package manager\n```\n\nNow you'll need to pick a host to deploy it to.\n\n### Do It Yourself\n\nIf you're familiar with deploying Node.JS applications, the built-in Remix app server is production-ready.\n\nMake sure to deploy the output of `remix build`\n\n- `build/`\n- `public/build/`\n\n## Author\n\n- Twitter - [@iamprincemuel](https://www.x.com/princemuel)\n- LinkedIn - [@princemuel](https://www.linkedin.com/in/princemuel)\n\n## Acknowledgments\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprincemuel%2Finvoicemanager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprincemuel%2Finvoicemanager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprincemuel%2Finvoicemanager/lists"}