- The mission tick now runs two idempotent sweeps: auto-start flips Scheduled missions whose start time has arrived to Active, and auto-complete sets missions whose scheduled window (start plus duration) has fully passed to Completed with a stamped completedAt. - Side ops are created as quiet Ready drafts and committed with a "Mark as Scheduled" control, which is what posts the Discord roll-call and hands the op to the lifecycle above. - Daily schedule reminders nudge the creator to set a draft live (web notification plus Discord DM). - Adds the mission lifecycle/commit event types and the matching notification labels, and updates the bot docs.
126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
import { CollectionConfig } from "payload";
|
|
import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
|
|
|
|
const TARGET_COLLECTIONS = [
|
|
{ label: "Game Structures", value: "game-structures" },
|
|
{ label: "Game Vehicles", value: "game-vehicles" },
|
|
{ label: "Game NPCs", value: "game-npcs" },
|
|
{ label: "NPC Staffing", value: "npc-staffing" },
|
|
{ label: "Structures", value: "structures" },
|
|
{ label: "Resources", value: "resources" },
|
|
{ label: "Assets", value: "assets" },
|
|
{ label: "Vehicles", value: "vehicles" },
|
|
{ label: "Factions", value: "factions" },
|
|
{ label: "Missions", value: "missions" },
|
|
{ label: "Campaigns", value: "campaigns" },
|
|
{ label: "Users", value: "users" },
|
|
{ label: "Technologies", value: "technologies" },
|
|
{ label: "Maps", value: "maps" },
|
|
{ label: "Shipments", value: "shipments" },
|
|
{ label: "Bank Accounts", value: "bank-accounts" },
|
|
{ label: "Bank Transactions", value: "bank-transactions" },
|
|
{ label: "Ledger Entries", value: "ledger-entries" },
|
|
{ label: "Locker Storages", value: "locker-storages" },
|
|
{ label: "Loadouts", value: "loadouts" },
|
|
{ label: "Market Listings", value: "market-listings" },
|
|
{ label: "Market Negotiations", value: "market-negotiations" },
|
|
{ label: "Market State", value: "market-state" },
|
|
{ label: "Evaluations", value: "evaluations" },
|
|
{ label: "Tickets", value: "tickets" },
|
|
{ label: "Wiki Pages", value: "wiki-pages" },
|
|
{ label: "Wiki Revisions", value: "wiki-revisions" },
|
|
{ label: "Wiki Templates", value: "wiki-templates" },
|
|
{ label: "Game Servers", value: "game-servers" },
|
|
{ label: "Assignment Transfers", value: "assignment-transfers" },
|
|
{ label: "Voice Scripts", value: "voice-scripts" },
|
|
{ label: "Voice Submissions", value: "voice-submissions" },
|
|
{ label: "Story Beats", value: "story-beats" },
|
|
{ label: "Story Beat States", value: "story-beat-states" },
|
|
{ label: "Custom Minefields", value: "custom-minefields" },
|
|
{ label: "Custom Minefield Scores", value: "custom-minefield-scores" },
|
|
{ label: "Custom Radio Tests", value: "custom-radio-tests" },
|
|
{ label: "Custom Radio Test Scores", value: "custom-radio-test-scores" },
|
|
{ label: "Ribbon Submissions", value: "ribbon-submissions" },
|
|
{ label: "Promotion Nominations", value: "promotion-nominations" },
|
|
{ label: "Leadership Transfers", value: "leadership-transfers" },
|
|
] as const;
|
|
|
|
export const GameEventLogs: CollectionConfig = {
|
|
slug: "game-event-logs",
|
|
admin: {
|
|
group: "Game",
|
|
useAsTitle: "message",
|
|
defaultColumns: ["timestamp", "type", "system", "message", "actor", "structure"],
|
|
listSearchableFields: ["message", "type"],
|
|
pagination: { defaultLimit: 50 },
|
|
},
|
|
access: {
|
|
read: ({ req }) => !!req.user,
|
|
create: ({ req }) => !!req.user,
|
|
update: async ({ req }) => {
|
|
if (!req.user) return false;
|
|
return await hasPermission(req.payload, req.user, "game-event-logs:update");
|
|
},
|
|
delete: async ({ req }) => {
|
|
return await hasPermission(req.payload, req.user, "game-event-logs:delete");
|
|
},
|
|
},
|
|
fields: [
|
|
{
|
|
name: "system",
|
|
type: "checkbox",
|
|
defaultValue: true,
|
|
admin: {
|
|
description:
|
|
"System-generated events are created automatically. Uncheck for manual/GM narrative entries.",
|
|
},
|
|
},
|
|
{
|
|
name: "timestamp",
|
|
type: "date",
|
|
required: true,
|
|
defaultValue: () => new Date().toISOString(),
|
|
admin: {
|
|
date: { pickerAppearance: "dayAndTime" },
|
|
},
|
|
},
|
|
{
|
|
name: "type",
|
|
type: "text",
|
|
required: true,
|
|
index: true,
|
|
},
|
|
{
|
|
name: "message",
|
|
type: "text",
|
|
required: true,
|
|
},
|
|
{
|
|
name: "actor",
|
|
type: "relationship",
|
|
relationTo: "users",
|
|
},
|
|
{
|
|
name: "structure",
|
|
type: "relationship",
|
|
relationTo: "game-structures",
|
|
hasMany: false,
|
|
},
|
|
{
|
|
name: "targetCollection",
|
|
type: "select",
|
|
options: TARGET_COLLECTIONS.map((c) => ({ label: c.label, value: c.value })),
|
|
admin: {
|
|
description: "Which collection the event primarily relates to.",
|
|
},
|
|
},
|
|
{
|
|
name: "targetId",
|
|
type: "number",
|
|
},
|
|
{
|
|
name: "data",
|
|
type: "json",
|
|
},
|
|
],
|
|
};
|