fix: remittances totalAmount+clientId (FIBEROPS-221), create client URL (FIBEROPS-222), tickets /messages (FIBEROPS-224)

This commit is contained in:
root
2026-03-31 04:15:33 +00:00
parent 5843499815
commit a77759cc97
3 changed files with 10 additions and 10 deletions

View File

@@ -60,11 +60,11 @@ export default function ClientsPage() {
const createClient = useMutation({ const createClient = useMutation({
mutationFn: async () => { mutationFn: async () => {
const res = await api.post("/api/v1/clients/onboard", { const res = await api.post("/api/v1/clients", {
firstName: form.firstName, lastName: form.lastName, firstName: form.firstName, lastName: form.lastName,
email: form.email, phone: form.phone, address: form.address, email: form.email, phone: form.phone, address: form.address,
areaId: form.areaId || undefined, planId: form.planId, areaId: form.areaId || undefined,
billingType: form.billingType, planId: form.planId || undefined,
}); });
return res.data; return res.data;
}, },
@@ -73,7 +73,7 @@ export default function ClientsPage() {
qc.invalidateQueries({ queryKey: ["clients"] }); qc.invalidateQueries({ queryKey: ["clients"] });
setShowAdd(false); setShowAdd(false);
setForm({ firstName: "", lastName: "", email: "", phone: "", address: "", areaId: "", planId: "", billingType: "POSTPAID" }); setForm({ firstName: "", lastName: "", email: "", phone: "", address: "", areaId: "", planId: "", billingType: "POSTPAID" });
if (data?.client?.id) router.push(`/clients/${data.client.id}`); if (data?.id) router.push(`/clients/${data.id}`);
}, },
onError: (e: any) => toast.error(e.response?.data?.message ?? "Failed to create client"), onError: (e: any) => toast.error(e.response?.data?.message ?? "Failed to create client"),
}); });

View File

@@ -114,7 +114,7 @@ export default function RemittancesPage() {
<TableRow key={r.id} data-testid="remittance-row" onClick={() => setSelected(r)} className="cursor-pointer hover:bg-blue-50 transition-colors"> <TableRow key={r.id} data-testid="remittance-row" onClick={() => setSelected(r)} className="cursor-pointer hover:bg-blue-50 transition-colors">
<Td>{formatDate(r.createdAt)}</Td> <Td>{formatDate(r.createdAt)}</Td>
<Td className="font-medium">{r.collector ? `${r.collector.firstName} ${r.collector.lastName}` : "—"}</Td> <Td className="font-medium">{r.collector ? `${r.collector.firstName} ${r.collector.lastName}` : "—"}</Td>
<Td className="font-semibold text-blue-700">{formatCurrency(Number(r.amount))}</Td> <Td className="font-semibold text-blue-700">{formatCurrency(Number(r.totalAmount))}</Td>
<Td><Badge variant={statusVariant[r.status] ?? "muted"}>{r.status}</Badge></Td> <Td><Badge variant={statusVariant[r.status] ?? "muted"}>{r.status}</Badge></Td>
<Td className="text-gray-500 text-sm">{r.notes ?? "—"}</Td> <Td className="text-gray-500 text-sm">{r.notes ?? "—"}</Td>
<Td> <Td>
@@ -152,7 +152,7 @@ export default function RemittancesPage() {
<div className="space-y-1.5 max-h-48 overflow-y-auto"> <div className="space-y-1.5 max-h-48 overflow-y-auto">
{selected.payments.map(p => ( {selected.payments.map(p => (
<div key={p.id} className="flex justify-between text-sm bg-white border rounded-lg px-3 py-2"> <div key={p.id} className="flex justify-between text-sm bg-white border rounded-lg px-3 py-2">
<span className="text-gray-600">{p.client ? `${p.client.firstName} ${p.client.lastName}` : p.clientId.slice(0, 8)}</span> <span className="text-gray-600">{p.client ? `${p.client.firstName} ${p.client.lastName}` : p.clientId ? p.clientId.slice(0, 8) : p.id.slice(0, 8)}</span>
<span className="font-medium text-green-700">{formatCurrency(Number(p.amount))}</span> <span className="font-medium text-green-700">{formatCurrency(Number(p.amount))}</span>
</div> </div>
))} ))}

View File

@@ -13,7 +13,7 @@ import { formatDate } from "@/lib/utils";
import api from "@/lib/api"; import api from "@/lib/api";
import { toast } from "sonner"; import { toast } from "sonner";
interface TicketComment { id: string; body: string; createdAt: string; author?: { firstName: string; lastName: string }; } interface TicketComment { id: string; body: string; createdAt: string; sender?: { firstName: string; lastName: string }; author?: { firstName: string; lastName: string }; }
interface TicketItem { interface TicketItem {
id: string; ticketNumber?: string; subject: string; description?: string; id: string; ticketNumber?: string; subject: string; description?: string;
type: string; priority: string; status: string; clientId?: string; type: string; priority: string; status: string; clientId?: string;
@@ -86,7 +86,7 @@ export default function TicketsPage() {
const addComment = useMutation({ const addComment = useMutation({
mutationFn: async () => { mutationFn: async () => {
await api.post(`/api/v1/tickets/${selected!.id}/comments`, { body: comment }); await api.post(`/api/v1/tickets/${selected!.id}/messages`, { body: comment });
}, },
onSuccess: () => { toast.success("Comment added"); setComment(""); refetchDetail(); }, onSuccess: () => { toast.success("Comment added"); setComment(""); refetchDetail(); },
onError: (e: any) => toast.error(e.response?.data?.message ?? "Failed"), onError: (e: any) => toast.error(e.response?.data?.message ?? "Failed"),
@@ -112,7 +112,7 @@ export default function TicketsPage() {
const tickets = data?.data ?? []; const tickets = data?.data ?? [];
const total = data?.meta?.total ?? 0; const total = data?.meta?.total ?? 0;
const detail = ticketDetail ?? selected; const detail = ticketDetail ?? selected;
const comments = (ticketDetail as any)?.comments ?? []; const comments = (ticketDetail as any)?.messages ?? [];
const nextStatus: Record<string, string> = { OPEN: "IN_PROGRESS", IN_PROGRESS: "RESOLVED", RESOLVED: "CLOSED" }; const nextStatus: Record<string, string> = { OPEN: "IN_PROGRESS", IN_PROGRESS: "RESOLVED", RESOLVED: "CLOSED" };
@@ -216,7 +216,7 @@ export default function TicketsPage() {
{comments.length === 0 ? <p className="text-sm text-gray-400">No comments yet.</p> : {comments.length === 0 ? <p className="text-sm text-gray-400">No comments yet.</p> :
comments.map((c: TicketComment) => ( comments.map((c: TicketComment) => (
<div key={c.id} className="bg-white border rounded-lg px-3 py-2 text-sm"> <div key={c.id} className="bg-white border rounded-lg px-3 py-2 text-sm">
<p className="font-medium text-gray-700 text-xs">{c.author ? `${c.author.firstName} ${c.author.lastName}` : "Staff"} · {formatDate(c.createdAt)}</p> <p className="font-medium text-gray-700 text-xs">{(c.sender || c.author) ? `${(c.sender || c.author)!.firstName} ${(c.sender || c.author)!.lastName}` : "Staff"} · {formatDate(c.createdAt)}</p>
<p className="text-gray-800 mt-0.5">{c.body}</p> <p className="text-gray-800 mt-0.5">{c.body}</p>
</div> </div>
)) ))