From 8c71674c22ab6b4293481144ba16bab89bc93d96 Mon Sep 17 00:00:00 2001 From: Daniel Dyrnes Date: Fri, 1 Jul 2022 13:02:05 +0900 Subject: [PATCH] Added desktop view --- assets/images/logo.png | Bin 0 -> 2589 bytes components/CategoryChart.tsx | 127 +++++++++++++++++++++++++++++++++++ components/HeaderNavbar.tsx | 99 +++++++++++++++++++++++++++ components/MobileNavbar.tsx | 7 +- components/Protected.tsx | 2 + next.config.js | 2 +- pages/account/categories.tsx | 5 +- pages/dashboard.tsx | 95 ++++++++++++++++++++++++++ pages/index.tsx | 10 ++- 9 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 assets/images/logo.png create mode 100644 components/CategoryChart.tsx create mode 100644 components/HeaderNavbar.tsx create mode 100644 pages/dashboard.tsx diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e270d85b4349875e3a4a14b39615000dc146f3d0 GIT binary patch literal 2589 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 literal 0 HcmV?d00001 diff --git a/components/CategoryChart.tsx b/components/CategoryChart.tsx new file mode 100644 index 0000000..d2d37e8 --- /dev/null +++ b/components/CategoryChart.tsx @@ -0,0 +1,127 @@ +import React from "react"; +import { useIntl } from "react-intl"; +import { useQuery, gql } from "@apollo/client"; +import { PieChart, Pie, LabelList } from "recharts"; +import Dinero from "dinero.js"; +import Loading from "components/Loading"; + +const GET_CATEGORY_CHART_DATA = gql` + query categoryChartData { + queryTransaction { + id + amount + category { + id + icon + name + } + } + } +`; + +const CategoryChart: React.FC = () => { + const intl = useIntl(); + const { + data: pieChartData, + loading, + error, + } = useQuery(GET_CATEGORY_CHART_DATA, {}); + if (error) { + console.error(error); + } + + const formatChartData = () => { + let data: { + name: string; + icon: string; + value: number; + categoryId: string; + }[] = []; + + if (!pieChartData || !pieChartData.queryTransaction) { + return data; + } + + pieChartData.queryTransaction.forEach((item: any) => { + const categoryIndex = data.findIndex( + (c) => c.categoryId === item.category.id + ); + if (item.amount < 0) { + if (categoryIndex !== -1) { + data[categoryIndex].value += Math.abs(item.amount); + } else { + data.push({ + icon: item.category.icon, + name: item.category.name, + value: Math.abs(item.amount), + categoryId: item.category.id, + }); + } + } + }); + + return data; + }; + + if (loading) { + return ; + } + + return ( +
+ + + { + // console.log("value", value); + // return value; + // }} + /> + + +
+

+ {intl.formatMessage({ defaultMessage: "Spend by category" })} +

+ {formatChartData().map((item: any) => ( +
+
+
+ {item.icon} +
+
+

{item.name}

+
+
+

+ -  + {Dinero({ amount: item.value, precision: 2 }).toFormat("$0,0.00")} +

+
+ ))} +
+
+ ); +}; + +export default CategoryChart; diff --git a/components/HeaderNavbar.tsx b/components/HeaderNavbar.tsx new file mode 100644 index 0000000..cd6ebf6 --- /dev/null +++ b/components/HeaderNavbar.tsx @@ -0,0 +1,99 @@ +import React, { useState } from "react"; +import Image from "next/image"; +import { useIntl } from "react-intl"; +import { useSession } from "next-auth/react"; +import toast from "react-hot-toast"; +import { gql, useMutation } from "@apollo/client"; +import { parseCurrency } from "utils/currency"; +import { Dialog } from "@headlessui/react"; +import TransactionForm, { TransactionFormValues } from "forms/TransactionForm"; +import { + GET_TRANSACTIONS, + TRANSACTION_AMOUNT_AGGREGATE, +} from "constants/queries"; +import logo from "assets/images/logo.png"; + +const ADD_TRANSACTION = gql` + mutation AddTransaction($input: [AddTransactionInput!]!) { + addTransaction(input: $input) { + transaction { + id + } + } + } +`; + +const HeaderNavbar: React.FC = () => { + const intl = useIntl(); + const { data: session } = useSession(); + const [addTransaction] = useMutation(ADD_TRANSACTION, {}); + + const [open, setOpen] = useState(false); + + const onNewTransaction = async (values: TransactionFormValues) => { + try { + let amount = values.type === "income" ? values.amount : -values.amount; + + await addTransaction({ + variables: { + input: [ + { + amount: parseCurrency(amount), + date: values.date, + category: { id: values.category }, + user: { + // @ts-ignore + id: session.user?.id, + }, + }, + ], + }, + refetchQueries: [GET_TRANSACTIONS, TRANSACTION_AMOUNT_AGGREGATE], + }); + toast.success( + intl.formatMessage({ defaultMessage: "Transaction added" }) + ); + setOpen(false); + } catch (err: any) { + console.error(err); + toast.error(err.message); + } + }; + + return ( +
+
+
+ + logo + profile +
+
+ setOpen(false)}> + +
+
+ +
+
+
+
+ ); +}; + +export default HeaderNavbar; diff --git a/components/MobileNavbar.tsx b/components/MobileNavbar.tsx index 53a9188..d61b055 100644 --- a/components/MobileNavbar.tsx +++ b/components/MobileNavbar.tsx @@ -87,7 +87,7 @@ const MobileNavbar: React.FC = () => { return ( -
+
setOpen(false)}> -
+
diff --git a/components/Protected.tsx b/components/Protected.tsx index 8b3e7a3..a73cc9b 100644 --- a/components/Protected.tsx +++ b/components/Protected.tsx @@ -21,6 +21,7 @@ import { useIntl } from "react-intl"; import toast from "react-hot-toast"; import { UserContext } from "contexts/User"; import Loading from "components/Loading"; +import HeaderNavbar from "components/HeaderNavbar"; import MobileNavbar from "components/MobileNavbar"; const Protected: React.FC = (props) => { @@ -46,6 +47,7 @@ const Protected: React.FC = (props) => { return (
+ {session?.user && }
{props.children}
{session?.user && }
diff --git a/next.config.js b/next.config.js index 08a8de5..e4a2f0b 100644 --- a/next.config.js +++ b/next.config.js @@ -26,7 +26,7 @@ const moduleExports = { localeDetection: true, }, images: { - domains: [], + domains: ["lh3.googleusercontent.com"], }, webpack(config, { dev, ...other }) { if (!dev) { diff --git a/pages/account/categories.tsx b/pages/account/categories.tsx index b8af991..6de2595 100644 --- a/pages/account/categories.tsx +++ b/pages/account/categories.tsx @@ -112,7 +112,10 @@ const Categories: NextPage = () => {
setOpen(false)}> -
+
diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx new file mode 100644 index 0000000..0387253 --- /dev/null +++ b/pages/dashboard.tsx @@ -0,0 +1,95 @@ +import type { NextPage } from "next"; +import { Fragment } from "react"; +import { useIntl } from "react-intl"; +import { useQuery, gql } from "@apollo/client"; +import toast from "react-hot-toast"; +import { TRANSACTION_AMOUNT_AGGREGATE } from "constants/queries"; +import { GET_TRANSACTIONS } from "constants/queries"; +import Protected from "components/Protected"; +import LargeNumberCard from "components/LargeNumberCard"; +import SmallNumberCard from "components/SmallNumberCard"; +import Transaction from "components/Transaction"; +import CategoryChart from "components/CategoryChart"; + +const Dashboard: NextPage = () => { + const intl = useIntl(); + const { data: income, error: incomeError } = useQuery( + TRANSACTION_AMOUNT_AGGREGATE, + { + variables: { filter: { and: [{ amount: { gt: 0 } }] } }, + } + ); + if (incomeError) { + console.error(incomeError); + toast.error(incomeError.message); + } + const { data: expense, error: expenseError } = useQuery( + TRANSACTION_AMOUNT_AGGREGATE, + { + variables: { filter: { and: [{ amount: { lt: 0 } }] } }, + } + ); + if (expenseError) { + console.error(expenseError); + toast.error(expenseError.message); + } + + const { data: transactions, error } = useQuery(GET_TRANSACTIONS, { + variables: { + order: { desc: "date" }, + limit: 30, + }, + }); + if (error) { + console.error(error); + toast.error(error.message); + } + + return ( + +
+
+
+
+ +
+ + +
+
+
+ +
+
+
+ {!transactions?.queryTransaction.length ? ( +

+ {intl.formatMessage({ defaultMessage: "No transactions" })} +

+ ) : ( + + {transactions.queryTransaction.map((transaction: any) => ( + + ))} + + )} +
+
+
+
+ ); +}; + +export default Dashboard; diff --git a/pages/index.tsx b/pages/index.tsx index b247ad9..e601354 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -15,6 +15,7 @@ */ import type { NextPage } from "next"; +import { useEffect } from "react"; import { useIntl } from "react-intl"; import { useQuery } from "@apollo/client"; import toast from "react-hot-toast"; @@ -47,9 +48,16 @@ const Home: NextPage = () => { toast.error(expenseError.message); } + useEffect(() => { + const width = screen.width; + if (width > 992) { + window.location.replace("/dashboard"); + } + }, []); + return ( -
+