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:
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user