project-standalo-todo-super/app/lib/points.ts

174 lines
3.6 KiB
TypeScript

/**
* Points management utilities
*/
import {
Transaction,
TransactionType
} from './types';
import {
createTransaction,
getUserTransactions,
getUserBalance
} from './db/store';
/**
* Add points to a user's account
*/
export function addPoints(
userId: string,
amount: number,
source: string
): Transaction {
if (amount <= 0) {
throw new Error('Amount must be positive');
}
return createTransaction(userId, amount, TransactionType.EARNED, source);
}
/**
* Deduct points from a user's account
*/
export function deductPoints(
userId: string,
amount: number,
source: string
): Transaction {
if (amount <= 0) {
throw new Error('Amount must be positive');
}
const currentBalance = getBalance(userId);
if (currentBalance < amount) {
throw new Error('Insufficient balance');
}
return createTransaction(userId, amount, TransactionType.SPENT, source);
}
/**
* Get user's current points balance
*/
export function getBalance(userId: string): number {
return getUserBalance(userId);
}
/**
* Get all transactions for a user
*/
export function getTransactions(userId: string): Transaction[] {
return getUserTransactions(userId).sort(
(a, b) => b.createdAt.getTime() - a.createdAt.getTime()
);
}
/**
* Get recent transactions (last N)
*/
export function getRecentTransactions(
userId: string,
limit: number = 10
): Transaction[] {
const allTransactions = getTransactions(userId);
return allTransactions.slice(0, limit);
}
/**
* Get transaction statistics for a user
*/
export function getTransactionStats(userId: string): {
totalEarned: number;
totalSpent: number;
balance: number;
transactionCount: number;
} {
const transactions = getUserTransactions(userId);
const totalEarned = transactions
.filter(t => t.type === TransactionType.EARNED)
.reduce((sum, t) => sum + t.amount, 0);
const totalSpent = transactions
.filter(t => t.type === TransactionType.SPENT)
.reduce((sum, t) => sum + t.amount, 0);
return {
totalEarned,
totalSpent,
balance: totalEarned - totalSpent,
transactionCount: transactions.length
};
}
/**
* Get points earned from specific source
*/
export function getPointsBySource(
userId: string,
source: string
): number {
const transactions = getUserTransactions(userId);
return transactions
.filter(t => t.type === TransactionType.EARNED && t.source === source)
.reduce((sum, t) => sum + t.amount, 0);
}
/**
* Get breakdown of points by source
*/
export function getPointsBreakdown(userId: string): Record<string, number> {
const transactions = getUserTransactions(userId);
const breakdown: Record<string, number> = {};
transactions
.filter(t => t.type === TransactionType.EARNED)
.forEach(t => {
breakdown[t.source] = (breakdown[t.source] || 0) + t.amount;
});
return breakdown;
}
/**
* Check if user can afford a purchase
*/
export function canAfford(userId: string, amount: number): boolean {
return getBalance(userId) >= amount;
}
/**
* Award task completion points
*/
export function awardTaskPoints(
userId: string,
taskId: string,
amount: number
): Transaction {
return addPoints(userId, amount, `task:${taskId}`);
}
/**
* Award referral bonus points
*/
export function awardReferralBonus(
userId: string,
referralId: string,
amount: number
): Transaction {
return addPoints(userId, amount, `referral:${referralId}`);
}
/**
* Award daily streak bonus
*/
export function awardStreakBonus(
userId: string,
streakDays: number
): Transaction {
const bonusAmount = Math.min(streakDays * 5, 50); // Max 50 points
return addPoints(userId, bonusAmount, `streak:${streakDays}days`);
}