diff --git a/oldhomepage.txt b/oldhomepage.txt
new file mode 100644
index 0000000..c4b731d
--- /dev/null
+++ b/oldhomepage.txt
@@ -0,0 +1,477 @@
+import React, { useState, useEffect } from 'react';
+import { Pie } from 'react-chartjs-2';
+import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
+import { doc, getDoc, setDoc, updateDoc, deleteDoc } from 'firebase/firestore';
+import { getAuth, onAuthStateChanged, signOut, deleteUser, reauthenticateWithCredential, EmailAuthProvider } from 'firebase/auth';
+import { db } from './firebase-config';
+import './homepage.css';
+import Modal from './Modal.js';
+import { set } from 'lodash';
+
+ChartJS.register(ArcElement, Tooltip, Legend);
+
+const Homepage = () => {
+ const [catData, setCatData] = useState([]);
+ const [catLabels, setCatLabels] = useState([]);
+ const [showModal, setShowModal] = useState({ log: false, update: false, new: false, income: false, deleteAccount: false });
+ const [formData, setFormData] = useState({ amount: '', category: '', memo: '', oldCategory: '', newCategory: { name: '', amount: 0 }, income: '' });
+ const [userId, setUserId] = useState(null);
+ const [user, setUser] = useState(null);
+ const [tutorial, setTutorial] = useState(false);
+ const [currentStep, setCurrentStep] = useState(0);
+ const [income, setIncome] = useState(0);
+ const [password, setPassword] = useState('');
+ const [transactionHistory, setTransactionHistory] = useState([]);
+
+ const tutorialSteps = [
+ {
+ title: 'Step 1: Set Up Income',
+ content: 'To set up your income, go to the income section and enter your monthly income.',
+ },
+ {
+ title: 'Step 2: Create a Category',
+ content: 'To create a new category, click on the "New Category" button and fill in the details.',
+ },
+ {
+ title: 'Step 3: Log a Transaction',
+ content: 'To log a transaction, click on the "Log Transaction" button and enter the transaction details.',
+ },
+ ];
+
+ useEffect(() => {
+ const auth = getAuth();
+
+ const unsubscribe = onAuthStateChanged(auth, async (user) => {
+ if (user) {
+ setUser(user);
+ setUserId(user.uid);
+ const userId = user.uid;
+ const userDocRef = doc(db, 'users', userId);
+
+ try {
+ const userDoc = await getDoc(userDocRef);
+ if (!userDoc.exists()) {
+ await setDoc(
+ userDocRef,
+ {
+ email: user.email,
+ name: user.displayName,
+ createdAt: new Date(),
+ budgetData: [],
+ tutorial: false,
+ },
+ { merge: true }
+ );
+ console.log('User document created with initial data');
+ } else {
+ console.log('User document already exists. Data fetched successfully.');
+ }
+
+ const userData = userDoc.data();
+ const budgetData = userData.budgetData || [];
+ const incomeEntry = budgetData.find(entry => entry.category === 'Income');
+ const incomeAmount = incomeEntry ? incomeEntry.amount : 0;
+ setIncome(incomeAmount);
+
+ const categories = budgetData.filter(entry => entry.type === 'category').map((entry) => entry.category);
+ const amounts = budgetData.filter(entry => entry.type === 'category').map((entry) => entry.amount);
+
+ setCatData(amounts);
+ setCatLabels(categories);
+ setTutorial(userData.tutorial || false);
+
+ const transactions = budgetData
+ .filter(entry => entry.type === 'category')
+ .flatMap(entry => entry.transactions || [])
+ .map(transaction => ({
+ category: transaction.category,
+ memo: transaction.memo,
+ amount: transaction.amount,
+ date: transaction.date,
+ time: transaction.time
+ }))
+ .sort((a, b) => new Date(b.date + ' ' + b.time) - new Date(a.date + ' ' + a.time));
+ setTransactionHistory(transactions);
+ } catch (error) {
+ console.error('Error checking or creating user document:', error);
+ }
+ } else {
+ console.log('No user is currently logged in');
+ }
+ });
+
+ return unsubscribe;
+ }, []);
+
+ const handleOpenModal = (type) => {
+ setShowModal({ ...showModal, [type]: true });
+ };
+
+ const handleCloseModal = () => {
+ setShowModal({ log: false, update: false, new: false, income: false, deleteAccount: false });
+ setFormData({ amount: '', category: '', memo: '', oldCategory: '', newCategory: { name: '', amount: 0 }, income: '' });
+ };
+
+ const handleInputChange = (e) => {
+ const { name, value } = e.target;
+ const keys = name.split('.');
+ setFormData((prevState) => {
+ let newState = { ...prevState };
+ let current = newState;
+ for (let i = 0; i < keys.length - 1; i++) {
+ current = current[keys[i]];
+ }
+ current[keys[keys.length - 1]] = value;
+ return newState;
+ });
+ };
+
+ const handleFormSubmit = async (event, type) => {
+ event.preventDefault();
+
+ if (user && userId) {
+ const userDocRef = doc(db, 'users', userId);
+
+ try {
+ const userDoc = await getDoc(userDocRef);
+ let updatedData;
+
+ if (userDoc.exists()) {
+ const currentData = userDoc.data().budgetData || [];
+
+ if (type === 'log') {
+ const newTransaction = {
+ amount: parseInt(formData.amount),
+ memo: formData.memo,
+ type: 'transaction',
+ date: new Date().toLocaleDateString(),
+ time: new Date().toLocaleTimeString(),
+ category: formData.category
+ };
+ updatedData = currentData.map((entry) => {
+ if (entry.category === formData.category && entry.type === 'category') {
+ return { ...entry, transactions: [newTransaction, ...(entry.transactions || [])] };
+ }
+ return entry;
+ });
+
+ await setDoc(userDocRef, { budgetData: updatedData }, { merge: true });
+ setFormData({...formData, amount: '', memo: ''});
+
+ setTransactionHistory(prevHistory => [{ ...newTransaction }, ...prevHistory]);
+ } else if (type === 'update') {
+ updatedData = currentData.map((entry) =>
+ entry.category === formData.oldCategory ? { ...entry, category: formData.newCategory.name, amount: parseInt(formData.newCategory.amount), type: 'category' } : entry
+ );
+ updatedData = updatedData.filter((entry, index, self) =>
+ index === self.findIndex((e) => e.category === entry.category)
+ );
+ await updateDoc(userDocRef, { budgetData: updatedData });
+ } else if (type === 'new') {
+ const newEntry = { category: formData.newCategory.name, amount: parseInt(formData.newCategory.amount), type: 'category', transactions: [] };
+ updatedData = [...currentData, newEntry];
+ await updateDoc(userDocRef, { budgetData: updatedData });
+ } else if (type === 'income') {
+ const incomeEntry = { category: 'Income', amount: parseInt(formData.income), type: 'income' };
+ updatedData = currentData.filter(entry => entry.category !== 'Income');
+ updatedData.push(incomeEntry);
+ await updateDoc(userDocRef, { budgetData: updatedData });
+ setIncome(incomeEntry.amount);
+ }
+
+ setCatData(updatedData.filter(entry => entry.type === 'category').map((entry) => entry.amount));
+ setCatLabels(updatedData.filter(entry => entry.type === 'category').map((entry) => entry.category));
+ console.log('Data updated successfully');
+ } else {
+ const newEntry = {
+ category: formData.category,
+ memo: formData.memo,
+ amount: parseInt(formData.amount),
+ type: 'transaction',
+ date: new Date().toLocaleDateString(),
+ time: new Date().toLocaleTimeString()
+ };
+ updatedData = [newEntry];
+
+ await setDoc(
+ userDocRef,
+ {
+ email: user.email,
+ name: user.displayName,
+ createdAt: new Date(),
+ budgetData: updatedData,
+ },
+ { merge: true }
+ );
+ setCatData(updatedData.filter(entry => entry.type === 'category').map((entry) => entry.amount));
+ setCatLabels(updatedData.filter(entry => entry.type === 'category').map((entry) => entry.category));
+ }
+
+ handleCloseModal();
+ } catch (error) {
+ console.error('Error updating Firestore:', error);
+ }
+ } else {
+ console.error('No user is currently authenticated');
+ }
+ };
+
+ const handleNextStep = () => {
+ if (currentStep < tutorialSteps.length - 1) {
+ setCurrentStep(currentStep + 1);
+ }
+ };
+
+ const handlePrevStep = () => {
+ if (currentStep > 0) {
+ setCurrentStep(currentStep - 1);
+ }
+ };
+
+ const handleTutorialClick = async () => {
+ if (user && userId) {
+ const userDocRef = doc(db, 'users', userId);
+ try {
+ await updateDoc(userDocRef, { tutorial: true });
+ setTutorial(true);
+ console.log('Tutorial status updated successfully');
+ } catch (error) {
+ console.error('Error updating tutorial status:', error);
+ }
+ } else {
+ console.error('No user is currently authenticated');
+ }
+ };
+
+ const handleExitTutorial = async () => {
+ if (user && userId) {
+ const userDocRef = doc(db, 'users', userId);
+ try {
+ await updateDoc(userDocRef, { tutorial: false });
+ setTutorial(false);
+ console.log('Tutorial status updated successfully');
+ } catch (error) {
+ console.error('Error updating tutorial status:', error);
+ }
+ } else {
+ console.error('No user is currently authenticated');
+ }
+ };
+
+ const handleClearData = async () => {
+ if (user && userId) {
+ const userDocRef = doc(db, 'users', userId);
+ try {
+ await updateDoc(userDocRef, { budgetData: [] });
+ setCatData([]);
+ setCatLabels([]);
+ setTransactionHistory([]);
+ console.log('Data cleared successfully');
+ } catch (error) {
+ console.error('Error clearing data:', error);
+ }
+ } else {
+ console.error('No user is currently authenticated');
+ }
+ };
+
+ const handleLogout = async () => {
+ const auth = getAuth();
+ try {
+ await signOut(auth);
+ console.log('User logged out successfully');
+ window.location.href = '/';
+ } catch (error) {
+ console.error('Logout error:', error.message);
+ alert('Logout failed');
+ }
+ };
+
+ const handleDeleteAccount = async () => {
+ if (user && userId) {
+ const userDocRef = doc(db, 'users', userId);
+ try {
+ await deleteDoc(userDocRef);
+ await deleteUser(user);
+ console.log('Account and data deleted successfully');
+ window.location.href = '/';
+ } catch (error) {
+ console.error('Account deletion error:', error.message);
+ alert('Account deletion failed');
+ }
+ } else {
+ console.error('No user is currently authenticated');
+ }
+ };
+
+ const totalCategoryAmount = catData.reduce((a, b) => a + b, 0);
+ const remainingAmount = Math.max(0, income - totalCategoryAmount);
+
+ const pieData = {
+ labels: [...catLabels, 'Remaining'],
+ datasets: [
+ {
+ label: 'Dollars Spent',
+ data: [...catData, remainingAmount],
+ backgroundColor: [...catLabels.map(() => '#66AA11'), '#CCCCCC'],
+ hoverBackgroundColor: [...catLabels.map(() => '#66AA11'), '#CCCCCC'],
+ },
+ ],
+ };
+
+ const options = {
+ plugins: {
+ legend: {
+ labels: {
+ color: 'white',
+ },
+ },
+ },
+ };
+
+ const tutorialBoxStyle = {
+ position: 'fixed',
+ top: '20%',
+ left: '50%',
+ transform: 'translate(-50%, -20%)',
+ width: '80%',
+ maxWidth: '600px',
+ padding: '20px',
+ backgroundColor: 'white',
+ boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
+ zIndex: 1000,
+ };
+
+ return (
+
+
Money Gremlin
+
+
+
Budget Dashboard
+
+ {catData.length > 0 ? (
+
+ ) : (
+
No data available to display
+ )}
+
+
+
+
handleOpenModal('log')}>
+ Log Transaction
+
+
handleOpenModal('update')}>
+ Update Category
+
+
handleOpenModal('new')}>
+ New Category
+
+
handleOpenModal('income')}>
+ Change Income
+
+
handleOpenModal('history')}>
+ Transaction History
+
+
+
+
+
+ Log Transaction
+
+
+
+
+ Update Category
+
+
+
+
+ New Category
+
+
+
+
+ Change Income
+
+
+
+
+ Transaction History
+
+
+
+ | Date |
+ Time |
+ Category |
+ Memo |
+ Amount |
+
+
+
+ {transactionHistory.map((transaction, index) => (
+
+ | {transaction.date} |
+ {transaction.time} |
+ {transaction.category} |
+ {transaction.memo} |
+ {transaction.amount} |
+
+ ))}
+
+
+
+
+
+ Delete Account
+ Are you sure you want to delete your account? This will erase all data associated with the account and is irreversible.
+
+
+
+
+
+
+ );
+};
+
+export default Homepage;
\ No newline at end of file
diff --git a/src/components/ActionButtons/ActionButtons.js b/src/components/ActionButtons/ActionButtons.js
index 933ae74..2c5a407 100644
--- a/src/components/ActionButtons/ActionButtons.js
+++ b/src/components/ActionButtons/ActionButtons.js
@@ -16,6 +16,9 @@ const ActionButtons = ({ onActionClick }) => {
onActionClick('income')}>
Change Income
+ onActionClick('transactionHistory')}>
+ Transaction History
+
);
};
diff --git a/src/components/BudgetDashboard/BudgetDashboard.css b/src/components/BudgetDashboard/BudgetDashboard.css
index db02190..8ddf177 100644
--- a/src/components/BudgetDashboard/BudgetDashboard.css
+++ b/src/components/BudgetDashboard/BudgetDashboard.css
@@ -58,25 +58,29 @@
padding: 2rem;
}
- .back-button {
- position: absolute;
- top: -40px;
- right: 0;
- background-color: #66AA11;
- color: white;
- border: none;
- padding: 8px 16px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 14px;
- transition: background-color 0.2s;
- }
-
- .back-button:hover {
- background-color: #558800;
- }
-
- .pie-chart-container {
- position: relative;
- /* ... existing styles ... */
- }
\ No newline at end of file
+.dashboard-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 1.5rem;
+}
+
+.back-button {
+ background-color: #66AA11;
+ color: white;
+ border: none;
+ border-radius: 50%;
+ width: 32px;
+ height: 32px;
+ font-size: 18px;
+ cursor: pointer;
+ margin-right: 1rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.2s;
+ transform: translateY(-10px);
+}
+
+.back-button:hover {
+ background-color: #558800;
+}
diff --git a/src/components/BudgetDashboard/BudgetDashboard.js b/src/components/BudgetDashboard/BudgetDashboard.js
index f5cdde2..1cac78b 100644
--- a/src/components/BudgetDashboard/BudgetDashboard.js
+++ b/src/components/BudgetDashboard/BudgetDashboard.js
@@ -6,7 +6,6 @@ import {
Tooltip,
Legend
} from 'chart.js';
-
import './BudgetDashboard.css';
ChartJS.register(
@@ -21,6 +20,31 @@ const BudgetDashboard = ({ catData, catLabels, income, transactions }) => {
const totalCategoryAmount = catData.reduce((a, b) => a + b, 0);
const remainingAmount = Math.max(0, income - totalCategoryAmount);
+ // Helper function to get transactions from last 30 days
+ const getRecentTransactions = (category) => {
+ const thirtyDaysAgo = new Date();
+ thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
+
+ return transactions.filter(t => {
+ const transactionDate = new Date(t.date);
+ return t.category === category && transactionDate >= thirtyDaysAgo;
+ });
+ };
+
+ // Calculate category-specific data
+ const getCategoryData = (category) => {
+ const categoryBudget = catData[catLabels.indexOf(category)];
+ const recentTransactions = getRecentTransactions(category);
+ const spentAmount = recentTransactions.reduce((sum, t) => sum + t.amount, 0);
+ const remainingAmount = Math.max(0, categoryBudget - spentAmount);
+
+ return {
+ budget: categoryBudget,
+ spent: spentAmount,
+ remaining: remainingAmount,
+ transactionCount: recentTransactions.length
+ };
+ };
// Define bright colors for categories
const categoryColors = [
@@ -36,50 +60,40 @@ const BudgetDashboard = ({ catData, catLabels, income, transactions }) => {
'#45B7D1', // Sky blue
];
- const getMainPieData = () => ({
+
+ const pieData = selectedCategory ? {
+ labels: ['Spent', 'Remaining'],
+ datasets: [{
+ data: [
+ getCategoryData(selectedCategory).spent,
+ getCategoryData(selectedCategory).remaining
+ ],
+ backgroundColor: [categoryColors[catLabels.indexOf(selectedCategory)], '#E0E0E0'],
+ hoverBackgroundColor: [
+ categoryColors[catLabels.indexOf(selectedCategory)].replace('FF', 'DD'),
+ '#CCCCCC'
+ ]
+ }]
+ } : {
labels: [...catLabels, 'Remaining'],
datasets: [{
data: [...catData, remainingAmount],
backgroundColor: [
...catLabels.map((_, index) => categoryColors[index % categoryColors.length]),
- '#E0E0E0' // Light gray for remaining amount
+
+ '#E0E0E0'
+
],
hoverBackgroundColor: [
...catLabels.map((_, index) => {
const color = categoryColors[index % categoryColors.length];
return color.replace('FF', 'DD');
}),
- '#CCCCCC' // Slightly darker gray for remaining amount hover
+
+ '#CCCCCC'
],
- }],
- });
-
- const getTransactionPieData = () => {
- const categoryTransactions = transactions.filter(t => t.category === selectedCategory);
- const categoryIndex = catLabels.indexOf(selectedCategory);
- const categoryBudget = catData[categoryIndex];
- const totalSpent = categoryTransactions.reduce((sum, t) => sum + t.amount, 0);
- const remainingInCategory = Math.max(0, categoryBudget - totalSpent);
-
- return {
- labels: [...categoryTransactions.map(t => t.memo || 'No memo'), 'Remaining'],
- datasets: [{
- data: [...categoryTransactions.map(t => t.amount), remainingInCategory],
- backgroundColor: [
- ...categoryTransactions.map((_, index) =>
- categoryColors[index % categoryColors.length]
- ),
- '#E0E0E0' // Same light gray for remaining amount
- ],
- hoverBackgroundColor: [
- ...categoryTransactions.map((_, index) => {
- const color = categoryColors[index % categoryColors.length];
- return color.replace('FF', 'DD');
- }),
- '#CCCCCC' // Same hover gray as main chart
- ],
- }],
- };
+ }]
+
};
const mainOptions = {
@@ -89,9 +103,7 @@ const BudgetDashboard = ({ catData, catLabels, income, transactions }) => {
labels: {
color: 'white',
padding: 20,
- font: {
- size: 14
- }
+ font: { size: 14 }
}
},
tooltip: {
@@ -104,61 +116,71 @@ const BudgetDashboard = ({ catData, catLabels, income, transactions }) => {
}
}
},
- maintainAspectRatio: true,
- responsive: true,
onClick: (event, elements) => {
- if (elements.length > 0) {
+ if (elements.length > 0 && !selectedCategory) {
const index = elements[0].index;
if (index < catLabels.length) { // Don't select "Remaining" slice
setSelectedCategory(catLabels[index]);
}
}
- }
- };
+ },
+ maintainAspectRatio: true,
+ responsive: true,
- const transactionOptions = {
- ...mainOptions,
- onClick: undefined, // Remove click handler for transaction view
- plugins: {
- ...mainOptions.plugins,
- tooltip: {
- callbacks: {
- label: function(context) {
- const label = context.label || '';
- const value = context.raw || 0;
- if (label === 'Remaining') {
- return `Remaining: $${value.toFixed(2)}`;
- }
- const date = transactions.find(t => t.memo === label)?.date;
- return [
- `${label}`,
- `Amount: $${value.toFixed(2)}`,
- date ? `Date: ${new Date(date).toLocaleDateString()}` : '',
- ].filter(Boolean);
- }
- }
- }
- }
- };
+ elements: {
+ arc: {
+ cursor: (ctx) => {
+ const index = ctx.dataIndex;
+ return (!selectedCategory && index < catLabels.length) ? 'pointer' : 'default';
return (
-
Budget Overview
+
+ {selectedCategory && (
+
+ )}
+
{selectedCategory ? `${selectedCategory} Overview` : 'Budget Overview'}
+
+
${income.toFixed(2)}
-
-
- ${totalCategoryAmount.toFixed(2)}
-
-
-
- ${remainingAmount.toFixed(2)}
-
+ {selectedCategory ? (
+ <>
+
+
+ ${getCategoryData(selectedCategory).budget.toFixed(2)}
+
+
+
+ ${getCategoryData(selectedCategory).remaining.toFixed(2)}
+
+
+
+ {getCategoryData(selectedCategory).transactionCount}
+
+ >
+ ) : (
+ <>
+
+
+ ${totalCategoryAmount.toFixed(2)}
+
+
+
+ ${remainingAmount.toFixed(2)}
+
+ >
+ )}
-
+
{selectedCategory && (