fix(participants): show Me contextually per logged-in user
Participant id=1 was named "Me" in the DB, causing Sonu and other users to see "Me" referring to Siddharth when viewing splits and shared expenses. - Rename participant id=1 from "Me" to "Siddharth" in the DB - /api/participants now substitutes "Me" for whichever participant matches the current user, so the label is always relative to the viewer - split-modal: default split uses currentUser.id instead of name === "Me" - transactions/page: filter and display logic uses participant ID not name - shared/page: split chips show "Me" when participant_id === current user Also includes add-transaction-modal tags support (pre-existing staged change).
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useCreateTransaction, useParticipants } from "@/lib/hooks";
|
||||
import { useCreateTransaction, useParticipants, useTags } from "@/lib/hooks";
|
||||
import { CATEGORIES, formatCategory } from "@/lib/categories";
|
||||
|
||||
const TRANSACTION_TYPES = ["debit", "credit", "payment", "refund", "fee", "interest", "transfer"];
|
||||
@@ -27,6 +27,7 @@ export function AddTransactionModal({
|
||||
}) {
|
||||
const createTransaction = useCreateTransaction();
|
||||
const { data: participants = [] } = useParticipants();
|
||||
const { data: allTags = [] } = useTags();
|
||||
|
||||
const [date, setDate] = useState(prefill?.date ?? new Date().toISOString().slice(0, 10));
|
||||
const [description, setDescription] = useState(prefill?.description ?? "");
|
||||
@@ -34,6 +35,7 @@ export function AddTransactionModal({
|
||||
const [type, setType] = useState(prefill?.transaction_type ?? "debit");
|
||||
const [merchant, setMerchant] = useState(prefill?.merchant_normalized ?? "");
|
||||
const [category, setCategory] = useState(prefill?.category ?? "");
|
||||
const [selectedTagIds, setSelectedTagIds] = useState<number[]>([]);
|
||||
const [splits, setSplits] = useState<{ participant_id: number; share_percent: number }[]>(
|
||||
prefill?.splits ?? []
|
||||
);
|
||||
@@ -51,9 +53,15 @@ export function AddTransactionModal({
|
||||
setSplits(splits.filter((_, idx) => idx !== i));
|
||||
}
|
||||
|
||||
function toggleTag(id: number) {
|
||||
setSelectedTagIds((prev) =>
|
||||
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
|
||||
);
|
||||
}
|
||||
|
||||
async function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
await createTransaction.mutateAsync({
|
||||
const result = await createTransaction.mutateAsync({
|
||||
date,
|
||||
description,
|
||||
amount: parseFloat(amount),
|
||||
@@ -62,6 +70,17 @@ export function AddTransactionModal({
|
||||
category: category || undefined,
|
||||
splits: splits.length ? splits : undefined,
|
||||
});
|
||||
if (selectedTagIds.length && result?.id) {
|
||||
await Promise.all(
|
||||
selectedTagIds.map((tagId) =>
|
||||
fetch(`/api/transactions/${result.id}/tags`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ tag_id: tagId }),
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
onClose();
|
||||
}
|
||||
|
||||
@@ -150,6 +169,37 @@ export function AddTransactionModal({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tags */}
|
||||
{allTags.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-xs text-zinc-500 mb-1">Tags (optional)</label>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{allTags.map((tag) => {
|
||||
const selected = selectedTagIds.includes(tag.id);
|
||||
return (
|
||||
<button
|
||||
key={tag.id}
|
||||
type="button"
|
||||
onClick={() => toggleTag(tag.id)}
|
||||
className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-xs border transition-colors ${
|
||||
selected
|
||||
? "border-transparent text-white"
|
||||
: "border-zinc-600 text-zinc-400 hover:border-zinc-500 hover:text-zinc-300"
|
||||
}`}
|
||||
style={selected ? { backgroundColor: tag.color + "cc", borderColor: tag.color } : {}}
|
||||
>
|
||||
<span
|
||||
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
|
||||
style={{ backgroundColor: tag.color }}
|
||||
/>
|
||||
{tag.name}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Splits */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useParticipants, useSetSplits, useTransactionSplits, useBulkAction, useCreateRule } from "@/lib/hooks";
|
||||
import { useParticipants, useSetSplits, useTransactionSplits, useBulkAction, useCreateRule, useCurrentUser } from "@/lib/hooks";
|
||||
|
||||
interface Split {
|
||||
participant_id: number;
|
||||
@@ -22,6 +22,7 @@ export function SplitModal({ transactionId, transactionIds, amount, description,
|
||||
const singleId = transactionId ?? 0;
|
||||
|
||||
const { data: participants } = useParticipants();
|
||||
const { data: currentUser } = useCurrentUser();
|
||||
const { data: existingSplits } = useTransactionSplits(isBulk ? 0 : singleId);
|
||||
const setSplits = useSetSplits();
|
||||
const bulkAction = useBulkAction();
|
||||
@@ -34,10 +35,9 @@ export function SplitModal({ transactionId, transactionIds, amount, description,
|
||||
|
||||
// Initialise: bulk always defaults to 100% Me; single loads existing splits
|
||||
useEffect(() => {
|
||||
if (!participants || participants.length === 0) return;
|
||||
const me = participants.find((p) => p.name === "Me");
|
||||
if (!participants || participants.length === 0 || !currentUser) return;
|
||||
if (isBulk) {
|
||||
if (me) setSplitsState([{ participant_id: me.id, share_percent: 100 }]);
|
||||
setSplitsState([{ participant_id: currentUser.id, share_percent: 100 }]);
|
||||
} else if (existingSplits && existingSplits.length > 0) {
|
||||
setSplitsState(
|
||||
existingSplits.map((s: { participant_id: number; share_percent: number }) => ({
|
||||
@@ -45,10 +45,10 @@ export function SplitModal({ transactionId, transactionIds, amount, description,
|
||||
share_percent: Number(s.share_percent),
|
||||
}))
|
||||
);
|
||||
} else if (me) {
|
||||
setSplitsState([{ participant_id: me.id, share_percent: 100 }]);
|
||||
} else {
|
||||
setSplitsState([{ participant_id: currentUser.id, share_percent: 100 }]);
|
||||
}
|
||||
}, [existingSplits, participants, isBulk]);
|
||||
}, [existingSplits, participants, isBulk, currentUser]);
|
||||
|
||||
const total = splits.reduce((sum, s) => sum + s.share_percent, 0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user