Add delete action to invitations and UI improvements

Introduces a delete button for unused invitations in InvitationsListCard, allowing admins to remove invites. Also improves layout and spacing in the invitations card, formats order totals with currency in RecentOrdersCard, and hides several unused cards in the admin page as requested.
This commit is contained in:
NotII
2025-10-15 17:53:01 +01:00
parent e7c06e4352
commit 2808ce6919
3 changed files with 26 additions and 45 deletions

View File

@@ -17,50 +17,12 @@ export default function AdminPage() {
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3 items-stretch"> <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3 items-stretch">
<SystemStatusCard /> <SystemStatusCard />
<a href="#" className="group rounded-lg border border-border/60 bg-background p-4 hover:bg-muted/40 transition-colors h-full min-h-[200px]">
<div className="flex items-start justify-between">
<div>
<h2 className="font-medium">Logs</h2>
<p className="text-sm text-muted-foreground mt-1">View recent errors and warnings</p>
</div>
<span className="text-xs px-2 py-0.5 rounded bg-amber-500/15 text-amber-400">New</span>
</div>
</a>
<InviteVendorCard /> <InviteVendorCard />
<BanUserCard /> <BanUserCard />
<RecentOrdersCard /> <RecentOrdersCard />
<InvitationsListCard /> <InvitationsListCard />
<a href="#" className="group rounded-lg border border-border/60 bg-background p-4 hover:bg-muted/40 transition-colors"> {/* Disabled/hidden cards as requested */}
<div className="flex items-start justify-between">
<div>
<h2 className="font-medium">Broadcast</h2>
<p className="text-sm text-muted-foreground mt-1">Send a message to users</p>
</div>
<span className="text-xs px-2 py-0.5 rounded bg-fuchsia-500/15 text-fuchsia-400">Tools</span>
</div>
</a>
<a href="#" className="group rounded-lg border border-border/60 bg-background p-4 hover:bg-muted/40 transition-colors h-full min-h-[200px]">
<div className="flex items-start justify-between">
<div>
<h2 className="font-medium">Config</h2>
<p className="text-sm text-muted-foreground mt-1">Feature flags and settings</p>
</div>
<span className="text-xs px-2 py-0.5 rounded bg-indigo-500/15 text-indigo-400">Edit</span>
</div>
</a>
<a href="#" className="group rounded-lg border border-border/60 bg-background p-4 hover:bg-muted/40 transition-colors h-full min-h-[200px]">
<div className="flex items-start justify-between">
<div>
<h2 className="font-medium">Payments</h2>
<p className="text-sm text-muted-foreground mt-1">Gateways and webhooks</p>
</div>
<span className="text-xs px-2 py-0.5 rounded bg-teal-500/15 text-teal-400">Setup</span>
</div>
</a>
</div> </div>
</div> </div>
); );

View File

@@ -16,6 +16,15 @@ export default function InvitationsListCard() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
async function deleteInvite(id: string) {
try {
await fetchClient(`/admin/invitations/${id}`, { method: 'DELETE' });
setInvites(prev => prev.filter(i => i._id !== id));
} catch (e: any) {
setError(e?.message || 'Failed to delete invitation');
}
}
useEffect(() => { useEffect(() => {
let mounted = true; let mounted = true;
(async () => { (async () => {
@@ -47,8 +56,8 @@ export default function InvitationsListCard() {
{invites.map((inv) => { {invites.map((inv) => {
const expired = new Date(inv.expiresAt).getTime() < Date.now(); const expired = new Date(inv.expiresAt).getTime() < Date.now();
return ( return (
<div key={inv._id} className="rounded border border-border/50 p-3 text-sm flex items-center justify-between"> <div key={inv._id} className="rounded border border-border/50 p-3 text-sm flex items-center justify-between gap-3">
<div className="space-y-1"> <div className="space-y-1 min-w-0">
<div> <div>
Code: <span className="font-mono px-1.5 py-0.5 rounded bg-muted">{inv.code}</span> Code: <span className="font-mono px-1.5 py-0.5 rounded bg-muted">{inv.code}</span>
</div> </div>
@@ -56,9 +65,19 @@ export default function InvitationsListCard() {
Created: {new Date(inv.createdAt).toLocaleString()} · Expires: {new Date(inv.expiresAt).toLocaleString()} Created: {new Date(inv.createdAt).toLocaleString()} · Expires: {new Date(inv.expiresAt).toLocaleString()}
</div> </div>
</div> </div>
<div className="flex items-center gap-2 shrink-0">
<span className={`text-xs px-2 py-0.5 rounded ${inv.isUsed ? 'bg-emerald-500/15 text-emerald-400' : expired ? 'bg-rose-500/15 text-rose-400' : 'bg-amber-500/15 text-amber-400'}`}> <span className={`text-xs px-2 py-0.5 rounded ${inv.isUsed ? 'bg-emerald-500/15 text-emerald-400' : expired ? 'bg-rose-500/15 text-rose-400' : 'bg-amber-500/15 text-amber-400'}`}>
{inv.isUsed ? 'Used' : expired ? 'Expired' : 'Active'} {inv.isUsed ? 'Used' : expired ? 'Expired' : 'Active'}
</span> </span>
{!inv.isUsed && (
<button
onClick={() => deleteInvite(inv._id)}
className="text-xs px-2 py-1 rounded border border-border hover:bg-muted/40"
>
Delete
</button>
)}
</div>
</div> </div>
); );
})} })}

View File

@@ -54,7 +54,7 @@ export default function RecentOrdersCard() {
<div className="text-muted-foreground">{new Date(o.createdAt).toLocaleString()}</div> <div className="text-muted-foreground">{new Date(o.createdAt).toLocaleString()}</div>
</div> </div>
<div className="mt-1 text-xs text-muted-foreground"> <div className="mt-1 text-xs text-muted-foreground">
User: {o.userId} · Total: {o.total} User: {o.userId} · Total: £{Number(o.total).toFixed(2)}
</div> </div>
{o.items && o.items.length > 0 && ( {o.items && o.items.length > 0 && (
<ul className="mt-2 text-xs list-disc pl-4 text-muted-foreground"> <ul className="mt-2 text-xs list-disc pl-4 text-muted-foreground">