import { PencilIcon, PlusIcon, TagIcon, XIcon } from "lucide-react"; import React from "react"; import { toast } from "sonner"; import { Badge } from "src/components/ui/badge"; import { Button } from "src/components/ui/button"; import { Input } from "src/components/ui/input"; import { request } from "src/utils/request"; import { useSWRConfig } from "swr"; type Props = { id: number; labels?: Record; transactionListKey: string; }; type Row = { key: string; value: string }; function toRows(labels: Record | undefined): Row[] { if (!labels || Object.keys(labels).length === 0) { return [{ key: "", value: "" }]; } return Object.entries(labels).map(([key, value]) => ({ key, value })); } function rowsToLabels(rows: Row[]): Record { const result: Record = {}; for (const row of rows) { const key = row.key.trim(); const value = row.value.trim(); if (key && value) { result[key] = value; } } return result; } function TransactionLabels({ id, labels: initialLabels, transactionListKey, }: Props) { const { mutate } = useSWRConfig(); const [isEditing, setIsEditing] = React.useState(false); const [loading, setLoading] = React.useState(false); const [labels, setLabels] = React.useState>( initialLabels ?? {} ); const [rows, setRows] = React.useState(() => toRows(initialLabels)); React.useEffect(() => { setLabels(initialLabels ?? {}); if (!isEditing) { setRows(toRows(initialLabels)); } }, [initialLabels, isEditing]); const labelEntries = Object.entries(labels); const updateRow = (index: number, patch: Partial) => { setRows((prev) => prev.map((row, i) => (i === index ? { ...row, ...patch } : row)) ); }; const removeRow = (index: number) => { setRows((prev) => prev.filter((_, i) => i !== index)); }; const addRow = () => { setRows((prev) => [...prev, { key: "", value: "" }]); }; const startEditing = () => { setRows(toRows(labels)); setIsEditing(true); }; const cancelEditing = () => { setRows(toRows(labels)); setIsEditing(false); }; const saveLabels = async () => { const nextLabels = rowsToLabels(rows); setLoading(true); try { await request(`/api/transactions/${id}/labels`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ labels: nextLabels }), }); setLabels(nextLabels); setRows(toRows(nextLabels)); setIsEditing(false); await mutate(transactionListKey); toast("Labels saved"); } catch (error) { console.error(error); toast.error("Failed to save labels", { description: String(error), }); } finally { setLoading(false); } }; return (

Labels

{!isEditing && ( )}
{isEditing ? (
{rows.map((row, index) => (
updateRow(index, { key: e.target.value })} className="w-1/3" /> updateRow(index, { value: e.target.value })} />
))}
) : labelEntries.length > 0 ? (
{labelEntries.map(([key, value]) => ( {key}: {value} ))}
) : (

No labels yet.

)}
); } export default TransactionLabels;