From e08a4f10814e2043b873118d870ad8014db864d8 Mon Sep 17 00:00:00 2001 From: OSEH-svg Date: Tue, 29 Jul 2025 19:03:56 +0100 Subject: [PATCH 1/6] first commit --- .../admin/transactions/components/Chart.tsx | 83 +++++ .../transactions/components/ChartLegend.tsx | 24 ++ .../transactions/components/Pagination.tsx | 23 ++ .../components/SearchFilterBar.tsx | 20 ++ .../transactions/components/StatsCard.tsx | 57 +++ .../transactions/components/TabNavigation.tsx | 59 +++ .../transactions/components/TimeFilter.tsx | 56 +++ .../components/TransactionTable.tsx | 99 +++++ src/app/dashboard/admin/transactions/page.tsx | 236 +++++++++++- src/components/landingpage/NavBar.tsx | 340 +++++++++--------- 10 files changed, 817 insertions(+), 180 deletions(-) create mode 100644 src/app/dashboard/admin/transactions/components/Chart.tsx create mode 100644 src/app/dashboard/admin/transactions/components/ChartLegend.tsx create mode 100644 src/app/dashboard/admin/transactions/components/Pagination.tsx create mode 100644 src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx create mode 100644 src/app/dashboard/admin/transactions/components/StatsCard.tsx create mode 100644 src/app/dashboard/admin/transactions/components/TabNavigation.tsx create mode 100644 src/app/dashboard/admin/transactions/components/TimeFilter.tsx create mode 100644 src/app/dashboard/admin/transactions/components/TransactionTable.tsx diff --git a/src/app/dashboard/admin/transactions/components/Chart.tsx b/src/app/dashboard/admin/transactions/components/Chart.tsx new file mode 100644 index 0000000..43b74a8 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/Chart.tsx @@ -0,0 +1,83 @@ +export interface ChartData { + label: string; + value: number; + color: string; +} + +interface ChartProps { + data: ChartData[]; + size?: number; +} + +const Chart: React.FC = ({ data, size = 200 }) => { + const radius = (size - 40) / 2; // 40 = strokeWidth + const strokeWidth = 40; + const center = size / 2; + const circumference = 2 * Math.PI * radius; + + let cumulativePercent = 0; + + return ( +
+ + {/* Centered background ring */} + + + {/* Colored segments */} + {data.map((item, index) => { + const percent = item.value / 100; + const strokeLength = percent * circumference; + const strokeGap = circumference - strokeLength; + + const dashArray = `${strokeLength} ${strokeGap}`; + const dashOffset = -cumulativePercent * circumference; + + cumulativePercent += percent; + + return ( + + ); + })} + + + {/* Donut center */} +
+
+ ); +}; + +export default Chart; + diff --git a/src/app/dashboard/admin/transactions/components/ChartLegend.tsx b/src/app/dashboard/admin/transactions/components/ChartLegend.tsx new file mode 100644 index 0000000..82e2bd8 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/ChartLegend.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import type { ChartData } from './Chart'; + +interface ChartLegendProps { + data: ChartData[]; + } + + const ChartLegend: React.FC = ({ data }) => ( +
+ {data.map((item, index) => ( +
+
+ + {item.value}% {item.label} + +
+ ))} +
+ ); + +export default ChartLegend; \ No newline at end of file diff --git a/src/app/dashboard/admin/transactions/components/Pagination.tsx b/src/app/dashboard/admin/transactions/components/Pagination.tsx new file mode 100644 index 0000000..74707dd --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/Pagination.tsx @@ -0,0 +1,23 @@ +import { ChevronDown } from "lucide-react"; + +interface PaginationProps { + currentItems: number; + totalItems: number; + itemsPerPage: number; + } + + const Pagination: React.FC = ({ currentItems, totalItems}) => ( +
+
+ Showing 1 to {currentItems} of {totalItems} +
+
+ + +
+
+ ); + +export default Pagination \ No newline at end of file diff --git a/src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx b/src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx new file mode 100644 index 0000000..64b339a --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx @@ -0,0 +1,20 @@ +import TimeFilter from "./TimeFilter"; + +interface SearchFilterBarProps { + timeFilter: string; + onTimeFilterChange: (filter: string) => void; + searchPlaceholder?: string; + } + + const SearchFilterBar: React.FC = ({ + timeFilter, + onTimeFilterChange + }) => ( +
+
+ +
Filter by
+
+
+ ); +export default SearchFilterBar \ No newline at end of file diff --git a/src/app/dashboard/admin/transactions/components/StatsCard.tsx b/src/app/dashboard/admin/transactions/components/StatsCard.tsx new file mode 100644 index 0000000..443e720 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/StatsCard.tsx @@ -0,0 +1,57 @@ +import Image from "next/image"; + +interface StatsCardProps { + title: string; + value: string; + currency?: string; + growth?: string; + variant?: "default" | "primary"; + size?: "small" | "large"; + currencyTextSize?: string; + currencyFontWeight?: string; + classname?: string; +} + +const StatsCard: React.FC = ({ + title, + value, + currency, + growth, + variant = "default", + size = "large", + currencyTextSize = "text-sm", + currencyFontWeight = "font-normal", +}) => { + const cardClass = + variant === "primary" + ? "bg-[#edf7ff] border-[#edf7ff]" + : "bg-white border-gray-200"; + + const textSize = size === "large" ? "text-3xl" : "text-lg"; + const paddingClass = size === "large" ? "p-6" : "p-4"; + const heightClass = size === "large" ? "h-full" : "h-20"; + const alignmentClass = size === "large" ? "justify-center items-center text-center" : "justify-center"; + + return ( +
+

{title}

+
+ {currency && ( + strk + )} + + {value} {currency && {currency}} + +
+ {growth && ( + + {growth} + + )} +
+ ); +}; + +export default StatsCard; diff --git a/src/app/dashboard/admin/transactions/components/TabNavigation.tsx b/src/app/dashboard/admin/transactions/components/TabNavigation.tsx new file mode 100644 index 0000000..d406499 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/TabNavigation.tsx @@ -0,0 +1,59 @@ +import { Search, Filter } from "lucide-react"; + +export interface Tab { + name: string; + badge?: string; + active?: boolean; +} + +interface TabNavigationProps { + tabs: Tab[]; + activeTab: string; + onTabClick: (tab: string) => void; + // searchPlaceholder?: string; +} + +const TabNavigation: React.FC = ({ tabs, activeTab, onTabClick }) => ( +
+
+ {tabs.map((tab) => ( + + ))} +
+ +
+
+ + +
+ + +
+
+); + +export default TabNavigation; + diff --git a/src/app/dashboard/admin/transactions/components/TimeFilter.tsx b/src/app/dashboard/admin/transactions/components/TimeFilter.tsx new file mode 100644 index 0000000..47c9168 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/TimeFilter.tsx @@ -0,0 +1,56 @@ +interface TimeFilterProps { + activeFilter: string; + onFilterChange: (filter: string) => void; + showDateRange?: boolean; + showApplyButton?: boolean; + } + + const TimeFilter: React.FC = ({ + activeFilter, + onFilterChange, + showDateRange = true, + showApplyButton = true + }) => { + const filters = ['This Week', 'This Month', 'This Year', 'All Time']; + + return ( +
+ {filters.map((filter) => ( + + ))} + + {showDateRange && ( +
+ + to + + {showApplyButton && ( + + )} +
+ )} +
+ ); + }; + +export default TimeFilter; \ No newline at end of file diff --git a/src/app/dashboard/admin/transactions/components/TransactionTable.tsx b/src/app/dashboard/admin/transactions/components/TransactionTable.tsx new file mode 100644 index 0000000..d2a19f1 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/TransactionTable.tsx @@ -0,0 +1,99 @@ +import Image from "next/image"; +// import starknet from "../../../../../../public/starknet.png" + +export interface Transaction { + id: string; + type: string; + amount: string; + user: string; + userName: string; + bookTitle: string; + status: string; + date: string; +} + +interface TransactionTableProps { + transactions: Transaction[]; +} + +const TransactionTable: React.FC = ({ + transactions, +}) => ( +
+ + + + {[ + "Transaction ID", + "Transaction Type", + "Amount (STRK)", + "User", + "User Name", + "Book Title", + "Status", + "Date", + "", + ].map((header) => ( + + ))} + + + + {transactions.map((transaction, index) => ( + + + + + + + + + + + + ))} + +
+ {header} +
+ {transaction.id} + + {transaction.type} + +
+ starknet icon + {transaction.amount} +
+
+ {transaction.user} + + {transaction.userName} + + {transaction.bookTitle} + + + {transaction.status} + + + {transaction.date} + + +
+
+); + +export default TransactionTable; diff --git a/src/app/dashboard/admin/transactions/page.tsx b/src/app/dashboard/admin/transactions/page.tsx index 3698b6c..6cc0f42 100644 --- a/src/app/dashboard/admin/transactions/page.tsx +++ b/src/app/dashboard/admin/transactions/page.tsx @@ -1,21 +1,237 @@ -"use-client"; +"use client"; +import { useState } from "react"; +import { Filter } from "lucide-react"; import { Header } from "@/components/dashboard/header"; +import Chart from "./components/Chart"; +import type { ChartData } from "./components/Chart"; +import ChartLegend from "./components/ChartLegend"; +import Pagination from "./components/Pagination"; +import SearchFilterBar from "./components/SearchFilterBar"; +import StatsCard from "./components/StatsCard"; +import TimeFilter from "./components/TimeFilter"; +import TabNavigation from "./components/TabNavigation"; +import type { Tab } from "./components/TabNavigation"; +import type { Transaction } from "./components/TransactionTable"; +import TransactionTable from "./components/TransactionTable"; +const Transactions = () => { + const [activeTab, setActiveTab] = useState("Transaction History"); + const [timeFilter, setTimeFilter] = useState("This Week"); + const [salesTimeFilter, setSalesTimeFilter] = useState("This Week"); + const [tableTimeFilter, setTableTimeFilter] = useState("This Week"); -export default function Transactions() { + // Chart data + const chartData: ChartData[] = [ + { label: "Subscription", value: 45, color: "#096cff" }, + { label: "NFT Edition", value: 32, color: "#822ecb" }, + { label: "One Time Purchase", value: 23, color: "#b6e0ff" }, + ]; + // Tab configuration + const tabs: Tab[] = [ + { name: "Transaction History", active: true }, + { name: "Payout Request", badge: "27" }, + { name: "Subscription", active: false }, + { name: "NFT Minting", active: false }, + ]; + + // Sample transaction data + const transactions: Transaction[] = [ + { + id: "Tran-124B", + type: "Subscription", + amount: "10.99 STRK", + user: "Reader", + userName: "jake_reader", + bookTitle: "-", + status: "Successful", + date: "26 April", + }, + { + id: "Tran-124B", + type: "Purchase", + amount: "76.09 STRK", + user: "Reader", + userName: "Paul_John", + bookTitle: "Book Purchase", + status: "Successful", + date: "25 April", + }, + { + id: "Tran-124B", + type: "Payout", + amount: "1560.00 STRK", + user: "Writer", + userName: "Nana_Hamza", + bookTitle: "-", + status: "Failed", + date: "20 April", + }, + { + id: "Tran-124B", + type: "Payout", + amount: "7.02 STRK", + user: "Writer", + userName: "Leo_17", + bookTitle: "-", + status: "Successful", + date: "18 April", + }, + { + id: "Tran-124B", + type: "Purchase", + amount: "8.09 STRK", + user: "Reader", + userName: "Lil_ma", + bookTitle: "Who Me Out", + status: "Successful", + date: "18 April", + }, + ]; + + const handleTabClick = (tab: string) => { + setActiveTab(tab); + // Modal logic will be handled by parent component + }; return ( <> -
-
-
-

Transactions

-

- Welcome to Transactions section of the admin dashboard! Here, you can manage and monitor all transactions on your platform. This space is designed to help you ensure the integrity and security of financial activities, providing insights into transaction history, status, and details. You can also handle any issues related to transactions efficiently. -

+
+ +
+ {/* Time Filter */} +
+ + +
+ + {/* Top Stats Cards */} +
+ {/* Large rectangular card on the left - controlled width */} +
+ {" "} + {/* You can adjust this width as needed */} + +
+ + {/* 2x2 grid of smaller cards on the right */} +
+ +
+ + + + +
+
+
+ + {/* Sales Distribution */} +
+

+ Sales Distribution +

+ + + +
+ {/* Chart */} +
+

+ Top Read Genres +

+ +
+ +
+ +
+
+
+ + {/* Stats */} +
+ + + +
+
+
+ + {/* Transaction Table Section */} +
+
+ + + + + + + +
); -} +}; + +export default Transactions; diff --git a/src/components/landingpage/NavBar.tsx b/src/components/landingpage/NavBar.tsx index 6d063c5..e28e9cf 100644 --- a/src/components/landingpage/NavBar.tsx +++ b/src/components/landingpage/NavBar.tsx @@ -1,185 +1,185 @@ -"use client"; - -import React from "react"; -import Image from "next/image"; -import Link from "next/link"; -import Image4 from "@/assets/Images/ImageLogo.png"; -import { useState, useRef, useEffect, SetStateAction } from "react"; -import { MoreVertical } from "lucide-react"; -import AnimationWrapper from "@/components/motion/Animation-wrapper"; -import WalletDisconnectModal from "../blockchain/Wallet-disconnect-modal"; -import { ConnectButton } from "../../components/blockchain/connect-button"; -// starknet imports -import { useWalletContext } from "../blockchain/WalletProvider"; -import { usePathname } from "next/navigation"; -import clsx from "clsx"; - -const NavBar = () => { - const pathname = usePathname(); - const [isConnectModalOpen, setIsConnectModalOpen] = useState(false); - const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false); - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const path = usePathname() - - const dropdownRef = useRef(null); - const navItems = [ - { label: "Home", href: "/" }, - { label: "Books", href: "/books" }, - { label: "How It Works", href: "/how-it-works" }, - { label: "About ChainLib", href: "/about-us" }, - ]; - - const { account, connectWallet, disconnectWallet, connectors } = - useWalletContext(); - - // Close dropdown when clicking outside - useEffect(() => { - function handleClickOutside(event: MouseEvent) { - if ( - dropdownRef.current && - !dropdownRef.current.contains(event.target as Node) - ) { - setIsDropdownOpen(false); + "use client"; + + import React from "react"; + import Image from "next/image"; + import Link from "next/link"; + import Image4 from "@/assets/Images/ImageLogo.png"; + import { useState, useRef, useEffect, SetStateAction } from "react"; + import { MoreVertical } from "lucide-react"; + import AnimationWrapper from "@/components/motion/Animation-wrapper"; + import WalletDisconnectModal from "../blockchain/Wallet-disconnect-modal"; + import { ConnectButton } from "../../components/blockchain/connect-button"; + // starknet imports + import { useWalletContext } from "../blockchain/WalletProvider"; + import { usePathname } from "next/navigation"; + import clsx from "clsx"; + + const NavBar = () => { + const pathname = usePathname(); + const [isConnectModalOpen, setIsConnectModalOpen] = useState(false); + const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const path = usePathname() + + const dropdownRef = useRef(null); + const navItems = [ + { label: "Home", href: "/" }, + { label: "Books", href: "/books" }, + { label: "How It Works", href: "/how-it-works" }, + { label: "About ChainLib", href: "/about-us" }, + ]; + + const { account, connectWallet, disconnectWallet, connectors } = + useWalletContext(); + + // Close dropdown when clicking outside + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + setIsDropdownOpen(false); + } } - } - document.addEventListener("mousedown", handleClickOutside); - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, []); + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); - const toggleDropdown = () => setIsDropdownOpen(!isDropdownOpen); + const toggleDropdown = () => setIsDropdownOpen(!isDropdownOpen); - const handleWalletSelect = (walletId: string) => { - const connector = connectors.find((c) => c.id === walletId); - if (connector) { - connectWallet(connector); // invoke Starknet-React’s useConnect() :contentReference[oaicite:3]{index=3} - } - setIsConnectModalOpen(false); - }; + const handleWalletSelect = (walletId: string) => { + const connector = connectors.find((c) => c.id === walletId); + if (connector) { + connectWallet(connector); // invoke Starknet-React’s useConnect() :contentReference[oaicite:3]{index=3} + } + setIsConnectModalOpen(false); + }; - const handleConnectWallet = () => { - setIsConnectModalOpen(true); - }; + const handleConnectWallet = () => { + setIsConnectModalOpen(true); + }; - const handleWalletClick = () => { - setIsDisconnectModalOpen(true); - }; + const handleWalletClick = () => { + setIsDisconnectModalOpen(true); + }; - const handleDisconnect = () => { - disconnectWallet(); // real Starknet-React disconnect :contentReference[oaicite:4]{index=4} - setIsDisconnectModalOpen(false); - }; + const handleDisconnect = () => { + disconnectWallet(); // real Starknet-React disconnect :contentReference[oaicite:4]{index=4} + setIsDisconnectModalOpen(false); + }; + + if (path.includes("dashboard")) { + return + } - if (path.includes("dashboard")) { - return - } - - return ( - <> -
- - - Logo - - ChainLib + return ( + <> +
+ + + Logo + + ChainLib + - - - - - - - {/* Wallet Connection Button or Connected Wallet */} -
- - {!account ? ( - - ) : ( -
-
+ ))} + + + + {/* Wallet Connection Button or Connected Wallet */} +
+ + {!account ? ( + + ) : ( +
+
- - -
- - {/* Custom Dropdown Menu */} - {isDropdownOpen && ( -
-
- - +
+ Wallet Avatar
+ + {account.slice(0, 6)}…{account.slice(-4)} + +
- )} -
- )} - + + {/* Custom Dropdown Menu */} + {isDropdownOpen && ( +
+
+ + +
+
+ )} +
+ )} +
+
-
- - setIsDisconnectModalOpen(false)} - onDisconnect={handleDisconnect} - /> - - ); -}; - -export default NavBar; + + setIsDisconnectModalOpen(false)} + onDisconnect={handleDisconnect} + /> + + ); + }; + + export default NavBar; From a307af39727455154f15bac552a2cb20f75c50bc Mon Sep 17 00:00:00 2001 From: OSEH-svg Date: Wed, 30 Jul 2025 13:08:39 +0100 Subject: [PATCH 2/6] second commit --- .../components/NFTMintingTable.tsx | 0 .../components/PayoutRequestDetailModal.tsx | 157 +++++++++++ .../components/PayoutRequestTable.tsx | 109 ++++++++ .../components/SearchFilterBar.tsx | 45 +-- .../components/SubscriptionTable.tsx | 81 ++++++ .../components/TransactionDetailModal.tsx | 193 +++++++++++++ .../components/TransactionTable.tsx | 19 +- src/app/dashboard/admin/transactions/page.tsx | 264 +++++++++++++++--- 8 files changed, 801 insertions(+), 67 deletions(-) create mode 100644 src/app/dashboard/admin/transactions/components/NFTMintingTable.tsx create mode 100644 src/app/dashboard/admin/transactions/components/PayoutRequestDetailModal.tsx create mode 100644 src/app/dashboard/admin/transactions/components/PayoutRequestTable.tsx create mode 100644 src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx create mode 100644 src/app/dashboard/admin/transactions/components/TransactionDetailModal.tsx diff --git a/src/app/dashboard/admin/transactions/components/NFTMintingTable.tsx b/src/app/dashboard/admin/transactions/components/NFTMintingTable.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dashboard/admin/transactions/components/PayoutRequestDetailModal.tsx b/src/app/dashboard/admin/transactions/components/PayoutRequestDetailModal.tsx new file mode 100644 index 0000000..24c84dc --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/PayoutRequestDetailModal.tsx @@ -0,0 +1,157 @@ +import React from "react"; +import { X, Link2Off } from "lucide-react"; +import type { PayoutRequest } from "./PayoutRequestTable"; +import Image from "next/image"; + +interface PayoutRequestDetailModalProps { + isOpen: boolean; + onClose: () => void; + request: PayoutRequest | null; + onApprove: (request: PayoutRequest) => void; + onDecline: (request: PayoutRequest) => void; +} + +const PayoutRequestDetailModal: React.FC = ({ + isOpen, + onClose, + request, + // onApprove, + // onDecline, +}) => { + if (!isOpen || !request) return null; + + return ( +
+
+
+

+ Payment Request Details +

+ +
+ +
+
+ {/* User Info */} +
+
+
+

+ Nana_Hamza +

+

nanahamza@gmail.com

+
+
+ + {/* Wallet Info */} +
+
+ Braavos +
+
+ + 0x04a093c37ab61065d001550089b108...979 + + + +
+
+ +
+ {/* Left Column */} +
+
+

Transaction Type

+

Withdraw

+
+
+

Status

+

+ Pending +

+
+
+

Request Date

+

12 March, 2025

+
+
+

Time

+

16:32

+
+
+ + {/* Right Column */} +
+
+

Amount to Paid

+
+ STRK +

800.00 STRK

+
+
+
+

Gas fee

+
+ STRK +

-80.00 STRK

+
+
+
+

To be Paid

+
+ STRK +

720.00 STRK

+
+
+
+
+ + {/* Action Buttons */} +
+ + +
+
+
+
+
+ ); +}; + +export default PayoutRequestDetailModal; diff --git a/src/app/dashboard/admin/transactions/components/PayoutRequestTable.tsx b/src/app/dashboard/admin/transactions/components/PayoutRequestTable.tsx new file mode 100644 index 0000000..d62fe67 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/PayoutRequestTable.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import Image from 'next/image'; + +export interface PayoutRequest { + author: string; + email: string; + amount: string; + walletAddress: string; + requestDate: string; + status: string; +} + +interface PayoutRequestTableProps { + requests: PayoutRequest[]; + onApprove: (request: PayoutRequest) => void; + onDecline: (request: PayoutRequest) => void; + onRowClick: (request: PayoutRequest) => void; +} + +const PayoutRequestTable: React.FC = ({ + requests, + onApprove, + onDecline, + onRowClick +}) => ( +
+ + + + {[ + "Author", + "Amount", + "Wallet Address", + "Request Date", + "Status", + "Actions" + ].map((header) => ( + + ))} + + + + {requests.map((request, index) => ( + onRowClick(request)} + > + + + + + + + + ))} + +
+ {header} +
+
+
{request.author}
+
{request.email}
+
+
+
+ starknet icon + {request.amount} +
+
+ {request.walletAddress} + + {request.requestDate} + + + {request.status} + + + + +
+
+); + +export default PayoutRequestTable; diff --git a/src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx b/src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx index 64b339a..0d5312c 100644 --- a/src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx +++ b/src/app/dashboard/admin/transactions/components/SearchFilterBar.tsx @@ -1,20 +1,29 @@ -import TimeFilter from "./TimeFilter"; +import TimeFilter from "./TimeFilter"; interface SearchFilterBarProps { - timeFilter: string; - onTimeFilterChange: (filter: string) => void; - searchPlaceholder?: string; - } - - const SearchFilterBar: React.FC = ({ - timeFilter, - onTimeFilterChange - }) => ( -
-
- -
Filter by
-
-
- ); -export default SearchFilterBar \ No newline at end of file + timeFilter: string; + onTimeFilterChange: (filter: string) => void; + searchPlaceholder?: string; +} + +const SearchFilterBar: React.FC = ({ + timeFilter, + onTimeFilterChange, +}) => ( +
+
+ {/* Time Filter on the left */} + + + {/* Button on the right */} + +
+
+ +); +export default SearchFilterBar; diff --git a/src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx b/src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx new file mode 100644 index 0000000..d76ab40 --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx @@ -0,0 +1,81 @@ +import Image from "next/image"; + +export interface Subscription { + id: string; + type: string; + amount: string; + user: string; + userName: string; + status: string; + date: string; +} + +interface SubscriptionTableProps { + transactions: Subscription[]; + onViewDetails: (tx: Subscription) => void; +} + +const SubscriptionTable: React.FC = ({ + transactions, + onViewDetails, +}) => { + // Only keep "Successful" and "Failed" transactions + const filtered = transactions.filter(tx => + tx.status === "Successful" || tx.status === "Failed" + ); + + return ( +
+ + + + + + + + + + + + + + + {filtered.map((transaction, index) => ( + + + + + + + + + + + ))} + +
Transaction IDTransaction TypeAmount (STRK)UserUser NameStatusDate
{transaction.id}{transaction.type} +
+ starknet icon + {transaction.amount} +
+
{transaction.user}{transaction.userName} + + {transaction.status} + + {transaction.date} + +
+
+ ); +}; + +export default SubscriptionTable; diff --git a/src/app/dashboard/admin/transactions/components/TransactionDetailModal.tsx b/src/app/dashboard/admin/transactions/components/TransactionDetailModal.tsx new file mode 100644 index 0000000..5b1b17f --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/TransactionDetailModal.tsx @@ -0,0 +1,193 @@ +import Image from "next/image"; +import { X, Download } from "lucide-react"; + +export interface Transaction { + type: "Subscription" | "Purchase" | string; + amount: string; + status: "Successful" | "Failed" | string; + bookTitle?: string; + userName: string; +} + +interface TransactionDetailModalProps { + isOpen: boolean; + onClose: () => void; + transaction: Transaction | null; +} + +const TransactionDetailModal: React.FC = ({ + isOpen, + onClose, + transaction, +}) => { + if (!isOpen || !transaction) return null; + + return ( +
+
+ + {/* Header */} +
+

+ {transaction.type === "Purchase" + ? "Book Purchase Details" + : transaction.type === "Subscription" + ? "Subscription Details" + : "Transaction Details"} +

+ +
+ +
+ {/* Top Section - Book Info and Payment Breakdown */} +
+ {/* Left - Book Info */} +
+
+
+
+

+ {transaction.bookTitle && transaction.bookTitle !== "-" + ? transaction.bookTitle + : "99 Laws of Power"} +

+

Series

+

Regular Book

+
+
+
+ + {/* Right - Payment Breakdown */} +
+
+ Amount + 800.00 STRK +
+
+ Royalty Earned + 80.00 STRK +
+
+ Platform Commission + 80.00 STRK +
+
+ Total Paid +
+ starknet icon + 916.00 STRK +
+
+
+
+ + {/* Bottom Section Transaction Details */} +
+ {/* Left Column */} +
+
+ + Transaction Type + + Book Purchase +
+
+ Status + + {transaction.status} + +
+
+ Date + + 12 March, 2025 + +
+
+ Time + 16:32 +
+
+ + {/* Right Column */} +
+
+ Reader + + {transaction.userName} + +
+
+ + Transaction ID + + TRANS-00112 +
+
+ + Transaction Hash + +
+ + 0x5a3f7b...d6e7f8g + + + + +
+
+
+ + Confirmation Status + + 12/12 +
+
+
+ + {/* Action Buttons */} +
+ + +
+
+
+
+ ); +}; + +export default TransactionDetailModal; diff --git a/src/app/dashboard/admin/transactions/components/TransactionTable.tsx b/src/app/dashboard/admin/transactions/components/TransactionTable.tsx index d2a19f1..d87fd97 100644 --- a/src/app/dashboard/admin/transactions/components/TransactionTable.tsx +++ b/src/app/dashboard/admin/transactions/components/TransactionTable.tsx @@ -1,5 +1,4 @@ import Image from "next/image"; -// import starknet from "../../../../../../public/starknet.png" export interface Transaction { id: string; @@ -14,10 +13,11 @@ export interface Transaction { interface TransactionTableProps { transactions: Transaction[]; + onViewDetails: (tx: Transaction) => void; } const TransactionTable: React.FC = ({ - transactions, + transactions, onViewDetails }) => (
@@ -76,8 +76,10 @@ const TransactionTable: React.FC = ({ {transaction.status} @@ -86,8 +88,13 @@ const TransactionTable: React.FC = ({ - ))} diff --git a/src/app/dashboard/admin/transactions/page.tsx b/src/app/dashboard/admin/transactions/page.tsx index 6cc0f42..693c4c0 100644 --- a/src/app/dashboard/admin/transactions/page.tsx +++ b/src/app/dashboard/admin/transactions/page.tsx @@ -12,13 +12,25 @@ import TimeFilter from "./components/TimeFilter"; import TabNavigation from "./components/TabNavigation"; import type { Tab } from "./components/TabNavigation"; import type { Transaction } from "./components/TransactionTable"; +import type { PayoutRequest } from "./components/PayoutRequestTable"; +import type { Subscription } from "./components/SubscriptionTable"; import TransactionTable from "./components/TransactionTable"; +import TransactionDetailModal from "./components/TransactionDetailModal"; +import PayoutRequestTable from "./components/PayoutRequestTable"; +import PayoutRequestDetailModal from "./components/PayoutRequestDetailModal"; const Transactions = () => { const [activeTab, setActiveTab] = useState("Transaction History"); const [timeFilter, setTimeFilter] = useState("This Week"); const [salesTimeFilter, setSalesTimeFilter] = useState("This Week"); const [tableTimeFilter, setTableTimeFilter] = useState("This Week"); + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedTransaction, setSelectedTransaction] = + useState(null); + const [selectedPayoutRequest, setSelectedPayoutRequest] = + useState(null); + // const [isTransactionModalOpen, setIsTransactionModalOpen] = useState(false); + const [isPayoutModalOpen, setIsPayoutModalOpen] = useState(false); // Chart data const chartData: ChartData[] = [ @@ -36,7 +48,7 @@ const Transactions = () => { ]; // Sample transaction data - const transactions: Transaction[] = [ + const transactionHistoryData: Transaction[] = [ { id: "Tran-124B", type: "Subscription", @@ -89,9 +101,141 @@ const Transactions = () => { }, ]; + // Payout request data + const payoutRequestData: PayoutRequest[] = [ + { + author: "Olu Ademola", + email: "oluade...@gmail.com", + amount: "500.67 STR", + walletAddress: "0xABC...789", + requestDate: "27 May,2025", + status: "Pending", + }, + { + author: "Olu Ademola", + email: "oluade...@gmail.com", + amount: "500.67 STR", + walletAddress: "0xABC...789", + requestDate: "27 May,2025", + status: "Pending", + }, + { + author: "Olu Ademola", + email: "oluade...@gmail.com", + amount: "500.67 STR", + walletAddress: "0xABC...789", + requestDate: "27 May,2025", + status: "Pending", + }, + { + author: "Olu Ademola", + email: "oluade...@gmail.com", + amount: "500.67 STR", + walletAddress: "0xABC...789", + requestDate: "27 May,2025", + status: "Pending", + }, + { + author: "Olu Ademola", + email: "oluade...@gmail.com", + amount: "500.67 STR", + walletAddress: "0xABC...789", + requestDate: "27 May,2025", + status: "Pending", + }, + ]; + + // Subscription data + const subscriptionData: Subscription[] = [ + { + id: "Sub-001", + type: "Monthly Subscription", + amount: "29.99 STRK", + user: "Reader", + userName: "alex_reader", + status: "Successful", + date: "28 July", + }, + { + id: "Sub-002", + type: "Annual Subscription", + amount: "299.99 STRK", + user: "Reader", + userName: "sarah_writer", + status: "Successful", + date: "27 July", + }, + ]; + + // NFT Minting data + const nftMintingData: Transaction[] = [ + { + id: "NFT-001", + type: "NFT Mint", + amount: "150.00 STRK", + user: "Writer", + userName: "crypto_author", + bookTitle: "Digital Art Collection", + status: "Successful", + date: "29 July", + }, + { + id: "NFT-002", + type: "NFT Mint", + amount: "200.00 STRK", + user: "Writer", + userName: "nft_creator", + bookTitle: "Limited Edition Series", + status: "Pending", + date: "28 July", + }, + ]; + + // const getTableData = (): Transaction[] | PayoutRequest[] | Subscription[] => { + // switch (activeTab) { + // case "Payout Request": + // return payoutRequestData; + // case "Subscription": + // return subscriptionData; + // case "NFT Minting": + // return nftMintingData; + // default: + // return transactionHistoryData; + // } + // }; + const handleTabClick = (tab: string) => { setActiveTab(tab); - // Modal logic will be handled by parent component + }; + + const handlePayoutRowClick = (request: PayoutRequest) => { + setSelectedPayoutRequest(request); + setIsPayoutModalOpen(true); + }; + + const handleClosePayoutModal = () => { + setIsPayoutModalOpen(false); + setSelectedPayoutRequest(null); + }; + + const handleViewDetails = (transaction: Transaction) => { + setSelectedTransaction(transaction); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + setSelectedTransaction(null); + }; + + const handleApprove = (request: PayoutRequest) => { + console.log("Approving:", request); + // Handle approval logic + }; + + const handleDecline = (request: PayoutRequest) => { + console.log("Declining:", request); + // Handle decline logic }; return ( @@ -127,34 +271,33 @@ const Transactions = () => { {/* 2x2 grid of smaller cards on the right */}
- -
- - - - -
+
+ + + + +
@@ -212,24 +355,59 @@ const Transactions = () => { {/* Transaction Table Section */}
-
- +
+ - + - + {activeTab === "Payout Request" ? ( + + ) : ( + { + switch (activeTab) { + case "NFT Minting": + return nftMintingData; + default: + return transactionHistoryData; + } + })()} + onViewDetails={handleViewDetails} + /> + )} - -
+ +
+ + {/* Transaction Detail Modal */} + + + {/* Payout Request Detail Modal */} + ); }; From b2403733eae656f41b2ab1da8fd5b815746acaf8 Mon Sep 17 00:00:00 2001 From: OSEH-svg Date: Thu, 31 Jul 2025 01:18:37 +0100 Subject: [PATCH 3/6] Transaction page completed --- .../components/NFTDetailModal.tsx | 278 ++++++++++++++++++ .../components/NFTMintingTable.tsx | 120 ++++++++ .../components/SubscriptionTable.tsx | 5 +- .../transactions/components/TabNavigation.tsx | 4 +- src/app/dashboard/admin/transactions/page.tsx | 200 +++++++++---- 5 files changed, 541 insertions(+), 66 deletions(-) create mode 100644 src/app/dashboard/admin/transactions/components/NFTDetailModal.tsx diff --git a/src/app/dashboard/admin/transactions/components/NFTDetailModal.tsx b/src/app/dashboard/admin/transactions/components/NFTDetailModal.tsx new file mode 100644 index 0000000..082a3ba --- /dev/null +++ b/src/app/dashboard/admin/transactions/components/NFTDetailModal.tsx @@ -0,0 +1,278 @@ +import React from "react"; +import { X, ArrowUpRight } from "lucide-react"; +import type { NFTMinting } from "./NFTMintingTable"; +import Image from "next/image"; + +interface NFTDetailModalProps { + isOpen: boolean; + onClose: () => void; + nft: NFTMinting | null; + onRetryMinting?: (nft: NFTMinting) => void; +} + +const NFTDetailModal: React.FC = ({ + isOpen, + onClose, + nft, + onRetryMinting, +}) => { + if (!isOpen || !nft) return null; + + const isSuccessful = nft.status === "Minted"; + const isFailed = nft.status === "Failed"; + + return ( +
+
+ {/* Header */} +
+

{nft.title}

+ +
+ +
+ {/* Top Section - Book Info */} +
+ {/* Left: Book Details */} +
+ {/* Book Cover */} +
+ +
+ + {/* Book Info */} +
+

{nft.title}

+ +
+ By {nft.author} + {/* Verified icon */} + + + +
+ +
+ + NFT Edition + +
+ +
+ + 4.5 +
+
+
+ + {/* Right: Retry Button aligned to bottom */} +
+ {isFailed && onRetryMinting && ( + + )} +
+
+ + {/* Detail Stat Boxes */} +
+
+
Payment Type
+
+ One-Time Payment +
+
+
+
Price
+
+ {nft.feeAmount} +
+
+
+
Published Date
+
+ {nft.mintDate} +
+
+
+
Sold
+
57
+
+
+
Transaction
+
+ + All Transactions +
+
+
+ + {/* Blockchain Details Section */} +
+

+ Blockchain Details +

+ +
+
+
+ Smart Contract Address +
+
+ {nft.smartContractAddress || "0x9f...d45A"} +
+
+
+
Token Standard
+
+ {nft.tokenStandard || "ERC-721"} +
+
+
+
Network
+
+ {nft.network} +
+
+
+
Status
+
+ + {isSuccessful ? "Successful" : nft.status} + +
+
+
+
Fee Amount
+
+ {nft.smartContractAddress || "0x9f...d45A"} +
+
+
+
Fee Amount
+
+ + + {nft.feeAmount} + +
+
+
+
Mint Date
+
+ {nft.mintDate} +
+
+
+
+ + {/* Description Section */} +
+

+ Description +

+
+

+ {nft.description || + "Delves into the complex and often insidious ways in which indigenous peoples and their unique experiences are rendered unseen and unheard in the modern era."} +

+ +
+
+ + {/* Additional Details Grid */} +
+
+
Genre(s)
+
+ + Fiction + + + Comic + +
+
+
+
Page count
+
+ 21 Pages +
+
+
+
Language
+
English
+
+
+
Date published
+
+ 12 April, 2025 +
+
+
+
ISBN
+
+ 978-3-16-148410-0 +
+
+
+ + {/* Action Buttons */} + {/*
+ + +
*/} +
+
+
+ ); +}; + +export default NFTDetailModal; diff --git a/src/app/dashboard/admin/transactions/components/NFTMintingTable.tsx b/src/app/dashboard/admin/transactions/components/NFTMintingTable.tsx index e69de29..5574b1c 100644 --- a/src/app/dashboard/admin/transactions/components/NFTMintingTable.tsx +++ b/src/app/dashboard/admin/transactions/components/NFTMintingTable.tsx @@ -0,0 +1,120 @@ +// NFTMintingTable.tsx +import React from "react"; +import Image from "next/image"; + +export interface NFTMinting { + id: string; + title: string; + author: string; + feeAmount: string; + network: string; + mintDate: string; + status: "Minted" | "Failed" | "Pending"; + coverImage?: string; + smartContractAddress?: string; + tokenStandard?: string; + transactionHash?: string; + confirmationStatus?: string; + description?: string; + genres?: string[]; + pageCount?: number; + language?: string; + isbn?: string; +} + +interface NFTMintingTableProps { + nftMinings: NFTMinting[]; + onViewMore: (nft: NFTMinting) => void; +} + +const NFTMintingTable: React.FC = ({ + nftMinings, + onViewMore, +}) => { + return ( +
+
{transaction.date} - + +
+ + + + + + + + + + + + + {nftMinings.map((nft, index) => ( + + + + + + + + + + ))} + +
+ Cover + + Title and Author + + Fee Amount + + Network + + Mint Date + + Status +
+
+
+
+
+ {nft.title} +
+
by {nft.author}
+
+
+
+ starknet icon + {nft.feeAmount} +
+
+ {nft.network} + + {nft.mintDate} + + + {nft.status} + + + +
+
+ ); +}; + +export default NFTMintingTable; diff --git a/src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx b/src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx index d76ab40..0f3d22b 100644 --- a/src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx +++ b/src/app/dashboard/admin/transactions/components/SubscriptionTable.tsx @@ -12,12 +12,10 @@ export interface Subscription { interface SubscriptionTableProps { transactions: Subscription[]; - onViewDetails: (tx: Subscription) => void; } const SubscriptionTable: React.FC = ({ transactions, - onViewDetails, }) => { // Only keep "Successful" and "Failed" transactions const filtered = transactions.filter(tx => @@ -27,7 +25,7 @@ const SubscriptionTable: React.FC = ({ return (
- + @@ -65,7 +63,6 @@ const SubscriptionTable: React.FC = ({
Transaction ID Transaction Type diff --git a/src/app/dashboard/admin/transactions/components/TabNavigation.tsx b/src/app/dashboard/admin/transactions/components/TabNavigation.tsx index d406499..32e6810 100644 --- a/src/app/dashboard/admin/transactions/components/TabNavigation.tsx +++ b/src/app/dashboard/admin/transactions/components/TabNavigation.tsx @@ -14,13 +14,13 @@ interface TabNavigationProps { } const TabNavigation: React.FC = ({ tabs, activeTab, onTabClick }) => ( -
+
{tabs.map((tab) => ( - ) : ( -
-
-
- Wallet Avatar -
- - {account.slice(0, 6)}…{account.slice(-4)} - - -
- - {/* Custom Dropdown Menu */} - {isDropdownOpen && ( -
-
- - -
-
- )} -
- )} - -
-
- - setIsDisconnectModalOpen(false)} - onDisconnect={handleDisconnect} - /> - - ); - }; - - export default NavBar; From af1cefa9b27500cf2df160e46a9e20fafd4b5dcc Mon Sep 17 00:00:00 2001 From: OSEH-svg Date: Tue, 5 Aug 2025 12:33:32 +0100 Subject: [PATCH 6/6] latest changes. merge conflict resolves --- src/components/landingpage/NavBar.tsx | 183 ++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 src/components/landingpage/NavBar.tsx diff --git a/src/components/landingpage/NavBar.tsx b/src/components/landingpage/NavBar.tsx new file mode 100644 index 0000000..7540829 --- /dev/null +++ b/src/components/landingpage/NavBar.tsx @@ -0,0 +1,183 @@ +"use client"; + +import React from "react"; +import Image from "next/image"; +import Link from "next/link"; +import Image4 from "@/assets/Images/ImageLogo.png"; +import { useState, useRef, useEffect, SetStateAction } from "react"; +import { MoreVertical } from "lucide-react"; +import AnimationWrapper from "@/components/motion/Animation-wrapper"; +import WalletDisconnectModal from "../blockchain/Wallet-disconnect-modal"; +import { ConnectButton } from "../../components/blockchain/connect-button"; +// starknet imports +import { useWalletContext } from "../blockchain/WalletProvider"; +import { usePathname } from "next/navigation"; +import clsx from "clsx"; + +const NavBar = () => { + const pathname = usePathname(); + const [isConnectModalOpen, setIsConnectModalOpen] = useState(false); + const [isDisconnectModalOpen, setIsDisconnectModalOpen] = useState(false); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const path = usePathname(); + + const dropdownRef = useRef(null); + const navItems = [ + { label: "Home", href: "/" }, + { label: "Books", href: "/books" }, + { label: "How It Works", href: "/how-it-works" }, + { label: "About ChainLib", href: "/about-us" }, + ]; + + const { account, connectWallet, disconnectWallet, connectors } = + useWalletContext(); + + // Close dropdown when clicking outside + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + setIsDropdownOpen(false); + } + } + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + const toggleDropdown = () => setIsDropdownOpen(!isDropdownOpen); + + const handleWalletSelect = (walletId: string) => { + const connector = connectors.find((c) => c.id === walletId); + if (connector) { + connectWallet(connector); // invoke Starknet-React’s useConnect() :contentReference[oaicite:3]{index=3} + } + setIsConnectModalOpen(false); + }; + + const handleConnectWallet = () => { + setIsConnectModalOpen(true); + }; + + const handleWalletClick = () => { + setIsDisconnectModalOpen(true); + }; + + const handleDisconnect = () => { + disconnectWallet(); // real Starknet-React disconnect :contentReference[oaicite:4]{index=4} + setIsDisconnectModalOpen(false); + }; + + if (path.includes("dashboard")) { + return; + } + + return ( + <> +
+ + + Logo + + ChainLib + + + + + + + {/* Wallet Connection Button or Connected Wallet */} +
+ + {!account ? ( + + ) : ( +
+
+
+ Wallet Avatar +
+ + {account.slice(0, 6)}…{account.slice(-4)} + + +
+ + {/* Custom Dropdown Menu */} + {isDropdownOpen && ( +
+
+ +
+
+ )} +
+ )} +
+
+
+ + setIsDisconnectModalOpen(false)} + onDisconnect={handleDisconnect} + /> + + ); +}; + +export default NavBar;