Add system status and invitations cards to admin page
Introduces SystemStatusCard and InvitationsListCard components to the admin dashboard for displaying system metrics and active invitations. Refactors InviteVendorCard to generate and display invitation codes, and updates card layouts for consistent sizing. Improves admin page structure and enhances visibility of system and invitation data.
This commit is contained in:
@@ -3,23 +3,18 @@ import { useState } from "react";
|
||||
import { fetchClient } from "@/lib/api-client";
|
||||
|
||||
export default function InviteVendorCard() {
|
||||
const [username, setUsername] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState<string | null>(null);
|
||||
const [code, setCode] = useState<string | null>(null);
|
||||
|
||||
async function handleInvite(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
async function handleInvite() {
|
||||
setLoading(true);
|
||||
setMessage(null);
|
||||
setCode(null);
|
||||
try {
|
||||
await fetchClient("/admin/invitations", {
|
||||
method: "POST",
|
||||
body: { username, email }
|
||||
});
|
||||
setMessage("Invitation sent");
|
||||
setUsername("");
|
||||
setEmail("");
|
||||
const res = await fetchClient<{ code: string }>("/admin/invitations", { method: "POST" });
|
||||
setMessage("Invitation created");
|
||||
setCode(res.code);
|
||||
} catch (e: any) {
|
||||
setMessage(e?.message || "Failed to send invitation");
|
||||
} finally {
|
||||
@@ -28,33 +23,24 @@ export default function InviteVendorCard() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-border/60 bg-background p-4">
|
||||
<div className="rounded-lg border border-border/60 bg-background p-4 h-full min-h-[200px]">
|
||||
<h2 className="font-medium">Invite Vendor</h2>
|
||||
<p className="text-sm text-muted-foreground mt-1">Generate and send an invite</p>
|
||||
<form onSubmit={handleInvite} className="mt-4 space-y-3">
|
||||
<input
|
||||
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="Vendor username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
|
||||
placeholder="Email (optional)"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
type="email"
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground mt-1">Generate a new invitation code</p>
|
||||
<div className="mt-4 space-y-3">
|
||||
<button
|
||||
type="submit"
|
||||
onClick={handleInvite}
|
||||
className="inline-flex items-center rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground disabled:opacity-60"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "Sending..." : "Send Invite"}
|
||||
{loading ? "Generating..." : "Generate Invite Code"}
|
||||
</button>
|
||||
{message && <p className="text-xs text-muted-foreground">{message}</p>}
|
||||
</form>
|
||||
{code && (
|
||||
<div className="text-sm">
|
||||
Code: <span className="font-mono px-1.5 py-0.5 rounded bg-muted">{code}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user