From f02dc1b86ce5b9b04b441fa72b13456409a2d8b7 Mon Sep 17 00:00:00 2001 From: Oshioke-Salaki Date: Sun, 10 Aug 2025 18:38:53 +0100 Subject: [PATCH 1/7] feat: admin community and notifications pages --- .../components/CommunityAndEventsFilter.tsx | 86 ++++ .../components/CommunityStats.tsx | 26 ++ .../components/CommunityStrip.tsx | 45 ++ .../admin/community-and-events/page.tsx | 46 +- .../admin/components/admin-sidenavbar.tsx | 16 +- src/app/dashboard/admin/layout.tsx | 16 +- .../components/NotificationFilter.tsx | 91 ++++ .../components/NotificationTable.tsx | 138 ++++++ .../components/NotificationTableHeader.tsx | 16 + .../NotificationTablePagination.tsx | 58 +++ .../components/NotificationTableRow.tsx | 53 +++ .../dashboard/admin/notifications/page.tsx | 28 +- src/app/dashboard/admin/page.tsx | 402 +++++++++--------- src/app/globals.css | 17 - src/app/layout.tsx | 5 +- 15 files changed, 783 insertions(+), 260 deletions(-) create mode 100644 src/app/dashboard/admin/community-and-events/components/CommunityAndEventsFilter.tsx create mode 100644 src/app/dashboard/admin/community-and-events/components/CommunityStats.tsx create mode 100644 src/app/dashboard/admin/community-and-events/components/CommunityStrip.tsx create mode 100644 src/app/dashboard/admin/notifications/components/NotificationFilter.tsx create mode 100644 src/app/dashboard/admin/notifications/components/NotificationTable.tsx create mode 100644 src/app/dashboard/admin/notifications/components/NotificationTableHeader.tsx create mode 100644 src/app/dashboard/admin/notifications/components/NotificationTablePagination.tsx create mode 100644 src/app/dashboard/admin/notifications/components/NotificationTableRow.tsx diff --git a/src/app/dashboard/admin/community-and-events/components/CommunityAndEventsFilter.tsx b/src/app/dashboard/admin/community-and-events/components/CommunityAndEventsFilter.tsx new file mode 100644 index 0000000..50d48c4 --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/components/CommunityAndEventsFilter.tsx @@ -0,0 +1,86 @@ +"use client"; +import { ListFilter } from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; + +const FILTER_OPTIONS = [ + { key: "week", label: "This Week" }, + { key: "month", label: "This Month" }, + { key: "year", label: "This Year" }, + { key: "all", label: "All Time" }, +]; + +function CommunityAndEventsFilter() { + const router = useRouter(); + const searchParams = useSearchParams(); + + const activeFilter = searchParams.get("filter") || "week"; + const start = searchParams.get("start") || ""; + const end = searchParams.get("end") || ""; + + const pushWith = (next: Record) => { + const params = new URLSearchParams(searchParams.toString()); + Object.entries(next).forEach(([k, v]) => { + if (v === undefined || v === null || v === "") params.delete(k); + else params.set(k, String(v)); + }); + // reset pagination when filter changes + params.delete("page"); + router.push(`?${params.toString()}`); + }; + + const onFilterClick = (key: string) => { + pushWith({ filter: key, start: undefined, end: undefined }); + }; + + const onApplyDates = () => { + pushWith({ start, end, filter: undefined }); + }; + + return ( +
+
+ {FILTER_OPTIONS.map((opt) => ( + + ))} +
+ +
+
+ pushWith({ start: e.target.value })} + className="w-[106px] py-[6px] px-3 border border-[#E7E7E7] rounded-[8px] outline-none" + /> + to + pushWith({ end: e.target.value })} + className="w-[106px] py-[6px] px-3 border border-[#E7E7E7] rounded-[8px] outline-none" + /> +
+ +
+
+ ); +} + +export default CommunityAndEventsFilter; diff --git a/src/app/dashboard/admin/community-and-events/components/CommunityStats.tsx b/src/app/dashboard/admin/community-and-events/components/CommunityStats.tsx new file mode 100644 index 0000000..9a4646e --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/components/CommunityStats.tsx @@ -0,0 +1,26 @@ +import React from "react"; + +function CommunityStats() { + return ( +
+
+

Total Communities

+

49

+
+
+

Live Events

+

4

+
+
+

Public Communities

+

32

+
+
+

Private Communities

+

18

+
+
+ ); +} + +export default CommunityStats; diff --git a/src/app/dashboard/admin/community-and-events/components/CommunityStrip.tsx b/src/app/dashboard/admin/community-and-events/components/CommunityStrip.tsx new file mode 100644 index 0000000..1cf3c86 --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/components/CommunityStrip.tsx @@ -0,0 +1,45 @@ +import { ClipboardList, Earth, Users } from "lucide-react"; +import React from "react"; + +function CommunityStrip() { + return ( +
+
+
+
+
+

+ Fantasy Enthusiasts +

+
+ Ongoing Live Event +
+
+
+
+ Public +
+
+ 1.5k+ +
+
+ 2+ Sessions a month +
+
+
+
+ + +
+ ); +} + +export default CommunityStrip; diff --git a/src/app/dashboard/admin/community-and-events/page.tsx b/src/app/dashboard/admin/community-and-events/page.tsx index 3f4ef37..880ef62 100644 --- a/src/app/dashboard/admin/community-and-events/page.tsx +++ b/src/app/dashboard/admin/community-and-events/page.tsx @@ -1,19 +1,45 @@ "use-client"; import { Header } from "@/components/dashboard/header"; - +import CommunityAndEventsFilter from "./components/CommunityAndEventsFilter"; +import CommunityStats from "./components/CommunityStats"; +import { ListFilter, Search } from "lucide-react"; +import CommunityStrip from "./components/CommunityStrip"; export default function CommunityAndEvents() { - - return ( <> -
-
-
-

Community and events

-

- Welcome to Community and Events section of the admin dashboard! Here, you can manage community interactions, events, and engagement activities. This space is designed to help you foster a vibrant community around your platform, ensuring that users have a place to connect, share, and participate in events. -

+
+
+
+ + + {/* */} +
+
+
+
+
+ +
+ +
+ +
+ Filter + +
+
+ +
+ + + + +
diff --git a/src/app/dashboard/admin/components/admin-sidenavbar.tsx b/src/app/dashboard/admin/components/admin-sidenavbar.tsx index cb180bf..22928a5 100644 --- a/src/app/dashboard/admin/components/admin-sidenavbar.tsx +++ b/src/app/dashboard/admin/components/admin-sidenavbar.tsx @@ -1,6 +1,14 @@ "use client"; -import { LayoutDashboard, Users2, FileText, BarChart3, CalendarDays, Bell, MessagesSquare } from 'lucide-react'; +import { + LayoutDashboard, + Users2, + FileText, + BarChart3, + CalendarDays, + Bell, + MessagesSquare, +} from "lucide-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; @@ -17,7 +25,7 @@ export function Sidebar() { { icon: Users2, label: "User Management", - href: "/dashboard/admin/user-management", + href: "/dashboard/admin/user-management", }, { icon: FileText, @@ -45,11 +53,10 @@ export function Sidebar() { label: "Community and Events", href: "/dashboard/admin/community-and-events", }, - ]; return ( -
+
C @@ -76,7 +83,6 @@ export function Sidebar() { ))} -
diff --git a/src/app/dashboard/admin/layout.tsx b/src/app/dashboard/admin/layout.tsx index bf55f54..6f3fca0 100644 --- a/src/app/dashboard/admin/layout.tsx +++ b/src/app/dashboard/admin/layout.tsx @@ -1,11 +1,7 @@ - - - import type React from "react"; import { Sidebar } from "./components/admin-sidenavbar"; import "@/app/globals.css"; - export default function RootLayout({ children, }: { @@ -13,14 +9,10 @@ export default function RootLayout({ }) { return ( <> - -
- -
- {children} +
+ +
{children}
-
- ); -} \ No newline at end of file +} diff --git a/src/app/dashboard/admin/notifications/components/NotificationFilter.tsx b/src/app/dashboard/admin/notifications/components/NotificationFilter.tsx new file mode 100644 index 0000000..2298b4d --- /dev/null +++ b/src/app/dashboard/admin/notifications/components/NotificationFilter.tsx @@ -0,0 +1,91 @@ +"use client"; +import { ListFilter } from "lucide-react"; +import React from "react"; +import { useRouter, useSearchParams } from "next/navigation"; + +const FILTER_OPTIONS = [ + { key: "week", label: "This Week" }, + { key: "month", label: "This Month" }, + { key: "year", label: "This Year" }, + { key: "all", label: "All Time" }, +]; + +export default function NotificationFilter() { + const router = useRouter(); + const searchParams = useSearchParams(); + + const activeFilter = searchParams.get("filter") || "week"; + const start = searchParams.get("start") || ""; + const end = searchParams.get("end") || ""; + + const pushWith = (next: Record) => { + const params = new URLSearchParams(searchParams.toString()); + Object.entries(next).forEach(([k, v]) => { + if (v === undefined || v === null || v === "") params.delete(k); + else params.set(k, String(v)); + }); + // reset pagination when filter changes + params.delete("page"); + router.push(`?${params.toString()}`); + }; + + const onFilterClick = (key: string) => { + pushWith({ filter: key, start: undefined, end: undefined }); + }; + + const onApplyDates = () => { + pushWith({ start, end, filter: undefined }); + }; + + return ( +
+
+
+ {FILTER_OPTIONS.map((opt) => ( + + ))} +
+ +
+
+ pushWith({ start: e.target.value })} + className="w-[106px] py-[6px] px-3 border border-[#E7E7E7] rounded-[8px] outline-none" + /> + to + pushWith({ end: e.target.value })} + className="w-[106px] py-[6px] px-3 border border-[#E7E7E7] rounded-[8px] outline-none" + /> +
+ +
+
+ +
+ Filter +
+
+ ); +} diff --git a/src/app/dashboard/admin/notifications/components/NotificationTable.tsx b/src/app/dashboard/admin/notifications/components/NotificationTable.tsx new file mode 100644 index 0000000..4bab1e9 --- /dev/null +++ b/src/app/dashboard/admin/notifications/components/NotificationTable.tsx @@ -0,0 +1,138 @@ +"use client"; +import React, { useMemo } from "react"; +import { useSearchParams } from "next/navigation"; +import NotificationTableHeader from "./NotificationTableHeader"; +import NotificationTableRow, { NotificationItem } from "./NotificationTableRow"; +import NotificationTablePagination from "./NotificationTablePagination"; + +// ---------- Dummy data ---------- +const DUMMY: NotificationItem[] = Array.from({ length: 28 }).map((_, i) => { + const d = new Date(); + d.setDate(d.getDate() - i); // spread over last 28 days + const statuses: NotificationItem["status"][] = ["Sent", "Pending", "Failed"]; + const recipients: NotificationItem["receiver"][] = [ + "Private", + "Write", + "General", + "Writer", + "Reader", + ]; + return { + id: String(i + 1), + title: + i % 3 === 0 + ? "New File Format Supported" + : i % 3 === 1 + ? "Announcement Sent" + : "Access Policy Updated", + email: `user${i + 1}@example.com`, + receiver: recipients[i % recipients.length], + status: statuses[i % statuses.length], + date: d.toISOString(), + }; +}); + +// ---------- Helpers ---------- +const parseDDMMYYYY = (val?: string | null) => { + if (!val) return undefined; + const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(val); + if (!m) return undefined; + const [_, dd, mm, yyyy] = m; + const d = new Date(Number(yyyy), Number(mm) - 1, Number(dd)); + return isNaN(d.getTime()) ? undefined : d; +}; + +const startOfToday = () => { + const d = new Date(); + d.setHours(0, 0, 0, 0); + return d; +}; + +const applyQuickFilter = ( + items: NotificationItem[], + filter?: string | null +) => { + if (!filter || filter === "all") return items; + const today = startOfToday(); + let from = new Date(today); + if (filter === "week") from.setDate(today.getDate() - 7); + else if (filter === "month") from.setMonth(today.getMonth() - 1); + else if (filter === "year") from.setFullYear(today.getFullYear() - 1); + return items.filter((it) => new Date(it.date) >= from); +}; + +// ---------- Component ---------- +export default function NotificationTable() { + const searchParams = useSearchParams(); + + const filter = searchParams.get("filter"); // week|month|year|all + const start = parseDDMMYYYY(searchParams.get("start")); + const end = parseDDMMYYYY(searchParams.get("end")); + const page = Number(searchParams.get("page") || 1); + const pageSize = Number(searchParams.get("pageSize") || 5); + + // 1) Start with dummy data, newest first + const sorted = useMemo( + () => [...DUMMY].sort((a, b) => +new Date(b.date) - +new Date(a.date)), + [] + ); + + // 2) Apply date range OR quick filter + const filtered = useMemo(() => { + if (start || end) { + const s = start ?? new Date(0); + const e = end + ? new Date( + end.getFullYear(), + end.getMonth(), + end.getDate(), + 23, + 59, + 59, + 999 + ) + : new Date(); + return sorted.filter((it) => { + const d = new Date(it.date); + return d >= s && d <= e; + }); + } + return applyQuickFilter(sorted, filter); + }, [sorted, filter, start, end]); + + // 3) Pagination slice + const total = filtered.length; + const totalPages = Math.max(1, Math.ceil(total / pageSize)); + const safePage = Math.min(Math.max(1, page), totalPages); + const startIdx = (safePage - 1) * pageSize; + const pageItems = filtered.slice(startIdx, startIdx + pageSize); + + return ( +
+
+
+ All Notifications +
+
+
+ +
+ + + {pageItems.length === 0 ? ( +
+ No notifications match your filter. +
+ ) : ( + pageItems.map((it) => ) + )} + + +
+
+ ); +} diff --git a/src/app/dashboard/admin/notifications/components/NotificationTableHeader.tsx b/src/app/dashboard/admin/notifications/components/NotificationTableHeader.tsx new file mode 100644 index 0000000..cd03d02 --- /dev/null +++ b/src/app/dashboard/admin/notifications/components/NotificationTableHeader.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +function NotificationTableHeader() { + return ( +
+
Title
+
Sender
+
Receiver
+
Status
+
Date
+
+
+ ); +} + +export default NotificationTableHeader; diff --git a/src/app/dashboard/admin/notifications/components/NotificationTablePagination.tsx b/src/app/dashboard/admin/notifications/components/NotificationTablePagination.tsx new file mode 100644 index 0000000..e8de55b --- /dev/null +++ b/src/app/dashboard/admin/notifications/components/NotificationTablePagination.tsx @@ -0,0 +1,58 @@ +"use client"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import { useRouter, useSearchParams } from "next/navigation"; +import React from "react"; + +type Props = { + total: number; + page: number; + pageSize: number; +}; + +export default function NotificationTablePagination({ + total, + page, + pageSize, +}: Props) { + const router = useRouter(); + const searchParams = useSearchParams(); + + const totalPages = Math.max(1, Math.ceil(total / pageSize)); + const clampedPage = Math.min(Math.max(1, page), totalPages); + const from = total === 0 ? 0 : (clampedPage - 1) * pageSize + 1; + const to = Math.min(clampedPage * pageSize, total); + + const pushWith = (next: Record) => { + const params = new URLSearchParams(searchParams.toString()); + Object.entries(next).forEach(([k, v]) => { + if (v === undefined || v === null || v === "") params.delete(k); + else params.set(k, String(v)); + }); + router.push(`?${params.toString()}`); + }; + + return ( +
+
+ {total === 0 ? "No results" : `Showing ${from} to ${to} of ${total}`} +
+ +
+ + +
+
+ ); +} diff --git a/src/app/dashboard/admin/notifications/components/NotificationTableRow.tsx b/src/app/dashboard/admin/notifications/components/NotificationTableRow.tsx new file mode 100644 index 0000000..2deab8b --- /dev/null +++ b/src/app/dashboard/admin/notifications/components/NotificationTableRow.tsx @@ -0,0 +1,53 @@ +import React from "react"; + +export type NotificationItem = { + id: string; + title: string; + email: string; + receiver: "Private" | "Write" | "General" | "Writer" | "Reader"; + status: "Sent" | "Pending" | "Failed"; + date: string; // ISO string +}; + +export default function NotificationTableRow({ + item, +}: { + item: NotificationItem; +}) { + const date = new Date(item.date); + const formatted = date.toLocaleDateString("en-GB", { + day: "2-digit", + month: "long", + year: "numeric", + }); + + const statusStyles = + item.status === "Sent" + ? "!border-[#34A853] bg-[#34A8531A] text-[#34A853]" + : item.status === "Pending" + ? "!border-[#F9A825] bg-[#F9A8251A] text-[#F9A825]" + : "!border-[#EB5757] bg-[#EB57571A] text-[#EB5757]"; + + return ( +
+
{item.title}
+
+
+ {item.email} +
+
+
{item.receiver}
+
+
+ {item.status} +
+
+
{formatted}
+
+ +
+
+ ); +} diff --git a/src/app/dashboard/admin/notifications/page.tsx b/src/app/dashboard/admin/notifications/page.tsx index 39bd566..39f9474 100644 --- a/src/app/dashboard/admin/notifications/page.tsx +++ b/src/app/dashboard/admin/notifications/page.tsx @@ -1,18 +1,30 @@ "use-client"; import { Header } from "@/components/dashboard/header"; - +import { ListFilter } from "lucide-react"; +import NotificationFilter from "./components/NotificationFilter"; +import NotificationTable from "./components/NotificationTable"; export default function Notifications() { - return ( <>
-
-
-

Notification

-

- Welcome to the Notification section of the admin dashboard! Here, you can manage all aspects of notifications on your platform, including user alerts, system messages, and other important communications. This space is designed to help you keep users informed and engaged with timely updates and announcements. -

+
+
+ + +
+ + + +
diff --git a/src/app/dashboard/admin/page.tsx b/src/app/dashboard/admin/page.tsx index 36f9382..c52636a 100644 --- a/src/app/dashboard/admin/page.tsx +++ b/src/app/dashboard/admin/page.tsx @@ -41,229 +41,223 @@ const statCard = (label: string, value: string) => ( export default function DashboardHome() { return ( <> - - -
-
-
- {[ - { - label: "Number of Regular", - value: "257", - color: "text-blue-600", - icon: , - }, - { - label: "NFT Books", - value: "83", - color: "text-purple-600", - icon: , - }, - { - label: "Readers", - value: "1093", - color: "text-green-600", - icon: , - }, - { - label: "Writers", - value: "204", - color: "text-orange-500", - icon: , - }, - ].map((card, idx) => ( -
-
-
-

{card.label}

- {card.icon} -
-

- {card.value} -

-
+
+
+
+ {[ + { + label: "Number of Regular", + value: "257", + color: "text-blue-600", + icon: , + }, + { + label: "NFT Books", + value: "83", + color: "text-purple-600", + icon: , + }, + { + label: "Readers", + value: "1093", + color: "text-green-600", + icon: , + }, + { + label: "Writers", + value: "204", + color: "text-orange-500", + icon: , + }, + ].map((card, idx) => ( +
+
+
+

{card.label}

+ {card.icon}
- ))} -
- -
-
-

- Transactions -

+

+ {card.value} +

+
+ ))} +
-
-
-

Total Transactions

-
- -

- 109,837.06 -

-
- - 20% ▲ - -
+
+
+

+ Transactions +

+
-
- {statCard("Commission Eared", "21,070.93")} - {statCard("Payout Sent", "51,070.93")} - {statCard("Pending Payout", "12,070.93")} - {statCard("Payout Sent", "21,070.93")} -
+
+
+

Total Transactions

+
+ +

109,837.06

+ + 20% ▲ +
-
-
-

New Payout Requests

- -
-
- - - - - - - - - - - - - {Array(5) - .fill(0) - .map((_, i) => ( - - - - - - - - - ))} - -
AuthorAmountWallet AddressRequest DateStatusActions
-
-

Olu Ademola

-

- olusx_dgmail.com -

-
-
-
- - 500.67 STR -
-
0xABC...78927 May,2025 - - Pending - - - - -
-
- Showing 1 to 5 of 12 -
-
+
+ {statCard("Commission Eared", "21,070.93")} + {statCard("Payout Sent", "51,070.93")} + {statCard("Pending Payout", "12,070.93")} + {statCard("Payout Sent", "21,070.93")}
+
+
-
-

Trending Books

-
- {books.map((book, idx) => ( -
- {book.imgSrc} -
-

{book.title}

-

- By {book.author}{" "} -

-
+
+
+

New Payout Requests

+ +
+
+ + + + + + + + + + + + + {Array(5) + .fill(0) + .map((_, i) => ( + + + + + + + + + ))} + +
AuthorAmountWallet AddressRequest DateStatusActions
+
+

Olu Ademola

+

+ olusx_dgmail.com +

+
+
- {" "} - {book.price} + + 500.67 STR
- -
- - - {book.rating} +
0xABC...78927 May,2025 + + Pending - + + + +
+
+ Showing 1 to 5 of 12 +
+
+
+ +
+

Trending Books

+
+ {books.map((book, idx) => ( +
+ {book.imgSrc} +
+

{book.title}

+

+ By {book.author}{" "} +

+
+
+ {" "} + {book.price}
- ))} +
+ + + {book.rating} + +
+
-
+ ))} +
+
-
-
-

Top Authors

- -
+
+
+

Top Authors

+ +
-
- {[ - { - name: "Elizabeth Joe", - img: "https://randomuser.me/api/portraits/women/65.jpg", - }, - { - name: "Alex Paul", - img: "https://randomuser.me/api/portraits/men/32.jpg", - }, - { - name: "Samson Tersoor", - img: "https://randomuser.me/api/portraits/men/77.jpg", - }, - { - name: "Vamika Maya", - img: "https://randomuser.me/api/portraits/women/49.jpg", - }, - { - name: "Samson Tersoor", - img: "https://randomuser.me/api/portraits/women/44.jpg", - }, - ].map((author, i) => ( -
-
- {author.name} -
-
- ))} +
+ {[ + { + name: "Elizabeth Joe", + img: "https://randomuser.me/api/portraits/women/65.jpg", + }, + { + name: "Alex Paul", + img: "https://randomuser.me/api/portraits/men/32.jpg", + }, + { + name: "Samson Tersoor", + img: "https://randomuser.me/api/portraits/men/77.jpg", + }, + { + name: "Vamika Maya", + img: "https://randomuser.me/api/portraits/women/49.jpg", + }, + { + name: "Samson Tersoor", + img: "https://randomuser.me/api/portraits/women/44.jpg", + }, + ].map((author, i) => ( +
+
+ {author.name} +
-
+ ))}
- - +
+
); } diff --git a/src/app/globals.css b/src/app/globals.css index ea3adcb..c4e266f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -213,10 +213,6 @@ --sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-border: oklch(0.922 0 0); --sidebar-ring: oklch(0.708 0 0); - - * { - /* outline: 1px solid black; */ - } } .dark { @@ -253,19 +249,6 @@ --sidebar-ring: oklch(0.556 0 0); } -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -} - -* { - @apply border-border outline-ring/50; -} - body { @apply bg-background text-foreground; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5899eee..02b4adb 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,4 +1,3 @@ - import { Metadata } from "next"; import { WalletProvider } from "../components/blockchain/WalletProvider"; import "@/app/globals.css"; @@ -105,9 +104,7 @@ export default function RootLayout({ - - {children} - + {children} ); From d2ecfdb6353944f9c84219258695a95676b289e1 Mon Sep 17 00:00:00 2001 From: Oshioke-Salaki Date: Sun, 10 Aug 2025 19:18:41 +0100 Subject: [PATCH 2/7] feat: notification announcements --- .../admin/components/admin-sidenavbar.tsx | 2 +- .../components/AnnouncementModal.tsx | 122 +++++++++++++++++ .../components/NotificationDetailsModal.tsx | 114 ++++++++++++++++ .../components/NotificationFilter.tsx | 33 ++++- .../components/NotificationFilterModal.tsx | 123 +++++++++++++++++ .../components/NotificationTable.tsx | 125 ++++-------------- .../components/NotificationTableRow.tsx | 48 ++++--- .../dashboard/admin/notifications/page.tsx | 23 +++- .../admin/notifications/utils/dummy_data.ts | 42 ++++++ 9 files changed, 497 insertions(+), 135 deletions(-) create mode 100644 src/app/dashboard/admin/notifications/components/AnnouncementModal.tsx create mode 100644 src/app/dashboard/admin/notifications/components/NotificationDetailsModal.tsx create mode 100644 src/app/dashboard/admin/notifications/components/NotificationFilterModal.tsx create mode 100644 src/app/dashboard/admin/notifications/utils/dummy_data.ts diff --git a/src/app/dashboard/admin/components/admin-sidenavbar.tsx b/src/app/dashboard/admin/components/admin-sidenavbar.tsx index 22928a5..68b5be3 100644 --- a/src/app/dashboard/admin/components/admin-sidenavbar.tsx +++ b/src/app/dashboard/admin/components/admin-sidenavbar.tsx @@ -70,7 +70,7 @@ export function Sidebar() {
  • { + const params = new URLSearchParams(sp.toString()); + params.delete("announce"); + router.push(`?${params.toString()}`); + }; + + // lock scroll while open + React.useEffect(() => { + if (!open) return; + const prev = document.body.style.overflow; + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = prev; + }; + }, [open]); + + if (!open) return null; + + return ( +
    + {/* backdrop */} +
    + + {/* card */} +
    + {/* back */} + + +
    + {/* Title input */} + setTitle(e.target.value)} + placeholder="Title" + className="w-full border-[1px] border-[#D1D1D1] rounded-lg px-4 py-3 text-xl sm:text-2xl font-semibold outline-none placeholder:text-[#B7BCC2]" + /> + + {/* simple toolbar (top) */} +
    + + + +
    + + {/* Body textarea */} +