Skip to content
This repository was archived by the owner on Dec 2, 2025. It is now read-only.
Merged

a #100

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/components/layouts/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { Link, LogOut, SettingsIcon } from "lucide-react";
import { SettingsIcon } from "lucide-react";
import {
Sidebar,
SidebarContent,
Expand All @@ -14,7 +14,6 @@ import {
SidebarRail,
} from "@/components/ui/sidebar";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import {
Tooltip,
Expand Down
13 changes: 10 additions & 3 deletions src/components/modules/admin/hooks/useLoanOfferRequests.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface LoanOffer {
id: string;
borrower: string;
amount: number;
status: string;
status?: string;
createdAt: number;
}

Expand All @@ -39,7 +39,14 @@ export function useLoanOfferRequests() {
const fetchOffers = async () => {
setLoading(true);
const res = await getAllLoanOffers({ status: "pending" });
if (res.success && res.data) setLoanOffers(res.data);
if (res.success && res.data) {
setLoanOffers(
res.data.map((offer) => ({
...offer,
createdAt: offer.createdAt ? offer.createdAt.seconds : 0,
})),
);
}
setLoading(false);
};

Expand All @@ -62,7 +69,7 @@ export function useLoanOfferRequests() {
} else {
toast.error("Failed to approve offer");
}
} catch (error) {
} catch {
toast.error("An error occurred while approving the offer");
}
};
Expand Down
17 changes: 8 additions & 9 deletions src/components/modules/auth/ui/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@

import { useEffect, useState } from "react";
import { ArrowRight, Wallet, BadgeCheck } from "lucide-react";
import Image from "next/image";
import { Button } from "@/components/ui/button";
import { useGlobalAuthenticationStore } from "@/core/store/data";
import { Badge } from "@/components/ui/badge";
import Link from "next/link";
import { useWallet } from "../../wallet/hooks/wallet.hook";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { format } from "date-fns";
import { getApprovedLoanOffers } from "@/components/modules/dashboard/marketplace/server/marketplace.firebase";

interface ApprovedLoan {
id: string;
title: string;
maxAmount: number;
platformFee: number;
}

export default function HomePage() {
const { handleConnect, handleDisconnect } = useWallet();
const { address } = useGlobalAuthenticationStore();
const [approvedLoans, setApprovedLoans] = useState<any[]>([]);
const [approvedLoans, setApprovedLoans] = useState<ApprovedLoan[]>([]);

useEffect(() => {
const fetchApprovedLoans = async () => {
Expand All @@ -36,11 +40,6 @@ export default function HomePage() {
};
}, []);

const formatDate = (seconds: number) => {
if (!seconds) return "Unknown date";
return format(new Date(seconds * 1000), "dd MMM yyyy");
};

return (
<div className="flex flex-col min-h-screen">
<section className="w-full py-12 md:py-24 lg:py-32">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,43 @@ import {
query,
where,
DocumentReference,
DocumentData,
QueryDocumentSnapshot,
} from "firebase/firestore";

interface LoanOffer {
borrower: string;
amount: number;
title: string;
maxAmount: number;
platformFee: number;
platformAddress: string;
approver: string;
disputeResolver: string;
releaseSigner: string;
submittedBy?: {
name?: string;
email?: string;
address?: string;
};
milestones?: {
description: string;
}[];
createdAt?: {
seconds: number;
nanoseconds: number;
};
updatedAt?: {
seconds: number;
nanoseconds: number;
};
status?: string;
lenderWallet?: string;
description?: string;
}

interface AddLoanOfferProps {
payload: any;
payload: LoanOffer;
lenderWallet: string;
}

Expand All @@ -25,12 +58,12 @@ const addLoanOffer = async ({
}: AddLoanOfferProps): Promise<{
success: boolean;
message: string;
data?: any;
data?: { id: string } & LoanOffer;
}> => {
try {
const ref = collection(db, "loan_offers");

const docRef: DocumentReference = await addDoc(ref, {
const docRef: DocumentReference<DocumentData> = await addDoc(ref, {
...payload,
lenderWallet,
status: "pending",
Expand All @@ -43,12 +76,13 @@ const addLoanOffer = async ({
return {
success: true,
message: "Loan offer created successfully",
data: { id: docRef.id, ...createdDoc.data() },
data: { id: docRef.id, ...(createdDoc.data() as LoanOffer) },
};
} catch (error: any) {
} catch (error: unknown) {
return {
success: false,
message: error.message || "Failed to create loan offer",
message:
error instanceof Error ? error.message : "Failed to create loan offer",
};
}
};
Expand All @@ -59,7 +93,7 @@ const getLoanOffersByStatus = async ({
status?: string;
}): Promise<{
success: boolean;
data?: any[];
data?: ({ id: string } & LoanOffer)[];
message?: string;
}> => {
try {
Expand All @@ -68,26 +102,27 @@ const getLoanOffersByStatus = async ({

const snapshot = await getDocs(q);

const offers = snapshot.docs.map((doc) => ({
const offers = snapshot.docs.map((doc: QueryDocumentSnapshot) => ({
id: doc.id,
...doc.data(),
...(doc.data() as LoanOffer),
}));

return {
success: true,
data: offers,
};
} catch (error: any) {
} catch (error: unknown) {
return {
success: false,
message: error.message || "Error retrieving loan offers",
message:
error instanceof Error ? error.message : "Error retrieving loan offers",
};
}
};

const getApprovedLoanOffers = async (): Promise<{
success: boolean;
data?: any[];
data?: ({ id: string } & LoanOffer)[];
message?: string;
}> => getLoanOffersByStatus({ status: "approved" });

Expand All @@ -111,10 +146,11 @@ const approveLoanOffer = async ({
success: true,
message: `Loan offer ${offerId} approved successfully`,
};
} catch (error: any) {
} catch (error: unknown) {
return {
success: false,
message: error.message || "Failed to approve loan offer",
message:
error instanceof Error ? error.message : "Failed to approve loan offer",
};
}
};
Expand Down
34 changes: 32 additions & 2 deletions src/components/modules/dashboard/marketplace/store/marketplace.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
import { create } from "zustand";

interface Timestamp {
seconds: number;
nanoseconds: number;
}

export interface ApprovedLoan {
id: string;
title: string;
maxAmount: number;
platformFee: number;
borrower: string;
amount: number;
createdAt?: Timestamp;
platformAddress?: string;
approver?: string;
disputeResolver?: string;
releaseSigner?: string;
description?: string;
submittedBy?: {
name?: string;
email?: string;
address?: string;
};
milestones?: {
description: string;
}[];
status?: string;
lenderWallet?: string;
updatedAt?: Timestamp;
}
interface MarketplaceStore {
selectedLoan: any | null;
setSelectedLoan: (loan: any) => void;
selectedLoan: ApprovedLoan | null;
setSelectedLoan: (loan: ApprovedLoan) => void;
clearSelectedLoan: () => void;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ import {
import { useMarketplaceStore } from "../../store/marketplace";
import { Badge } from "@/components/ui/badge";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import type { ApprovedLoan } from "../../store/marketplace";

interface MarketplaceLoanDetailDialogProps {
isDialogOpen: boolean;
setIsDialogOpen: (value: boolean) => void;
selectedLoan?: any;
setSelectedLoan: (value?: any) => void;
selectedLoan?: ApprovedLoan;
setSelectedLoan: (value?: ApprovedLoan) => void;
}

const MarketplaceLoanDetailDialog = ({
Expand All @@ -46,11 +47,12 @@ const MarketplaceLoanDetailDialog = ({

const handleClose = () => {
setIsDialogOpen(false);
setSelectedLoan(undefined);
};

const handleRequestLoan = () => {
setSelectedLoan(selectedLoan);
if (selectedLoan) {
setSelectedLoan(selectedLoan);
}
router.push("/dashboard/loans/loan-request");
};

Expand Down Expand Up @@ -175,8 +177,8 @@ const MarketplaceLoanDetailDialog = ({
<Calendar className="h-4 w-4 text-muted-foreground" />
<p className="text-sm">
{formatDateFromFirebase(
selectedLoan.createdAt?.seconds,
selectedLoan.createdAt?.nanoseconds,
selectedLoan.createdAt?.seconds ?? 0,
selectedLoan.createdAt?.nanoseconds ?? 0,
)}
</p>
</div>
Expand All @@ -195,9 +197,9 @@ const MarketplaceLoanDetailDialog = ({
<h3 className="font-medium">Loan Milestones</h3>
</div>

{selectedLoan.milestones?.length > 0 ? (
{(selectedLoan.milestones?.length ?? 0 > 0) ? (
<div className="space-y-3">
{selectedLoan.milestones?.map((m: any, idx: number) => (
{selectedLoan.milestones?.map((m, idx) => (
<div
key={idx}
className="border p-4 rounded-md bg-muted/30 hover:bg-muted/50 transition-colors"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ import {
} from "@/components/ui/collapsible";
import { Separator } from "@/components/ui/separator";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import type { ApprovedLoan } from "../../store/marketplace";

export default function MarketplaceOffersList() {
const [loanOffers, setLoanOffers] = useState<any[]>([]);
const [loanOffers, setLoanOffers] = useState<ApprovedLoan[]>([]);
const [selectedLoan, setSelectedLoan] = useState<ApprovedLoan | undefined>(
undefined,
);
const [loading, setLoading] = useState(true);
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [selectedLoan, setSelectedLoan] = useState<any | undefined>(undefined);
const [isFilterOpen, setIsFilterOpen] = useState(true);

const [searchTitle, setSearchTitle] = useState("");
Expand Down Expand Up @@ -307,7 +310,9 @@ export default function MarketplaceOffersList() {
<div className="flex items-center gap-2 text-muted-foreground text-sm mb-4">
<User className="h-4 w-4" />
<p className="truncate">
{formatAddress(offer.lenderWallet)}
{offer.lenderWallet
? formatAddress(offer.lenderWallet)
: "Unknown address"}
</p>
</div>

Expand All @@ -321,12 +326,12 @@ export default function MarketplaceOffersList() {
</div>
</div>

{offer.milestones?.length > 0 && (
{offer.milestones && offer.milestones.length > 0 && (
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-2">
<Milestone className="h-3.5 w-3.5" />
<span>
{offer.milestones.length} milestone
{offer.milestones.length !== 1 ? "s" : ""}
{offer.milestones?.length || 0} milestone
{offer.milestones?.length !== 1 ? "s" : ""}
</span>
</div>
)}
Expand Down Expand Up @@ -373,7 +378,7 @@ export default function MarketplaceOffersList() {
<div className="flex items-center gap-2 text-muted-foreground text-xs">
<User className="h-3.5 w-3.5" />
<p className="truncate">
{formatAddress(offer.lenderWallet)}
{formatAddress(offer.lenderWallet || "Unknown")}
</p>
<span className="text-muted-foreground/50">β€’</span>
<Calendar className="h-3.5 w-3.5" />
Expand Down
Loading