diff --git a/package.json b/package.json index c2b91ef..22a1b65 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.2.3", "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", diff --git a/public/Chat.svg b/public/Chat.svg new file mode 100644 index 0000000..9fa1d2b --- /dev/null +++ b/public/Chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/dashboard/admin/content-management/components/BookDetails.tsx b/src/app/dashboard/admin/content-management/components/BookDetails.tsx new file mode 100644 index 0000000..1ebe3a5 --- /dev/null +++ b/src/app/dashboard/admin/content-management/components/BookDetails.tsx @@ -0,0 +1,476 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Star, CheckCircle, Eye, Download } from "lucide-react"; +import Image from "next/image"; +import check from "../../../../../../public/Cover.png"; +import message from "../../../../../../public/Chat.svg"; +import starknet from "../../../../../../public/starknet.png"; +import user1 from "../../../../../../public/user1.svg"; + +interface Book { + id: string; + title: string; + author: string; + cover: string; + type: "Regular" | "NFT Edition"; + status: + | "Approved" + | "Pending" + | "Rejected" + | "Unresolved" + | "Resolved" + | "Valid" + | "Reported"; + dateSubmitted: string; + datePublished?: string; + interactions?: string; + tokenId?: string; + walletAddress?: string; + mintingDate?: string; + reportType?: string; + reportedBy?: string; + rating?: number; + price?: string; + sold?: number; + isbn?: string; + language?: string; + pageCount?: string; + genre?: string[]; +} + +interface BookDetailsProps { + book: Book; + currentView: string; + onReject?: () => void; + onApprove?: () => void; +} + +export function BookDetails({ + book, + currentView, + onReject, + onApprove, +}: BookDetailsProps) { + return ( +
+ {/* Book Info */} +
+
+
+
+ {book.title} +
+
+
+

+ Native Invisibility +

+
+

+ By Darrin Collins +

+ + + +
+ +
+
+ + + {book.rating || 4.5} + +
+
+
+
+
+ +
+ + {currentView === "book-approval" && ( +
+ + +
+ )} + {currentView === "reported-content" && ( +
+ + +
+ )} + {currentView === "book-details" && ( + + )} +
+
+ + {currentView === "book-approval" && ( + + )} + + {currentView === "reported-content" && ( + + )} + + {/* Book Details Grid */} +
+
+
Payment Type
+
One-Time Payment
+
+
+
Price
+
+ dollar + {book.price || "N/A"} +
+
+
+
Published Date
+
{book.dateSubmitted}
+
+
+
Sold
+
47
+
+
+
Transaction
+
+ +
All Transactions
+
+
+
+
+ + {/* Description */} +
+
+

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 Info */} +
+
+
Genre(s)
+
+
+ Fiction +
+
+ Comic +
+
+
+
+
Page count
+
+ {book.pageCount || "21 Pages"} +
+
+
+
Language
+
+ {book.language || "English"} +
+
+
+
Published Date
+
+ {book.dateSubmitted || "21 April 2025"} +
+
+
+
ISBN
+
+ {book.isbn || "ISBN: 978-3-16-148410-0"} +
+
+
+
+ + {/* Performance Stats */} + {currentView === "book-details" && ( +
+
+

+ Book Performance Stats +

+
+ + + + +
+ +
+ + +
Read
+
+
192
+
+ -10% +
+
+
+
+ + + +
+ Read Completion Rate +
+
+
75%
+
+ +5% +
+
+
+
+ + + +
Purchase
+
+
62
+
+ +8% +
+
+
+
+ + +
+ Total Complete Read +
+
70%
+
+
+ + +
+ Total Earning +
+
+ dollar + 370.00 +
+
+
+ + +
+ Average Rating +
+
+ + 3.5 +
+
+
+
+
+
+ )} + + {currentView == "book-details" && ( +
+

+ Reviews and Rating +

+ +
+
+
+ 3.5 +
+
+ {[1, 2, 3, 4, 5].map((star) => ( + + ))} +
+
+ 1202 Reviews +
+
+ +
+ {[5, 4, 3, 2, 1].map((rating) => ( +
+ {rating} + +
+ ))} +
+
+ + {/* Individual Reviews */} +
+ {[1, 2, 3, 4, 5, 6].map((review) => ( + + +
+
+ + + AS + + Adeja Samad +
+ +
+
+
+ {[1, 2, 3, 4].map((star) => ( + + ))} + +
+ Yesterday +
+ +

+ 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. +

+ +
+
+ + {/* Footer aligned at the bottom */} +
+ delete + Author Replied + • 1 Reply +
+
+
+ ))} +
+
+ )} +
+ ); +} diff --git a/src/app/dashboard/admin/content-management/components/BookTable.tsx b/src/app/dashboard/admin/content-management/components/BookTable.tsx new file mode 100644 index 0000000..168e2fa --- /dev/null +++ b/src/app/dashboard/admin/content-management/components/BookTable.tsx @@ -0,0 +1,391 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Search, Filter, Eye } from "lucide-react"; +import Image from "next/image"; + +interface Book { + id: string; + title: string; + author: string; + cover: string; + type: "Regular" | "NFT Edition"; + status: + | "Approved" + | "Pending" + | "Rejected" + | "Unresolved" + | "Resolved" + | "Valid" + | "Reported"; + dateSubmitted: string; + datePublished?: string; + interactions?: string; + tokenId?: string; + walletAddress?: string; + mintingDate?: string; + reportType?: string; + reportedBy?: string; + rating?: number; + price?: string; + sold?: number; + isbn?: string; + language?: string; + pageCount?: string; + genre?: string[]; +} + +interface BookTableProps { + books: Book[]; + activeTab: string; + setActiveTab: (tab: string) => void; + onBookClick: (book: Book) => void; +} + +export function BookTable({ + books, + activeTab, + setActiveTab, + onBookClick, +}: BookTableProps) { + const getTableHeaders = (tabType: string) => { + switch (tabType) { + case "all-books": + return [ + "Cover", + "Title and Author", + "Status", + "Type", + "Date Published", + "Interactions", + "", + ]; + case "pending-approval": + return [ + "Cover", + "Title and Author", + "Type", + "Date Submitted", + "Review Status", + "", + ]; + case "reported-content": + return [ + "Cover", + "Title and Author", + "Report Type", + "Reported By", + "Date", + "Status", + "", + ]; + default: + return []; + } + }; + + const renderBookRow = (book: Book, tabType: string) => { + return ( + + + {"Book + + +
+ {book.title} +
+
+ by {book.author} +
+ + {tabType === "all-books" && ( + <> + + + {book.status} + + + + {book.type} + + + {book.datePublished} + + +
+ + {book.interactions} +
+ + + )} + {tabType === "pending-approval" && ( + <> + + {book.type} + + + {book.dateSubmitted} + + + + {book.status} + + + + )} + {tabType === "reported-content" && ( + <> + + {book.reportType} + + + {book.reportedBy} + + + {book.dateSubmitted} + + + + {book.status} + + + + )} + + + + + ); + }; + + return ( + setActiveTab(value)} + className="bg-white rounded-lg border border-[#E7E7E7] p-4" + > +
+ + + All Books + + + Pending Approval + + 27 + + + + Reported Content + + + +
+
+ + +
+
+ + + + + + e.preventDefault()}> + Date Submitted + + e.preventDefault()}> + Title + + e.preventDefault()}> + Author + + + + + + + + + e.preventDefault()} + > + Regular + + e.preventDefault()} + > + NFT Edition + + + +
+
+
+ + +
+
+ + + + {getTableHeaders("all-books").map((header, index) => ( + + ))} + + + + {books + .filter((book) => book.status === "Approved") + .map((book) => renderBookRow(book, "all-books"))} + +
+ {header} +
+
+
+
+ + +
+
+ + + + {getTableHeaders("pending-approval").map((header, index) => ( + + ))} + + + + {books + .filter((book) => book.status === "Pending") + .map((book) => renderBookRow(book, "pending-approval"))} + +
+ {header} +
+
+
+
+ + +
+
+ + + + {getTableHeaders("reported-content").map((header, index) => ( + + ))} + + + + {books + .filter( + (book) => + book.status === "Unresolved" || book.status === "Resolved" + ) + .map((book) => renderBookRow(book, "reported-content"))} + +
+ {header} +
+
+
+
+ +
+ Showing 1 to 10 of 107 + +
+
+ ); +} diff --git a/src/app/dashboard/admin/content-management/components/RejectModal.tsx b/src/app/dashboard/admin/content-management/components/RejectModal.tsx new file mode 100644 index 0000000..d744f8b --- /dev/null +++ b/src/app/dashboard/admin/content-management/components/RejectModal.tsx @@ -0,0 +1,81 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { X } from "lucide-react"; + +interface RejectModalProps { + isOpen: boolean; + rejectReason: string; + setRejectReason: (reason: string) => void; + onCancel: () => void; + onSubmit: () => void; +} + +export function RejectModal({ + isOpen, + rejectReason, + setRejectReason, + onCancel, + onSubmit, +}: RejectModalProps) { + if (!isOpen) return null; + + return ( +
+
+
+

+ Reject Publication +

+ +
+ +

+ Enter the reason you are rejecting this book for publication +

+ +
+

+ Rejected by: Ola**p@gmail.com +

+
+ +
+ +