diff --git a/README.md b/README.md index eedca73..a6c9c0f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Overview -Horus Holdings is a sophisticated cash flow management tool designed to help users track their incomes and expenses, providing a clear visualization of their cash flow over time. +Horus Holdings is a cash flow management tool designed to help users track their incomes and expenses, providing a clear visualization of their cash flow over time. Named after the Egyptian god Horus, who symbolizes protection, stability, and prosperity, this application aims to bring financial clarity and control to its users. By offering robust user authentication, Horus Holdings ensures that each user's financial data remains secure and private, echoing the protective nature of its namesake. diff --git a/package.json b/package.json index 50ce7c3..64d797a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "horusholdings", "private": true, - "version": "0.2.1", + "version": "0.2.2", "type": "module", "scripts": { "dev": "concurrently \"vite\" \"cd ./server && yarn dev\"", diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/server/src/index.ts b/server/src/index.ts index bb783fd..cb63279 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -33,8 +33,9 @@ app.use( resave: false, saveUninitialized: false, cookie: { - secure: false, + secure: !isDevelopment, httpOnly: true, + sameSite: isDevelopment ? 'lax' : 'none', }, }) ); diff --git a/src/assets/style/App.css b/src/assets/style/App.css index 75fe2f1..d68abe4 100644 --- a/src/assets/style/App.css +++ b/src/assets/style/App.css @@ -99,5 +99,6 @@ h1 { .content { margin-top: 60px; + padding: 0; } } \ No newline at end of file diff --git a/src/assets/style/AuthForm.css b/src/assets/style/AuthForm.css index f19e994..baf4a5a 100644 --- a/src/assets/style/AuthForm.css +++ b/src/assets/style/AuthForm.css @@ -50,4 +50,14 @@ justify-content: center; gap: 2rem; margin-top: 2rem; +} + +@media (max-width: 725px) { + .auth-container { + min-height: calc(100vh - 100px); + } + + .auth-form { + width: 100%; + } } \ No newline at end of file diff --git a/src/assets/style/FlowChart.css b/src/assets/style/FlowChart.css new file mode 100644 index 0000000..cc4c9ec --- /dev/null +++ b/src/assets/style/FlowChart.css @@ -0,0 +1,22 @@ +.flow-chart-container { + display: flex; + flex-direction: column; + width: 100%; +} + +.pie { + max-width: 600px; + width: 100%; + margin: 0 auto; +} + +.line, +.bar { + width: 100%; +} + +@media (max-width: 725px) { + .pie { + max-width: 100%; + } +} \ No newline at end of file diff --git a/src/components/FlowChart.tsx b/src/components/FlowChart.tsx index 0f6505d..68d96e9 100644 --- a/src/components/FlowChart.tsx +++ b/src/components/FlowChart.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState, useCallback } from 'react'; import axios from 'axios'; import dayjs, { Dayjs } from 'dayjs'; -import { Bar, Line } from 'react-chartjs-2'; +import { Bar, Line, Pie } from 'react-chartjs-2'; import { Chart as ChartJS, CategoryScale, @@ -12,6 +12,7 @@ import { Legend, PointElement, LineElement, + ArcElement } from 'chart.js'; import { useWebSocketContext } from '../context/WebSocketContext'; @@ -21,6 +22,8 @@ import DateRange from './DateRange'; import { Expense } from '../types/Expense'; import { Income } from '../types/Income'; +import '../assets/style/FlowChart.css'; + ChartJS.register( CategoryScale, LinearScale, @@ -29,7 +32,8 @@ ChartJS.register( Tooltip, Legend, PointElement, - LineElement + LineElement, + ArcElement ); interface ChartData { @@ -56,6 +60,14 @@ interface ChartData { tension: number; }[]; }; + pieData: { + labels: string[]; + datasets: { + label: string; + backgroundColor: string[]; + data: number[]; + }[]; + }; } const FlowChart: React.FC = () => { @@ -237,6 +249,9 @@ const FlowChart: React.FC = () => { }, }; + const totalIncome = incomeData.reduce((sum, val) => sum + val, 0); + const totalExpenses = expenseData.reduce((sum, val) => sum + val, 0); + return { labels, barData: { @@ -279,10 +294,20 @@ const FlowChart: React.FC = () => { }, ], }, + pieData: { + labels: ['Total Income', 'Total Expenses'], + datasets: [ + { + label: 'Budget Overview', + backgroundColor: ['rgba(44, 182, 125, 0.4)', 'rgba(255, 127, 80, 0.4)'], + data: [totalIncome, totalExpenses], + }, + ], + }, }; }; - const { barData, lineData } = processChartData( + const { barData, lineData, pieData } = processChartData( incomes, expenses, startDate, @@ -291,29 +316,33 @@ const FlowChart: React.FC = () => { const options = { responsive: true, - plugins: { - legend: { position: 'top' as const }, - title: { - display: true, - text: 'Cash Flow Overview', - }, - }, + plugins: { legend: { position: 'top' as const } }, }; + const handleDateRangeChange = (start: Dayjs, end: Dayjs) => { setStartDate(start); setEndDate(end); }; return ( -
- - - +
+
+ +
+
+ +
+
+ +
+
+ +
); }; diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index eab9566..9d06115 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { MenuOutlined } from '@ant-design/icons'; @@ -13,6 +13,7 @@ const Navbar: React.FC = () => { const { user, logout } = useAuth(); const { width } = useViewport(); const [isMenuOpen, setIsMenuOpen] = useState(false); + const menuRef = useRef(null); const isMobile = width < 725; @@ -32,6 +33,24 @@ const Navbar: React.FC = () => { setIsMenuOpen(!isMenuOpen); }; + const handleClickOutside = (event: MouseEvent) => { + if ( menuRef.current && !menuRef.current.contains(event.target as Node) ) { + setIsMenuOpen(false); + } + }; + + useEffect(() => { + if ( isMenuOpen ) { + document.addEventListener('mousedown', handleClickOutside); + } else { + document.removeEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isMenuOpen]); + return (