1
0
Fork 0
polaris-task-force/src/collections/locker/LockerStorages.ts
Z8MB1E 653caa8064 feat(rbac): migrate collection access controls to RBAC permissions
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.
2026-08-19 19:47:51 -04:00

166 lines
4.6 KiB
TypeScript

import { CollectionConfig } from "payload";
import { requirePermission, hasPermission } from "@/utils/access-control/hasPermission";
export const LockerStorages: CollectionConfig = {
slug: "locker-storages",
admin: {
group: "Locker",
useAsTitle: "name",
defaultColumns: ["name", "ownerUser", "itemCount"],
},
access: {
read: async ({ req }) => {
if (!req.user) return false;
if (await hasPermission(req.payload, req.user, "locker-storages:read")) return true;
return { ownerUser: { equals: req.user.id } };
},
create: ({ req }) => {
if (!req.user) return false;
return hasPermission(req.payload, req.user, "locker-storages:create");
},
update: async ({ req }) => {
if (!req.user) return false;
if (await hasPermission(req.payload, req.user, "locker-storages:update")) return true;
return { ownerUser: { equals: req.user.id } };
},
delete: async ({ req }) => {
return await hasPermission(req.payload, req.user, "locker-storages:delete");
},
},
fields: [
{
name: "name",
type: "text",
required: true,
defaultValue: "Personal Locker",
admin: {
description: "Display name for this locker.",
},
},
{
name: "ownerUser",
type: "relationship",
relationTo: "users",
required: true,
admin: {
description: "The operator who owns this locker.",
},
},
{
name: "items",
type: "array",
admin: {
description:
"Items stored in the locker grid. Position and rotation are managed from the member locker UI.",
},
fields: [
{
name: "asset",
type: "relationship",
relationTo: "assets",
required: true,
},
{
name: "quantity",
type: "number",
required: true,
min: 1,
defaultValue: 1,
},
{
name: "gridX",
type: "number",
required: true,
min: 0,
defaultValue: 0,
admin: {
description: "Column position in the locker grid (0-indexed).",
},
},
{
name: "gridY",
type: "number",
required: true,
min: 0,
defaultValue: 0,
admin: {
description: "Row position in the locker grid (0-indexed).",
},
},
{
name: "rotated",
type: "checkbox",
defaultValue: false,
admin: {
description: "Whether this item is rotated 90 degrees (swaps width and height).",
},
},
{
name: "equipment",
type: "group",
label: "Equipment Customization",
admin: {
description:
"Per-instance customization for this item: installed attachments, applied skin, and a custom name. Managed from the member locker UI.",
},
fields: [
{
name: "attachments",
type: "array",
label: "Installed Attachments",
admin: {
description:
"Attachments installed on this item (e.g. optics, muzzles, bipods on a weapon). Installing consumes the attachment from the locker grid.",
},
fields: [
{
name: "asset",
type: "relationship",
relationTo: "assets",
required: true,
},
],
},
{
name: "skin",
type: "relationship",
relationTo: "assets",
admin: {
description:
"Skin applied to this item. Applying consumes the skin from the locker grid.",
},
filterOptions: {
assetType: {
equals: "skin",
},
},
},
{
name: "customName",
type: "text",
admin: {
description: 'Optional custom name for this item (e.g. "Frank\'s Karma").',
},
},
],
},
],
},
{
name: "itemCount",
type: "number",
admin: {
readOnly: true,
description: "Total number of distinct entries currently in this locker.",
},
hooks: {
beforeChange: [
({ data }) => {
const items = (data as { items?: unknown[] } | undefined)?.items;
return Array.isArray(items) ? items.length : 0;
},
],
},
},
],
};