diff --git a/components/CoinsManager.tsx b/components/CoinsManager.tsx
index f344465..c67685a 100644
--- a/components/CoinsManager.tsx
+++ b/components/CoinsManager.tsx
@@ -10,7 +10,7 @@ import { useCoins } from '@/hooks/useCoins'
import { currentUserAtom, settingsAtom, usersAtom } from '@/lib/atoms'
import { MAX_COIN_LIMIT } from '@/lib/constants'
import { TransactionType } from '@/lib/types'
-import { d2s, t2d } from '@/lib/utils'
+import { calculateTransactionsToday, d2s, t2d } from '@/lib/utils'
import { useAtom } from 'jotai'
import { History } from 'lucide-react'
import { useTranslations } from 'next-intl'
@@ -33,8 +33,7 @@ export default function CoinsManager() {
coinsEarnedToday,
totalEarned,
totalSpent,
- coinsSpentToday,
- transactionsToday
+ coinsSpentToday
} = useCoins({ selectedUser })
const [settings] = useAtom(settingsAtom)
const [usersData] = useAtom(usersAtom)
@@ -252,7 +251,7 @@ export default function CoinsManager() {
{t('todaysTransactionsLabel')}
- {transactionsToday} 📊
+ {calculateTransactionsToday(transactions, settings.system.timezone)} 📊
diff --git a/hooks/useCoins.tsx b/hooks/useCoins.tsx
index 019004a..5a3431f 100644
--- a/hooks/useCoins.tsx
+++ b/hooks/useCoins.tsx
@@ -2,15 +2,14 @@ import { addCoins, removeCoins, saveCoinsData } from '@/app/actions/data';
import { toast } from '@/hooks/use-toast';
import {
coinsAtom,
- coinsBalanceAtom,
coinsEarnedTodayAtom,
coinsSpentTodayAtom,
currentUserAtom,
+ currentUserIdAtom,
settingsAtom,
totalEarnedAtom,
totalSpentAtom,
- transactionsTodayAtom,
- usersAtom,
+ usersAtom
} from '@/lib/atoms';
import { MAX_COIN_LIMIT } from '@/lib/constants';
import { CoinsData } from '@/lib/types';
@@ -24,27 +23,26 @@ export function useCoins(options?: { selectedUser?: string }) {
const tCommon = useTranslations('Common');
const [coins, setCoins] = useAtom(coinsAtom)
const [settings] = useAtom(settingsAtom)
- const [users] = useAtom(usersAtom)
+ const [{users}] = useAtom(usersAtom)
const [currentUser] = useAtom(currentUserAtom)
- const [allCoinsData] = useAtom(coinsAtom) // All coin transactions
- const [loggedInUserBalance] = useAtom(coinsBalanceAtom) // Balance of the *currently logged-in* user
+ const [coinsData] = useAtom(coinsAtom) // All coin transactions
+ const [loggedInUserId] = useAtom(currentUserIdAtom);
+ const loggedInUserBalance = loggedInUserId ? coins.transactions.filter(transaction => transaction.userId === loggedInUserId).reduce((sum, transaction) => sum + transaction.amount, 0) : 0;
const [atomCoinsEarnedToday] = useAtom(coinsEarnedTodayAtom);
const [atomTotalEarned] = useAtom(totalEarnedAtom)
const [atomTotalSpent] = useAtom(totalSpentAtom)
const [atomCoinsSpentToday] = useAtom(coinsSpentTodayAtom);
- const [atomTransactionsToday] = useAtom(transactionsTodayAtom);
- const targetUser = options?.selectedUser ? users.users.find(u => u.id === options.selectedUser) : currentUser
+ const targetUser = options?.selectedUser ? users.find(u => u.id === options.selectedUser) : currentUser
const transactions = useMemo(() => {
- return allCoinsData.transactions.filter(t => t.userId === targetUser?.id);
- }, [allCoinsData, targetUser?.id]);
+ return coinsData.transactions.filter(t => t.userId === targetUser?.id);
+ }, [coinsData, targetUser?.id]);
const timezone = settings.system.timezone;
const [coinsEarnedToday, setCoinsEarnedToday] = useState(0);
const [totalEarned, setTotalEarned] = useState(0);
const [totalSpent, setTotalSpent] = useState(0);
const [coinsSpentToday, setCoinsSpentToday] = useState(0);
- const [transactionsToday, setTransactionsToday] = useState(0);
const [balance, setBalance] = useState(0);
useEffect(() => {
@@ -55,7 +53,6 @@ export function useCoins(options?: { selectedUser?: string }) {
setTotalEarned(atomTotalEarned);
setTotalSpent(atomTotalSpent);
setCoinsSpentToday(atomCoinsSpentToday);
- setTransactionsToday(atomTransactionsToday);
setBalance(loggedInUserBalance);
} else if (targetUser?.id) {
// If an admin is viewing another user, calculate their metrics manually
@@ -71,8 +68,6 @@ export function useCoins(options?: { selectedUser?: string }) {
const spentToday = calculateCoinsSpentToday(transactions, timezone);
setCoinsSpentToday(roundToInteger(spentToday));
- setTransactionsToday(calculateTransactionsToday(transactions, timezone)); // This is a count
-
const calculatedBalance = transactions.reduce((acc, t) => acc + t.amount, 0);
setBalance(roundToInteger(calculatedBalance));
}
@@ -85,8 +80,7 @@ export function useCoins(options?: { selectedUser?: string }) {
atomCoinsEarnedToday,
atomTotalEarned,
atomTotalSpent,
- atomCoinsSpentToday,
- atomTransactionsToday,
+ atomCoinsSpentToday
]);
const add = async (amount: number, description: string, note?: string) => {
@@ -187,7 +181,6 @@ export function useCoins(options?: { selectedUser?: string }) {
coinsEarnedToday,
totalEarned,
totalSpent,
- coinsSpentToday,
- transactionsToday
+ coinsSpentToday
}
}
diff --git a/lib/atoms.ts b/lib/atoms.ts
index e8e4730..bdeedc3 100644
--- a/lib/atoms.ts
+++ b/lib/atoms.ts
@@ -3,10 +3,7 @@ import {
calculateCoinsSpentToday,
calculateTotalEarned,
calculateTotalSpent,
- calculateTransactionsToday,
generateCryptoHash,
- getCompletionsForToday,
- getHabitFreq,
isHabitDue,
prepareDataForHashing,
roundToInteger,
@@ -16,9 +13,9 @@ import { atom } from "jotai";
import { atomFamily, atomWithStorage } from "jotai/utils";
import { DateTime } from "luxon";
import {
+ BrowserSettings,
CoinsData,
CompletionCache,
- Freq,
getDefaultCoinsData,
getDefaultHabitsData,
getDefaultServerSettings,
@@ -27,6 +24,7 @@ import {
getDefaultWishlistData,
Habit,
HabitsData,
+ PomodoroAtom,
ServerSettings,
Settings,
UserData,
@@ -34,12 +32,6 @@ import {
WishlistData
} from "./types";
-export interface BrowserSettings {
- expandedHabits: boolean
- expandedTasks: boolean
- expandedWishlist: boolean
-}
-
export const browserSettingsAtom = atomWithStorage('browserSettings', {
expandedHabits: false,
expandedTasks: false,
@@ -47,11 +39,21 @@ export const browserSettingsAtom = atomWithStorage('browserSettings', {
} as BrowserSettings)
export const usersAtom = atom(getDefaultUsersData())
+export const currentUserIdAtom = atom(undefined);
export const settingsAtom = atom(getDefaultSettings());
export const habitsAtom = atom(getDefaultHabitsData());
export const coinsAtom = atom(getDefaultCoinsData());
export const wishlistAtom = atom(getDefaultWishlistData());
export const serverSettingsAtom = atom(getDefaultServerSettings());
+export const userSelectAtom = atom(false)
+export const aboutOpenAtom = atom(false)
+
+export const pomodoroAtom = atom({
+ show: false,
+ selectedHabitId: null,
+ autoStart: true,
+ minimized: false,
+})
// Derived atom for coins earned today
export const coinsEarnedTodayAtom = atom((get) => {
@@ -83,54 +85,12 @@ export const coinsSpentTodayAtom = atom((get) => {
return roundToInteger(value);
});
-// Derived atom for transactions today
-export const transactionsTodayAtom = atom((get) => {
- const coins = get(coinsAtom);
- const settings = get(settingsAtom);
- return calculateTransactionsToday(coins.transactions, settings.system.timezone);
-});
-
-// Atom to store the current logged-in user's ID.
-// This should be set by your application when the user session is available.
-export const currentUserIdAtom = atom(undefined);
-
export const currentUserAtom = atom((get) => {
const currentUserId = get(currentUserIdAtom);
const users = get(usersAtom);
return users.users.find(user => user.id === currentUserId);
})
-// Derived atom for current balance for the logged-in user
-export const coinsBalanceAtom = atom((get) => {
- const loggedInUserId = get(currentUserIdAtom);
- if (!loggedInUserId) {
- return 0; // No user logged in or ID not set, so balance is 0
- }
- const coins = get(coinsAtom);
- const balance = coins.transactions
- .filter(transaction => transaction.userId === loggedInUserId)
- .reduce((sum, transaction) => sum + transaction.amount, 0);
- return roundToInteger(balance);
-});
-
-/* transient atoms */
-export interface PomodoroAtom {
- show: boolean
- selectedHabitId: string | null
- autoStart: boolean
- minimized: boolean
-}
-
-export const pomodoroAtom = atom({
- show: false,
- selectedHabitId: null,
- autoStart: true,
- minimized: false,
-})
-
-export const userSelectAtom = atom(false)
-export const aboutOpenAtom = atom(false)
-
/**
* Asynchronous atom that calculates a freshness token (hash) based on the current client-side data.
* This token can be compared with a server-generated token to detect data discrepancies.
@@ -147,34 +107,26 @@ export const clientFreshnessTokenAtom = atom(async (get) => {
return hash;
});
-// Derived atom for completion cache
-export const completionCacheAtom = atom((get) => {
+// Derived atom for completed habits by date, using the cache
+export const completedHabitsMapAtom = atom((get) => {
const habits = get(habitsAtom).habits;
+ const completionCache: CompletionCache = {};
+ const map = new Map();
const timezone = get(settingsAtom).system.timezone;
- const cache: CompletionCache = {};
habits.forEach(habit => {
habit.completions.forEach(utcTimestamp => {
const localDate = t2d({ timestamp: utcTimestamp, timezone })
.toFormat('yyyy-MM-dd');
- if (!cache[localDate]) {
- cache[localDate] = {};
+ if (!completionCache[localDate]) {
+ completionCache[localDate] = {};
}
- cache[localDate][habit.id] = (cache[localDate][habit.id] || 0) + 1;
+ completionCache[localDate][habit.id] = (completionCache[localDate][habit.id] || 0) + 1;
});
});
- return cache;
-});
-
-// Derived atom for completed habits by date, using the cache
-export const completedHabitsMapAtom = atom((get) => {
- const habits = get(habitsAtom).habits;
- const completionCache = get(completionCacheAtom);
- const map = new Map();
-
// For each date in the cache
Object.entries(completionCache).forEach(([dateKey, habitCompletions]) => {
const completedHabits = habits.filter(habit => {
diff --git a/lib/types.ts b/lib/types.ts
index 25d5b33..f4eecf0 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -210,3 +210,16 @@ export interface ParsedFrequencyResult {
message: string | null
result: ParsedResultType
}
+
+export interface PomodoroAtom {
+ show: boolean
+ selectedHabitId: string | null
+ autoStart: boolean
+ minimized: boolean
+}
+
+export interface BrowserSettings {
+ expandedHabits: boolean
+ expandedTasks: boolean
+ expandedWishlist: boolean
+}
\ No newline at end of file
diff --git a/lib/utils.ts b/lib/utils.ts
index 85e238a..28939a5 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,12 +1,11 @@
import { toast } from "@/hooks/use-toast"
-import { CoinsData, CoinTransaction, Freq, Habit, HabitsData, ParsedFrequencyResult, ParsedResultType, SafeUser, Settings, User, UserData, WishlistData } from '@/lib/types'
+import { CoinsData, CoinTransaction, Freq, Habit, HabitsData, ParsedFrequencyResult, ParsedResultType, PomodoroAtom, SafeUser, Settings, User, UserData, WishlistData } from '@/lib/types'
import * as chrono from 'chrono-node'
import { clsx, type ClassValue } from "clsx"
import { DateTime, DateTimeFormatOptions } from "luxon"
import { Formats } from "next-intl"
import { datetime, RRule } from 'rrule'
import { twMerge } from "tailwind-merge"
-import { PomodoroAtom } from "./atoms"
import { DUE_MAP, INITIAL_DUE, RECURRENCE_RULE_MAP } from "./constants"
export function cn(...inputs: ClassValue[]) {