1
0
Fork 0

feat(awards): add grantConfig permissions to Awards collection with migration

This commit is contained in:
Jason Fraley 2026-08-31 12:27:36 -04:00
parent 24c5259056
commit 7f19b35b40
5 changed files with 19770 additions and 25 deletions

View file

@ -84,5 +84,64 @@ export const Awards: CollectionConfig = {
condition: (data) => data?.type === "medal", condition: (data) => data?.type === "medal",
}, },
}, },
{
name: "grantConfig",
type: "group",
label: "Grant Permissions",
admin: {
description:
"Configure who can grant this award and to whom. Only applies to ribbons and commendations.",
condition: (data) => data?.type !== "medal",
},
fields: [
{
name: "allowedGrantorAssignmentTypes",
type: "select",
hasMany: true,
label: "Eligible Grantor Assignments",
options: [
{ label: "Division Leader", value: "division" },
{ label: "Squad Leader", value: "squad" },
{ label: "Team Leader", value: "team" },
{ label: "Special Assignment Leader", value: "special" },
],
admin: {
description:
"Leaders of these assignment types can grant this award. Leave empty for admin/developer only.",
},
},
{
name: "maxGrantsPerGrantor",
type: "number",
label: "Max Grants Per Leader",
min: 1,
admin: {
description: "Maximum times each grantor can issue this award. Empty = unlimited.",
},
},
{
name: "recipientScope",
type: "select",
label: "Recipient Eligibility",
defaultValue: "same_command",
options: [
{ label: "Own Command (grantor's assignment tree)", value: "same_command" },
{ label: "Whitelist (specific assignments)", value: "whitelist" },
{ label: "Anyone (unit-wide)", value: "anyone" },
],
},
{
name: "eligibleRecipientAssignments",
type: "relationship",
relationTo: "assignments",
hasMany: true,
label: "Eligible Recipient Assignments",
admin: {
condition: (data) => data?.grantConfig?.recipientScope === "whitelist",
description: "Only members of these assignments can receive this award.",
},
},
],
},
], ],
}; };

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
await db.execute(sql`
CREATE TYPE "public"."enum_awards_grant_config_allowed_grantor_assignment_types" AS ENUM('division', 'squad', 'team', 'special');
CREATE TYPE "public"."enum_awards_grant_config_recipient_scope" AS ENUM('same_command', 'whitelist', 'anyone');
CREATE TABLE "awards_grant_config_allowed_grantor_assignment_types" (
"order" integer NOT NULL,
"parent_id" integer NOT NULL,
"value" "enum_awards_grant_config_allowed_grantor_assignment_types",
"id" serial PRIMARY KEY NOT NULL
);
CREATE TABLE "awards_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"assignments_id" integer
);
ALTER TABLE "awards" ADD COLUMN "grant_config_max_grants_per_grantor" numeric;
ALTER TABLE "awards" ADD COLUMN "grant_config_recipient_scope" "enum_awards_grant_config_recipient_scope" DEFAULT 'same_command';
ALTER TABLE "awards_grant_config_allowed_grantor_assignment_types" ADD CONSTRAINT "awards_grant_config_allowed_grantor_assignment_types_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."awards"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "awards_rels" ADD CONSTRAINT "awards_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."awards"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "awards_rels" ADD CONSTRAINT "awards_rels_assignments_fk" FOREIGN KEY ("assignments_id") REFERENCES "public"."assignments"("id") ON DELETE cascade ON UPDATE no action;
CREATE INDEX "awards_grant_config_allowed_grantor_assignment_types_order_idx" ON "awards_grant_config_allowed_grantor_assignment_types" USING btree ("order");
CREATE INDEX "awards_grant_config_allowed_grantor_assignment_types_parent_idx" ON "awards_grant_config_allowed_grantor_assignment_types" USING btree ("parent_id");
CREATE INDEX "awards_rels_order_idx" ON "awards_rels" USING btree ("order");
CREATE INDEX "awards_rels_parent_idx" ON "awards_rels" USING btree ("parent_id");
CREATE INDEX "awards_rels_path_idx" ON "awards_rels" USING btree ("path");
CREATE INDEX "awards_rels_assignments_id_idx" ON "awards_rels" USING btree ("assignments_id");`)
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
await db.execute(sql`
DROP TABLE "awards_grant_config_allowed_grantor_assignment_types" CASCADE;
DROP TABLE "awards_rels" CASCADE;
ALTER TABLE "awards" DROP COLUMN "grant_config_max_grants_per_grantor";
ALTER TABLE "awards" DROP COLUMN "grant_config_recipient_scope";
DROP TYPE "public"."enum_awards_grant_config_allowed_grantor_assignment_types";
DROP TYPE "public"."enum_awards_grant_config_recipient_scope";`)
}

View file

@ -1,45 +1,46 @@
import * as migration_20260820_004157 from "./20260820_004157"; import * as migration_20260820_004157 from './20260820_004157';
import * as migration_20260825_220000 from "./20260825_220000"; import * as migration_20260825_220000 from './20260825_220000';
import * as migration_20260826_050000_repair_shims_auxiliary_tables from "./20260826_050000_repair_shims_auxiliary_tables"; import * as migration_20260826_050000_repair_shims_auxiliary_tables from './20260826_050000_repair_shims_auxiliary_tables';
import * as migration_20260826_060000_add_game_servers_event_target from "./20260826_060000_add_game_servers_event_target"; import * as migration_20260826_060000_add_game_servers_event_target from './20260826_060000_add_game_servers_event_target';
import * as migration_20260827_005757 from "./20260827_005757"; import * as migration_20260827_005757 from './20260827_005757';
import * as migration_20260827_022334_add_eod_minesweeper_best_flags from "./20260827_022334_add_eod_minesweeper_best_flags"; import * as migration_20260827_022334_add_eod_minesweeper_best_flags from './20260827_022334_add_eod_minesweeper_best_flags';
import * as migration_20260828_011500_add_background_texture_preference from "./20260828_011500_add_background_texture_preference";
import * as migration_20260827_213857_add_evaluations_collection from './20260827_213857_add_evaluations_collection'; import * as migration_20260827_213857_add_evaluations_collection from './20260827_213857_add_evaluations_collection';
import * as migration_20260827_233224_add_mission_completed_at from './20260827_233224_add_mission_completed_at'; import * as migration_20260827_233224_add_mission_completed_at from './20260827_233224_add_mission_completed_at';
import * as migration_20260828_011500_add_background_texture_preference from './20260828_011500_add_background_texture_preference';
import * as migration_20260828_041615_add_campaign_mode from './20260828_041615_add_campaign_mode'; import * as migration_20260828_041615_add_campaign_mode from './20260828_041615_add_campaign_mode';
import * as migration_20260828_044506 from './20260828_044506'; import * as migration_20260828_044506 from './20260828_044506';
import * as migration_20260831_142503 from './20260831_142503';
export const migrations = [ export const migrations = [
{ {
up: migration_20260820_004157.up, up: migration_20260820_004157.up,
down: migration_20260820_004157.down, down: migration_20260820_004157.down,
name: "20260820_004157", name: '20260820_004157',
}, },
{ {
up: migration_20260825_220000.up, up: migration_20260825_220000.up,
down: migration_20260825_220000.down, down: migration_20260825_220000.down,
name: "20260825_220000", name: '20260825_220000',
}, },
{ {
up: migration_20260826_050000_repair_shims_auxiliary_tables.up, up: migration_20260826_050000_repair_shims_auxiliary_tables.up,
down: migration_20260826_050000_repair_shims_auxiliary_tables.down, down: migration_20260826_050000_repair_shims_auxiliary_tables.down,
name: "20260826_050000_repair_shims_auxiliary_tables", name: '20260826_050000_repair_shims_auxiliary_tables',
}, },
{ {
up: migration_20260826_060000_add_game_servers_event_target.up, up: migration_20260826_060000_add_game_servers_event_target.up,
down: migration_20260826_060000_add_game_servers_event_target.down, down: migration_20260826_060000_add_game_servers_event_target.down,
name: "20260826_060000_add_game_servers_event_target", name: '20260826_060000_add_game_servers_event_target',
}, },
{ {
up: migration_20260827_005757.up, up: migration_20260827_005757.up,
down: migration_20260827_005757.down, down: migration_20260827_005757.down,
name: "20260827_005757", name: '20260827_005757',
}, },
{ {
up: migration_20260827_022334_add_eod_minesweeper_best_flags.up, up: migration_20260827_022334_add_eod_minesweeper_best_flags.up,
down: migration_20260827_022334_add_eod_minesweeper_best_flags.down, down: migration_20260827_022334_add_eod_minesweeper_best_flags.down,
name: "20260827_022334_add_eod_minesweeper_best_flags", name: '20260827_022334_add_eod_minesweeper_best_flags',
}, },
{ {
up: migration_20260827_213857_add_evaluations_collection.up, up: migration_20260827_213857_add_evaluations_collection.up,
@ -54,7 +55,7 @@ export const migrations = [
{ {
up: migration_20260828_011500_add_background_texture_preference.up, up: migration_20260828_011500_add_background_texture_preference.up,
down: migration_20260828_011500_add_background_texture_preference.down, down: migration_20260828_011500_add_background_texture_preference.down,
name: "20260828_011500_add_background_texture_preference", name: '20260828_011500_add_background_texture_preference',
}, },
{ {
up: migration_20260828_041615_add_campaign_mode.up, up: migration_20260828_041615_add_campaign_mode.up,
@ -64,6 +65,11 @@ export const migrations = [
{ {
up: migration_20260828_044506.up, up: migration_20260828_044506.up,
down: migration_20260828_044506.down, down: migration_20260828_044506.down,
name: '20260828_044506' name: '20260828_044506',
},
{
up: migration_20260831_142503.up,
down: migration_20260831_142503.down,
name: '20260831_142503'
}, },
]; ];

View file

@ -1753,16 +1753,24 @@ export interface Award {
| number | number
| boolean | boolean
| null; | null;
updatedAt: string; /**
createdAt: string; * Configure who can grant this award and to whom. Only applies to ribbons and commendations.
} */
/** grantConfig?: {
* This interface was referenced by `Config`'s JSON-Schema /**
* via the `definition` "qualifications". * Leaders of these assignment types can grant this award. Leave empty for admin/developer only.
*/ */
export interface Qualification { allowedGrantorAssignmentTypes?: ('division' | 'squad' | 'team' | 'special')[] | null;
id: number; /**
name: string; * Maximum times each grantor can issue this award. Empty = unlimited.
*/
maxGrantsPerGrantor?: number | null;
recipientScope?: ('same_command' | 'whitelist' | 'anyone') | null;
/**
* Only members of these assignments can receive this award.
*/
eligibleRecipientAssignments?: (number | Assignment)[] | null;
};
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
@ -1810,6 +1818,16 @@ export interface Assignment {
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "qualifications".
*/
export interface Qualification {
id: number;
name: string;
updatedAt: string;
createdAt: string;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "experience". * via the `definition` "experience".
@ -3547,6 +3565,14 @@ export interface AwardsSelect<T extends boolean = true> {
experiencePoints?: T; experiencePoints?: T;
ribbonDesign?: T; ribbonDesign?: T;
medalDesign?: T; medalDesign?: T;
grantConfig?:
| T
| {
allowedGrantorAssignmentTypes?: T;
maxGrantsPerGrantor?: T;
recipientScope?: T;
eligibleRecipientAssignments?: T;
};
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;
} }