Skip to content

tediko/invoice-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

383 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Invoice app

Design preview for the Invoice app coding challenge

Table of contents

Overview

Intro

Hello! This is an invoicing application build with React JS and styled-components. The application is used to manage invoices and allows the user to create, read, update, filter by status and delete invoices. There is an option in the app to switch between a dark and a light theme. All transitions are smoothly displayed by using Framer Motion library to create animations.

Usage

  • Invoices can be created either as drafts or as pending. Clicking "Save as Draft" allow the user to leave any form field blank and set the status to "draft". Clicking "Save & Send" require all forms fields to be filled in, and set the status to "pending". The created invoice receives a unique ID. Each ID consists of the 2 random uppercased letters followed by 4 random numbers.
  • When saving changes to an invoice, all fields are required when the "Save Changes" button is clicked. If the user clicks "Cancel", any unsaved changes reset.
  • If the invoice being edited is a "draft" and the "Save Changes" button is clicked the status update to "pending". All fields are required at this stage.
  • Clicking the "Mark as Paid" button change the invoice's status to "paid". This option is displayed only when the invoice status is "pending".
  • Users receive a confirmation modal when trying to delete or change status of invoice because both of those actions are irreversible.
  • Clicking the sun/moon button toggle light and dark mode.
  • The Tab and Shift+Tab keys will cycle through the focus trap's tabbable elements but will not leave the focus trap when modal or form is open.
  • Invoices list can be filtered by status (draft/pending/paid) by using "Filter by status" button.
  • Keep track of any changes with localStorage.

Links

My process

The first time I used useReducer hook to manage application state. I noticed that my state logic getting more complex as the few elements of my state relies on the value of another element of my state. Together with useReducer, the useContext hook turned out to be handy. It allowed me to create common data that can be accessed throughout the component hierarchy without passing the props down manually to each level which in turn allowed me to avoid Prop drilling (also called "threading") that is the process you have to go through to get data to parts of the React Component tree.

In order to create a theme switcher and provide colors for components I used styled-components <ThemeProvider> wrapper component. By leveraging the theme provider I only need to write my styles in one single object and invoke them in any component that is a descendant of that provider.

When creating the form I learned what Controlled Component is. In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState(). Then the React component that renders a form also controls what happens in that form on subsequent user input.

To make application more ADA compliant (which means the website should be entirely accessible using just keyboard) I prevent focus go outside the modal once the modal is opened. In this case, the focus trap turns on when the form or modal with invoice deletion/status change is opened. I did this by using a simple function that is invoking on the keydown event. Function saves to the variable all focusable elements in modal container and check if the event.key is Tab. If so and it encounters the last focusable element, select the first element. The same logic corresponds to Shift + Tab but with a reverse action. Esc key closes the modal. In order to create an accessible modal I followed this great tutorial that follow the WAI-ARIA Practices.

It was by far the largest and most comprehensive project I have done so far. It showed me how important it is to plan so that you don't have to change things that previously worked well in the middle of the project. A valuable lesson!

Built with

  • ReactJS
  • Styled-components
  • Framer Motion API
  • react-datepicker
  • Webpack
  • Mobile first
  • Semantic HTML5 markup

Features

  • I used ReactJS library to create an app. React is a declarative, efficient, and flexible JavaScript library for building user interfaces. It lets you compose complex UIs from small and isolated pieces of code called “components”.
  • To style my app I used styled-components library. Styled Components are one of the new ways to use CSS in modern JavaScript. It is the meant to be a successor of CSS Modules, a way to write CSS that's scoped to a single component, and not leak to any other element in the page. Also allows you to write plain CSS in your components without worrying about class name collisions.
  • To animate the pages transitions and modals I used Framer Motion API. Framer Motion is an open source, production-ready library that's designed for creating creative animations. In order to support users who have enabled their device’s Reduced Motion setting and make accessible animations I used useReducedMotion hook. Based on whether useReducedMotion returns true or not we're passing different values to animate. That replace position transitions with opacity.
  • Added prefers-color-scheme CSS media feature which is used to detect if the user has requested a light or dark color theme and save it to Local Storage. I made it with window interface matchMedia() method. It returns a new MediaQueryList object that can then be used to determine if the document matches the media query string. In this case prefers-color-scheme.
  • Implemented focus trap inside modal to make it ADA compliant. Focus trap in short prevent our focus go outside the modal once the modal is opened.
  • Learned about the optional chaining operator (?.) that enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid. The ?. operator is like the . chaining operator, except that instead of causing an error if a reference is nullish (null or undefined).
  • Handle 404 routes in React Router and provide a fallback component for displaying an imfamous 404 Page Not Found error to the user. Try to enter a page that doesn't exist - like 'invoice-tediko.netlify.app/gotcha'
  • For Invoice Date picker I used React Date Picker package. This is a simple and reusable Datepicker component for React.
  • :focus-visible pseudo class. This selector only indicate focus when it is helpful to the user - such as in cases where the user interacts with the page via a keyboard or some other non-pointing device. It isn't supported by Safari yet, but there is simple workaround.
  • To create this project I used webpack. More specifically i used laravel mix which is a wrapper for webpack and targets the 80% usecase.

Setup

To run this project, clone it and install it locally using npm:

$ git clone [email protected]:tediko/invoice-app.git
$ cd invoice-app
$ npm install

Use npm to build and compile assets in a local environment:

$ npm run build

Watch assets for changes and rebuild your bundle each time you update a file with:

$ npm run mix-watch
or
$ npx mix watch

Useful resources

About

Invoicing application build with ReactJS and styled-components.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors