102 lines
2.6 KiB
TypeScript
102 lines
2.6 KiB
TypeScript
"use client";
|
|
import { create } from "zustand";
|
|
import { calculateSplits, type Payment } from "@/lib/utils/expense";
|
|
import type { Group, User } from "@/server/db/schema";
|
|
|
|
export enum SplitTypeEnum {
|
|
Fixed = "fixed",
|
|
Percentage = "percentage",
|
|
Equal = "equal",
|
|
}
|
|
export type SplitType = keyof typeof SplitTypeEnum;
|
|
export const splitTypeKeys = Object.keys(SplitTypeEnum);
|
|
|
|
interface ExpenseStore {
|
|
amount: number;
|
|
|
|
participants: User[];
|
|
groupBadges: Group[];
|
|
|
|
friendTarget?: string;
|
|
splitType: SplitType;
|
|
payments: Payment[];
|
|
splits: Record<string, string>;
|
|
|
|
setAmount: (amount: number) => void;
|
|
|
|
addGroupBadges: (groupIds: Group[]) => void;
|
|
removeGroupBadge: (groupIds: string[]) => void;
|
|
|
|
addParticipants: (user: User[]) => void;
|
|
removeParticipant: (userId: string) => void;
|
|
|
|
setFriendTarget: (id?: string) => void;
|
|
setSplitType: (type: SplitType) => void;
|
|
setPayments: (payments: Array<Payment>) => void;
|
|
recalculateSplits: () => void;
|
|
resetExpenseStore: () => void;
|
|
}
|
|
|
|
const defaultValues: Pick<
|
|
ExpenseStore,
|
|
| "amount"
|
|
| "participants"
|
|
| "friendTarget"
|
|
| "splitType"
|
|
| "payments"
|
|
| "splits"
|
|
| "groupBadges"
|
|
> = {
|
|
amount: 0,
|
|
participants: [],
|
|
groupBadges: [],
|
|
friendTarget: undefined,
|
|
splitType: "Equal",
|
|
payments: [],
|
|
splits: {},
|
|
};
|
|
|
|
export const useExpenseStore = create<ExpenseStore>((set, get) => ({
|
|
...defaultValues,
|
|
setAmount: (amount) => set({ amount }),
|
|
addParticipants: (newUsers) => {
|
|
const { participants } = get();
|
|
const uniqueUsers = newUsers.filter(
|
|
(user) => !participants.find((u) => u.id === user.id)
|
|
);
|
|
set({ participants: [...participants, ...uniqueUsers] });
|
|
},
|
|
removeParticipant: (userId) => {
|
|
const filtered = get().participants.filter((user) => user.id !== userId);
|
|
set({ participants: filtered });
|
|
},
|
|
|
|
setFriendTarget: (id) => set({ friendTarget: id }),
|
|
setSplitType: (type) => set({ splitType: type }),
|
|
setPayments: (payments) => set({ payments }),
|
|
|
|
recalculateSplits: () => {
|
|
const { amount, participants, splitType } = get();
|
|
const splits = calculateSplits({
|
|
amount,
|
|
users: participants.map((u) => u.id!),
|
|
splitType,
|
|
});
|
|
set({ splits });
|
|
},
|
|
|
|
addGroupBadges: (newGroups) => {
|
|
const { groupBadges } = get();
|
|
const uniqueGroups = newGroups.filter(
|
|
(group) => !groupBadges.find((g) => g.id === group.id)
|
|
);
|
|
set({ groupBadges: [...groupBadges, ...uniqueGroups] });
|
|
},
|
|
removeGroupBadge: (groupIds) =>
|
|
set({
|
|
groupBadges: get().groupBadges.filter(({ id }) => !groupIds.includes(id)),
|
|
}),
|
|
|
|
resetExpenseStore: () => set(defaultValues),
|
|
}));
|