diff --git a/dapp/public/images/bg-image.png b/dapp/public/images/bg-image.png new file mode 100644 index 0000000..1f0f999 Binary files /dev/null and b/dapp/public/images/bg-image.png differ diff --git a/dapp/public/images/trajectifi-logo-text.png b/dapp/public/images/trajectifi-logo-text.png new file mode 100644 index 0000000..378b4b1 Binary files /dev/null and b/dapp/public/images/trajectifi-logo-text.png differ diff --git a/dapp/public/images/trajectifi-logo.png b/dapp/public/images/trajectifi-logo.png new file mode 100644 index 0000000..0411f86 Binary files /dev/null and b/dapp/public/images/trajectifi-logo.png differ diff --git a/dapp/src/app/components/navbar.tsx b/dapp/src/app/components/navbar.tsx new file mode 100644 index 0000000..4182117 --- /dev/null +++ b/dapp/src/app/components/navbar.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; +import { + Search as SearchIcon, + Bell as BellIcon, + HelpCircle as HelpIcon +} from "lucide-react"; + +export default function Navbar() { + const [searchQuery, setSearchQuery] = useState(""); + + return ( + + ); +} \ No newline at end of file diff --git a/dapp/src/app/components/sidebar.tsx b/dapp/src/app/components/sidebar.tsx new file mode 100644 index 0000000..a6d5c78 --- /dev/null +++ b/dapp/src/app/components/sidebar.tsx @@ -0,0 +1,163 @@ +"use client"; +import Image from "next/image"; +import trajectifylogo from "../../../public/images/trajectifi-logo.png"; +import trajectword from "../../../public/images/trajectifi-logo-text.png"; + +import { useState, useEffect } from "react"; +import Link from "next/link"; +import { + LayoutGrid as AssetsIcon, + CreditCard as LoanIcon, + Search as SearchIcon, +} from "lucide-react"; + +export default function Sidebar() { + const [isCollapsed, setIsCollapsed] = useState(true); // Default to collapsed + const [isHovered, setIsHovered] = useState(false); + + // Auto-collapse on mobile, maintain collapsed state on larger screens + useEffect(() => { + const handleResize = () => { + if (window.innerWidth < 768) { + setIsCollapsed(true); + } + }; + + // Set initial state + handleResize(); + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + + // Toggle sidebar expanded/collapsed state + const toggleSidebar = () => { + setIsCollapsed(!isCollapsed); + }; + + // Determine if sidebar should be expanded + const expanded = !isCollapsed || isHovered; + + return ( + + ); +} + +interface SidebarItemProps { + href: string; + icon: React.ReactNode; + text: string; + expanded: boolean; + active?: boolean; +} + +interface SidebarItemProps { + href: string; + icon: React.ReactNode; + text: string; + expanded: boolean; + active?: boolean; +} + +function SidebarItem({ + href, + icon, + text, + expanded, + active = false, +}: SidebarItemProps) { + return ( + e.stopPropagation()} + > + {expanded ? ( +
+ {icon} + {text} +
+ ) : ( + {icon} + )} + + ); +} \ No newline at end of file diff --git a/dapp/src/app/globals.css b/dapp/src/app/globals.css index 0c78e34..74eea95 100644 --- a/dapp/src/app/globals.css +++ b/dapp/src/app/globals.css @@ -9,13 +9,18 @@ @media (prefers-color-scheme: dark) { :root { - --background: #0a0a0a; + --background: #080a1f; --foreground: #ededed; } } body { - color: var(--foreground); - background: #06071B; - font-family: Arial, Helvetica, sans-serif; + color: rgb(var(--foreground-rgb)); + background-color: #b56203; +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } } diff --git a/dapp/src/app/layout.tsx b/dapp/src/app/layout.tsx index c0ba8d9..0011196 100644 --- a/dapp/src/app/layout.tsx +++ b/dapp/src/app/layout.tsx @@ -1,9 +1,8 @@ -"use client"; -// import type { Metadata } from "next"; -import React, { useState } from "react"; +import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; -import { Header, Sidebar } from "@/components"; +import Navbar from "../app/components/navbar"; +import Sidebar from "../app/components/sidebar"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -15,47 +14,29 @@ const geistMono = Geist_Mono({ subsets: ["latin"], }); -// export const metadata: Metadata = { -// title: "Create Next App", -// description: "Generated by create next app", -// }; +export const metadata: Metadata = { + title: "Trajectifi", + description: "Trajectifi DApp", +}; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - const [sidebarOpen, setSidebarOpen] = useState(false); - const toggleSidebar = () => { - setSidebarOpen(!sidebarOpen); - }; return ( -
- {/* Mobile sidebar overlay */} - {sidebarOpen && ( -
setSidebarOpen(false)} - /> - )} - - {/* Sidebar */} - - - {/* Main content */} -
-
- -
-
{children}
-
+
+ +
+ +
{children}
); -} +} \ No newline at end of file diff --git a/dapp/src/app/my_assets/components/CollectionSidebar.tsx b/dapp/src/app/my_assets/components/CollectionSidebar.tsx new file mode 100644 index 0000000..abf7610 --- /dev/null +++ b/dapp/src/app/my_assets/components/CollectionSidebar.tsx @@ -0,0 +1,54 @@ +// src/app/my_assets/components/CollectionsSidebar.tsx +import Image from "next/image"; +import React from "react"; +import { Collection } from "../../types"; + +interface CollectionsSidebarProps { + collections: Collection[]; + searchQuery: string; + toggleCollection: (id: string) => void; + setSearchQuery: (query: string) => void; +} + +export const CollectionsSidebar = ({ + collections, + searchQuery, + toggleCollection, + setSearchQuery, +}: CollectionsSidebarProps) => ( +
+

Collections

+ +
+ setSearchQuery(e.target.value)} + /> + + + +
+ +
+ {collections.map(collection => ( +
+ toggleCollection(collection.id)} + className="w-4 h-4 rounded accent-purple-600" + /> +
+
+ {collection.name} +
+ {collection.name} +
+
+ ))} +
+
+); \ No newline at end of file diff --git a/dapp/src/app/my_assets/components/NFTGrid.tsx b/dapp/src/app/my_assets/components/NFTGrid.tsx new file mode 100644 index 0000000..e5049c3 --- /dev/null +++ b/dapp/src/app/my_assets/components/NFTGrid.tsx @@ -0,0 +1,59 @@ +// src/app/my_assets/components/NFTGrid.tsx +import Image from "next/image"; +import React from "react"; +import { NFT } from "../../types"; +import Link from "next/link"; + +interface NFTGridProps { + nfts: NFT[]; +} + +export const NFTGrid = ({ nfts }: NFTGridProps) => ( +
+ {nfts.map((nft) => ( + +
+
+ {nft.name} +
+
+
+

{nft.name}

+ + {nft.status} + +
+

+ {nft.collectionName} ({nft.collectionTag}) +

+
+
+ + ))} +
+); diff --git a/dapp/src/app/my_assets/components/NFTList.tsx b/dapp/src/app/my_assets/components/NFTList.tsx new file mode 100644 index 0000000..f0d1afb --- /dev/null +++ b/dapp/src/app/my_assets/components/NFTList.tsx @@ -0,0 +1,44 @@ +// src/app/my_assets/components/NFTList.tsx +import Image from "next/image"; +import React from "react"; +import { NFT } from "../../types"; +import Link from "next/link"; + +interface NFTListProps { + nfts: NFT[]; +} + +export const NFTList = ({ nfts }: NFTListProps) => ( +
+ {nfts.map((nft) => ( + +
+ {nft.name} +
+
+
+

{nft.name}

+

+ {nft.collectionName} ({nft.collectionTag}) +

+
+ + {nft.status} + +
+ + ))} +
+); diff --git a/dapp/src/app/my_assets/components/StatsCards.tsx b/dapp/src/app/my_assets/components/StatsCards.tsx new file mode 100644 index 0000000..c9bbf53 --- /dev/null +++ b/dapp/src/app/my_assets/components/StatsCards.tsx @@ -0,0 +1,51 @@ +// src/app/my_assets/components/StatsCards.tsx +import Image from "next/image"; +import React from "react"; +import purple from '../../../../public/images/purple.png'; +import pink from '../../../../public/images/pink.png'; +import orange from '../../../../public/images/orange.png'; +import green from '../../../../public/images/green.png'; + +export const StatsCards = () => ( +
+
+
+ wallet +
+
+

Total NFTs Owned

+

5

+
+
+ +
+
+ wallet +
+
+

Total Interest Earned

+

4.32 STRK

+
+
+ +
+
+ wallet +
+
+

In Escrow

+

0

+
+
+ +
+
+ wallet +
+
+

Successful Loans

+

0

+
+
+
+); \ No newline at end of file diff --git a/dapp/src/app/my_assets/components/ViewToggle.tsx b/dapp/src/app/my_assets/components/ViewToggle.tsx new file mode 100644 index 0000000..d8412e6 --- /dev/null +++ b/dapp/src/app/my_assets/components/ViewToggle.tsx @@ -0,0 +1,30 @@ +// src/app/my_assets/components/ViewToggle.tsx +import React from "react"; + +interface ViewToggleProps { + viewMode: 'grid' | 'list'; + setViewMode: (mode: 'grid' | 'list') => void; +} + +export const ViewToggle = ({ viewMode, setViewMode }: ViewToggleProps) => ( +
+ + +
+); \ No newline at end of file diff --git a/dapp/src/app/my_assets/page.tsx b/dapp/src/app/my_assets/page.tsx new file mode 100644 index 0000000..7e5290d --- /dev/null +++ b/dapp/src/app/my_assets/page.tsx @@ -0,0 +1,163 @@ +// src/app/my_assets/page.tsx +"use client"; +import React, { useState } from "react"; +import NFT1 from "../../../public/images/NFT1.png"; +import NFT2 from "../../../public/images/NFT2.png"; +import NFT3 from "../../../public/images/NFT3.png"; +import NFT4 from "../../../public/images/NFT4.png"; +import NFT5 from "../../../public/images/NFT5.png"; +import Avatar1 from "../../../public/images/Avatar1.png"; +import Avatar2 from "../../../public/images/Avatar2.png"; +import Avatar3 from "../../../public/images/Avatar3.png"; +import Avatar4 from "../../../public/images/Avatar4.png"; +import Avatar5 from "../../../public/images/Avatar5.png"; +import Avatar6 from "../../../public/images/Avatar6.png"; +import { Collection, NFT } from "../types"; +import { StatsCards } from "./components/StatsCards"; +import { CollectionsSidebar } from "./components/CollectionSidebar"; +import { ViewToggle } from "./components/ViewToggle"; +import { NFTGrid } from "./components/NFTGrid"; +import { NFTList } from "./components/NFTList"; + +export default function NFTMarketplace() { + const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); + const [activeTab, setActiveTab] = useState("All"); + const [searchQuery, setSearchQuery] = useState(""); + + const [collections, setCollections] = useState([ + { id: "1", name: "Cryptopunk VQ (Wrapped)", selected: true, icon: Avatar1 }, + { id: "2", name: "CLONE X-X", selected: true, icon: Avatar2 }, + { id: "3", name: "KOLLABEAR", selected: true, icon: Avatar3 }, + { id: "4", name: "NFT Locked Bundle", selected: true, icon: Avatar4 }, + { id: "5", name: "FLUF", selected: true, icon: Avatar5 }, + { id: "6", name: "RANGA", selected: true, icon: Avatar6 }, + ]); + + const nfts: NFT[] = [ + { + id: "1", + name: "Cryptopunk #1232", + collectionName: "Cryptopunk VQ (Wrapped)", + status: "Escrow", + image: NFT1, + collectionTag: "STRK", + }, + { + id: "2", + name: "Cryptopunk #1232", + collectionName: "Cryptopunk VQ (Wrapped)", + status: "Listed", + image: NFT2, + collectionTag: "STRK", + }, + { + id: "3", + name: "CLONE X #456", + collectionName: "CLONE X-X", + status: "Unlisted", + image: NFT3, + collectionTag: "ETH", + }, + { + id: "4", + name: "KOLLABEAR #789", + collectionName: "KOLLABEAR", + status: "Unlisted", + image: NFT4, + collectionTag: "SOL", + }, + { + id: "5", + name: "FLUF #101", + collectionName: "FLUF", + status: "Unlisted", + image: NFT5, + collectionTag: "ETH", + }, + ]; + + const toggleCollection = (id: string) => { + setCollections( + collections.map((collection) => + collection.id === id + ? { ...collection, selected: !collection.selected } + : collection + ) + ); + }; + + const filteredNfts = nfts.filter((nft) => { + const statusMatch = + activeTab === "All" || + (activeTab === "In Escrow" + ? nft.status === "Escrow" + : nft.status === activeTab); + + const collectionMatch = collections.some( + (collection) => + collection.selected && + nft.collectionName.includes(collection.name.split(" ")[0]) + ); + + const searchMatch = + searchQuery === "" || + nft.name.toLowerCase().includes(searchQuery.toLowerCase()) || + nft.collectionName.toLowerCase().includes(searchQuery.toLowerCase()); + + return statusMatch && collectionMatch && searchMatch; + }); + + return ( +
+

My Assets

+ + + +
+

NFTs Owned

+ +
+ {["All", "Listed", "Unlisted", "In Escrow"].map((tab) => ( + + ))} +
+
+ +
+ + +
+
+

+ Search Results ({filteredNfts.length}) +

+ +
+ + {filteredNfts.length === 0 ? ( +
+

No NFTs match your filters

+
+ ) : viewMode === "grid" ? ( + + ) : ( + + )} +
+
+
+ ); +} diff --git a/dapp/src/app/page.tsx b/dapp/src/app/page.tsx index 43283e8..097a17d 100644 --- a/dapp/src/app/page.tsx +++ b/dapp/src/app/page.tsx @@ -1,24 +1,52 @@ "use client"; -import { MyAssets, ConnectWallet } from "@/components"; -import { useState } from "react"; +import Link from "next/link"; +import { ArrowRight } from "lucide-react"; +import { usePathname } from "next/navigation"; -const Home = () => { - const [isSidebarOpen, setIsSidebarOpen] = useState(false); - const toggleSidebar = () => { - setIsSidebarOpen(!isSidebarOpen); +export default function Home() { + const pathname = usePathname(); + const getPageTitle = () => { + const path = pathname.slice(1) || "home"; + return path + .split("_") + .join(" ") + .split("-") + .join(" ") + .split(" ") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); }; + return ( -
- - {isSidebarOpen ? : } +
+

{getPageTitle()}

+ +
+
+
+

Connect Your Wallet

+

+ To view your assets, please connect your wallet. +

+ + + +
+ + {/* Dark overlay */} +
+
+
); -}; - -export default Home; +} diff --git a/dapp/src/app/view-listed-asset/component/FilterBar.tsx b/dapp/src/app/view-listed-asset/component/FilterBar.tsx new file mode 100644 index 0000000..edea660 --- /dev/null +++ b/dapp/src/app/view-listed-asset/component/FilterBar.tsx @@ -0,0 +1,16 @@ +export default function FilterBar() { + return ( +
+

filter:

+
+ + + +
+
+ + +
+
+ ); +} diff --git a/dapp/src/app/view-listed-asset/component/ListingInfo.tsx b/dapp/src/app/view-listed-asset/component/ListingInfo.tsx new file mode 100644 index 0000000..105fda1 --- /dev/null +++ b/dapp/src/app/view-listed-asset/component/ListingInfo.tsx @@ -0,0 +1,70 @@ +import Image from "next/image"; +import Punk from "@/assets/punk.png"; +import Net from "@/assets/net.png"; +import Reload from "@/assets/reload.png"; +import Angle from "@/assets/angle.png"; + +export default function ListingInfo() { + return ( +
+
+

Listing Information

+
+ network + angle + reload +
+
+ +
+ CryptoPunk +
+
+

Cryptopunk #1232

+

Cryptopunk VQ (STRK)

+
+ + Listed + +
+
+ +
+
+ Currency + All +
+
+ Acceptable Amount + 0.001 STRK +
+
+ Loan Duration + 14 days +
+
+ APR + 8% +
+
+ +

+ You can unlist your item at any time. +

+ +
+ + +
+
+ ); +} diff --git a/dapp/src/app/view-listed-asset/component/LoanRow.tsx b/dapp/src/app/view-listed-asset/component/LoanRow.tsx new file mode 100644 index 0000000..9eb829b --- /dev/null +++ b/dapp/src/app/view-listed-asset/component/LoanRow.tsx @@ -0,0 +1,17 @@ +export default function LoanRow() { + return ( + + 2,856.41 + 0D673a4...2 + 1.17% + 14.19% + 30 days + 2,891.75 + + + + + ); +} diff --git a/dapp/src/app/view-listed-asset/component/LoanTable.tsx b/dapp/src/app/view-listed-asset/component/LoanTable.tsx new file mode 100644 index 0000000..045a31d --- /dev/null +++ b/dapp/src/app/view-listed-asset/component/LoanTable.tsx @@ -0,0 +1,33 @@ +import FilterBar from './FilterBar'; +import LoanRow from './LoanRow'; + +export default function LoanTable() { + return ( +
+ +
+ + + + + + + + + + + + + + {[...Array(6)].map((_, i) => ( + + ))} + +
Loan valueLenderInterestAPRDurationRepaymentAction
+
+ Showing 1 to 6 of 13 items +
+
+
+ ); +} diff --git a/dapp/src/app/view-listed-asset/page.tsx b/dapp/src/app/view-listed-asset/page.tsx new file mode 100644 index 0000000..0a40202 --- /dev/null +++ b/dapp/src/app/view-listed-asset/page.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import ListingInfo from './component/ListingInfo'; +import LoanTable from './component/LoanTable'; + +const Page = () => { + return ( +
+ + +
+ + +
+ +
+
+
+ ); +}; + +export default Page; diff --git a/dapp/tailwind.config.ts b/dapp/tailwind.config.ts index 109807b..95de64d 100644 --- a/dapp/tailwind.config.ts +++ b/dapp/tailwind.config.ts @@ -11,6 +11,17 @@ export default { colors: { background: "var(--background)", foreground: "var(--foreground)", + purple: { + 500: "#8855FF", + 600: "#7040E0", + 700: "#5C30C0", + }, + gray: { + 800: "#1A1A2E", + }, + }, + backgroundImage: { + 'page-bg': "url('/images/bg-image.png')", }, }, },