-
Notifications
You must be signed in to change notification settings - Fork 0
BSL-40-AdminEventUI #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| "use client"; | ||
|
|
||
| import { useEffect, useState } from "react"; | ||
| import AdminLayout from "@/components/admin/AdminLayout"; | ||
|
|
||
| type Event = { | ||
| id: string; | ||
| title: string; | ||
| description?: string | null; | ||
| startAt: string; | ||
| endAt?: string | null; | ||
| location?: string | null; | ||
| link?: string | null; | ||
| }; | ||
|
|
||
| export default function AdminEventsPage() { | ||
| const [events, setEvents] = useState<Event[]>([]); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Continuing with the RBAC comments, add this right under AdminEventsPage() and keep the other stuff: |
||
| const [loading, setLoading] = useState(true); | ||
|
|
||
| const [title, setTitle] = useState(""); | ||
| const [startAt, setStartAt] = useState(""); | ||
| const [location, setLocation] = useState(""); | ||
|
|
||
| const [message, setMessage] = useState(""); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Track success vs error... const [message, setMessage] = useState<{ text: string; isError: boolean } | null>(null); Now have to update every setMessage(...) call (I will create a comment on each one to help) |
||
|
|
||
| async function loadEvents() { | ||
| setLoading(true); | ||
| setMessage(""); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setMessage(null); |
||
|
|
||
| try { | ||
| const res = await fetch("/api/admin/events"); | ||
| const data = await res.json(); | ||
|
|
||
| if (!res.ok) { | ||
| setEvents([]); | ||
| setMessage(data?.error || "Failed to load events"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setMessage({ text: data?.error || "Failed to load events", isError: true }); |
||
| return; | ||
| } | ||
|
|
||
| // Ensure events is always an array | ||
| setEvents(Array.isArray(data) ? data : []); | ||
| } catch { | ||
| setEvents([]); | ||
| setMessage("Failed to load events"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setMessage({ text: "Failed to load events", isError: true }); |
||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| } | ||
|
|
||
| useEffect(() => { | ||
| loadEvents(); | ||
| }, []); | ||
|
|
||
| async function createEvent(e: React.FormEvent) { | ||
| e.preventDefault(); | ||
| setMessage(""); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setMessage(null); |
||
|
|
||
| try { | ||
| const res = await fetch("/api/admin/events", { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ title, startAt, location }), | ||
| }); | ||
|
|
||
| const data = await res.json(); | ||
|
|
||
| if (!res.ok) { | ||
| setMessage(data?.error || "Failed to create event"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setMessage({ text: data?.error || "Failed to create event", isError: true }); |
||
| return; | ||
| } | ||
|
|
||
| setMessage("Event created successfully"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. setMessage({ text: "Event created successfully", isError: false }); |
||
|
|
||
| setTitle(""); | ||
| setStartAt(""); | ||
| setLocation(""); | ||
|
|
||
| loadEvents(); | ||
| } catch { | ||
| setMessage("Failed to create event"); | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like this for the role checking, adding to what i added above: |
||
| <AdminLayout> | ||
| <h1 className="text-2xl font-semibold">Admin Events</h1> | ||
|
|
||
| {/* Create Event Form */} | ||
| <form onSubmit={createEvent} className="mt-6 space-y-4 max-w-md"> | ||
| <div> | ||
| <label className="block text-sm font-medium">Title</label> | ||
| <input | ||
| className="w-full border rounded px-3 py-2" | ||
| value={title} | ||
| onChange={(e) => setTitle(e.target.value)} | ||
| required | ||
| /> | ||
| </div> | ||
|
|
||
| <div> | ||
| <label className="block text-sm font-medium">Start Time</label> | ||
| <input | ||
| type="datetime-local" | ||
| className="w-full border rounded px-3 py-2" | ||
| value={startAt} | ||
| onChange={(e) => setStartAt(e.target.value)} | ||
| required | ||
| /> | ||
| </div> | ||
|
|
||
| <div> | ||
| <label className="block text-sm font-medium">Location</label> | ||
| <input | ||
| className="w-full border rounded px-3 py-2" | ||
| value={location} | ||
| onChange={(e) => setLocation(e.target.value)} | ||
| /> | ||
| </div> | ||
|
|
||
| <button className="border px-4 py-2 rounded hover:bg-gray-50"> | ||
| Create Event | ||
| </button> | ||
|
|
||
| {message && ( | ||
| <p className="text-sm mt-2 text-red-600"> | ||
| {message} | ||
| </p> | ||
| )} | ||
|
Comment on lines
+126
to
+130
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Event created successfully" renders in red because message is a plain string with no distinction between success and error. Change message state to |
||
| </form> | ||
|
|
||
| {/* Events Table */} | ||
| <div className="mt-10"> | ||
| <h2 className="text-lg font-semibold mb-4">Events</h2> | ||
|
|
||
| {loading ? ( | ||
| <p>Loading...</p> | ||
| ) : events.length === 0 ? ( | ||
| <p>No events yet.</p> | ||
| ) : ( | ||
| <table className="w-full border"> | ||
| <thead className="bg-gray-50"> | ||
| <tr> | ||
| <th className="text-left p-2 border">Title</th> | ||
| <th className="text-left p-2 border">Start</th> | ||
| <th className="text-left p-2 border">Location</th> | ||
| </tr> | ||
| </thead> | ||
|
|
||
| <tbody> | ||
| {events.map((event) => ( | ||
| <tr key={event.id}> | ||
| <td className="p-2 border">{event.title}</td> | ||
| <td className="p-2 border"> | ||
| {new Date(event.startAt).toLocaleString()} | ||
| </td> | ||
| <td className="p-2 border">{event.location || "-"}</td> | ||
| </tr> | ||
| ))} | ||
| </tbody> | ||
| </table> | ||
| )} | ||
| </div> | ||
| </AdminLayout> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to incorporate RBAC (Role-based access control). So incorporate useSession() and hasRole()
here add this: