diff --git a/.gitignore b/.gitignore
index 3f7bf98..d09d672 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ node_modules
/build
/public/build
.env
+/prisma/dev.db
diff --git a/README.md b/README.md
index ac67d06..2ba8ac8 100644
--- a/README.md
+++ b/README.md
@@ -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**.
@@ -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
@@ -39,17 +56,17 @@ to actions, but keep in mind, it's just a mock up, and real-world applications n

-
## 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
@@ -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.
@@ -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.
diff --git a/app/components/AddTransactionModal.jsx b/app/components/AddTransactionModal.jsx
new file mode 100644
index 0000000..d758524
--- /dev/null
+++ b/app/components/AddTransactionModal.jsx
@@ -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" ? (
+
+ Adding
+
+ ) : (
+
+ Add Transaction
+
+ )
+
+ 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 (
+
-
Finance Manager
-
Let's get this done!
+
+ {isModalOpen ? (
+
+ ) : null}
+
+
+
- );
+ )
}
diff --git a/app/routes/transactions.jsx b/app/routes/transactions.jsx
new file mode 100644
index 0000000..fb53de0
--- /dev/null
+++ b/app/routes/transactions.jsx
@@ -0,0 +1,93 @@
+import { json } from "@remix-run/node"
+import { useLoaderData } from "@remix-run/react"
+import { useState } from "react"
+
+import Filter from "~/components/Filter"
+import SearchBar from "~/components/SearchBar"
+import Topbar from "~/components/Topbar"
+import TransactionsList from "~/components/TransactionsList"
+import useDebounce from "~/hooks/useDebounce"
+import { db } from "~/utils/db.server"
+import { filter } from "~/utils/filter"
+import { getDateObj } from "~/utils/formatDate"
+import { search } from "~/utils/search"
+
+export const loader = async () => {
+ const transactions = await db.transactions.findMany({
+ orderBy: { date: "asc" },
+ })
+ return json({ transactions })
+}
+
+export const meta = () => ({
+ title: "Finance Manager | Transactions History",
+})
+
+export default function Transactions() {
+ const { transactions } = useLoaderData()
+
+ const [selectedCategories, setSelectedCategories] = useState([])
+ const [startDate, setStartDate] = useState(
+ getDateObj(transactions[0]?.date || new Date())
+ )
+ const [endDate, setEndDate] = useState(new Date())
+
+ const [searchTerm, setSearchTerm] = useState("")
+
+ const debouncedSearchTerm = useDebounce(searchTerm, 500)
+
+ const searchedTransactions = search(transactions, debouncedSearchTerm)
+
+ const filteredTransactions = filter(
+ searchedTransactions,
+ selectedCategories,
+ startDate,
+ endDate
+ )
+
+ const handleCategoryChange = (selected) => {
+ setSelectedCategories(selected)
+ }
+
+ const handleStartDateChange = (date) => {
+ setStartDate(date)
+ }
+
+ const handleEndDateChange = (date) => {
+ setEndDate(date)
+ }
+
+ const handleClearClick = () => {
+ setSelectedCategories([])
+ setStartDate(getDateObj(transactions[0]?.date || new Date()))
+ setEndDate(new Date())
+ }
+
+ const handleSearchChange = (e) => {
+ setSearchTerm(e.target.value)
+ }
+
+ const handleSearchClearClick = () => {
+ setSearchTerm("")
+ }
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/app/styles/root.css b/app/styles/root.css
index 6692dad..278ec06 100644
--- a/app/styles/root.css
+++ b/app/styles/root.css
@@ -1,16 +1,542 @@
* {
box-sizing: border-box;
- font-family: inherit;
+ font-family: inter;
margin: 0;
padding: 0;
+ font-size: 10px;
}
-.index-page {
- font-family: system-ui, sans-serif;
+body {
+ background-color: #e8e8e8;
+}
+
+a {
+ text-decoration: none;
+}
+
+.error-container {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ row-gap: 10px;
+ padding: 3rem;
+}
+
+.catch-boundary .status {
+ font-size: 5rem;
+}
+.catch-boundary .status-text {
+ font-size: 3rem;
+}
+.catch-boundary .message {
+ font-size: 2rem;
+}
+
+.error-boundary .title {
+ font-size: 5rem;
+}
+.error-boundary .message {
+ font-size: 2rem;
+}
+
+.layout {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+}
+.layout .header {
+ grid-column: 1/2;
+}
+.layout .children {
+ grid-column: 2/6;
+}
+
+.header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ background: linear-gradient(180deg, #30475e 0%, #222831 100%);
+ height: 100%;
+ width: 20%;
+}
+.header .logo {
+ display: flex;
+ align-items: center;
+ font-size: 2.2rem;
+ font-weight: 600;
+ color: #f05454;
+ margin: 15% 0;
+}
+.header .logo .icon {
+ width: 3rem;
+ height: 3rem;
+ margin-right: 0.5rem;
+}
+
+.navbar {
+ margin-top: 40%;
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ row-gap: 3rem;
+}
+
+.nav-item {
+ color: #d4d4d8;
+ font-size: 2.2rem;
+ font-weight: 400;
+}
+
+.active {
+ color: #fff;
+}
+
+.topbar {
+ padding: 1.8rem;
+ background-color: #f7f4f3;
+ box-shadow: 0px 1px 8px rgba(34, 40, 49, 0.05);
+ height: 10%;
+}
+.topbar .title {
+ font-size: 2.2rem;
+ font-weight: 500;
+}
+
+.transaction {
+ display: flex;
+ justify-content: space-between;
+ padding: 0.8rem 2rem;
+ background-color: rgb(248, 250, 252);
+ border-radius: 7px;
+ margin-bottom: 1rem;
+ color: rgb(51, 65, 85);
+}
+.transaction .wrapper {
+ display: flex;
+ align-items: center;
+ column-gap: 1.8rem;
+}
+.transaction .icon {
+ width: 3.2rem;
+ height: 3.2rem;
+ padding: 0.5rem;
+ border-radius: 50%;
+}
+.transaction .icon.green {
+ background-color: rgba(5, 150, 105, 0.2);
+ color: rgb(5, 150, 105);
+}
+.transaction .icon.blue {
+ background-color: rgba(2, 132, 199, 0.2);
+ color: rgb(2, 132, 199);
+}
+.transaction .note {
+ width: 40rem;
+ font-size: 1.6rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.transaction .date {
+ font-size: 1.6rem;
+}
+.transaction .amount {
+ padding: 0.6rem 1.4rem;
+ border-radius: 6px;
+ font-size: 1.5rem;
+ font-weight: 600;
text-align: center;
+}
+.transaction .amount.green {
+ background-color: rgba(14, 165, 233, 0.15);
+ color: rgb(14, 165, 233);
+}
+.transaction .amount.red {
+ color: rgb(239, 42, 76);
+ background-color: rgba(239, 42, 76, 0.15);
+}
+
+.cards {
+ display: flex;
+ column-gap: 3rem;
+ width: 80%;
+ height: 25%;
+ padding: 2.5rem;
+}
+
+.card {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.5rem;
+ width: 32%;
+ height: 11rem;
+ border-radius: 7px;
+ color: #fff;
+ padding: 2rem;
+}
+.card.blue {
+ background: linear-gradient(90deg, #7dd3fc 0%, #bae6fd 102.82%);
+}
+.card.gray {
+ background: linear-gradient(90deg, #d4d4d8 0%, #e4e4e7 102.82%);
+}
+.card.red {
+ background: linear-gradient(90deg, #fda4af 0%, #fecdd3 102.82%);
+}
+.card .title {
+ font-size: 1.8rem;
+ font-weight: 600;
+}
+.card .title.blue {
+ color: #1a74c7;
+}
+.card .title.gray {
+ color: #71717a;
+}
+.card .title.red {
+ color: #ef2a4c;
+}
+.card .details-button {
+ border-radius: 12px;
+ font-size: 1.2rem;
+ border: none;
+ color: #fff;
+ justify-self: end;
+ padding: 0.2em 1rem;
+}
+.card .details-button.blue {
+ background-color: rgba(56, 189, 248, 0.75);
+}
+.card .details-button.gray {
+ background-color: rgba(113, 113, 122, 0.75);
+}
+.card .details-button.red {
+ background-color: rgba(251, 113, 133, 0.75);
+}
+.card .amount {
+ font-size: 3rem;
+ font-weight: 600;
+}
+
+.latest-transactions {
+ display: flex;
+ flex-direction: column;
+ row-gap: 1rem;
+ width: 80%;
+ padding: 0 2.5rem;
+}
+.latest-transactions .wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.latest-transactions .title {
+ font-size: 2.2rem;
+}
+.latest-transactions .add-transaction {
+ align-self: flex-end;
+ padding: 0.8rem 1.6rem;
+ border-radius: 50px;
+ background-color: rgba(28, 101, 140, 0.15);
+ font-size: 1.6rem;
+ color: rgb(28, 101, 140);
+ border: none;
+}
+
+.modal {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 100;
+}
+.modal .modal-content {
+ position: absolute;
+ top: 45%;
+ left: 50%;
+ transform: translate(-50%, -45%);
+ background-color: rgb(248, 250, 252);
+ padding: 1.8rem 2.5rem;
+ border-radius: 1.5rem;
+ width: 60%;
+ height: 65%;
+}
+.modal .modal-content .modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.modal .modal-content .modal-header .title {
+ font-size: 1.6rem;
+ font-weight: 400;
+ color: rgb(0, 0, 0);
+}
+.modal .modal-content .modal-header .close {
+ cursor: pointer;
+ border: none;
+ background-color: transparent;
+}
+.modal .modal-content .modal-header .close .icon {
+ width: 1.6rem;
+ height: 1.6rem;
+ color: rgb(17, 24, 39);
+}
+.modal .modal-content .modal-body {
+ margin-top: 2rem;
+ height: 90%;
+}
+.modal .modal-content .modal-body .modal-form {
+ height: 100%;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ column-gap: 3rem;
+ row-gap: 0.5rem;
+}
+.modal .modal-content .modal-body .modal-form .form-group {
+ display: flex;
+ flex-direction: column;
+ row-gap: 1rem;
+}
+.modal .modal-content .modal-body .modal-form .form-group input,
+.modal .modal-content .modal-body .modal-form .form-group textarea {
+ height: 100%;
+ padding: 1rem;
+ resize: none;
+}
+.modal .modal-content .modal-body .modal-form .form-group.textarea {
+ grid-column: span 2;
+}
+.modal .modal-content .modal-body .modal-form .form-group .date,
+.modal .modal-content .modal-body .modal-form .form-group .amount,
+.modal .modal-content .modal-body .modal-form .form-group .category {
+ height: 3.8rem;
+ width: 100%;
+}
+.modal .modal-content .modal-body .modal-form .form-group .amount {
+ display: flex;
+ align-items: center;
+ border-radius: 7px;
+ border: 1px solid rgb(148, 163, 184);
+}
+.modal .modal-content .modal-body .modal-form .form-group .amount .amount-icon {
+ width: 2rem;
+ height: 2rem;
+ color: rgb(148, 163, 184);
+}
+.modal .modal-content .modal-body .modal-form .form-group .amount .amount-input {
+ width: 100%;
+ border: none;
+ outline: none;
+}
+.modal .modal-content .modal-body .modal-form .form-group .radio-container {
+ display: flex;
+ align-items: center;
+ column-gap: 1rem;
+ margin-top: 2rem;
+}
+.modal .modal-content .modal-body .modal-form .form-group .radio-input {
+ display: none;
+}
+.modal .modal-content .modal-body .modal-form .form-group .radio-input:checked + .radio-label {
+ border: 1px solid rgb(148, 163, 184);
+}
+.modal .modal-content .modal-body .modal-form .form-group .radio-label {
+ padding: 0.8rem 1.5rem;
+ border: 1px solid rgba(148, 163, 184, 0.3);
+ border-radius: 0.7rem;
+ cursor: pointer;
+}
+.modal .modal-content .modal-body .modal-form .form-button-group {
+ grid-column: span 3;
+ justify-self: end;
+ align-self: flex-end;
+ display: flex;
+ align-items: center;
+ column-gap: 0.5rem;
+}
+.modal .modal-content .modal-body .modal-form .form-button-group .button {
+ padding: 0.5rem 1rem;
+ border-radius: 5rem;
+ font-size: 1.2rem;
+ font-weight: 500;
+ border: none;
+}
+.modal .modal-content .modal-body .modal-form .form-button-group .button.primary {
+ background-color: rgba(28, 101, 140, 0.15);
+ color: rgb(28, 101, 140);
+}
+.modal .modal-content .modal-body .modal-form .form-button-group .button.cancel {
+ background-color: rgb(248, 250, 252);
+ color: rgb(0, 0, 0);
+ border: 1px solid rgb(0, 0, 0);
+}
+
+.form-validation-error {
+ color: red;
+ font-size: 1rem;
+}
+
+.overview-page {
+ min-height: 100vh;
+}
+
+.search-bar {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ column-gap: 1rem;
+ width: 95%;
+ margin: 2rem auto;
+ height: 4.8rem;
+ border-radius: 0.7rem;
+ padding-left: 0.8rem;
+ background-color: rgb(255, 255, 255);
+}
+.search-bar .wrapper {
+ grid-column: 1/6;
+ display: flex;
+ align-items: center;
+}
+.search-bar .icon {
+ width: 2rem;
+ height: 2rem;
+ color: rgb(126, 133, 148);
+}
+.search-bar .input {
+ width: 100%;
+ height: 100%;
+ padding: 0 0.8rem;
+ border: none;
+ outline: none;
+ background-color: transparent;
+ font-size: 1.4rem;
+ line-height: 1.7rem;
+ color: rgb(126, 133, 148);
+}
+.search-bar .clear-button {
+ width: 9.5rem;
+ grid-column: 6/7;
+ justify-self: end;
+ background-color: rgba(28, 101, 140, 0.75);
+ border: none;
+ border-radius: 0 0.7rem 0.7rem 0;
+ padding: 0.8rem 2rem;
+ font-size: 2rem;
+ line-height: 2.5rem;
+ color: rgb(255, 255, 255);
+}
+
+.filter {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 0 auto;
+ padding-left: 2.2rem;
+ width: 95%;
+ height: 6.8rem;
+ background-color: rgb(248, 250, 252);
+ border-radius: 0.7rem;
+}
+.filter .wrapper {
+ display: flex;
+ align-items: center;
+ column-gap: 1rem;
+}
+.filter .icon {
+ width: 1.8rem;
+ height: 1.8rem;
+ color: rgb(126, 133, 148);
+ margin-right: 2.6rem;
+}
+.filter .dropdown {
+ width: 30rem;
+}
+.filter .date {
+ width: 24rem;
+ height: 4rem;
+ border-radius: 0.7rem;
+ background-color: rgb(255, 255, 255);
+ border: none;
+ outline: none;
+ padding: 0.6rem 0.8rem;
+ box-shadow: inset 0px 0px 2px rgba(0, 0, 0, 0.25);
+ font-size: 1.6rem;
+ line-height: 1.9rem;
+ color: rgb(17, 24, 39);
+}
+.filter .clear-button {
+ width: 9.5rem;
+ height: 100%;
+ background-color: rgba(28, 101, 140, 0.75);
+ border: none;
+ border-radius: 0 0.7rem 0.7rem 0;
+ padding: 0.8rem 2rem;
+ font-size: 2rem;
+ line-height: 2.5rem;
+ color: rgb(255, 255, 255);
+}
+
+.transactions-list {
+ display: flex;
+ flex-direction: column;
+ width: 80%;
+ padding: 2.5rem;
+}
+.transactions-list .pagination {
+ align-self: flex-end;
+ display: flex;
+ list-style: none;
+ column-gap: 0.5rem;
+}
+.transactions-list .pagination .page {
+ width: 3rem;
+ height: 3rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: rgb(255, 255, 255);
+ border-radius: 1.6rem;
+ cursor: pointer;
+}
+.transactions-list .pagination .page a {
+ font-size: 1.6rem;
+ line-height: 1.9rem;
+ color: rgb(28, 101, 140);
+}
+.transactions-list .pagination .previous,
+.transactions-list .pagination .next {
+ width: 3rem;
+ height: 3rem;
+ display: flex;
justify-content: center;
+ align-items: center;
+ cursor: pointer;
+}
+.transactions-list .pagination .previous svg,
+.transactions-list .pagination .next svg {
+ width: 2rem;
+ height: 2rem;
+ color: rgb(28, 101, 140);
+}
+.transactions-list .pagination .active {
+ background-color: rgba(28, 101, 140, 0.75);
+}
+.transactions-list .pagination .active a {
+ color: rgb(255, 255, 255);
+}
+.transactions-list .pagination .disabled {
+ cursor: not-allowed;
+}
+.transactions-list .pagination .disabled svg {
+ color: rgb(149, 159, 180);
+}
+
+#transactions-page {
+ min-height: 100vh;
}
diff --git a/app/utils/calculateBalance.js b/app/utils/calculateBalance.js
new file mode 100644
index 0000000..8daf75c
--- /dev/null
+++ b/app/utils/calculateBalance.js
@@ -0,0 +1,19 @@
+import { getDateObj } from "./formatDate"
+
+const today = new Date()
+const thisMonth = new Date(today.getFullYear(), today.getMonth(), 1)
+
+export const income = (transactions) =>
+ transactions
+ .filter((transaction) => transaction.type === "income")
+ .filter((transaction) => getDateObj(transaction.date) >= thisMonth)
+ .reduce((sum, transaction) => sum + parseInt(transaction.amount, 10), 0)
+
+export const expense = (transactions) =>
+ transactions
+ .filter((transaction) => transaction.type === "expense")
+ .filter((transaction) => getDateObj(transaction.date) >= thisMonth)
+ .reduce((sum, transaction) => sum + parseInt(transaction.amount, 10), 0)
+
+export const balance = (transactions) =>
+ income(transactions) - expense(transactions)
diff --git a/app/utils/categories.js b/app/utils/categories.js
new file mode 100644
index 0000000..7c6c6e3
--- /dev/null
+++ b/app/utils/categories.js
@@ -0,0 +1,42 @@
+export const options = {
+ income: [
+ {
+ value: "salary",
+ label: "Salary",
+ },
+ {
+ value: "loan",
+ label: "Loan",
+ },
+ {
+ value: "gift",
+ label: "Gift",
+ },
+ ],
+ expense: [
+ {
+ value: "tech",
+ label: "Tech",
+ },
+ {
+ value: "food",
+ label: "Food",
+ },
+ {
+ value: "bills",
+ label: "Bills",
+ },
+ {
+ value: "sports",
+ label: "Sports",
+ },
+ {
+ value: "health",
+ label: "Health",
+ },
+ {
+ value: "clothes",
+ label: "Clothes",
+ },
+ ],
+}
diff --git a/app/utils/db.server.ts b/app/utils/db.server.ts
new file mode 100644
index 0000000..1f861a4
--- /dev/null
+++ b/app/utils/db.server.ts
@@ -0,0 +1,21 @@
+import { PrismaClient } from "@prisma/client";
+
+let db: PrismaClient;
+
+declare global {
+ var __db: PrismaClient | undefined;
+}
+
+// this is needed because in development we don't want to restart
+// the server with every change, but we want to make sure we don't
+// create a new connection to the DB with every change either.
+if (process.env.NODE_ENV === "production") {
+ db = new PrismaClient();
+} else {
+ if (!global.__db) {
+ global.__db = new PrismaClient();
+ }
+ db = global.__db;
+}
+
+export { db };
\ No newline at end of file
diff --git a/app/utils/filter.js b/app/utils/filter.js
new file mode 100644
index 0000000..7c246bd
--- /dev/null
+++ b/app/utils/filter.js
@@ -0,0 +1,14 @@
+import { getDateObj } from "./formatDate"
+
+export const filter = (list, selectedCategories, startDate, endDate) =>
+ list.filter((transaction) => {
+ const isCategorySelected = selectedCategories.length
+ ? selectedCategories.some(
+ (category) => category.value === transaction.category
+ )
+ : true
+ const isDateInRange =
+ getDateObj(transaction.date) >= startDate &&
+ getDateObj(transaction.date) <= endDate
+ return isCategorySelected && isDateInRange
+ })
diff --git a/app/utils/formatAmount.js b/app/utils/formatAmount.js
new file mode 100644
index 0000000..f8f35af
--- /dev/null
+++ b/app/utils/formatAmount.js
@@ -0,0 +1,15 @@
+export const formatAmount = (amount) => {
+ const parsedAmount = parseInt(amount, 10)
+ const suffixes = [
+ { value: 1000000000, suffix: "B" },
+ { value: 1000000, suffix: "M" },
+ { value: 1000, suffix: "K" },
+ ]
+ for (const suffix of suffixes) {
+ if (parsedAmount >= suffix.value) {
+ return `${(parsedAmount / suffix.value).toFixed(1)}${suffix.suffix}`
+ }
+ }
+
+ return parsedAmount
+}
diff --git a/app/utils/formatDate.js b/app/utils/formatDate.js
new file mode 100644
index 0000000..448922d
--- /dev/null
+++ b/app/utils/formatDate.js
@@ -0,0 +1,24 @@
+export const formatDate = (date) => {
+ const today = new Date()
+ const yesterday = new Date(
+ today.getFullYear(),
+ today.getMonth(),
+ today.getDate() - 1
+ )
+
+ // I add plus 1 to getMonth because it starts from 0
+ const dateObj = new Date(date)
+ if (dateObj.toDateString() === today.toDateString()) {
+ return "Today"
+ } else if (dateObj.toDateString() === yesterday.toDateString()) {
+ return "Yesterday"
+ } else {
+ return `${dateObj.getDate()}/${
+ dateObj.getMonth() + 1
+ }/${dateObj.getFullYear()}`
+ }
+}
+
+export const getDateObj = (date) => {
+ return new Date(date)
+}
diff --git a/app/utils/getLatestTransactions.js b/app/utils/getLatestTransactions.js
new file mode 100644
index 0000000..8e06fdf
--- /dev/null
+++ b/app/utils/getLatestTransactions.js
@@ -0,0 +1,57 @@
+import { getDateObj } from "./formatDate"
+
+export let title = ""
+export const getLatestTransactions = (transactions) => {
+ // Get current date
+ const currentDate = new Date()
+
+ // Get dates for last seven days, last month, and this year
+ const lastWeek = new Date(
+ currentDate.getFullYear(),
+ currentDate.getMonth(),
+ currentDate.getDate() - 7
+ )
+ const lastMonth = new Date(
+ currentDate.getFullYear(),
+ currentDate.getMonth() - 1,
+ currentDate.getDate()
+ )
+ const thisYear = new Date(currentDate.getFullYear(), 0, 1)
+
+ // Initialize an array to hold all transactions
+ let allTransactions = []
+
+ //Check for transactions this week
+ if (
+ transactions.some((transaction) => getDateObj(transaction.date) >= lastWeek)
+ ) {
+ allTransactions = transactions.filter(
+ (transaction) => getDateObj(transaction.date) >= lastWeek
+ )
+ title = "This Week"
+ }
+ // Check for transactions last month
+ else if (
+ transactions.some(
+ (transaction) => getDateObj(transaction.date) >= lastMonth
+ )
+ ) {
+ allTransactions = transactions.filter(
+ (transaction) => getDateObj(transaction.date) >= lastMonth
+ )
+ title = "Last Month"
+ }
+
+ // Check for transactions this year
+ else if (
+ transactions.some((transaction) => getDateObj(transaction.date) >= thisYear)
+ ) {
+ allTransactions = transactions.filter(
+ (transaction) => getDateObj(transaction.date) >= thisYear
+ )
+ title = "This Year"
+ }
+
+ // Return the last 10 transactions
+ return allTransactions.slice(Math.max(allTransactions.length - 10, 0))
+}
diff --git a/app/utils/pagination.js b/app/utils/pagination.js
new file mode 100644
index 0000000..a480b76
--- /dev/null
+++ b/app/utils/pagination.js
@@ -0,0 +1,13 @@
+export const paginate = (list, pageSize, currentPage) => {
+ const pageCount = Math.ceil(list.length / pageSize)
+
+ const startIndex = (currentPage - 1) * pageSize
+ const endIndex = startIndex + pageSize
+
+ const paginatedList = list.slice(startIndex, endIndex)
+
+ return {
+ list: paginatedList,
+ pageCount,
+ }
+}
diff --git a/app/utils/route.js b/app/utils/route.js
new file mode 100644
index 0000000..4a2d939
--- /dev/null
+++ b/app/utils/route.js
@@ -0,0 +1,4 @@
+export const routes = [
+ { path: "/", title: "Overview" },
+ { path: "/transactions", title: "Transaction History" },
+]
diff --git a/app/utils/search.js b/app/utils/search.js
new file mode 100644
index 0000000..1c42c4e
--- /dev/null
+++ b/app/utils/search.js
@@ -0,0 +1,8 @@
+export const search = (list, term) =>
+ list.filter((item) => {
+ const note = item.note.toLowerCase()
+ const amount = item.amount.toString()
+ const isNoteMatch = note.includes(term.toLowerCase())
+ const isAmountMatch = amount.includes(term)
+ return isNoteMatch || isAmountMatch
+ })
diff --git a/app/utils/validations.js b/app/utils/validations.js
new file mode 100644
index 0000000..41dcd19
--- /dev/null
+++ b/app/utils/validations.js
@@ -0,0 +1,42 @@
+import { options } from "./categories"
+import { getDateObj } from "./formatDate"
+
+const categoryOptions = [...options["income"], ...options["expense"]].map(
+ (option) => option.value
+)
+
+export const validateCategory = (category) => {
+ if (!category) return "Category is required"
+ if (!categoryOptions.includes(category)) return "Category is invalid"
+
+ return null
+}
+
+export const validateDate = (date) => {
+ if (!date) return "Date is required"
+ if (getDateObj(date) > new Date()) return "Date cannot be in the future"
+
+ return null
+}
+
+export const validateAmount = (amount) => {
+ if (!amount) return "Amount is required"
+ if (parseInt(amount, 10) < 0) return "Amount cannot be negative"
+
+ return null
+}
+
+export const validateType = (type) => {
+ if (!type) return "Type is required"
+ if (!["income", "expense"].includes(type)) return "Type is invalid"
+
+ return null
+}
+
+export const validateNote = (note) => {
+ if (!note) return "Note is required"
+ if (note.length > 350 || note.length < 0)
+ return "Note must be between 0 and 350 characters"
+
+ return null
+}
diff --git a/jsconfig.json b/jsconfig.json
index bf4ce14..ac501c5 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -1,5 +1,5 @@
{
- "include": ["app/**/*.js", "app/**/*.jsx"],
+ "include": ["app/**/*.js", "app/**/*.jsx", "app/utils/db.server.ts"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"isolatedModules": true,
diff --git a/package.json b/package.json
index 1b2c0f3..c7ccfbb 100644
--- a/package.json
+++ b/package.json
@@ -6,9 +6,11 @@
"sass:dev": "sass -q --no-source-map --watch styles/:app/styles",
"sass:build": "sass styles/:app/styles",
"build": "yarn sass:build && remix build",
- "start": "remix-serve build"
+ "start": "remix-serve build",
+ "seed": "node prisma/seed.js"
},
"dependencies": {
+ "@prisma/client": "^4.11.0",
"@remix-run/node": "^1.9.0",
"@remix-run/react": "^1.9.0",
"@remix-run/serve": "^1.9.0",
@@ -17,16 +19,22 @@
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.11",
"isbot": "^3.6.5",
+ "prop-types": "^15.8.1",
"react": "^18.2.0",
+ "react-datepicker": "^4.10.0",
"react-dom": "^18.2.0",
+ "react-icons": "^4.8.0",
+ "react-paginate": "^8.1.4",
+ "react-select": "^5.7.0",
"sass": "^1.57.1"
},
"devDependencies": {
"@remix-run/dev": "^1.9.0",
"@remix-run/eslint-config": "^1.9.0",
- "eslint": "^8.31.0"
+ "eslint": "^8.31.0",
+ "prisma": "^4.11.0"
},
"engines": {
"node": ">=14"
}
-}
+}
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
new file mode 100644
index 0000000..1e7d862
--- /dev/null
+++ b/prisma/schema.prisma
@@ -0,0 +1,22 @@
+// This is your Prisma schema file,
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
+
+generator client {
+ provider = "prisma-client-js"
+}
+
+datasource db {
+ provider = "sqlite"
+ url = env("DATABASE_URL")
+}
+
+model Transactions {
+ id String @id @default(uuid())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ category String
+ date DateTime
+ amount Decimal
+ type String
+ note String
+}
diff --git a/prisma/seed.js b/prisma/seed.js
new file mode 100644
index 0000000..eed5743
--- /dev/null
+++ b/prisma/seed.js
@@ -0,0 +1,80 @@
+const { PrismaClient } = require("@prisma/client")
+const db = new PrismaClient()
+
+async function seed() {
+ await Promise.all(
+ getTransactions().map((transaction) => {
+ return db.transactions.create({ data: transaction })
+ })
+ )
+}
+
+seed()
+
+function getTransactions() {
+ return [
+ {
+ category: "salary",
+ date: new Date(),
+ amount: 1000,
+ type: "income",
+ note: "Salary for the month of February",
+ },
+ {
+ category: "loan",
+ date: new Date(),
+ amount: 5000,
+ type: "income",
+ note: "Loan from the bank",
+ },
+ {
+ category: "gift",
+ date: new Date(),
+ amount: 200,
+ type: "income",
+ note: "Gift from my parents",
+ },
+ {
+ category: "tech",
+ date: new Date(),
+ amount: 1500,
+ type: "expense",
+ note: "New laptop",
+ },
+ {
+ category: "food",
+ date: new Date(),
+ amount: 100,
+ type: "expense",
+ note: "Groceries",
+ },
+ {
+ category: "bills",
+ date: new Date(),
+ amount: 500,
+ type: "expense",
+ note: "Electricity bill",
+ },
+ {
+ category: "sports",
+ date: new Date(),
+ amount: 100,
+ type: "expense",
+ note: "New shoes",
+ },
+ {
+ category: "health",
+ date: new Date(),
+ amount: 200,
+ type: "expense",
+ note: "New medicine",
+ },
+ {
+ category: "clothes",
+ date: new Date(),
+ amount: 100,
+ type: "expense",
+ note: "New shirt",
+ },
+ ]
+}
diff --git a/styles/_global.scss b/styles/_global.scss
index a9ead6f..a91c0ef 100644
--- a/styles/_global.scss
+++ b/styles/_global.scss
@@ -1,6 +1,43 @@
* {
box-sizing: border-box;
- font-family: inherit;
+ font-family: inter;
margin: 0;
padding: 0;
-}
\ No newline at end of file
+ font-size: 10px;
+}
+body {
+ background-color: #e8e8e8;
+}
+a {
+ text-decoration: none;
+}
+.error-container {
+ width: 100%;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ row-gap: 10px;
+ padding: 3rem;
+}
+.catch-boundary {
+ .status {
+ font-size: 5rem;
+ }
+ .status-text {
+ font-size: 3rem;
+ }
+ .message {
+ font-size: 2rem;
+ }
+}
+
+.error-boundary {
+ .title {
+ font-size: 5rem;
+ }
+ .message {
+ font-size: 2rem;
+ }
+}
diff --git a/styles/components/_addTransactionModal.scss b/styles/components/_addTransactionModal.scss
new file mode 100644
index 0000000..4279354
--- /dev/null
+++ b/styles/components/_addTransactionModal.scss
@@ -0,0 +1,129 @@
+.modal {
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 100;
+ .modal-content {
+ position: absolute;
+ top: 45%;
+ left: 50%;
+ transform: translate(-50%, -45%);
+ background-color: rgba(248, 250, 252, 1);
+ padding: 1.8rem 2.5rem;
+ border-radius: 1.5rem;
+ width: 60%;
+ height: 65%;
+ .modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ .title {
+ font-size: 1.6rem;
+ font-weight: 400;
+ color: rgba(0, 0, 0, 1);
+ }
+ .close {
+ cursor: pointer;
+ border: none;
+ background-color: transparent;
+ .icon {
+ width: 1.6rem;
+ height: 1.6rem;
+ color: rgba(17, 24, 39, 1);
+ }
+ }
+ }
+ .modal-body {
+ margin-top: 2rem;
+ height: 90%;
+ .modal-form {
+ height: 100%;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ column-gap: 3rem;
+ row-gap: 0.5rem;
+ .form-group {
+ display: flex;
+ flex-direction: column;
+ row-gap: 1rem;
+ input,
+ textarea {
+ height: 100%;
+ padding: 1rem;
+ resize: none;
+ }
+ &.textarea {
+ grid-column: span 2;
+ }
+ .date,
+ .amount,
+ .category {
+ height: 3.8rem;
+ width: 100%;
+ }
+ .amount {
+ display: flex;
+ align-items: center;
+ border-radius: 7px;
+ border: 1px solid rgba(148, 163, 184, 1);
+ .amount-icon {
+ width: 2rem;
+ height: 2rem;
+ color: rgba(148, 163, 184, 1);
+ }
+ .amount-input {
+ width: 100%;
+ border: none;
+ outline: none;
+ }
+ }
+ .radio-container {
+ display: flex;
+ align-items: center;
+ column-gap: 1rem;
+ margin-top: 2rem;
+ }
+ .radio-input {
+ display: none;
+ &:checked + .radio-label {
+ border: 1px solid rgba(148, 163, 184, 1);
+ }
+ }
+ .radio-label {
+ padding: 0.8rem 1.5rem;
+ border: 1px solid rgba(148, 163, 184, 0.3);
+ border-radius: 0.7rem;
+ cursor: pointer;
+ }
+ }
+ .form-button-group {
+ grid-column: span 3;
+ justify-self: end;
+ align-self: flex-end;
+ display: flex;
+ align-items: center;
+ column-gap: 0.5rem;
+ .button {
+ padding: 0.5rem 1rem;
+ border-radius: 5rem;
+ font-size: 1.2rem;
+ font-weight: 500;
+ border: none;
+ &.primary {
+ background-color: rgba(28, 101, 140, 0.15);
+ color: rgba(28, 101, 140, 1);
+ }
+ &.cancel {
+ background-color: rgba(248, 250, 252, 1);
+ color: rgba(0, 0, 0, 1);
+ border: 1px solid rgba(0, 0, 0, 1);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/styles/components/_card.scss b/styles/components/_card.scss
new file mode 100644
index 0000000..29ae45e
--- /dev/null
+++ b/styles/components/_card.scss
@@ -0,0 +1,54 @@
+.card {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.5rem;
+ width: 32%;
+ height: 11rem;
+ border-radius: 7px;
+
+ color: #fff;
+ padding: 2rem;
+ &.blue {
+ background: linear-gradient(90deg, #7dd3fc 0%, #bae6fd 102.82%);
+ }
+ &.gray {
+ background: linear-gradient(90deg, #d4d4d8 0%, #e4e4e7 102.82%);
+ }
+ &.red {
+ background: linear-gradient(90deg, #fda4af 0%, #fecdd3 102.82%);
+ }
+ .title {
+ font-size: 1.8rem;
+ font-weight: 600;
+ &.blue {
+ color: #1a74c7;
+ }
+ &.gray {
+ color: #71717a;
+ }
+ &.red {
+ color: #ef2a4c;
+ }
+ }
+ .details-button {
+ border-radius: 12px;
+ font-size: 1.2rem;
+ border: none;
+ color: #fff;
+ justify-self: end;
+ padding: 0.2em 1rem;
+ &.blue {
+ background-color: rgba(56, 189, 248, 0.75);
+ }
+ &.gray {
+ background-color: rgba(113, 113, 122, 0.75);
+ }
+ &.red {
+ background-color: rgba(251, 113, 133, 0.75);
+ }
+ }
+ .amount {
+ font-size: 3rem;
+ font-weight: 600;
+ }
+}
diff --git a/styles/components/_cards.scss b/styles/components/_cards.scss
new file mode 100644
index 0000000..5c32031
--- /dev/null
+++ b/styles/components/_cards.scss
@@ -0,0 +1,7 @@
+.cards {
+ display: flex;
+ column-gap: 3rem;
+ width: 80%;
+ height: 25%;
+ padding: 2.5rem;
+}
diff --git a/styles/components/_error.scss b/styles/components/_error.scss
new file mode 100644
index 0000000..5bd1a74
--- /dev/null
+++ b/styles/components/_error.scss
@@ -0,0 +1,4 @@
+.form-validation-error {
+ color: red;
+ font-size: 1rem;
+}
diff --git a/styles/components/_filter.scss b/styles/components/_filter.scss
new file mode 100644
index 0000000..deb7533
--- /dev/null
+++ b/styles/components/_filter.scss
@@ -0,0 +1,49 @@
+.filter {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin: 0 auto;
+ padding-left: 2.2rem;
+ width: 95%;
+ height: 6.8rem;
+ background-color: rgba(248, 250, 252, 1);
+ border-radius: 0.7rem;
+ .wrapper {
+ display: flex;
+ align-items: center;
+ column-gap: 1rem;
+ }
+ .icon {
+ width: 1.8rem;
+ height: 1.8rem;
+ color: rgba(126, 133, 148, 1);
+ margin-right: 2.6rem;
+ }
+ .dropdown {
+ width: 30rem;
+ }
+ .date {
+ width: 24rem;
+ height: 4rem;
+ border-radius: 0.7rem;
+ background-color: rgba(255, 255, 255, 1);
+ border: none;
+ outline: none;
+ padding: 0.6rem 0.8rem;
+ box-shadow: inset 0px 0px 2px rgba(0, 0, 0, 0.25);
+ font-size: 1.6rem;
+ line-height: 1.9rem;
+ color: rgba(17, 24, 39, 1);
+ }
+ .clear-button{
+ width: 9.5rem;
+ height: 100%;
+ background-color: rgba(28, 101, 140, 0.75);
+ border: none;
+ border-radius: 0 0.7rem 0.7rem 0;
+ padding: 0.8rem 2rem;
+ font-size: 2rem;
+ line-height: 2.5rem;
+ color: rgba(255, 255, 255, 1);
+ }
+}
diff --git a/styles/components/_header.scss b/styles/components/_header.scss
new file mode 100644
index 0000000..19ca876
--- /dev/null
+++ b/styles/components/_header.scss
@@ -0,0 +1,24 @@
+.header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ background: linear-gradient(180deg, #30475e 0%, #222831 100%);
+ height: 100%;
+ width: 20%;
+ .logo {
+ display: flex;
+ align-items: center;
+ font-size: 2.2rem;
+ font-weight: 600;
+ color: #f05454;
+ margin: 15% 0;
+ .icon {
+ width: 3rem;
+ height: 3rem;
+ margin-right: 0.5rem;
+ }
+ }
+}
diff --git a/styles/components/_latestTransactions.scss b/styles/components/_latestTransactions.scss
new file mode 100644
index 0000000..8d59410
--- /dev/null
+++ b/styles/components/_latestTransactions.scss
@@ -0,0 +1,24 @@
+.latest-transactions {
+ display: flex;
+ flex-direction: column;
+ row-gap: 1rem;
+ width: 80%;
+ padding: 0 2.5rem;
+ .wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+ .title {
+ font-size: 2.2rem;
+ }
+ .add-transaction {
+ align-self: flex-end;
+ padding: 0.8rem 1.6rem;
+ border-radius: 50px;
+ background-color: rgba(28, 101, 140, 0.15);
+ font-size: 1.6rem;
+ color: rgba(28, 101, 140, 1);
+ border: none;
+ }
+}
diff --git a/styles/components/_layout.scss b/styles/components/_layout.scss
new file mode 100644
index 0000000..77be677
--- /dev/null
+++ b/styles/components/_layout.scss
@@ -0,0 +1,10 @@
+.layout{
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ .header{
+ grid-column: 1 / 2;
+ }
+ .children{
+ grid-column: 2 / 6;
+ }
+}
\ No newline at end of file
diff --git a/styles/components/_navItem.scss b/styles/components/_navItem.scss
new file mode 100644
index 0000000..b653a5d
--- /dev/null
+++ b/styles/components/_navItem.scss
@@ -0,0 +1,9 @@
+.nav-item {
+ color: #d4d4d8;
+ font-size: 2.2rem;
+ font-weight: 400;
+}
+
+.active {
+ color: #fff;
+}
diff --git a/styles/components/_navbar.scss b/styles/components/_navbar.scss
new file mode 100644
index 0000000..336dea2
--- /dev/null
+++ b/styles/components/_navbar.scss
@@ -0,0 +1,7 @@
+.navbar {
+ margin-top: 40%;
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ row-gap: 3rem;
+}
diff --git a/styles/components/_searchBar.scss b/styles/components/_searchBar.scss
new file mode 100644
index 0000000..36d43c9
--- /dev/null
+++ b/styles/components/_searchBar.scss
@@ -0,0 +1,44 @@
+.search-bar {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ column-gap: 1rem;
+ width: 95%;
+ margin: 2rem auto;
+ height: 4.8rem;
+ border-radius: 0.7rem;
+ padding-left: 0.8rem;
+ background-color: rgba(255, 255, 255, 1);
+ .wrapper {
+ grid-column: 1/6;
+ display: flex;
+ align-items: center;
+ }
+ .icon {
+ width: 2rem;
+ height: 2rem;
+ color: rgba(126, 133, 148, 1);
+ }
+ .input {
+ width: 100%;
+ height: 100%;
+ padding: 0 0.8rem;
+ border: none;
+ outline: none;
+ background-color: transparent;
+ font-size: 1.4rem;
+ line-height: 1.7rem;
+ color: rgba(126, 133, 148, 1);
+ }
+ .clear-button {
+ width: 9.5rem;
+ grid-column: 6/7;
+ justify-self: end;
+ background-color: rgba(28, 101, 140, 0.75);
+ border: none;
+ border-radius: 0 0.7rem 0.7rem 0;
+ padding: 0.8rem 2rem;
+ font-size: 2rem;
+ line-height: 2.5rem;
+ color: rgba(255, 255, 255, 1);
+ }
+}
diff --git a/styles/components/_topbar.scss b/styles/components/_topbar.scss
new file mode 100644
index 0000000..f01132d
--- /dev/null
+++ b/styles/components/_topbar.scss
@@ -0,0 +1,10 @@
+.topbar {
+ padding: 1.8rem;
+ background-color: #f7f4f3;
+ box-shadow: 0px 1px 8px rgba(34, 40, 49, 0.05);
+ height: 10%;
+ .title {
+ font-size: 2.2rem;
+ font-weight: 500;
+ }
+}
diff --git a/styles/components/_transaction.scss b/styles/components/_transaction.scss
new file mode 100644
index 0000000..854057a
--- /dev/null
+++ b/styles/components/_transaction.scss
@@ -0,0 +1,53 @@
+.transaction {
+ display: flex;
+ justify-content: space-between;
+ padding: 0.8rem 2rem;
+ background-color: rgba(248, 250, 252, 1);
+ border-radius: 7px;
+ margin-bottom: 1rem;
+ color: rgba(51, 65, 85, 1);
+ .wrapper {
+ display: flex;
+ align-items: center;
+ column-gap: 1.8rem;
+ }
+ .icon {
+ width: 3.2rem;
+ height: 3.2rem;
+ padding: 0.5rem;
+ border-radius: 50%;
+ &.green {
+ background-color: rgba(5, 150, 105, 0.2);
+ color: rgba(5, 150, 105, 1);
+ }
+ &.blue {
+ background-color: rgba(2, 132, 199, 0.2);
+ color: rgba(2, 132, 199, 1);
+ }
+ }
+ .note {
+ width: 40rem;
+ font-size: 1.6rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ .date {
+ font-size: 1.6rem;
+ }
+ .amount {
+ padding: 0.6rem 1.4rem;
+ border-radius: 6px;
+ font-size: 1.5rem;
+ font-weight: 600;
+ text-align: center;
+ &.green {
+ background-color: rgba(14, 165, 233, 0.15);
+ color: rgba(14, 165, 233, 1);
+ }
+ &.red {
+ color: rgba(239, 42, 76, 1);
+ background-color: rgba(239, 42, 76, 0.15);
+ }
+ }
+}
diff --git a/styles/components/_transactionsList.scss b/styles/components/_transactionsList.scss
new file mode 100644
index 0000000..5bf9999
--- /dev/null
+++ b/styles/components/_transactionsList.scss
@@ -0,0 +1,53 @@
+.transactions-list {
+ display: flex;
+ flex-direction: column;
+ width: 80%;
+ padding: 2.5rem;
+ .pagination {
+ align-self: flex-end;
+ display: flex;
+ list-style: none;
+ column-gap: 0.5rem;
+ .page {
+ width: 3rem;
+ height: 3rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: rgba(255, 255, 255, 1);
+ border-radius: 1.6rem;
+ cursor: pointer;
+ a {
+ font-size: 1.6rem;
+ line-height: 1.9rem;
+ color: rgba(28, 101, 140, 1);
+ }
+ }
+ .previous,
+ .next {
+ width: 3rem;
+ height: 3rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ svg {
+ width: 2rem;
+ height: 2rem;
+ color: rgba(28, 101, 140, 1);
+ }
+ }
+ .active {
+ background-color: rgba(28, 101, 140, 0.75);
+ a {
+ color: rgba(255, 255, 255, 1);
+ }
+ }
+ .disabled {
+ cursor: not-allowed;
+ svg {
+ color: rgba(149, 159, 180, 1);
+ }
+ }
+ }
+}
diff --git a/styles/pages/_index.scss b/styles/pages/_index.scss
index 2a88350..ec7b75c 100644
--- a/styles/pages/_index.scss
+++ b/styles/pages/_index.scss
@@ -1,9 +1,9 @@
-.index-page {
- font-family: system-ui, sans-serif;
- width: 100%;
- height: 100vh;
- display: flex;
- flex-direction: column;
- text-align: center;
- justify-content: center;
-}
\ No newline at end of file
+@import "../components/cards";
+@import "../components/card";
+@import "../components/latestTransactions";
+@import "../components/addTransactionModal";
+@import "../components/error";
+
+.overview-page {
+ min-height: 100vh;
+}
diff --git a/styles/pages/_transactionHistory.scss b/styles/pages/_transactionHistory.scss
new file mode 100644
index 0000000..30826ec
--- /dev/null
+++ b/styles/pages/_transactionHistory.scss
@@ -0,0 +1,7 @@
+@import "../components/searchBar";
+@import "../components/filter";
+@import "../components/transactionsList";
+
+#transactions-page {
+ min-height: 100vh;
+}
diff --git a/styles/root.scss b/styles/root.scss
index 29f9b28..49250de 100644
--- a/styles/root.scss
+++ b/styles/root.scss
@@ -1,2 +1,11 @@
@import "global";
-@import "pages/index";
\ No newline at end of file
+
+@import "components/layout";
+@import "components/header";
+@import "components/navbar";
+@import "components/navItem";
+@import "components/topbar";
+@import "components/transaction";
+
+@import "pages/index";
+@import "pages/transactionHistory";
diff --git a/yarn.lock b/yarn.lock
index 09d6533..635011a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10,7 +10,7 @@
"@jridgewell/gen-mapping" "^0.1.0"
"@jridgewell/trace-mapping" "^0.3.9"
-"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.18.6":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
@@ -154,7 +154,7 @@
dependencies:
"@babel/types" "^7.20.7"
-"@babel/helper-module-imports@^7.18.6":
+"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
@@ -998,6 +998,13 @@
dependencies:
regenerator-runtime "^0.13.11"
+"@babel/runtime@^7.12.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
+ version "7.21.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
+ integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
+ dependencies:
+ regenerator-runtime "^0.13.11"
+
"@babel/template@^7.18.10", "@babel/template@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
@@ -1032,6 +1039,94 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
+"@emotion/babel-plugin@^11.10.6":
+ version "11.10.6"
+ resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead"
+ integrity sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/runtime" "^7.18.3"
+ "@emotion/hash" "^0.9.0"
+ "@emotion/memoize" "^0.8.0"
+ "@emotion/serialize" "^1.1.1"
+ babel-plugin-macros "^3.1.0"
+ convert-source-map "^1.5.0"
+ escape-string-regexp "^4.0.0"
+ find-root "^1.1.0"
+ source-map "^0.5.7"
+ stylis "4.1.3"
+
+"@emotion/cache@^11.10.5", "@emotion/cache@^11.4.0":
+ version "11.10.5"
+ resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12"
+ integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==
+ dependencies:
+ "@emotion/memoize" "^0.8.0"
+ "@emotion/sheet" "^1.2.1"
+ "@emotion/utils" "^1.2.0"
+ "@emotion/weak-memoize" "^0.3.0"
+ stylis "4.1.3"
+
+"@emotion/hash@^0.9.0":
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7"
+ integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==
+
+"@emotion/memoize@^0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
+ integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
+
+"@emotion/react@^11.8.1":
+ version "11.10.6"
+ resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.6.tgz#dbe5e650ab0f3b1d2e592e6ab1e006e75fd9ac11"
+ integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==
+ dependencies:
+ "@babel/runtime" "^7.18.3"
+ "@emotion/babel-plugin" "^11.10.6"
+ "@emotion/cache" "^11.10.5"
+ "@emotion/serialize" "^1.1.1"
+ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
+ "@emotion/utils" "^1.2.0"
+ "@emotion/weak-memoize" "^0.3.0"
+ hoist-non-react-statics "^3.3.1"
+
+"@emotion/serialize@^1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0"
+ integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==
+ dependencies:
+ "@emotion/hash" "^0.9.0"
+ "@emotion/memoize" "^0.8.0"
+ "@emotion/unitless" "^0.8.0"
+ "@emotion/utils" "^1.2.0"
+ csstype "^3.0.2"
+
+"@emotion/sheet@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c"
+ integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==
+
+"@emotion/unitless@^0.8.0":
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db"
+ integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==
+
+"@emotion/use-insertion-effect-with-fallbacks@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df"
+ integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==
+
+"@emotion/utils@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
+ integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
+
+"@emotion/weak-memoize@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
+ integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
+
"@esbuild-plugins/node-modules-polyfill@^0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.1.4.tgz#eb2f55da11967b2986c913f1a7957d1c868849c0"
@@ -1165,6 +1260,18 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
+"@floating-ui/core@^1.2.2":
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.2.tgz#66f62cf1b7de2ed23a09c101808536e68caffaec"
+ integrity sha512-FaO9KVLFnxknZaGWGmNtjD2CVFuc0u4yeGEofoyXO2wgRA7fLtkngT6UB0vtWQWuhH3iMTZZ/Y89CMeyGfn8pA==
+
+"@floating-ui/dom@^1.0.1":
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.3.tgz#8dc6fbf799fbb5c29f705b54bdd51f3ab0ee03a2"
+ integrity sha512-lK9cZUrHSJLMVAdCvDqs6Ug8gr0wmqksYiaoj/bxj2gweRQkSuhg2/V6Jswz2KiQ0RAULbqw1oQDJIMpQ5GfGA==
+ dependencies:
+ "@floating-ui/core" "^1.2.2"
+
"@gar/promisify@^1.0.1":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
@@ -1292,6 +1399,28 @@
tiny-glob "^0.2.9"
tslib "^2.4.0"
+"@popperjs/core@^2.9.2":
+ version "2.11.6"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
+ integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
+
+"@prisma/client@^4.11.0":
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.11.0.tgz#41d5664dea4172c954190a432f70b86d3e2e629b"
+ integrity sha512-0INHYkQIqgAjrt7NzhYpeDQi8x3Nvylc2uDngKyFDDj1tTRQ4uV1HnVmd1sQEraeVAN63SOK0dgCKQHlvjL0KA==
+ dependencies:
+ "@prisma/engines-version" "4.11.0-57.8fde8fef4033376662cad983758335009d522acb"
+
+"@prisma/engines-version@4.11.0-57.8fde8fef4033376662cad983758335009d522acb":
+ version "4.11.0-57.8fde8fef4033376662cad983758335009d522acb"
+ resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.11.0-57.8fde8fef4033376662cad983758335009d522acb.tgz#74af5ff56170c78e93ce46c56510160f58cd3c01"
+ integrity sha512-3Vd8Qq06d5xD8Ch5WauWcUUrsVPdMC6Ge8ILji8RFfyhUpqon6qSyGM0apvr1O8n8qH8cKkEFqRPsYjuz5r83g==
+
+"@prisma/engines@4.11.0":
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.11.0.tgz#c99749bfe20f58e8f4d2b5e04fee0785eba440e1"
+ integrity sha512-0AEBi2HXGV02cf6ASsBPhfsVIbVSDC9nbQed4iiY5eHttW9ZtMxHThuKZE1pnESbr8HRdgmFSa/Kn4OSNYuibg==
+
"@remix-run/dev@^1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@remix-run/dev/-/dev-1.9.0.tgz#2e594d143d47afc29e4bbdee02291649b0766dc6"
@@ -1623,6 +1752,32 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f"
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
+"@types/parse-json@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
+ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+
+"@types/prop-types@*":
+ version "15.7.5"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
+ integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+
+"@types/react-transition-group@^4.4.0":
+ version "4.4.5"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
+ integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*":
+ version "18.0.28"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065"
+ integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/responselike@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
@@ -1630,6 +1785,11 @@
dependencies:
"@types/node" "*"
+"@types/scheduler@*":
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+ integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+
"@types/semver@^7.3.12":
version "7.3.13"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
@@ -1985,6 +2145,15 @@ babel-core@^7.0.0-bridge.0:
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==
+babel-plugin-macros@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
+ integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ cosmiconfig "^7.0.0"
+ resolve "^1.19.0"
+
babel-plugin-polyfill-corejs2@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122"
@@ -2311,6 +2480,11 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
+classnames@^2.2.6:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
+ integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
+
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
@@ -2462,7 +2636,7 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-convert-source-map@^1.7.0:
+convert-source-map@^1.5.0, convert-source-map@^1.7.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
@@ -2509,6 +2683,17 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+cosmiconfig@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
+ integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
+ dependencies:
+ "@types/parse-json" "^4.0.0"
+ import-fresh "^3.2.1"
+ parse-json "^5.0.0"
+ path-type "^4.0.0"
+ yaml "^1.10.0"
+
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -2518,6 +2703,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
+csstype@^3.0.2:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
+ integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
+
damerau-levenshtein@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
@@ -2528,7 +2718,7 @@ data-uri-to-buffer@3, data-uri-to-buffer@^3.0.1:
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636"
integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==
-date-fns@^2.29.1:
+date-fns@^2.24.0, date-fns@^2.29.1:
version "2.29.3"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
@@ -2720,6 +2910,14 @@ dom-accessibility-api@^0.5.9:
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56"
integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==
+dom-helpers@^5.0.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
dotenv@^16.0.0:
version "16.0.3"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
@@ -2780,6 +2978,13 @@ enhanced-resolve@^5.10.0:
graceful-fs "^4.2.4"
tapable "^2.2.0"
+error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
es-abstract@^1.19.0, es-abstract@^1.20.4:
version "1.20.5"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.5.tgz#e6dc99177be37cacda5988e692c3fa8b218e95d2"
@@ -3479,6 +3684,11 @@ find-cache-dir@^2.0.0:
make-dir "^2.0.0"
pkg-dir "^3.0.0"
+find-root@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
+ integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
+
find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@@ -3914,6 +4124,13 @@ hast-util-whitespace@^2.0.0:
resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c"
integrity sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg==
+hoist-non-react-statics@^3.3.1:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+ integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+ dependencies:
+ react-is "^16.7.0"
+
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
@@ -4103,6 +4320,11 @@ is-arguments@^1.0.4, is-arguments@^1.1.0, is-arguments@^1.1.1:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
is-bigint@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
@@ -4501,7 +4723,7 @@ json-buffer@3.0.1:
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
-json-parse-even-better-errors@^2.3.1:
+json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
@@ -4616,6 +4838,11 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+lines-and-columns@^1.1.6:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+ integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
loader-utils@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
@@ -4668,7 +4895,7 @@ longest-streak@^3.0.0:
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==
-loose-envify@^1.1.0, loose-envify@^1.4.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -4853,6 +5080,11 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
+memoize-one@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
+ integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
+
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@@ -5695,6 +5927,16 @@ parse-entities@^4.0.0:
is-decimal "^2.0.0"
is-hexadecimal "^2.0.0"
+parse-json@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ error-ex "^1.3.1"
+ json-parse-even-better-errors "^2.3.0"
+ lines-and-columns "^1.1.6"
+
parse-ms@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d"
@@ -5832,6 +6074,13 @@ pretty-ms@^7.0.1:
dependencies:
parse-ms "^2.1.0"
+prisma@^4.11.0:
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.11.0.tgz#9695ba4129a43eab3e76b5f7a033c6c020377725"
+ integrity sha512-4zZmBXssPUEiX+GeL0MUq/Yyie4ltiKmGu7jCJFnYMamNrrulTBc+D+QwAQSJ01tyzeGHlD13kOnqPwRipnlNw==
+ dependencies:
+ "@prisma/engines" "4.11.0"
+
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -5842,7 +6091,7 @@ promise-inflight@^1.0.1:
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
-prop-types@^15.8.1:
+prop-types@^15, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -5945,6 +6194,18 @@ raw-body@2.5.1, raw-body@^2.2.0:
iconv-lite "0.4.24"
unpipe "1.0.0"
+react-datepicker@^4.10.0:
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.10.0.tgz#3f386ac5873dac5ea56544e51cdc01109938796c"
+ integrity sha512-6IfBCZyWj54ZZGLmEZJ9c4Yph0s9MVfEGDC2evOvf9AmVz+RRcfP2Czqad88Ff9wREbcbqa4dk7IFYeXF1d3Ag==
+ dependencies:
+ "@popperjs/core" "^2.9.2"
+ classnames "^2.2.6"
+ date-fns "^2.24.0"
+ prop-types "^15.7.2"
+ react-onclickoutside "^6.12.2"
+ react-popper "^2.3.0"
+
react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@@ -5953,7 +6214,17 @@ react-dom@^18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"
-react-is@^16.13.1:
+react-fast-compare@^3.0.1:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
+ integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
+
+react-icons@^4.8.0:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.8.0.tgz#621e900caa23b912f737e41be57f27f6b2bff445"
+ integrity sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==
+
+react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -5963,6 +6234,26 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+react-onclickoutside@^6.12.2:
+ version "6.12.2"
+ resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz#8e6cf80c7d17a79f2c908399918158a7b02dda01"
+ integrity sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA==
+
+react-paginate@^8.1.4:
+ version "8.1.4"
+ resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-8.1.4.tgz#f62bfef4f07bb420f3a994e0284d1065a865a6b9"
+ integrity sha512-c3rxjcTEqeDQa6LqXifxLeFguY2qy2CHGRphVjHLFFMGfIHyaJ+v3bOvIlLYEeohwQ1q+cQpknjsqBVrkc/SNA==
+ dependencies:
+ prop-types "^15"
+
+react-popper@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"
+ integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==
+ dependencies:
+ react-fast-compare "^3.0.1"
+ warning "^4.0.2"
+
react-router-dom@6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.5.0.tgz#3970bdcaa7c710a6e0b478a833ba0b4b8ae61a6f"
@@ -5978,6 +6269,31 @@ react-router@6.5.0:
dependencies:
"@remix-run/router" "1.1.0"
+react-select@^5.7.0:
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.7.0.tgz#82921b38f1fcf1471a0b62304da01f2896cd8ce6"
+ integrity sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==
+ dependencies:
+ "@babel/runtime" "^7.12.0"
+ "@emotion/cache" "^11.4.0"
+ "@emotion/react" "^11.8.1"
+ "@floating-ui/dom" "^1.0.1"
+ "@types/react-transition-group" "^4.4.0"
+ memoize-one "^6.0.0"
+ prop-types "^15.6.0"
+ react-transition-group "^4.3.0"
+ use-isomorphic-layout-effect "^1.1.2"
+
+react-transition-group@^4.3.0:
+ version "4.4.5"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
+ integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@@ -6188,7 +6504,7 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==
-resolve@^1.10.1, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0:
+resolve@^1.10.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
@@ -6553,7 +6869,7 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==
-source-map@^0.5.6:
+source-map@^0.5.6, source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
@@ -6717,6 +7033,11 @@ style-to-object@^0.3.0:
dependencies:
inline-style-parser "0.1.1"
+stylis@4.1.3:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7"
+ integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==
+
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -7135,6 +7456,11 @@ urix@^0.1.0:
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==
+use-isomorphic-layout-effect@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
+ integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
+
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
@@ -7210,6 +7536,13 @@ vm2@^3.9.8:
acorn "^8.7.0"
acorn-walk "^8.2.0"
+warning@^4.0.2:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
+ integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
+ dependencies:
+ loose-envify "^1.0.0"
+
wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
@@ -7371,6 +7704,11 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+yaml@^1.10.0:
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+ integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
+
yargs-parser@^21.1.1:
version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"