Replace legacy isDeveloper/isAdmin/hasRoles checks with hasPermission/requirePermission across all collections. Add roleDocs relationship to Users, enlistmentDate to Profiles, and field-level intel_excerpt permission gate on Profiles. Auto-generated payload-types updated to reflect the new Roles collection and roleDocs field.
157 lines
4 KiB
TypeScript
157 lines
4 KiB
TypeScript
import { CollectionConfig, Where } from "payload";
|
||
import type { User } from "@/payload-types";
|
||
import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
|
||
|
||
export const MarketNegotiations: CollectionConfig = {
|
||
slug: "market-negotiations",
|
||
admin: {
|
||
group: "Market",
|
||
useAsTitle: "id",
|
||
defaultColumns: [
|
||
"id",
|
||
"listing",
|
||
"buyer",
|
||
"amount",
|
||
"patience",
|
||
"proposedBy",
|
||
"status",
|
||
"updatedAt",
|
||
],
|
||
pagination: { defaultLimit: 50 },
|
||
},
|
||
access: {
|
||
read: async ({ req }) => {
|
||
const user = req.user as User | null;
|
||
if (!user) return false;
|
||
if (await hasPermission(req.payload, user, "market-negotiations:read")) return true;
|
||
return {
|
||
or: [{ buyer: { equals: user.id } }, { "listing.seller": { equals: user.id } }],
|
||
} as Where;
|
||
},
|
||
create: ({ req }) => !!req.user,
|
||
update: async ({ req }) => {
|
||
const user = req.user as User | null;
|
||
if (!user) return false;
|
||
if (await hasPermission(req.payload, user, "market-negotiations:update")) return true;
|
||
return {
|
||
or: [{ buyer: { equals: user.id } }, { "listing.seller": { equals: user.id } }],
|
||
} as Where;
|
||
},
|
||
delete: requirePermission("market-negotiations:delete"),
|
||
},
|
||
fields: [
|
||
{
|
||
name: "listing",
|
||
type: "relationship",
|
||
relationTo: "market-listings",
|
||
required: true,
|
||
index: true,
|
||
admin: {
|
||
description: "The market listing this negotiation thread belongs to.",
|
||
},
|
||
},
|
||
{
|
||
name: "buyer",
|
||
type: "relationship",
|
||
relationTo: "users",
|
||
required: true,
|
||
index: true,
|
||
admin: {
|
||
description: "The player interested in buying the listing.",
|
||
},
|
||
},
|
||
{
|
||
name: "status",
|
||
type: "select",
|
||
required: true,
|
||
defaultValue: "open",
|
||
options: [
|
||
{ label: "Open", value: "open" },
|
||
{ label: "Accepted", value: "accepted" },
|
||
{ label: "Rejected", value: "rejected" },
|
||
{ label: "Closed (patience exhausted)", value: "closed" },
|
||
{ label: "Cancelled", value: "cancelled" },
|
||
{ label: "Expired", value: "expired" },
|
||
],
|
||
index: true,
|
||
},
|
||
{
|
||
name: "patience",
|
||
type: "number",
|
||
defaultValue: 0,
|
||
min: 0,
|
||
max: 100,
|
||
admin: {
|
||
description:
|
||
"NPC vendor patience (0–100). Rises each bargaining round and sharply for lowball offers; at 100 the negotiation closes and the buyer can only buy at the asking price.",
|
||
},
|
||
},
|
||
{
|
||
name: "quantity",
|
||
type: "number",
|
||
min: 1,
|
||
defaultValue: 1,
|
||
admin: {
|
||
description: "How many units the buyer wants from the listing.",
|
||
},
|
||
},
|
||
{
|
||
name: "amount",
|
||
type: "number",
|
||
required: true,
|
||
min: 0,
|
||
admin: {
|
||
description:
|
||
"The per-unit price currently on the table. Proposed by the buyer or the seller depending on the proposal.",
|
||
},
|
||
},
|
||
{
|
||
name: "proposedBy",
|
||
type: "select",
|
||
required: true,
|
||
defaultValue: "buyer",
|
||
options: [
|
||
{ label: "Buyer", value: "buyer" },
|
||
{ label: "Seller", value: "seller" },
|
||
],
|
||
},
|
||
{
|
||
name: "acceptedPrice",
|
||
type: "number",
|
||
admin: {
|
||
description: "The per-unit price agreed when the negotiation is accepted.",
|
||
},
|
||
},
|
||
{
|
||
name: "history",
|
||
type: "array",
|
||
fields: [
|
||
{
|
||
name: "amount",
|
||
type: "number",
|
||
required: true,
|
||
},
|
||
{
|
||
name: "from",
|
||
type: "select",
|
||
required: true,
|
||
options: [
|
||
{ label: "Buyer", value: "buyer" },
|
||
{ label: "Seller", value: "seller" },
|
||
],
|
||
},
|
||
{
|
||
name: "note",
|
||
type: "text",
|
||
},
|
||
{
|
||
name: "at",
|
||
type: "date",
|
||
required: true,
|
||
defaultValue: () => new Date().toISOString(),
|
||
},
|
||
],
|
||
},
|
||
],
|
||
timestamps: true,
|
||
};
|