Skip to content
Merged
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
4 changes: 2 additions & 2 deletions components/dashboard/mobile-bottom-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Link from "next/link";

const navItems = [
{ icon: Home, label: "Dashboard", link: "/dashboard" },
{ icon: Bell, label: "Notification", link: "/dashboard/notification" },
{ icon: HistoryIcon, label: "History", link: "/dashboard/profile" },
{ icon: Bell, label: "Notification", link: "/dashboard/notifications" },
{ icon: HistoryIcon, label: "History", link: "/dashboard/history" },
{ icon: User, label: "Profile", link: "/dashboard/profile" },
];

Expand Down
4 changes: 2 additions & 2 deletions components/dashboard/purchase/PurchaseCommon/AmountGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function AmountGrid({
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => onChange(amount.toString())}
className={`p-3 rounded-xl transition-all border ${
className={`p-2 sm:p-3 rounded-lg text-sm sm:text-md transition-all border ${
value === amount.toString()
? " bg-primary/10 text-primary font-semibold"
: " hover:border-primary/50 hover:bg-accent"
Expand All @@ -78,7 +78,7 @@ export function AmountGrid({
value={value}
onChange={handleCustomAmountChange}
placeholder="Enter custom amount"
className={`w-full p-4 rounded-xl border-2 pr-10 transition-colors ${
className={`w-full sm:p-4 py-2 px-3 text-sm sm:text-md rounded-lg border-2 pr-10 transition-colors ${
isBelowMin || isAboveMax
? "border-red-500 focus:border-red-500 focus:ring-red-500/20"
: " focus:border-primary focus:ring-primary/20"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ export function CustomerInput({
};

return (
<div className={`space-y-3 ${className}`}>
<div className={`space-y-3 w-full ${className}`}>
<label className="block text-sm font-medium">
{config.customerLabel}
</label>

<div className="flex gap-2">
<div className="flex flex-1 w-full gap-2">
{type !== "electricity" && (
<div className="px-4 py-3 rounded-lg bg-muted text-foreground font-medium flex items-center justify-center">
<div className="sm:px-4 sm:py-3 text-sm sm:textn-md px-2 rounded-lg bg-muted text-foreground font-medium flex items-center justify-center">
+234
</div>
)}
Expand All @@ -56,7 +56,7 @@ export function CustomerInput({
value={value}
onChange={handleChange}
placeholder={config.placeholder}
className={`flex-1 p-4 rounded-lg border bg-background placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-colors ${
className={` sm:p-4 p-2 rounded-lg border bg-muted w-full placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-colors ${
type === "electricity" ? "" : ""
}`}
maxLength={type === "electricity" ? undefined : 10}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function PaymentSelection({
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
className="space-y-6 p-6"
className="space-y-6 sm:p-6"
>
<div className="flex items-center mb-4">
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function ProviderSelection({
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="space-y-6 p-6"
className="space-y-6 sm:p-6"
>
{/* Provider Selection */}
<div className="space-y-3">
Expand Down
38 changes: 19 additions & 19 deletions components/dashboard/purchase/PurchaseSteps/ReviewConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function ReviewConfirmation({
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
className="space-y-6 p-6"
className="space-y-6 sm:p-6"
>
<div className="flex items-center mb-4">
<button
Expand All @@ -42,48 +42,48 @@ export function ReviewConfirmation({
>
<ArrowLeft className="w-6 h-6" />
</button>
<h2 className="text-xl font-semibold">Transaction Summary</h2>
<h2 className="sm:text-xl text-lg font-semibold">Transaction Summary</h2>
</div>

<div className="bg-card border rounded-2xl p-6 space-y-4">
<div className="flex justify-between">
<span className="text-muted-foreground">Product</span>
<span className="capitalize font-medium">{type}</span>
<span className="text-muted-foreground font-[UbuntuSans] sm:text-md text-sm font-semibold">Product</span>
<span className= "capitalize font-medium text-sm font-[UbuntuSans] sm:text-md">{type}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Provider</span>
<span className="font-medium">
<span className="text-muted-foreground font-[UbuntuSans] sm:text-md text-sm font-semibold">Provider</span>
<span className="font-medium text-sm font-[UbuntuSans] sm:text-md">
{providers.find(p => p.serviceID === formData.service_id)?.name}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">{config.customerLabel}</span>
<span className="font-medium">
<span className="text-muted-foreground font-[UbuntuSans] sm:text-md text-sm font-semibold">{config.customerLabel}</span>
<span className="font-medium text-sm font-[UbuntuSans] sm:text-md">
{type !== "electricity" ? `234${formData.customer_id}` : formData.customer_id}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Payment Method</span>
<span className="font-medium capitalize">{selectedToken}</span>
<span className="text-muted-foreground font-[UbuntuSans] sm:text-md text-sm font-semibold">Payment Method</span>
<span className="font-medium text-sm font-[UbuntuSans] sm:text-md capitalize">{selectedToken}</span>
</div>
<div className="flex justify-between pt-4 border-t">
<span className="text-muted-foreground">Total Amount</span>
<span className="text-muted-foreground font-[UbuntuSans] sm:text-md text-sm font-semibold">Total Amount</span>
<span className="text-2xl font-bold">
₦{parseInt(formData.amount || "0").toLocaleString()}
</span>
</div>

{/* Crypto Amount */}
<div className="pt-4 border-t">
<div className="flex justify-between mb-2">
<span className="text-muted-foreground">Required Crypto</span>
<span className="font-medium">
<div className="pt-4 border-t w-full">
<div className="flex justify-between w-full mb-2">
<span className="text-muted-foreground font-[UbuntuSans] w-full sm:text-md text-sm font-semibold">Required Crypto</span>
<span className="font-medium text-sm w-full font-[UbuntuSans] text-end sm:text-md">
{requiredCryptoAmount.toFixed(6)} {selectedToken.toUpperCase()}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Your Balance</span>
<span className={`font-medium ${
<div className="flex justify-between w-full">
<span className="text-muted-foreground font-[UbuntuSans] sm:text-md w-full text-sm font-semibold">Your Balance</span>
<span className={`font-medium text-sm w-full text-end font-[UbuntuSans] sm:text-md ${
hasInsufficientBalance ? "text-red-600" : "text-green-600"
}`}>
{currentWalletBalance.toFixed(6)} {selectedToken.toUpperCase()}
Expand Down Expand Up @@ -122,7 +122,7 @@ export function ReviewConfirmation({
disabled={!!validationError || hasInsufficientBalance}
className={`flex-1 p-4 rounded-xl flex items-center justify-center gap-2 transition-colors ${
validationError || hasInsufficientBalance
? "bg-muted text-muted-foreground cursor-not-allowed"
? "bg-muted text-muted-foreground font-[UbuntuSans] sm:text-md text-sm font-semibold cursor-not-allowed"
: "bg-primary text-white hover:bg-primary/90"
}`}
>
Expand Down
2 changes: 1 addition & 1 deletion components/dashboard/top-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function TopNav() {
// const isHelp = tabTitle === "Help";

return (
<header className="fixed w-full max-w-[80%] mx-auto top-0 left-0 right-0 z-40 bg-background/95 backdrop-blur-md border-b">
<header className="fixed w-full md:max-w-[80%] mx-auto top-0 left-0 right-0 z-40 bg-background/95 backdrop-blur-md border-b">
<div className="flex h-16 items-center justify-between px-4 ">
{/* Actions */}

Expand Down
143 changes: 91 additions & 52 deletions components/dashboard/wallet-overview.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/cards";
import { Check, ChevronRight, Copy, Eye, EyeClosed } from "lucide-react";
import { CardContent } from "@/components/ui/cards";
import { ArrowDownToLine } from "lucide-react";
import Image from "next/image";
import { Button } from "../ui/buttons";
import { shortenAddress } from "../lib/utils";
import { Skeleton } from "@/components/ui/skeleton";
import { useCallback, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useWalletData } from "../hooks/useWalletData";
import QRModal from "../modals/qr-modal";
import { generateCompatibleQRCode } from "@/lib/utils/qr-utils";
import { fixStarknetAddress } from "../lib/utils";

interface WalletOverviewProps {
handleViewBalance: () => void;
Expand All @@ -22,7 +18,9 @@ export function WalletOverview({
hideBalalance,
}: WalletOverviewProps) {
const { addresses, breakdown } = useWalletData();

const [qrData, setQrData] = useState<string | null>(null);
const [selectedToken, setSelectedToken] = useState("");
console.log("qr data", qrData);
const [copiedAddress, setCopiedAddress] = useState<string | null>(null);

const walletData = (() => {
Expand Down Expand Up @@ -103,51 +101,87 @@ export function WalletOverview({
[]
);

// Get selected token data
const selectedTokenData = addresses?.find(
(token) => token.chain === selectedToken.toLowerCase()
);

// Generate QR code when selected token changes
useEffect(() => {
const generateQrCode = async () => {
if (!selectedTokenData?.address) {
setQrData("");
return;
}

try {
let addressToUse = selectedTokenData.address;

// Normalize Starknet address if needed
if (selectedTokenData.chain.toLowerCase() === "starknet") {
addressToUse = fixStarknetAddress(
addressToUse,
selectedTokenData.chain
);
}

const qrResult = await generateCompatibleQRCode(
selectedTokenData.chain,
addressToUse,
{
width: 200,
margin: 2,
errorCorrectionLevel: "M" as const,
}
);

setQrData(qrResult.dataUrl);
} catch (error) {
console.error("Error generating QR code:", error);
setQrData("");
}
};

generateQrCode();
}, [selectedTokenData]);

return (
// <Card className="border-border/50 mb-8 bg-card/50 w-full max-h-132 overflow-y-scroll backdrop-blur-sm">

<CardContent className="space-y-3 flex overflow-x-scroll md:grid grid-cols-2 md:grid-cols-4 gap-2 lg-gap-6 p-4">
{walletData?.map((wallet, index) => (
<div
key={index}
className="flex w-full items-start gap-3 justify-between p-3 min-w-[150px] shadow-lg border-border flex-col lg:p-4 rounded-lg bg-muted/30 hover:bg-muted/50 transition-colors"
>
<div className="flex w-full items-center gap-3 flex-1 min-w-0">
<div className="w-full flex justify-between items-start">
<div className="w-8 h-8 lg:w-10 lg:h-10 rounded-full relative">
<Image
src={`/${wallet.chain.toLowerCase()}.svg`}
alt={wallet.chain}
width={20}
height={20}
onError={(e) => {
(e.target as HTMLImageElement).style.display = "none";
(
e.target as HTMLImageElement
).nextElementSibling?.classList.remove("hidden");
}}
/>
</div>
{walletData.length === 0 ? (
<Skeleton className="h-4 w-20 mt-1 bg-gray-300" />
) : (
<p className="text-xs font-semibold text-green-600 mt-1">
{formatBalance(wallet.balance, wallet.symbol)}
</p>
)}
</div>

<div className="min-w-0 flex-1">
{/* <p className="font-medium text-xs lg:text-sm capitalize truncate">
{wallet.chain}
</p>
<p className="text-xs text-muted-foreground">
{shortenAddress(wallet.address as `0x${string}`, 6)}
</p> */}
<CardContent className="space-y-3 relative flex overflow-x-scroll md:grid grid-cols-2 md:grid-cols-4 gap-2 lg-gap-6 p-4">
{walletData?.map((wallet, index) => (
<div
key={index}
className="flex w-full items-start gap-3 justify-between p-3 min-w-[150px] shadow-lg border-border flex-col lg:p-4 rounded-lg bg-muted/30 hover:bg-muted/50 transition-colors"
>
<div className="flex w-full items-center gap-3 flex-1 min-w-0">
<div className="w-full flex justify-between items-start">
<div className="w-8 h-8 lg:w-10 lg:h-10 rounded-full relative">
<Image
src={`/${wallet.chain.toLowerCase()}.svg`}
alt={wallet.chain}
width={20}
height={20}
onError={(e) => {
(e.target as HTMLImageElement).style.display = "none";
(
e.target as HTMLImageElement
).nextElementSibling?.classList.remove("hidden");
}}
/>
</div>
{walletData.length === 0 ? (
<Skeleton className="h-4 w-20 mt-1 bg-gray-300" />
) : (
<p className="text-xs font-semibold text-green-600 mt-1">
{formatBalance(wallet.balance, wallet.symbol)}
</p>
)}
</div>
</div>

<div className="text-right flex-shrink-0 min-w-0">
<div className="w-full flex items-center justify-between min-w-0">
<div>
{walletData.length === 0 ? (
<Skeleton className="h-4 w-16 bg-gray-300" />
) : (
Expand All @@ -165,9 +199,14 @@ export function WalletOverview({
</>
)}
</div>
<button onClick={() => setSelectedToken(wallet.chain)}>
<ArrowDownToLine size={16} />
</button>
</div>
))}
</CardContent>
</div>
))}
{qrData && <QRModal qrData={qrData} setShow={setQrData} />}
</CardContent>
// </Card>
);
}
44 changes: 44 additions & 0 deletions components/modals/qr-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { Dispatch, SetStateAction } from "react";
import Image from "next/image";

export default function QRModal({
qrData,
setShow,
}: {
qrData: string;
setShow: Dispatch<SetStateAction<string | null>>;
}) {
const handleModalClick = (e: React.MouseEvent) => {
e.stopPropagation();
};

return (
<div
onClick={() => setShow(null)}
className="w-full h-screen z-50 fixed p-4 top-0 left-0 flex items-center justify-center bg-black/50"
>
<div className="w-full h-full absolute top-0 left-0 backdrop-blur-lg z-10" />
<div
onClick={handleModalClick}
className="relative z-20 w-full max-w-sm h-auto rounded-2xl bg-white p-4"
>
{qrData ? (
<Image
src={qrData}
alt="QR Code"
width={200}
height={200}
className="rounded-lg w-full h-full object-contain"
priority
/>
) : (
<div className="w-full h-full flex items-center justify-center text-gray-500">
Generating QR...
</div>
)}
<div className="w-full text-center text-black text-lg mt-3 font-black">
Scan Me! </div>
</div>
</div>
);
}
Loading