diff --git a/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionItem.tsx b/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionItem.tsx new file mode 100644 index 0000000..f091129 --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionItem.tsx @@ -0,0 +1,40 @@ +import { BadgeCheck } from "lucide-react"; +import React from "react"; + +function DiscussionItem(props: { + live?: boolean; + title: string; + author: string; + excerpt: string; +}) { + const { live, title, author, excerpt } = props; + return ( +
+
+
+
+ {live && ( + + Live + + )} +

+ {title} +

+
+ By {author} +
+
+ Book Description +
+
{excerpt}
+ {!live && ( +
2 weeks ago
+ )} +
+
+
+ ); +} + +export default DiscussionItem; diff --git a/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionSummary.tsx b/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionSummary.tsx new file mode 100644 index 0000000..2945341 --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionSummary.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { Community } from "../../utills/data"; +import { ClipboardList, Earth, Users } from "lucide-react"; + +function Tag({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} + +function DiscussionSummary({ community }: { community: Community }) { + return ( +
+
+
+

+ {community.name} +

+

+ {community.description} +

+
+ + {community.visibility} + + + {community.membersLabel} + + + {community.sessionsLabel} + +
+
+
+ ); +} + +export default DiscussionSummary; diff --git a/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionsTab.tsx b/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionsTab.tsx new file mode 100644 index 0000000..e3902bc --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/[id]/components/DiscussionsTab.tsx @@ -0,0 +1,21 @@ +import DiscussionItem from "./DiscussionItem"; + +function DiscussionsTab() { + return ( + <> + + + + ); +} + +export default DiscussionsTab; diff --git a/src/app/dashboard/admin/community-and-events/[id]/page.tsx b/src/app/dashboard/admin/community-and-events/[id]/page.tsx new file mode 100644 index 0000000..be772de --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/[id]/page.tsx @@ -0,0 +1,130 @@ +"use client"; +import { useRouter, useSearchParams, useParams } from "next/navigation"; +import { Header } from "@/components/dashboard/header"; +import { ArrowLeft } from "lucide-react"; +import { COMMUNITIES } from "../utills/data"; +import DiscussionsTab from "./components/DiscussionsTab"; +import DiscussionSummary from "./components/DiscussionSummary"; + +type TabKey = "discussion" | "live" | "about" | "members"; +const TABS: TabKey[] = ["discussion", "live", "about", "members"]; + +export default function Page() { + const router = useRouter(); + const sp = useSearchParams(); + const params = useParams<{ id: string }>(); + + const numericId = Number(params.id); + const community = COMMUNITIES.find((c) => c.id === numericId); + + // tabs from ?tab= + const tabParam = (sp.get("tab") as TabKey) || "discussion"; + const tab: TabKey = TABS.includes(tabParam) ? tabParam : "discussion"; + + const setTab = (next: TabKey) => { + const qp = new URLSearchParams(sp.toString()); + if (next === "discussion") qp.delete("tab"); + else qp.set("tab", next); + router.push(`?${qp.toString()}`); + }; + + if (!community) { + return ( + <> +
+
+ +
Community not found.
+
+ + ); + } + + return ( + <> +
+
+ + + + +
+
+ + + + +
+ +
+ {tab === "discussion" && } + + {tab === "live" && ( +
+ {community.liveNow + ? "A live session is currently running." + : "No live sessions right now."} +
+ )} + + {tab === "about" && ( +
+ {community.description} +
+ )} + + {tab === "members" && ( +
Members list coming soon…
+ )} +
+
+
+ + ); +} 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..d18bf27 --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/components/CommunityAndEventsFilter.tsx @@ -0,0 +1,84 @@ +"use client"; +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)); + }); + 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..f034841 --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/components/CommunityStrip.tsx @@ -0,0 +1,52 @@ +"use client"; +import { ClipboardList, Earth, Users } from "lucide-react"; +import React from "react"; +import { useRouter } from "next/navigation"; +import { Community } from "../utills/data"; + +export default function CommunityStrip({ item }: { item: Community }) { + const router = useRouter(); + + return ( +
+
+
+
+
+

{item.name}

+ {item.liveNow && ( +
+ Ongoing Live Event +
+ )} +
+ +
+
+ {item.visibility} +
+
+ {item.membersLabel} +
+
+ {item.sessionsLabel} +
+
+
+
+ + +
+ ); +} diff --git a/src/app/dashboard/admin/community-and-events/page.tsx b/src/app/dashboard/admin/community-and-events/page.tsx index 3f4ef37..db1096e 100644 --- a/src/app/dashboard/admin/community-and-events/page.tsx +++ b/src/app/dashboard/admin/community-and-events/page.tsx @@ -1,19 +1,44 @@ -"use-client"; +"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"; +import { COMMUNITIES } from "./utills/data"; 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 +
+
+ +
+ {COMMUNITIES.map((c) => ( + + ))} +
diff --git a/src/app/dashboard/admin/community-and-events/utills/data.ts b/src/app/dashboard/admin/community-and-events/utills/data.ts new file mode 100644 index 0000000..ed7cf2d --- /dev/null +++ b/src/app/dashboard/admin/community-and-events/utills/data.ts @@ -0,0 +1,50 @@ +export type Community = { + id: number; + name: string; + visibility: "Public" | "Private"; + membersLabel: string; + sessionsLabel: string; + liveNow?: boolean; + cover?: string; + description: string; +}; + +export const COMMUNITIES: Community[] = [ + { + id: 101, + name: "Fantasy Enthusiasts", + visibility: "Public", + membersLabel: "1.5k+ Members", + sessionsLabel: "2+ Session a Month", + liveNow: true, + description: + "Vibrant book club dedicated to exploring the vast and magical realms of fantasy literature. From epic sagas and dark fantasy to urban magic and whimsical tales, we delve into all corners of the genre.", + }, + { + id: 102, + name: "Rustaceans Lab", + visibility: "Public", + membersLabel: "3.2k+ Members", + sessionsLabel: "4 Sessions a Month", + description: + "Community for Rust learners and professionals focusing on systems programming, performance and safety.", + }, + { + id: 103, + name: "Creators Hub", + visibility: "Private", + membersLabel: "820 Members", + sessionsLabel: "Weekly Sessions", + description: + "A private space for content creators to share practices, tools and collaborate on projects.", + }, + { + id: 104, + name: "AI Readers Club", + visibility: "Public", + membersLabel: "2.1k+ Members", + sessionsLabel: "Bi-weekly", + description: + "We read and discuss approachable AI/ML books and articles. No PhD required—curiosity welcome.", + }, +]; diff --git a/src/app/dashboard/admin/components/admin-sidenavbar.tsx b/src/app/dashboard/admin/components/admin-sidenavbar.tsx index cb180bf..4e0fe41 100644 --- a/src/app/dashboard/admin/components/admin-sidenavbar.tsx +++ b/src/app/dashboard/admin/components/admin-sidenavbar.tsx @@ -1,6 +1,15 @@ "use client"; -import { LayoutDashboard, Users2, FileText, BarChart3, CalendarDays, Bell, MessagesSquare } from 'lucide-react'; +import { + LayoutDashboard, + Users2, + FileText, + BarChart3, + CalendarDays, + Bell, + MessagesSquare, + User, +} from "lucide-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; @@ -17,7 +26,7 @@ export function Sidebar() { { icon: Users2, label: "User Management", - href: "/dashboard/admin/user-management", + href: "/dashboard/admin/user-management", }, { icon: FileText, @@ -45,11 +54,15 @@ export function Sidebar() { label: "Community and Events", href: "/dashboard/admin/community-and-events", }, - + { + icon: User, + label: "Profile", + href: "/dashboard/admin/profile", + }, ]; return ( -
+
C @@ -63,7 +76,7 @@ export function Sidebar() {
  • ))} -
    diff --git a/src/app/dashboard/admin/layout.tsx b/src/app/dashboard/admin/layout.tsx index bf55f54..b3c701e 100644 --- a/src/app/dashboard/admin/layout.tsx +++ b/src/app/dashboard/admin/layout.tsx @@ -1,11 +1,8 @@ - - - +import { Suspense } from "react"; import type React from "react"; import { Sidebar } from "./components/admin-sidenavbar"; import "@/app/globals.css"; - export default function RootLayout({ children, }: { @@ -13,14 +10,12 @@ export default function RootLayout({ }) { return ( <> - -
    - -
    - {children} -
    -
    + +
    + +
    {children}
    +
    +
    - ); -} \ No newline at end of file +} diff --git a/src/app/dashboard/admin/notifications/components/AnnouncementModal.tsx b/src/app/dashboard/admin/notifications/components/AnnouncementModal.tsx new file mode 100644 index 0000000..f8d3c51 --- /dev/null +++ b/src/app/dashboard/admin/notifications/components/AnnouncementModal.tsx @@ -0,0 +1,112 @@ +"use client"; +import React from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { + ArrowLeft, + Bold, + Italic, + Underline, + Image as ImageIcon, + Link as LinkIcon, + Undo2, +} from "lucide-react"; + +export default function AnnouncementModal() { + const router = useRouter(); + const sp = useSearchParams(); + const open = sp.get("announce") === "1"; + + const [title, setTitle] = React.useState(""); + const [body, setBody] = React.useState(""); + + const close = () => { + 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 ( +
    +
    + +
    + + +
    + 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]" + /> + +
    + + + +
    + +