feat(admin): marketplace console + analytics degraded banners
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
f150aea8b9
commit
025af147cd
9 files changed
+355
-39
No files matched your search
@@ -0,0 +1,36 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { revalidatePath } from "next/cache";
|
||||||
|
import { requirePermissionRateLimited } from "@/lib/admin/guard";
|
||||||
|
import { PERMS } from "@/lib/permissions";
|
||||||
|
import { prisma } from "@/lib/prisma";
|
||||||
|
import { logStaffActivity } from "@/lib/services/staff-activity";
|
||||||
|
|
||||||
|
/** Cancel an active marketplace listing (state 1 → 0). */
|
||||||
|
export async function cancelMarketplaceListing(
|
||||||
|
formData: FormData,
|
||||||
|
): Promise<void> {
|
||||||
|
const staff = await requirePermissionRateLimited(PERMS.SHOP_EDIT);
|
||||||
|
const id = Number(formData.get("id"));
|
||||||
|
if (!(id > 0)) return;
|
||||||
|
|
||||||
|
const listing = await prisma.marketplaceItems.findUnique({
|
||||||
|
where: { id },
|
||||||
|
select: { id: true, state: true, userId: true, itemId: true, price: true },
|
||||||
|
});
|
||||||
|
if (listing?.state !== 1) return;
|
||||||
|
|
||||||
|
await prisma.marketplaceItems.update({
|
||||||
|
where: { id },
|
||||||
|
data: { state: 0 },
|
||||||
|
});
|
||||||
|
|
||||||
|
await logStaffActivity({
|
||||||
|
staffId: staff.id,
|
||||||
|
action: "marketplace_cancel",
|
||||||
|
description: `Cancelled marketplace listing #${id} (item ${listing.itemId}, user ${listing.userId}, price ${listing.price})`,
|
||||||
|
targetType: "marketplace",
|
||||||
|
targetId: id,
|
||||||
|
});
|
||||||
|
revalidatePath("/admin/marketplace");
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -6,18 +6,21 @@ export function AdminSimplePager({
|
|||||||
lastPage,
|
lastPage,
|
||||||
total,
|
total,
|
||||||
basePath,
|
basePath,
|
||||||
|
params,
|
||||||
label,
|
label,
|
||||||
}: {
|
}: {
|
||||||
page: number;
|
page: number;
|
||||||
lastPage: number;
|
lastPage: number;
|
||||||
total: number;
|
total: number;
|
||||||
basePath: string;
|
basePath: string;
|
||||||
|
/** Extra query params preserved across pages (e.g. filter=sold). */
|
||||||
|
params?: Record<string, string>;
|
||||||
label?: string;
|
label?: string;
|
||||||
}) {
|
}) {
|
||||||
if (lastPage <= 1) return null;
|
if (lastPage <= 1) return null;
|
||||||
|
|
||||||
const href = (p: number) => {
|
const href = (p: number) => {
|
||||||
const u = new URLSearchParams();
|
const u = new URLSearchParams(params);
|
||||||
u.set("page", String(p));
|
u.set("page", String(p));
|
||||||
return `${basePath}?${u.toString()}`;
|
return `${basePath}?${u.toString()}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -457,6 +457,12 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
|||||||
icon: ShoppingCart,
|
icon: ShoppingCart,
|
||||||
permission: PERMS.SHOP_VIEW,
|
permission: PERMS.SHOP_VIEW,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
href: "/admin/marketplace",
|
||||||
|
labelKey: "marketplace",
|
||||||
|
icon: Store,
|
||||||
|
permission: PERMS.SHOP_VIEW,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
href: "/admin/transactions",
|
href: "/admin/transactions",
|
||||||
labelKey: "transactions",
|
labelKey: "transactions",
|
||||||
|
|||||||
@@ -1041,6 +1041,7 @@
|
|||||||
"hubShop": "Shop & payments",
|
"hubShop": "Shop & payments",
|
||||||
"catalog": "Catalog",
|
"catalog": "Catalog",
|
||||||
"shop": "Shop",
|
"shop": "Shop",
|
||||||
|
"marketplace": "Marketplace",
|
||||||
"transactions": "Transactions",
|
"transactions": "Transactions",
|
||||||
"vouchers": "Vouchers",
|
"vouchers": "Vouchers",
|
||||||
"subscriptions": "Subscriptions",
|
"subscriptions": "Subscriptions",
|
||||||
@@ -1796,6 +1797,24 @@
|
|||||||
"cancel": "Cancel"
|
"cancel": "Cancel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"marketplace": {
|
||||||
|
"title": "Marketplace",
|
||||||
|
"subtitle": "Hotel marketplace listings",
|
||||||
|
"activeListings": "Active listings",
|
||||||
|
"soldListings": "Sold",
|
||||||
|
"filterActive": "Active",
|
||||||
|
"filterSold": "Sold",
|
||||||
|
"empty": "No marketplace listings to show.",
|
||||||
|
"loadError": "Could not load marketplace data (database unavailable).",
|
||||||
|
"cancel": "Cancel listing",
|
||||||
|
"colId": "ID",
|
||||||
|
"colItem": "Item ID",
|
||||||
|
"colSeller": "Seller",
|
||||||
|
"colPrice": "Price",
|
||||||
|
"colListed": "Listed",
|
||||||
|
"colSold": "Sold at",
|
||||||
|
"colActions": "Actions"
|
||||||
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"title": "Transactions",
|
"title": "Transactions",
|
||||||
"subtitle": "PayPal payment log",
|
"subtitle": "PayPal payment log",
|
||||||
|
|||||||
@@ -971,6 +971,7 @@
|
|||||||
"economy": "Economia",
|
"economy": "Economia",
|
||||||
"catalog": "Catalogo",
|
"catalog": "Catalogo",
|
||||||
"shop": "Negozio",
|
"shop": "Negozio",
|
||||||
|
"marketplace": "Marketplace",
|
||||||
"transactions": "Transazioni",
|
"transactions": "Transazioni",
|
||||||
"vouchers": "Buoni",
|
"vouchers": "Buoni",
|
||||||
"subscriptions": "Abbonamenti",
|
"subscriptions": "Abbonamenti",
|
||||||
@@ -1734,6 +1735,24 @@
|
|||||||
"cancel": "Annulla"
|
"cancel": "Annulla"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"marketplace": {
|
||||||
|
"title": "Marketplace",
|
||||||
|
"subtitle": "Annunci marketplace hotel",
|
||||||
|
"activeListings": "Annunci attivi",
|
||||||
|
"soldListings": "Venduti",
|
||||||
|
"filterActive": "Attivi",
|
||||||
|
"filterSold": "Venduti",
|
||||||
|
"empty": "Nessun annuncio marketplace da mostrare.",
|
||||||
|
"loadError": "Impossibile caricare i dati marketplace (database non disponibile).",
|
||||||
|
"cancel": "Annulla annuncio",
|
||||||
|
"colId": "ID",
|
||||||
|
"colItem": "Item ID",
|
||||||
|
"colSeller": "Venditore",
|
||||||
|
"colPrice": "Prezzo",
|
||||||
|
"colListed": "Pubblicato",
|
||||||
|
"colSold": "Venduto il",
|
||||||
|
"colActions": "Azioni"
|
||||||
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"title": "Transazioni",
|
"title": "Transazioni",
|
||||||
"subtitle": "Registro pagamenti PayPal",
|
"subtitle": "Registro pagamenti PayPal",
|
||||||
|
|||||||
@@ -1016,6 +1016,7 @@
|
|||||||
"economy": "Economie",
|
"economy": "Economie",
|
||||||
"catalog": "Catalogus",
|
"catalog": "Catalogus",
|
||||||
"shop": "Winkel",
|
"shop": "Winkel",
|
||||||
|
"marketplace": "Marketplace",
|
||||||
"transactions": "Transacties",
|
"transactions": "Transacties",
|
||||||
"vouchers": "Vouchers",
|
"vouchers": "Vouchers",
|
||||||
"subscriptions": "Abonnementen",
|
"subscriptions": "Abonnementen",
|
||||||
@@ -1710,6 +1711,24 @@
|
|||||||
"cancel": "Annuleren"
|
"cancel": "Annuleren"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"marketplace": {
|
||||||
|
"title": "Marketplace",
|
||||||
|
"subtitle": "Hotel marketplace-aanbiedingen",
|
||||||
|
"activeListings": "Actieve listings",
|
||||||
|
"soldListings": "Verkocht",
|
||||||
|
"filterActive": "Actief",
|
||||||
|
"filterSold": "Verkocht",
|
||||||
|
"empty": "Geen marketplace-listings om te tonen.",
|
||||||
|
"loadError": "Kon marketplace-gegevens niet laden (database niet beschikbaar).",
|
||||||
|
"cancel": "Listing annuleren",
|
||||||
|
"colId": "ID",
|
||||||
|
"colItem": "Item-ID",
|
||||||
|
"colSeller": "Verkoper",
|
||||||
|
"colPrice": "Prijs",
|
||||||
|
"colListed": "Geplaatst",
|
||||||
|
"colSold": "Verkocht op",
|
||||||
|
"colActions": "Acties"
|
||||||
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"title": "Transacties",
|
"title": "Transacties",
|
||||||
"subtitle": "PayPal betalingslogboek",
|
"subtitle": "PayPal betalingslogboek",
|
||||||
|
|||||||
Reference in new issue
Block a user