diff --git a/src/app/address-book/page.tsx b/src/app/address-book/page.tsx
new file mode 100644
index 0000000..f594dd6
--- /dev/null
+++ b/src/app/address-book/page.tsx
@@ -0,0 +1,144 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import {
+ getAddressBook,
+ addEntry,
+ updateEntry,
+ removeEntry,
+ type AddressEntry,
+} from "@/lib/addressBook";
+
+/**
+ * Address Book page — manage saved Stellar addresses with nicknames.
+ */
+export default function AddressBookPage() {
+ const [entries, setEntries] = useState([]);
+ const [nickname, setNickname] = useState("");
+ const [address, setAddress] = useState("");
+ const [editAddress, setEditAddress] = useState(null);
+ const [editNickname, setEditNickname] = useState("");
+
+ useEffect(() => {
+ setEntries(getAddressBook());
+ }, []);
+
+ const handleAdd = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!nickname.trim() || !address.trim()) return;
+ const updated = addEntry({ nickname: nickname.trim(), address: address.trim() });
+ setEntries(updated);
+ setNickname("");
+ setAddress("");
+ };
+
+ const handleEdit = (addr: string) => {
+ const entry = entries.find((e) => e.address === addr);
+ if (!entry) return;
+ setEditAddress(addr);
+ setEditNickname(entry.nickname);
+ };
+
+ const handleSaveEdit = (addr: string) => {
+ const updated = updateEntry(addr, { nickname: editNickname.trim() });
+ setEntries(updated);
+ setEditAddress(null);
+ };
+
+ const handleRemove = (addr: string) => {
+ setEntries(removeEntry(addr));
+ };
+
+ return (
+
+ Address Book
+
+ {/* Add new entry */}
+
+
+ {/* Saved entries */}
+ {entries.length === 0 ? (
+ No saved addresses yet.
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/src/app/invoice/[id]/page.tsx b/src/app/invoice/[id]/page.tsx
index 325594e..1cdbef6 100644
--- a/src/app/invoice/[id]/page.tsx
+++ b/src/app/invoice/[id]/page.tsx
@@ -5,6 +5,8 @@ import { splitClient } from "@/lib/stellar";
import { getFreighterPublicKey } from "@/lib/freighter";
import { formatAmount, parseAmount } from "@stellar-split/sdk";
import PaymentProgress from "@/components/PaymentProgress";
+import InstallmentPanel from "@/components/InstallmentPanel";
+import CommentSection from "@/components/CommentSection";
import type { Invoice } from "@stellar-split/sdk";
interface Props {
@@ -116,6 +118,11 @@ export default function InvoiceDetailPage({ params }: Props) {
+ {/* Installment schedule — only shown to payers with a registered plan */}
+ {publicKey && (
+
+ )}
+
{/* Pay form */}
{invoice.status === "Pending" && publicKey && (
)}
+
+ {/* Private notes — only visible to the connected wallet */}
+ {publicKey && (
+
+ )}
);
}
diff --git a/src/app/invoice/new/page.tsx b/src/app/invoice/new/page.tsx
index 6c4ae0c..2966b4f 100644
--- a/src/app/invoice/new/page.tsx
+++ b/src/app/invoice/new/page.tsx
@@ -26,6 +26,13 @@ export default function NewInvoicePage() {
);
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState(null);
+ const [equalSplit, setEqualSplit] = useState(false);
+ const [totalAmount, setTotalAmount] = useState("");
+
+ const perRecipientAmount =
+ equalSplit && totalAmount && recipients.length > 0
+ ? (parseFloat(totalAmount) / recipients.length).toFixed(7)
+ : undefined;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -39,7 +46,7 @@ export default function NewInvoicePage() {
creator,
recipients: recipients.map((r) => ({
address: r.address,
- amount: parseAmount(r.amount),
+ amount: parseAmount(equalSplit ? (perRecipientAmount ?? "0") : r.amount),
})),
token,
deadline: deadlineFromDays(deadlineDays),
@@ -58,12 +65,61 @@ export default function NewInvoicePage() {
Create Invoice