@@ -340,7 +348,10 @@ const UserProfilePage = () => {
{/* Library Stats */}
{libraryStats.map((stat, index) => (
-
+
{stat.label}
@@ -388,14 +399,30 @@ const UserProfilePage = () => {
)}
+ {/* Direct Notification Content */}
+ {activeTab === "Direct Notification" && (
+
+ )}
+
+ {/* Transaction Content */}
+ {activeTab === "Transactions" &&
}
+
+ {/* Reviews & Feedback Content */}
+ {activeTab === "Reviews & Feedback" && (
+
+ )}
+
{/* Other Tab Contents */}
- {activeTab !== "Library" && (
-
-
- {activeTab} content would go here
+ {activeTab !== "Library" &&
+ activeTab !== "Direct Notification" &&
+ activeTab !== "Transactions" &&
+ activeTab !== "Reviews & Feedback" && (
+
+
+ {activeTab} content would go here
+
-
- )}
+ )}
diff --git a/src/components/blockchain/wallet-connect-context.tsx b/src/components/blockchain/wallet-connect-context.tsx
index 93643c1..b1f88e3 100644
--- a/src/components/blockchain/wallet-connect-context.tsx
+++ b/src/components/blockchain/wallet-connect-context.tsx
@@ -1,5 +1,11 @@
-"use client"
-import React, { createContext, useContext, ReactNode, useState, useEffect } from "react";
+"use client";
+import React, {
+ createContext,
+ useContext,
+ ReactNode,
+ useState,
+ useEffect,
+} from "react";
import { AccountStatus, useAccount, useConnect } from "@starknet-react/core";
// Define the context type
@@ -27,13 +33,13 @@ export function WalletProvider({ children }: { children: ReactNode }) {
// Check if any wallet is available in the browser
useEffect(() => {
- const hasWallet = connectors.some(connector => connector.available());
+ const hasWallet = connectors.some((connector) => connector.available());
setIsWalletDetected(hasWallet);
}, [connectors]);
// Handle connection errors
useEffect(() => {
- if (status === "error" as AccountStatus) {
+ if (status === ("error" as AccountStatus)) {
setError(new Error("Failed to connect wallet"));
} else {
setError(null);
@@ -59,13 +65,11 @@ export function WalletProvider({ children }: { children: ReactNode }) {
error,
isModalOpen,
openConnectModal,
- closeConnectModal
+ closeConnectModal,
};
return (
-
- {children}
-
+
{children}
);
}
diff --git a/src/components/dashboard/DeleteReviewModal.tsx b/src/components/dashboard/DeleteReviewModal.tsx
new file mode 100644
index 0000000..84030b5
--- /dev/null
+++ b/src/components/dashboard/DeleteReviewModal.tsx
@@ -0,0 +1,75 @@
+"use client";
+
+import React from "react";
+
+interface DeleteReviewModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onConfirm: () => void;
+ review?: {
+ id: string;
+ bookTitle: string;
+ author: string;
+ reviewerName: string;
+ rating: number;
+ reviewText: string;
+ } | null;
+}
+
+const DeleteReviewModal: React.FC
= ({
+ isOpen,
+ onClose,
+ onConfirm,
+ review,
+}) => {
+ if (!isOpen || !review) return null;
+
+ return (
+
+
+ {/* Warning Icon */}
+
+
+ {/* Warning Message */}
+
+
+ You are about to delete{" "}
+
+ @{review.reviewerName}'s
+ {" "}
+ Review on the book{" "}
+
+ {review.bookTitle} by {review.author}
+
+
+
+
+ {/* Action Buttons */}
+
+
+ Delete
+
+
+ Cancel
+
+
+
+
+ );
+};
+
+export default DeleteReviewModal;
diff --git a/src/components/dashboard/DirectNotificationContent.tsx b/src/components/dashboard/DirectNotificationContent.tsx
new file mode 100644
index 0000000..bc945c8
--- /dev/null
+++ b/src/components/dashboard/DirectNotificationContent.tsx
@@ -0,0 +1,229 @@
+"use client";
+
+import React, { useState } from "react";
+import { Search } from "lucide-react";
+import NotificationDetailModal from "./NotificationDetailModal";
+import SendMessageModal from "./SendMessageModal";
+
+interface Notification {
+ id: string;
+ title: string;
+ sender: string;
+ message: string;
+ date: string;
+ avatar?: string;
+ receiver?: string;
+ time?: string;
+ status?: string;
+}
+
+const mockNotifications: Notification[] = [
+ {
+ id: "1",
+ title: "Policy Violation",
+ sender: "Ola**p@gmail.com",
+ message:
+ "Your recent account activity has been flagged for violating our Terms of Service concerning unauthorized access.",
+ date: "13 June, 2025",
+ },
+ {
+ id: "2",
+ title: "Policy Violation",
+ sender: "Ola**p@gmail.com",
+ message:
+ "Your recent account activity has been flagged for violating our Terms of Service concerning unauthorized access.",
+ date: "13 June, 2025",
+ },
+ {
+ id: "3",
+ title: "Policy Violation",
+ sender: "Ola**p@gmail.com",
+ message:
+ "Your recent account activity has been flagged for violating our Terms of Service concerning unauthorized access.",
+ date: "13 June, 2025",
+ },
+ {
+ id: "4",
+ title: "Policy Violation",
+ sender: "Ola**p@gmail.com",
+ message:
+ "Your recent account activity has been flagged for violating our Terms of Service concerning unauthorized access.",
+ date: "13 June, 2025",
+ },
+];
+
+const NotificationCard: React.FC<{
+ notification: Notification;
+ onViewProfile: (notification: Notification) => void;
+}> = ({ notification, onViewProfile }) => {
+ return (
+
+
+ {/* Avatar placeholder */}
+
+
+
+
+
+
+
+ {notification.title}
+
+
+ Sent by: {notification.sender}
+
+
+
+
+ {notification.message}
+
+
{notification.date}
+
+
+
+ onViewProfile(notification)}
+ className="text-blue-600 hover:text-blue-800 text-sm font-medium"
+ >
+ View Profile
+
+
+
+
+
+
+ );
+};
+
+const DirectNotificationContent: React.FC = () => {
+ const [activeFilter, setActiveFilter] = useState<"All" | "Sent by Me">("All");
+ const [searchQuery, setSearchQuery] = useState("");
+ const [selectedNotification, setSelectedNotification] =
+ useState(null);
+ const [isDetailModalOpen, setIsDetailModalOpen] = useState(false);
+ const [isSendModalOpen, setIsSendModalOpen] = useState(false);
+
+ const filteredNotifications = mockNotifications.filter((notification) => {
+ const matchesFilter =
+ activeFilter === "All" || activeFilter === "Sent by Me";
+ const matchesSearch =
+ notification.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ notification.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ notification.sender.toLowerCase().includes(searchQuery.toLowerCase());
+
+ return matchesFilter && matchesSearch;
+ });
+
+ const handleViewProfile = (notification: Notification) => {
+ setSelectedNotification(notification);
+ setIsDetailModalOpen(true);
+ };
+
+ const handleSendMessage = (title: string, body: string) => {
+ console.log("Sending message:", { title, body });
+ // Here you would typically send the message to your backend
+ };
+
+ const closeDetailModal = () => {
+ setIsDetailModalOpen(false);
+ setSelectedNotification(null);
+ };
+
+ const closeSendModal = () => {
+ setIsSendModalOpen(false);
+ };
+
+ return (
+
+ {/* Navigation and Search Bar */}
+
+ {/* Filter Tabs */}
+
+
setActiveFilter("All")}
+ className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
+ activeFilter === "All"
+ ? "bg-white text-gray-900 shadow-sm"
+ : "text-gray-600 hover:text-gray-900"
+ }`}
+ >
+ All
+
+
setActiveFilter("Sent by Me")}
+ className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
+ activeFilter === "Sent by Me"
+ ? "bg-white text-gray-900 shadow-sm"
+ : "text-gray-600 hover:text-gray-900"
+ }`}
+ >
+ Sent by Me
+
+
+
+ setSearchQuery(e.target.value)}
+ className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
+ />
+
+
+
+ {/* Search and Send Message */}
+
+ {/* Search Bar */}
+
+ {/* Send Message Button */}
+ setIsSendModalOpen(true)}
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
+ >
+ Send a Message
+
+
+
+
+ {/* Notifications List */}
+
+ {filteredNotifications.map((notification) => (
+
+ ))}
+
+
+ {/* Empty State */}
+ {filteredNotifications.length === 0 && (
+
+
+ No notifications found
+
+
+ {searchQuery
+ ? "Try adjusting your search terms"
+ : "No notifications to display"}
+
+
+ )}
+
+ {/* Modals */}
+
+
+
+
+ );
+};
+
+export default DirectNotificationContent;
diff --git a/src/components/dashboard/NotificationDetailModal.tsx b/src/components/dashboard/NotificationDetailModal.tsx
new file mode 100644
index 0000000..82eaf17
--- /dev/null
+++ b/src/components/dashboard/NotificationDetailModal.tsx
@@ -0,0 +1,124 @@
+"use client";
+
+import React from "react";
+import { ArrowLeft } from "lucide-react";
+
+interface NotificationDetailModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ notification: {
+ id: string;
+ title: string;
+ sender: string;
+ message: string;
+ date: string;
+ receiver?: string;
+ time?: string;
+ status?: string;
+ } | null;
+}
+
+const NotificationDetailModal: React.FC = ({
+ isOpen,
+ onClose,
+ notification,
+}) => {
+ if (!isOpen || !notification) return null;
+
+ return (
+
+
+ {/* Header with Back Button */}
+
+
+ {/* Content */}
+
+ {/* Sender Info */}
+
+
+
+ Sent by: {notification.sender}
+
+
+
+
+ {/* Title */}
+
+ {notification.title}
+
+
+ {/* Banner Image */}
+
+
+
+
+
+ COMING
+
+
+ SOON
+
+
+
+ {/* Subtle pattern overlay */}
+
+
+
+
+ {/* Message Content */}
+
+
+ We are excited to announce a series of improvements and new
+ features in our subscription plans. Starting this month,
+ subscribers will enjoy enhanced benefits tailored to provide
+ greater value and a more seamless experience.
+
+
+
+ {/* Details Table */}
+
+
+
+ Notification Details
+
+
+
+
+ Receiver:
+ Reader
+
+
+ Date:
+ 12 March, 2024
+
+
+ Time:
+ 12:14 PM
+
+
+ Status:
+
+ Sent
+
+
+
+
+
+
+
+ );
+};
+
+export default NotificationDetailModal;
diff --git a/src/components/dashboard/ReviewsAndFeedbackContent.tsx b/src/components/dashboard/ReviewsAndFeedbackContent.tsx
new file mode 100644
index 0000000..6d27123
--- /dev/null
+++ b/src/components/dashboard/ReviewsAndFeedbackContent.tsx
@@ -0,0 +1,183 @@
+"use client";
+
+import React, { useState } from "react";
+import { Star, Trash2 } from "lucide-react";
+import DeleteReviewModal from "./DeleteReviewModal";
+
+interface Review {
+ id: string;
+ bookTitle: string;
+ author: string;
+ reviewerName: string;
+ reviewerAvatar: string;
+ timestamp: string;
+ rating: number;
+ reviewText: string;
+}
+
+const mockReviews: Review[] = [
+ {
+ id: "1",
+ bookTitle: "Native Invisible",
+ author: "Darrin Collins",
+ reviewerName: "Adeja Samad",
+ reviewerAvatar: "/api/placeholder/40/40",
+ timestamp: "Yesterday",
+ rating: 4,
+ reviewText:
+ "This was a great read, and I was hooked. However, the death of my favorite character impacted my overall enjoyment, which is why I'm rating it 4 stars instead of 5.",
+ },
+ {
+ id: "2",
+ bookTitle: "Love at Night",
+ author: "Joe Graphix",
+ reviewerName: "Adeja Samad",
+ reviewerAvatar: "/api/placeholder/40/40",
+ timestamp: "Yesterday",
+ rating: 4,
+ reviewText:
+ "This was a great read, and I was hooked. However, the death of my favorite character impacted my overall enjoyment, which is why I'm rating it 4 stars instead of 5.",
+ },
+ {
+ id: "3",
+ bookTitle: "Designing For Humans",
+ author: "Joe Terkuma",
+ reviewerName: "Adeja Samad",
+ reviewerAvatar: "/api/placeholder/40/40",
+ timestamp: "Yesterday",
+ rating: 4,
+ reviewText:
+ "This was a great read, and I was hooked. However, the death of my favorite character impacted my overall enjoyment, which is why I'm rating it 4 stars instead of 5.",
+ },
+];
+
+const ReviewCard: React.FC<{
+ review: Review;
+ onDelete: (review: Review) => void;
+}> = ({ review, onDelete }) => {
+ const renderStars = (rating: number) => {
+ const stars = [];
+ for (let i = 1; i <= 5; i++) {
+ stars.push(
+
+ );
+ }
+ return stars;
+ };
+
+ return (
+
+ {/* Book Title Tag */}
+
+
+ {review.bookTitle} by {review.author}
+
+
+
+ {/* Review Content */}
+
+ {/* Reviewer Avatar */}
+
+
+ {review.reviewerName.charAt(0)}
+
+
+
+ {/* Review Details */}
+
+ {/* Reviewer Info and Rating */}
+
+
+
+ {review.reviewerName}
+
+
{review.timestamp}
+
+
+ {/* Delete Button and Rating */}
+
+
onDelete(review)}
+ className="text-gray-400 hover:text-red-500 text-xs bg-gray-100 hover:bg-red-50 px-3 py-1.5 rounded-2xl transition-colors"
+ >
+ Delete
+
+
+ {renderStars(review.rating)}
+
+
+
+
+ {/* Review Text */}
+
+ {review.reviewText}
+
+
+
+
+ );
+};
+
+const ReviewsAndFeedbackContent: React.FC = () => {
+ const [reviews, setReviews] = useState(mockReviews);
+ const [reviewToDelete, setReviewToDelete] = useState(null);
+ const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
+
+ const handleDeleteReview = (review: Review) => {
+ setReviewToDelete(review);
+ setIsDeleteModalOpen(true);
+ };
+
+ const confirmDelete = () => {
+ if (reviewToDelete) {
+ setReviews(reviews.filter((review) => review.id !== reviewToDelete.id));
+ setReviewToDelete(null);
+ setIsDeleteModalOpen(false);
+ }
+ };
+
+ const cancelDelete = () => {
+ setReviewToDelete(null);
+ setIsDeleteModalOpen(false);
+ };
+
+ return (
+
+ {/* Reviews List */}
+
+ {reviews.map((review) => (
+
+ ))}
+
+
+ {/* Empty State */}
+ {reviews.length === 0 && (
+
+
No reviews found
+
+ This user hasn't received any reviews yet
+
+
+ )}
+
+ {/* Delete Confirmation Modal */}
+
+
+ );
+};
+
+export default ReviewsAndFeedbackContent;
diff --git a/src/components/dashboard/SendMessageModal.tsx b/src/components/dashboard/SendMessageModal.tsx
new file mode 100644
index 0000000..ea88f5c
--- /dev/null
+++ b/src/components/dashboard/SendMessageModal.tsx
@@ -0,0 +1,134 @@
+"use client";
+
+import React, { useState } from "react";
+import { ArrowLeft, Bold, Italic, Underline, Image, Link } from "lucide-react";
+
+interface SendMessageModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onSend: (title: string, body: string) => void;
+}
+
+const SendMessageModal: React.FC = ({
+ isOpen,
+ onClose,
+ onSend,
+}) => {
+ const [title, setTitle] = useState("");
+ const [body, setBody] = useState("");
+
+ const handleSend = () => {
+ if (title.trim() && body.trim()) {
+ onSend(title, body);
+ setTitle("");
+ setBody("");
+ onClose();
+ }
+ };
+
+ const handleCancel = () => {
+ setTitle("");
+ setBody("");
+ onClose();
+ };
+
+ if (!isOpen) return null;
+
+ return (
+
+
+ {/* Header with Back Button */}
+
+
+ {/* Content */}
+
+ {/* Title Input */}
+
+
setTitle(e.target.value)}
+ className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-lg"
+ />
+
+ {/* Title Formatting Options */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Body Input */}
+
+
+
+ {/* Action Buttons */}
+
+
+ Cancel
+
+
+ Send
+
+
+
+
+ );
+};
+
+export default SendMessageModal;
diff --git a/src/components/dashboard/TransactionContent.tsx b/src/components/dashboard/TransactionContent.tsx
new file mode 100644
index 0000000..f7cd3e9
--- /dev/null
+++ b/src/components/dashboard/TransactionContent.tsx
@@ -0,0 +1,329 @@
+"use client";
+
+import React, { useState } from "react";
+import { ChevronRight, Calendar } from "lucide-react";
+import Image from "next/image";
+import strk from "../../../public/strk.png.svg";
+interface Transaction {
+ id: string;
+ type: string;
+ amount: number;
+ status: "Successful" | "Failed";
+ date: string;
+}
+
+const mockTransactions: Transaction[] = [
+ {
+ id: "Tran-124B",
+ type: "Monthly Subscription",
+ amount: 100.0,
+ status: "Successful",
+ date: "26 April",
+ },
+ {
+ id: "Tran-124B",
+ type: "Book Purchase",
+ amount: 800.45,
+ status: "Successful",
+ date: "25 April",
+ },
+ {
+ id: "Tran-124B",
+ type: "Book Purchase",
+ amount: 56.78,
+ status: "Failed",
+ date: "20 April",
+ },
+ {
+ id: "Tran-124B",
+ type: "NFT Purchase",
+ amount: 200.5,
+ status: "Successful",
+ date: "18 April",
+ },
+ {
+ id: "Tran-124B",
+ type: "NFT Purchase",
+ amount: 540.569,
+ status: "Successful",
+ date: "18 April",
+ },
+];
+
+const summaryData = {
+ totalAmount: 837.06,
+ breakdown: {
+ bookPurchase: 372.93,
+ nftPurchase: 403.93,
+ monthlySubscription: 201.93,
+ },
+};
+
+const TransactionContent: React.FC = () => {
+ const [activeTimeFilter, setActiveTimeFilter] = useState<
+ "This Week" | "This Month" | "This Year"
+ >("This Week");
+ const [startDate, setStartDate] = useState("");
+ const [endDate, setEndDate] = useState("");
+ const [currentPage, setCurrentPage] = useState(1);
+ const [pageSize] = useState(5);
+ const [totalTransactions] = useState(12);
+
+ const getStatusBadge = (status: string) => {
+ const baseClasses = "px-3 py-1 rounded-full text-xs font-medium";
+ if (status === "Successful") {
+ return `${baseClasses} bg-green-100 text-green-800`;
+ } else {
+ return `${baseClasses} bg-red-100 text-red-800`;
+ }
+ };
+
+ const startIndex = (currentPage - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+ const displayedTransactions = mockTransactions.slice(startIndex, endIndex);
+
+ return (
+
+ {/* Summary Section */}
+
+ {/* Total Amount Card - Left Side */}
+
+
+
+ Total Amount Transacted
+
+
+
+
+ {summaryData.totalAmount}
+
+
+
+
+
+ {/* Breakdown Cards - Right Side */}
+
+ {/* Book Purchase */}
+
+
+
Book Purchase
+
+
+
+ {summaryData.breakdown.bookPurchase} STR
+
+
+
+
+
+ {/* NFT Purchase */}
+
+
+
NFT Purchase
+
+
+
+ {summaryData.breakdown.nftPurchase} STRK
+
+
+
+
+
+ {/* Monthly Subscription */}
+
+
+
Monthly Subscription
+
+
+
+ {summaryData.breakdown.monthlySubscription} STRK
+
+
+
+
+
+
+
+ {/* Transaction History Section */}
+
+ {/* Filters */}
+
+
+ {/* Time Period Filters */}
+
+ setActiveTimeFilter("This Week")}
+ className={`px-4 py-2 text-sm font-medium rounded-lg transition-colors ${
+ activeTimeFilter === "This Week"
+ ? "bg-blue-600 text-white"
+ : "bg-gray-100 text-gray-600 hover:bg-gray-200"
+ }`}
+ >
+ This Week
+
+ setActiveTimeFilter("This Month")}
+ className={`px-4 py-2 text-sm font-medium rounded-lg transition-colors ${
+ activeTimeFilter === "This Month"
+ ? "bg-blue-600 text-white"
+ : "bg-gray-100 text-gray-600 hover:bg-gray-200"
+ }`}
+ >
+ This Month
+
+ setActiveTimeFilter("This Year")}
+ className={`px-4 py-2 text-sm font-medium rounded-lg transition-colors ${
+ activeTimeFilter === "This Year"
+ ? "bg-blue-600 text-white"
+ : "bg-gray-100 text-gray-600 hover:bg-gray-200"
+ }`}
+ >
+ This Year
+
+
+
+ {/* Date Range and Filter */}
+
+
+
+
+ {/* Transaction Table */}
+
+
+
+
+
+ Transaction ID
+
+
+ Transaction Type
+
+
+ Amount (STRK)
+
+
+ Status
+
+
+ Date
+
+
+ View Details
+
+
+
+
+ {displayedTransactions.map((transaction, index) => (
+
+
+ {transaction.id}
+
+
+ {transaction.type}
+
+
+
+
+
+
+ {transaction.amount.toFixed(
+ transaction.amount % 1 === 0 ? 0 : 3
+ )}{" "}
+ STRK
+
+
+
+
+ {transaction.status}
+
+
+
+ {transaction.date}
+
+
+
+ View Details
+
+
+
+ ))}
+
+
+
+
+ {/* Pagination */}
+
+
+ Showing {startIndex + 1} to {Math.min(endIndex, totalTransactions)}{" "}
+ of {totalTransactions}
+
+
+ View All
+
+
+
+
+
+ );
+};
+
+export default TransactionContent;