From ee3a27c65a4831aae6617ed329eae758f2abca06 Mon Sep 17 00:00:00 2001 From: Rampop01 Date: Wed, 26 Aug 2026 07:15:42 +0100 Subject: [PATCH 1/5] feat: Implement Savings Group Dashboard UI --- src/app/(dashboard)/groups/[id]/page.tsx | 112 +++++++++++- src/app/(dashboard)/layout.tsx | 19 ++ src/app/(dashboard)/payments/page.tsx | 4 +- .../payments/receipt/[id]/page.tsx | 163 +++++++++++++----- src/components/common/Modal/Modal.tsx | 38 +++- src/components/groups/GroupHeader.tsx | 46 +++++ src/components/groups/GroupStats.tsx | 35 ++++ src/components/groups/HelpCard.tsx | 17 ++ src/components/groups/InviteMemberModal.tsx | 70 ++++++++ src/components/groups/MembersList.tsx | 85 +++++++++ src/components/groups/NextMilestone.tsx | 99 +++++++++++ src/components/groups/RecentDeposits.tsx | 59 +++++++ src/components/layout/Header/Header.tsx | 32 +++- src/components/layout/Sidebar/Sidebar.tsx | 47 ++++- 14 files changed, 771 insertions(+), 55 deletions(-) create mode 100644 src/app/(dashboard)/layout.tsx create mode 100644 src/components/groups/GroupHeader.tsx create mode 100644 src/components/groups/GroupStats.tsx create mode 100644 src/components/groups/HelpCard.tsx create mode 100644 src/components/groups/InviteMemberModal.tsx create mode 100644 src/components/groups/MembersList.tsx create mode 100644 src/components/groups/NextMilestone.tsx create mode 100644 src/components/groups/RecentDeposits.tsx diff --git a/src/app/(dashboard)/groups/[id]/page.tsx b/src/app/(dashboard)/groups/[id]/page.tsx index 9b69909..7556dcf 100644 --- a/src/app/(dashboard)/groups/[id]/page.tsx +++ b/src/app/(dashboard)/groups/[id]/page.tsx @@ -1,3 +1,113 @@ +"use client"; + +import React, { useState } from "react"; +import { GroupHeader } from "../../../../components/groups/GroupHeader"; +import { NextMilestone } from "../../../../components/groups/NextMilestone"; +import { RecentDeposits } from "../../../../components/groups/RecentDeposits"; +import { MembersList, Member } from "../../../../components/groups/MembersList"; +import { GroupStats } from "../../../../components/groups/GroupStats"; +import { HelpCard } from "../../../../components/groups/HelpCard"; +import { InviteMemberModal } from "../../../../components/groups/InviteMemberModal"; + export default function GroupDetails() { - return
Group Details
; + const [isInviteModalOpen, setIsInviteModalOpen] = useState(false); + + // Mock data to match the Figma design + const members: Member[] = [ + { + id: "1", + name: "Segun Arinze", + role: "ADMIN", + status: "CONTRIBUTED", + avatarUrl: "https://i.pravatar.cc/150?u=1", + }, + { + id: "2", + name: "Titi Balogun", + role: "CONTRIBUTOR", + status: "CONTRIBUTED", + avatarUrl: "https://i.pravatar.cc/150?u=2", + }, + { + id: "3", + name: "Daniel K.", + role: "PENDING", + status: "WAITING", + avatarUrl: "https://i.pravatar.cc/150?u=3", + }, + { + id: "4", + name: "Mama Funke", + role: "CONTRIBUTOR", + status: "CONTRIBUTED", + avatarUrl: "https://i.pravatar.cc/150?u=4", + }, + ]; + + const deposits = [ + { + id: "d1", + name: "Uncle Segun", + time: "TODAY, 10:45 AM", + amount: 25000, + avatarUrl: "https://i.pravatar.cc/150?u=1", + isCurrentUser: false, + }, + { + id: "d2", + name: "Aunty Titi", + time: "YESTERDAY", + amount: 15000, + avatarUrl: "https://i.pravatar.cc/150?u=2", + isCurrentUser: true, + }, + ]; + + const handleInvite = (emailOrPhone: string) => { + console.log("Inviting:", emailOrPhone); + setIsInviteModalOpen(false); + }; + + return ( +
+ + +
+ {/* Main Content Area */} +
+
+ console.log("Contribute clicked")} + /> +
+ +
+ + {/* Sidebar Content Area */} +
+ setIsInviteModalOpen(true)} + /> + + +
+
+ + setIsInviteModalOpen(false)} + onInvite={handleInvite} + /> +
+ ); } diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx new file mode 100644 index 0000000..dc33ad7 --- /dev/null +++ b/src/app/(dashboard)/layout.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Header } from "../../components/layout/Header/Header"; +import { Sidebar } from "../../components/layout/Sidebar/Sidebar"; + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+
+ +
{children}
+
+
+ ); +} diff --git a/src/app/(dashboard)/payments/page.tsx b/src/app/(dashboard)/payments/page.tsx index 8e0d4d2..d785e95 100644 --- a/src/app/(dashboard)/payments/page.tsx +++ b/src/app/(dashboard)/payments/page.tsx @@ -4,8 +4,8 @@ export default function Payments() { return (

Payments

- View Last Transaction Receipt diff --git a/src/app/(dashboard)/payments/receipt/[id]/page.tsx b/src/app/(dashboard)/payments/receipt/[id]/page.tsx index 0efed26..8b01bb7 100644 --- a/src/app/(dashboard)/payments/receipt/[id]/page.tsx +++ b/src/app/(dashboard)/payments/receipt/[id]/page.tsx @@ -1,7 +1,15 @@ "use client"; import { motion } from "framer-motion"; -import { CheckCircle, XCircle, Share2, Download, ArrowLeft, ExternalLink, Loader2 } from "lucide-react"; +import { + CheckCircle, + XCircle, + Share2, + Download, + ArrowLeft, + ExternalLink, + Loader2, +} from "lucide-react"; import Link from "next/link"; import { use, useEffect, useState } from "react"; @@ -18,7 +26,11 @@ interface TxData { fullHash: string; } -export default function TransactionReceipt({ params }: { params: Promise<{ id: string }> }) { +export default function TransactionReceipt({ + params, +}: { + params: Promise<{ id: string }>; +}) { const unwrappedParams = use(params); const [mounted, setMounted] = useState(false); const [loading, setLoading] = useState(true); @@ -35,28 +47,47 @@ export default function TransactionReceipt({ params }: { params: Promise<{ id: s } try { - const txRes = await fetch(`https://horizon-testnet.stellar.org/transactions/${unwrappedParams.id}`); + const txRes = await fetch( + `https://horizon-testnet.stellar.org/transactions/${unwrappedParams.id}`, + ); if (txRes.ok) { const tx = await txRes.json(); - const opsRes = await fetch(`https://horizon-testnet.stellar.org/transactions/${unwrappedParams.id}/operations`); + const opsRes = await fetch( + `https://horizon-testnet.stellar.org/transactions/${unwrappedParams.id}/operations`, + ); const ops = await opsRes.json(); - const paymentOp = ops._embedded?.records?.find((op: { type: string; amount?: string; starting_balance?: string; to?: string; account?: string; asset_code?: string; }) => op.type === "payment" || op.type === "create_account"); - + const paymentOp = ops._embedded?.records?.find( + (op: { + type: string; + amount?: string; + starting_balance?: string; + to?: string; + account?: string; + asset_code?: string; + }) => op.type === "payment" || op.type === "create_account", + ); + if (active) { setTxData({ id: unwrappedParams.id, - amount: paymentOp?.amount || paymentOp?.starting_balance || "0.00", + amount: + paymentOp?.amount || paymentOp?.starting_balance || "0.00", currency: paymentOp?.asset_code || "XLM", status: tx.successful ? "Success" : "Failed", date: new Date(tx.created_at).toLocaleString("en-US", { - month: "short", day: "numeric", year: "numeric", - hour: "numeric", minute: "numeric", hour12: true, + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, }), from: tx.source_account, to: paymentOp?.to || paymentOp?.account || "Unknown", - networkFee: (parseInt(tx.fee_charged) / 10000000).toString() + " XLM", + networkFee: + (parseInt(tx.fee_charged) / 10000000).toString() + " XLM", network: "Stellar Testnet", - fullHash: unwrappedParams.id + fullHash: unwrappedParams.id, }); } } @@ -69,7 +100,7 @@ export default function TransactionReceipt({ params }: { params: Promise<{ id: s } } } - + loadTransaction(); return () => { @@ -86,14 +117,18 @@ export default function TransactionReceipt({ params }: { params: Promise<{ id: s currency: "USDC", status: "Success", date: new Date().toLocaleString("en-US", { - month: "short", day: "numeric", year: "numeric", - hour: "numeric", minute: "numeric", hour12: true, + month: "short", + day: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + hour12: true, }), from: "GBX434KV35F52345K2L3M", to: "GABC1234KJ5H234K5J23X1", networkFee: "0.00001 XLM", network: "Stellar Testnet", - fullHash: unwrappedParams.id || "f4a8b...19c2" + fullHash: unwrappedParams.id || "f4a8b...19c2", }; const shortenStr = (str: string) => { @@ -103,10 +138,12 @@ export default function TransactionReceipt({ params }: { params: Promise<{ id: s const handleShare = () => { if (navigator.share) { - navigator.share({ - title: 'Stellar Transaction Receipt', - url: window.location.href - }).catch(console.error); + navigator + .share({ + title: "Stellar Transaction Receipt", + url: window.location.href, + }) + .catch(console.error); } else { navigator.clipboard.writeText(window.location.href); alert("Link copied to clipboard!"); @@ -133,7 +170,7 @@ export default function TransactionReceipt({ params }: { params: Promise<{ id: s
- )} - - @@ -163,7 +205,7 @@ export default function TransactionReceipt({ params }: { params: Promise<{ id: s )} - Payment {transaction.status} - - {transaction.status === "Failed" + {transaction.status === "Failed" ? "Your transaction could not be completed." : "Your transaction has been processed."} @@ -191,16 +233,20 @@ export default function TransactionReceipt({ params }: { params: Promise<{ id: s {transaction.amount} - + {transaction.currency}
-
Date - {transaction.date} + + {transaction.date} +
- +
- +
From - {shortenStr(transaction.from)} + + {shortenStr(transaction.from)} +
To - {shortenStr(transaction.to)} + + {shortenStr(transaction.to)} +
- +
- Network Fee - {transaction.networkFee} + + Network Fee + + + {transaction.networkFee} +
- - - +
+
{children}
+
+
+ ); +}; diff --git a/src/components/groups/GroupHeader.tsx b/src/components/groups/GroupHeader.tsx new file mode 100644 index 0000000..d8cadf3 --- /dev/null +++ b/src/components/groups/GroupHeader.tsx @@ -0,0 +1,46 @@ +import React from "react"; + +interface GroupHeaderProps { + title: string; + totalSavings: number; + activeMembers: number; +} + +export const GroupHeader: React.FC = ({ + title, + totalSavings, + activeMembers, +}) => { + return ( +
+
+

{title}

+
+
+
+ avatar +
+
+ avatar +
+
+ avatar +
+
+ +5 +
+
+ {activeMembers} Members Active +
+
+
+

+ Total Group Savings +

+

+ ₦{totalSavings.toLocaleString()} +

+
+
+ ); +}; diff --git a/src/components/groups/GroupStats.tsx b/src/components/groups/GroupStats.tsx new file mode 100644 index 0000000..f7c0522 --- /dev/null +++ b/src/components/groups/GroupStats.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { TrendingUp, Clock } from "lucide-react"; + +interface GroupStatsProps { + efficiency: number; + streak: number; +} + +export const GroupStats: React.FC = ({ + efficiency, + streak, +}) => { + return ( +
+
+
+ +
+

+ Efficiency +

+

{efficiency}%

+
+
+
+ +
+

+ Streak +

+

{streak} Mos

+
+
+ ); +}; diff --git a/src/components/groups/HelpCard.tsx b/src/components/groups/HelpCard.tsx new file mode 100644 index 0000000..48de85a --- /dev/null +++ b/src/components/groups/HelpCard.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { MessageCircle } from "lucide-react"; + +export const HelpCard: React.FC = () => { + return ( +
+

Need Help?

+

+ Chat with your group assistant on WhatsApp +

+ +
+ ); +}; diff --git a/src/components/groups/InviteMemberModal.tsx b/src/components/groups/InviteMemberModal.tsx new file mode 100644 index 0000000..5106997 --- /dev/null +++ b/src/components/groups/InviteMemberModal.tsx @@ -0,0 +1,70 @@ +import React, { useState } from "react"; +import { Modal } from "../common/Modal/Modal"; + +interface InviteMemberModalProps { + isOpen: boolean; + onClose: () => void; + onInvite: (emailOrPhone: string) => void; +} + +export const InviteMemberModal: React.FC = ({ + isOpen, + onClose, + onInvite, +}) => { + const [inputValue, setInputValue] = useState(""); + const [error, setError] = useState(""); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (!inputValue.trim()) { + setError("Please enter an email address or phone number."); + return; + } + setError(""); + onInvite(inputValue); + setInputValue(""); + }; + + return ( + +
+
+ + { + setInputValue(e.target.value); + if (error) setError(""); + }} + /> + {error &&

{error}

} +
+
+ + +
+
+
+ ); +}; diff --git a/src/components/groups/MembersList.tsx b/src/components/groups/MembersList.tsx new file mode 100644 index 0000000..4b99061 --- /dev/null +++ b/src/components/groups/MembersList.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import { UserPlus } from "lucide-react"; + +export type MemberStatus = "CONTRIBUTED" | "WAITING" | "PENDING"; + +export interface Member { + id: string; + name: string; + role: string; + status: MemberStatus; + avatarUrl?: string; +} + +interface MembersListProps { + members: Member[]; + onInvite: () => void; +} + +export const MembersList: React.FC = ({ + members, + onInvite, +}) => { + const getStatusStyle = (status: MemberStatus) => { + switch (status) { + case "CONTRIBUTED": + return "bg-[#a3f4cd] text-[#047857]"; + case "WAITING": + return "bg-gray-100 text-gray-500"; + case "PENDING": + default: + return "bg-gray-100 text-gray-500"; + } + }; + + return ( +
+
+

Members

+ +
+ +
+ {members.map((member) => ( +
+
+
+ {member.avatarUrl ? ( + {member.name} + ) : ( +
+ {member.name.charAt(0)} +
+ )} +
+
+

+ {member.name} +

+

+ {member.role} +

+
+
+
+ + {member.status} + +
+
+ ))} +
+
+ ); +}; diff --git a/src/components/groups/NextMilestone.tsx b/src/components/groups/NextMilestone.tsx new file mode 100644 index 0000000..26a342b --- /dev/null +++ b/src/components/groups/NextMilestone.tsx @@ -0,0 +1,99 @@ +import React from "react"; +import { GraduationCap, PlusCircle } from "lucide-react"; + +interface NextMilestoneProps { + milestoneName: string; + progressPercentage: number; + amountLeft: number; + targetPayout: number; + dueDate: string; + onContribute: () => void; +} + +export const NextMilestone: React.FC = ({ + milestoneName, + progressPercentage, + amountLeft, + targetPayout, + dueDate, + onContribute, +}) => { + const circleRadius = 70; + const circleCircumference = 2 * Math.PI * circleRadius; + const strokeDashoffset = + circleCircumference - (progressPercentage / 100) * circleCircumference; + + return ( +
+
+ +
+ +
+

+ Next Milestone: {milestoneName} +

+
+ +
+
+ + + + +
+ + {progressPercentage}% + + + ₦{amountLeft.toLocaleString()} left + +
+
+
+ +
+
+

Target Payout

+

+ ₦{targetPayout.toLocaleString()} +

+
+
+

Due Date

+

{dueDate}

+
+
+ +
+ +
+
+ ); +}; diff --git a/src/components/groups/RecentDeposits.tsx b/src/components/groups/RecentDeposits.tsx new file mode 100644 index 0000000..2dbc707 --- /dev/null +++ b/src/components/groups/RecentDeposits.tsx @@ -0,0 +1,59 @@ +import React from "react"; + +interface Deposit { + id: string; + name: string; + time: string; + amount: number; + avatarUrl?: string; + isCurrentUser?: boolean; +} + +interface RecentDepositsProps { + deposits: Deposit[]; +} + +export const RecentDeposits: React.FC = ({ deposits }) => { + return ( +
+

Recent Deposits

+
+ {deposits.map((deposit) => ( +
+
+
+ {deposit.avatarUrl ? ( + {deposit.name} + ) : ( + + {deposit.name.charAt(0)} + + )} +
+
+

+ {deposit.name} +

+

+ {deposit.time} +

+
+
+
+ +₦{deposit.amount.toLocaleString()} +
+
+ ))} +
+
+ ); +}; diff --git a/src/components/layout/Header/Header.tsx b/src/components/layout/Header/Header.tsx index b2d9080..e418768 100644 --- a/src/components/layout/Header/Header.tsx +++ b/src/components/layout/Header/Header.tsx @@ -1 +1,31 @@ -export const Header = () =>
Header
; +import React from "react"; +import { ArrowLeft, Bell, Wallet } from "lucide-react"; + +export const Header = () => { + return ( +
+
+ +

Kolo

+
+
+ + +
+ Profile +
+
+
+ ); +}; diff --git a/src/components/layout/Sidebar/Sidebar.tsx b/src/components/layout/Sidebar/Sidebar.tsx index d7df5c1..4c0bed1 100644 --- a/src/components/layout/Sidebar/Sidebar.tsx +++ b/src/components/layout/Sidebar/Sidebar.tsx @@ -1 +1,46 @@ -export const Sidebar = () => ; +import React from "react"; +import { Home, PiggyBank, Users, CreditCard, Settings } from "lucide-react"; + +export const Sidebar = () => { + const navItems = [ + { name: "Home", icon: Home, active: false }, + { name: "Savings", icon: PiggyBank, active: false }, + { name: "Groups", icon: Users, active: true }, + { name: "Payments", icon: CreditCard, active: false }, + { name: "Settings", icon: Settings, active: false }, + ]; + + return ( + + ); +}; From 41f1b0354fc3f718363cfefd984563167fe8c0d5 Mon Sep 17 00:00:00 2001 From: Rampop01 Date: Wed, 26 Aug 2026 08:18:22 +0100 Subject: [PATCH 2/5] fix: Address review comments for Dashboard UI --- src/app/(dashboard)/groups/[id]/page.tsx | 45 +++++++- src/components/common/Modal/Modal.tsx | 1 + src/components/groups/InviteMemberModal.tsx | 14 ++- src/components/groups/MembersList.tsx | 12 +- src/components/layout/Header/Header.tsx | 25 ++++- src/components/layout/Sidebar/Sidebar.tsx | 116 +++++++++++++------- 6 files changed, 164 insertions(+), 49 deletions(-) diff --git a/src/app/(dashboard)/groups/[id]/page.tsx b/src/app/(dashboard)/groups/[id]/page.tsx index 7556dcf..05d6cd9 100644 --- a/src/app/(dashboard)/groups/[id]/page.tsx +++ b/src/app/(dashboard)/groups/[id]/page.tsx @@ -1,6 +1,9 @@ "use client"; -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; +import { useParams } from "next/navigation"; +import { groupsService } from "../../../../services/api/groups"; +import type { Group } from "../../../../types/group"; import { GroupHeader } from "../../../../components/groups/GroupHeader"; import { NextMilestone } from "../../../../components/groups/NextMilestone"; import { RecentDeposits } from "../../../../components/groups/RecentDeposits"; @@ -10,8 +13,22 @@ import { HelpCard } from "../../../../components/groups/HelpCard"; import { InviteMemberModal } from "../../../../components/groups/InviteMemberModal"; export default function GroupDetails() { + const params = useParams<{ id: string }>(); + const [group, setGroup] = useState(null); + const [isLoading, setIsLoading] = useState(true); const [isInviteModalOpen, setIsInviteModalOpen] = useState(false); + useEffect(() => { + if (params?.id) { + groupsService.getGroup(params.id).then((fetched) => { + setGroup(fetched); + setIsLoading(false); + }); + } else { + setIsLoading(false); + } + }, [params?.id]); + // Mock data to match the Figma design const members: Member[] = [ { @@ -68,12 +85,28 @@ export default function GroupDetails() { setIsInviteModalOpen(false); }; + if (isLoading) { + return ( +
+

Loading group...

+
+ ); + } + + if (!group) { + return ( +
+

Group not found or unavailable.

+
+ ); + } + return (
@@ -84,7 +117,11 @@ export default function GroupDetails() { milestoneName="Term Fees" progressPercentage={75} amountLeft={120000} - targetPayout={600000} + targetPayout={ + group.contributionAmount && group.memberCount + ? group.contributionAmount * group.memberCount + : 600000 + } dueDate="Sept 15" onContribute={() => console.log("Contribute clicked")} /> diff --git a/src/components/common/Modal/Modal.tsx b/src/components/common/Modal/Modal.tsx index 283f001..46d438a 100644 --- a/src/components/common/Modal/Modal.tsx +++ b/src/components/common/Modal/Modal.tsx @@ -23,6 +23,7 @@ export const Modal: React.FC = ({

{title}

Kolo

- - diff --git a/src/components/layout/Sidebar/Sidebar.tsx b/src/components/layout/Sidebar/Sidebar.tsx index 4c0bed1..1059975 100644 --- a/src/components/layout/Sidebar/Sidebar.tsx +++ b/src/components/layout/Sidebar/Sidebar.tsx @@ -1,46 +1,86 @@ -import React from "react"; -import { Home, PiggyBank, Users, CreditCard, Settings } from "lucide-react"; +"use client"; + +import React, { useState } from "react"; +import { + Home, + PiggyBank, + Users, + CreditCard, + Settings, + Menu, + X, +} from "lucide-react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; export const Sidebar = () => { + const [isOpen, setIsOpen] = useState(false); + const pathname = usePathname(); + const navItems = [ - { name: "Home", icon: Home, active: false }, - { name: "Savings", icon: PiggyBank, active: false }, - { name: "Groups", icon: Users, active: true }, - { name: "Payments", icon: CreditCard, active: false }, - { name: "Settings", icon: Settings, active: false }, + { name: "Home", icon: Home, href: "/dashboard" }, + { name: "Savings", icon: PiggyBank, href: "/groups" }, + { name: "Groups", icon: Users, href: "/groups" }, + { name: "Payments", icon: CreditCard, href: "/payments" }, + { name: "Settings", icon: Settings, href: "/profile" }, ]; return ( - + <> + + + + + {/* Mobile overlay */} + {isOpen && ( +
setIsOpen(false)} + /> + )} + ); }; From f5a18bdc8c6aeaf2e78579a6672fa86d9c7266a6 Mon Sep 17 00:00:00 2001 From: Rampop01 Date: Wed, 26 Aug 2026 08:24:34 +0100 Subject: [PATCH 3/5] fix: Resolve React hook set-state-in-effect lint error --- src/app/(dashboard)/groups/[id]/page.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/app/(dashboard)/groups/[id]/page.tsx b/src/app/(dashboard)/groups/[id]/page.tsx index 05d6cd9..4123404 100644 --- a/src/app/(dashboard)/groups/[id]/page.tsx +++ b/src/app/(dashboard)/groups/[id]/page.tsx @@ -19,14 +19,22 @@ export default function GroupDetails() { const [isInviteModalOpen, setIsInviteModalOpen] = useState(false); useEffect(() => { - if (params?.id) { - groupsService.getGroup(params.id).then((fetched) => { + let mounted = true; + const fetchGroup = async () => { + if (!params?.id) { + if (mounted) setIsLoading(false); + return; + } + const fetched = await groupsService.getGroup(params.id); + if (mounted) { setGroup(fetched); setIsLoading(false); - }); - } else { - setIsLoading(false); - } + } + }; + fetchGroup(); + return () => { + mounted = false; + }; }, [params?.id]); // Mock data to match the Figma design From ceebcb7ef09d7734b4091b1085a934511c7159ac Mon Sep 17 00:00:00 2001 From: Rampop01 Date: Wed, 26 Aug 2026 08:37:18 +0100 Subject: [PATCH 4/5] fix: Link groups index to individual dashboards --- src/app/(dashboard)/groups/page.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/(dashboard)/groups/page.tsx b/src/app/(dashboard)/groups/page.tsx index 1b980d2..b35e55f 100644 --- a/src/app/(dashboard)/groups/page.tsx +++ b/src/app/(dashboard)/groups/page.tsx @@ -1,5 +1,6 @@ import { groupsService } from "@/services/api/groups"; import { InviteButton } from "@/components/groups/InviteButton"; +import Link from "next/link"; export default async function Groups() { const groups = await groupsService.listGroups(); @@ -22,8 +23,8 @@ export default async function Groups() { className="rounded-2xl border border-slate-100 bg-white p-5 shadow-sm" >
-
-

+ +

{group.name}

{group.description && ( @@ -36,7 +37,7 @@ export default async function Groups() { {group.memberCount} members

)} -
+
From 2944ecf4a473e1cd8d7b896002e95b469ec540b6 Mon Sep 17 00:00:00 2001 From: Rampop01 Date: Wed, 26 Aug 2026 08:40:14 +0100 Subject: [PATCH 5/5] fix: Correct Savings nav href in Sidebar --- src/components/layout/Sidebar/Sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layout/Sidebar/Sidebar.tsx b/src/components/layout/Sidebar/Sidebar.tsx index 1059975..a37e4ff 100644 --- a/src/components/layout/Sidebar/Sidebar.tsx +++ b/src/components/layout/Sidebar/Sidebar.tsx @@ -19,7 +19,7 @@ export const Sidebar = () => { const navItems = [ { name: "Home", icon: Home, href: "/dashboard" }, - { name: "Savings", icon: PiggyBank, href: "/groups" }, + { name: "Savings", icon: PiggyBank, href: "/savings" }, { name: "Groups", icon: Users, href: "/groups" }, { name: "Payments", icon: CreditCard, href: "/payments" }, { name: "Settings", icon: Settings, href: "/profile" },