Feature: Add 24-hour cleanup for incomplete orders and update status display

- Create cleanupOrders utility to delete pending orders older than 24 hours
- Update admin notifications to only count recent pending orders (< 24h old)
- Automatically call cleanup when customer initiates checkout
- Change status display from "PENDING" to "Waiting payment" for customer-facing views
- Update both customer account page and admin orders page with new status label
- Incomplete orders automatically purged, reducing admin clutter

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Andymick
2026-07-19 07:50:55 +01:00
co-authored by Claude Haiku 4.5
parent fa202db4a7
commit 3e90928986
6 changed files with 48 additions and 3 deletions
+2 -1
View File
@@ -88,7 +88,8 @@
"Bash(git commit -m 'Add: Prominent '\\\\''Shop plain items'\\\\'' section to homepage *)",
"Bash(git commit -m 'Feature: Royal Mail shipping integration with weight-based postage *)",
"Bash(git commit -m 'Add: Weight field to product admin pages *)",
"Bash(git commit -m 'Add: Shipping selector component and checkout integration *)"
"Bash(git commit -m 'Add: Shipping selector component and checkout integration *)",
"Bash(node clear-pending-orders.js)"
]
}
}
+1
View File
@@ -13,6 +13,7 @@ function formatPrice(cents: number) {
function statusLabel(status: string) {
if (status === 'PAID') return 'Confirmed';
if (status === 'FAILED') return 'Failed';
if (status === 'PENDING') return 'Waiting payment';
return 'Processing';
}
+8 -1
View File
@@ -7,6 +7,13 @@ function formatPrice(cents: number) {
return `£${(cents / 100).toFixed(2)}`;
}
function statusLabel(status: string) {
if (status === 'PAID') return 'Confirmed';
if (status === 'FAILED') return 'Failed';
if (status === 'PENDING') return 'Waiting payment';
return 'Processing';
}
const STATUS_STYLES: Record<string, string> = {
PAID: 'bg-splash-blue/10 text-splash-blue',
PENDING: 'bg-splash-orange/10 text-splash-orange',
@@ -121,7 +128,7 @@ export default async function OrdersPage({ searchParams }: { searchParams: { arc
)}
<span className="font-mono text-sm">{formatPrice(order.total)}</span>
<span className={`tag-label rounded-full px-3 py-1 ${STATUS_STYLES[order.status] ?? ''}`}>
{order.status}
{statusLabel(order.status)}
</span>
<form action={showArchived ? unarchiveOrder : archiveOrder}>
<input type="hidden" name="id" value={order.id} />
+4
View File
@@ -6,6 +6,7 @@ import { getEffectivePrice } from '@/lib/pricing';
import { fetchActivePromotions } from '@/lib/promotions';
import { checkRateLimit, getClientIp } from '@/lib/rateLimit';
import { getRoyalMailShippingQuotes } from '@/lib/royalmail-shipping';
import { cleanupPendingOrders } from '@/lib/cleanupOrders';
type CheckoutCartItem = {
productId: string;
@@ -51,6 +52,9 @@ export async function POST(req: Request) {
);
}
// Clean up old incomplete orders (non-blocking)
cleanupPendingOrders().catch((err) => console.error('Cleanup error:', err));
const body = await req.json();
console.log('Checkout request received with items:', body.items?.length, 'customerId:', body.customerId);
const items: CheckoutCartItem[] = body.items ?? [];
+10 -1
View File
@@ -1,15 +1,24 @@
import { prisma } from '@/lib/prisma';
import { cleanupPendingOrders } from '@/lib/cleanupOrders';
import AdminNotificationsBadge from './AdminNotificationsBadge';
export default async function AdminNotifications() {
try {
// Clean up old incomplete orders (older than 24 hours)
await cleanupPendingOrders();
// Only count pending orders that are less than 24 hours old (still waiting for payment)
const recentThreshold = new Date(Date.now() - 24 * 60 * 60 * 1000);
const [
pendingOrders,
designApprovalsInReview,
photoRequests,
] = await Promise.all([
prisma.order.count({
where: { status: 'PENDING' },
where: {
status: 'PENDING',
createdAt: { gte: recentThreshold },
},
}),
prisma.designApproval.count({
where: { status: 'IN_REVIEW' },
+23
View File
@@ -0,0 +1,23 @@
import { prisma } from './prisma';
export async function cleanupPendingOrders() {
try {
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
const result = await prisma.order.deleteMany({
where: {
status: 'PENDING',
createdAt: { lt: twentyFourHoursAgo },
},
});
if (result.count > 0) {
console.log(`✓ Cleaned up ${result.count} incomplete orders older than 24 hours`);
}
return result.count;
} catch (error) {
console.error('Error cleaning up pending orders:', error);
return 0;
}
}