Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b554620
creating Layout component and install propTypes
ayubabdullah Mar 3, 2023
2f4d3cf
creating Header Component
ayubabdullah Mar 3, 2023
be2fd06
creating Navbar and NavItem components
ayubabdullah Mar 3, 2023
677e0f8
adding styles to header, layout, navbar
ayubabdullah Mar 3, 2023
1eae8cc
creating Topbar component
ayubabdullah Mar 3, 2023
bd9993f
creating Cards and Card component
ayubabdullah Mar 3, 2023
eca2f64
creating LatestTransactions and Transaction component
ayubabdullah Mar 3, 2023
6fa3cb5
creating transactions route and component
ayubabdullah Mar 4, 2023
217b963
creating SearchBar component
ayubabdullah Mar 4, 2023
e0cbd7c
creating Filter component
ayubabdullah Mar 4, 2023
7c55b8c
add padding to Filter component
ayubabdullah Mar 4, 2023
1b563a6
creating TransactionsList component
ayubabdullah Mar 4, 2023
42d2b0e
adding prisma
ayubabdullah Mar 4, 2023
1489412
adding dropdown
ayubabdullah Mar 4, 2023
8f24490
adding date picker
ayubabdullah Mar 4, 2023
f544a1e
adding pagination
ayubabdullah Mar 4, 2023
f01bfff
update previous icon on pagination
ayubabdullah Mar 4, 2023
474328d
creating AddTransactionModal component
ayubabdullah Mar 5, 2023
65bf0e2
finishing modal styles
ayubabdullah Mar 5, 2023
c3e253e
creating formatAmount, formatDate, getLatestTransactions helper funct…
ayubabdullah Mar 6, 2023
0042a0f
make some style changes
ayubabdullah Mar 6, 2023
7b42107
add functionality to AddTransactionModal and make some style changes
ayubabdullah Mar 6, 2023
174b1c0
adding pagination functionality
ayubabdullah Mar 6, 2023
0eb971c
adding pagination functionality
ayubabdullah Mar 6, 2023
45dc3de
adding search functionality and create useDebounce custom hook
ayubabdullah Mar 7, 2023
8bb7bf1
close AddTransaction Modal on success submit and order transactions
ayubabdullah Mar 7, 2023
2de21a6
adding functionality to cards amount
ayubabdullah Mar 7, 2023
8d9dcae
fix setStartDate on empty list of transactions
ayubabdullah Mar 7, 2023
6c4c725
refactor calculateBalance to return only this month
ayubabdullah Mar 7, 2023
a54588c
add $ before amount input, change the transactions order to chronolog…
ayubabdullah Mar 7, 2023
64060f5
adding validate helper functions and validate form data in server
ayubabdullah Mar 7, 2023
ffc8206
adding validation to add transaction form
ayubabdullah Mar 7, 2023
cfd2441
refactoring AddTransactionModal
ayubabdullah Mar 7, 2023
d72f346
fix pagination bug when searching and filtering transactions
ayubabdullah Mar 7, 2023
2feb2bd
some small refactors
ayubabdullah Mar 7, 2023
487b6ba
rendering routes dynamically
ayubabdullah Mar 7, 2023
e6a7481
add catchBoundary and errorBoundary to root
ayubabdullah Mar 8, 2023
4214641
refactor style imports
ayubabdullah Mar 8, 2023
602797e
refactor style files
ayubabdullah Mar 8, 2023
5406ae7
more style refactoring
ayubabdullah Mar 8, 2023
3b553f4
refactoring helper functions
ayubabdullah Mar 8, 2023
106a41c
add seeder
ayubabdullah Mar 8, 2023
bd1b3d1
adding meta tags
ayubabdullah Mar 8, 2023
6cb2ffe
final refactor
ayubabdullah Mar 8, 2023
56a6d81
update README
ayubabdullah Mar 8, 2023
78a347e
add library links to README
ayubabdullah Mar 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
/build
/public/build
.env
/prisma/dev.db
35 changes: 26 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

Personal Finance Manager is an application with limited financing functionality, used for tracking your income and expenses and managing your money efficiently.

## Steps To Run The Application

- Create .env file inside project directory
- Add DATABASE_URL="file:./dev.db" to .env
- In the terminal run npx prisma db push to created database file in prisma/dev.db
- In the terminal run yarn seed to add mock data to the database
- Finally run yarn dev to run the project

## Libraries And Packages That I Used

- [Prisma](https://www.prisma.io/) as ORM
- [PropTypes](https://www.npmjs.com/package/prop-types) for validating props
- [React DatePicker](https://reactdatepicker.com/) for handling range date input
- [React Icons](https://react-icons.github.io/react-icons/) for icons
- [React Paginate](https://www.npmjs.com/package/react-paginate) for pagination
- [React Select](https://react-select.com/home) for multiple selecting

## The Assignment

The assignment you're about to tackle is a simple application, you'll be building from the current template built with **Create Remix App**.
Expand All @@ -21,11 +38,11 @@ The application uses a simple data representation due to its limited functionali

Following best practices is especially important because it helps us to ensure that the code is clean, maintainable, and efficient. Some examples of best practices in coding include:

- Creating a clear and intuitive navigation structure.
- Using clear and descriptive names for variables, functions, and other elements of code.
- Using descriptive and meaningful variable and function names.
- Structuring code in a logical and organized way.
- while using version controll, keep you're commits simple, small, and clean.
- Creating a clear and intuitive navigation structure.
- Using clear and descriptive names for variables, functions, and other elements of code.
- Using descriptive and meaningful variable and function names.
- Structuring code in a logical and organized way.
- while using version control, keep you're commits simple, small, and clean.

## Design

Expand All @@ -39,17 +56,17 @@ to actions, but keep in mind, it's just a mock up, and real-world applications n

![image](https://user-images.githubusercontent.com/41629832/154848837-d53bf2fd-e721-4f78-8858-22d07cb36c43.png)


## Stories

### Sidebar & Topbar

- As a user, I want to see a sidebar in the left of the screen so I can easily navigate through the application.
Each link representing a page in the application, when I'm on a specific page, I want it's link to be `white`

When clicking on a link other than currently active link, user will be navigated to corresponding page.

Sidebar contains two navigation links:

1. Overview
2. Transaction History

Expand Down Expand Up @@ -83,7 +100,7 @@ to actions, but keep in mind, it's just a mock up, and real-world applications n
4. `type` input: multiple radio buttons, one can be selected at a time, determining the transactions type.
5. `note` input: I want to be able to write up to 350 characters as a note attached to the transaction.
- When `Dismiss` button clicked, the popup will be hidden and the state will be discarded.
- When `Add Transaction` button clicked:
- When `Add Transaction` button clicked:
- If one or more fields do not have acceptable values, each input will have an error message below it, showing a proper error message.
- If the form is valid, then a transaction will be saved to the transactions list.

Expand All @@ -100,4 +117,4 @@ to actions, but keep in mind, it's just a mock up, and real-world applications n
- As a user, I want to be able to see a Date input to select a `to` date to filter transactions until that date.
- in `to` date input, future dates must be disabled.
- When clicking on `Clear` button on the filter bar, all filters except search bar will be resetted to their initial values.
- As a user, I want to see pagination on the screen so I can change pages of which transactions is shown.
- As a user, I want to see pagination on the screen so I can change pages of which transactions is shown.
177 changes: 177 additions & 0 deletions app/components/AddTransactionModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { Form, useActionData, useTransition } from "@remix-run/react"
import propTypes from "prop-types"
import { useState } from "react"
import { BiDollar } from "react-icons/bi"
import { GrClose } from "react-icons/gr"
import { options } from "~/utils/categories"
import {
validateAmount,
validateCategory,
validateDate,
validateNote,
validateType,
} from "~/utils/validations"
import Error from "./Error"

// get the options from the utils folder
const categoryOptions = options

export default function AddTransactionModal({ onModalOpenClick }) {
const [type, setType] = useState("income")
const [note, setNote] = useState("")

const transition = useTransition()
const actionData = useActionData()

// disable the submit button while submitting
const submitButton =
transition.state === "submitting" ? (
<button className="button primary" type="submit" disabled>
Adding
</button>
) : (
<button className="button primary" type="submit">
Add Transaction
</button>
)

const handleFormSubmit = (e) => {
const category = validateCategory(e.target.category.value)
const date = validateDate(e.target.date.value)
const amount = validateAmount(e.target.amount.value)
const type = validateType(e.target.type.value)
const note = validateNote(e.target.note.value)
if (!category && !date && !amount && !type && !note) {
onModalOpenClick()
}
}
return (
<div className="modal">
<div className="modal-content">
<div className="modal-header">
<h5 className="title">Add Transaction</h5>
<button type="reset" onClick={onModalOpenClick} className="close">
<GrClose className="icon" />
</button>
</div>
<div className="modal-body">
<Form
method="POST"
className="modal-form"
onSubmit={handleFormSubmit}
>
<div className="form-group">
<label>Category</label>
<select name="category" id="category" className="category">
{categoryOptions[type].map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
{actionData?.fieldErrors?.category ? (
<Error error={actionData?.fieldErrors?.category} />
) : null}
</div>
<div className="form-group">
<label>Date</label>
<input
type="date"
name="date"
id="date"
defaultValue={new Date().toISOString().split("T")[0]}
className="date"
/>
{actionData?.fieldErrors?.date ? (
<Error error={actionData?.fieldErrors?.date} />
) : null}
</div>
<div className="form-group">
<label>Amount</label>

<div className="amount">
<BiDollar className="amount-icon" />
<input
type="number"
name="amount"
id="amount"
className="amount-input"
min={0}
/>
</div>
{actionData?.fieldErrors?.amount ? (
<Error error={actionData?.fieldErrors?.amount} />
) : null}
</div>
<div className="form-group">
<label>Type</label>
<div className="radio-container">
<div>
<input
type="radio"
name="type"
id="income"
value="income"
className="radio-input"
onChange={(e) => setType(e.target.value)}
checked={type === "income"}
/>
<label htmlFor="income" className="radio-label">
Income
</label>
</div>
<div>
<input
type="radio"
name="type"
id="expense"
value="expense"
className="radio-input"
onChange={(e) => setType(e.target.value)}
checked={type === "expense"}
/>
<label htmlFor="expense" className="radio-label">
Expense
</label>
</div>
</div>
{actionData?.fieldErrors?.type ? (
<Error error={actionData?.fieldErrors?.type} />
) : null}
</div>
<div className="form-group textarea">
<label>Note</label>
<textarea
name="note"
id="note"
cols="30"
rows="10"
maxLength={350}
value={note}
onChange={(e) => setNote(e.target.value)}
></textarea>
<p>{`${note.length} / 350`}</p>
{actionData?.fieldErrors?.note ? (
<Error error={actionData?.fieldErrors?.note} />
) : null}
</div>
<div className="form-button-group">
<button
onClick={onModalOpenClick}
type="reset"
className="button cancel"
>
Dismiss
</button>
{submitButton}
</div>
</Form>
</div>
</div>
</div>
)
}

AddTransactionModal.propTypes = {
onModalOpenClick: propTypes.func.isRequired,
}
18 changes: 18 additions & 0 deletions app/components/Card.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import propTypes from "prop-types"
import { formatAmount } from "~/utils/formatAmount"

export default function Card({ title, amount, color }) {
return (
<div className={`card ${color}`}>
<h3 className={`title ${color}`}>{title}</h3>
<button className={`details-button ${color}`}>details</button>
<p className="amount">${formatAmount(amount)}</p>
</div>
)
}

Card.propTypes = {
title: propTypes.string.isRequired,
amount: propTypes.number.isRequired,
color: propTypes.string.isRequired,
}
15 changes: 15 additions & 0 deletions app/components/Cards.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useLoaderData } from "@remix-run/react"
import { balance, expense, income } from "~/utils/calculateBalance"
import Card from "./Card"

export default function Cards() {
const { transactions } = useLoaderData()

return (
<section className="cards">
<Card title="Income" amount={income(transactions)} color="blue" />
<Card title="Balance" amount={balance(transactions)} color="gray" />
<Card title="Expense" amount={expense(transactions)} color="red" />
</section>
)
}
29 changes: 29 additions & 0 deletions app/components/Document.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
Links,
LiveReload,
Meta,
Scripts,
ScrollRestoration,
} from "@remix-run/react"

import propTypes from "prop-types"
export default function Document({ children }) {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
)
}

Document.propTypes = {
children: propTypes.node.isRequired,
}
8 changes: 8 additions & 0 deletions app/components/Error.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import propTypes from "prop-types"
export default function Error({ error }) {
return <p className="form-validation-error">{error}</p>
}

Error.propTypes = {
error: propTypes.string.isRequired,
}
Loading