feat(tech-tree): add tech-categories collection, permissions, and treePosition
Add the tech-categories collection that gives technologies a home off the
flat uncategorized pool, plus the schema support the admin editor needs.
- New `tech-categories` collection (unique name, timeline `position`,
optional hex `color`) with a `Technologies.category` FK (ON DELETE set
null) so deleting a category just unfiles its technologies.
- Add `Technologies.treePosition {x,y}` so the canvas can pin a node's
manual position; clearing it uses `{x:null,y:null}`.
- Register the two hand-written migrations (tech-categories + treePosition)
and regenerate payload-types, the drizzle schema, and the admin importMap.
- Register the `tech-categories:*` permission group and grant Intelligence
division members admin-panel access, mirroring technologies.
This commit is contained in:
parent
954e6e2c44
commit
15f2d0c573
12 changed files with 70877 additions and 3 deletions
|
|
@ -36,12 +36,14 @@ import { default as default_7fa52484aec46e988e9cfb5e86c0c759 } from '@/component
|
||||||
import { SelectField as SelectField_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
import { SelectField as SelectField_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
import { PromptEditorField as PromptEditorField_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
import { PromptEditorField as PromptEditorField_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
import { default as default_b16af798585cda7cfed92f7b1dd0c707 } from '@/components/admin/shims/ShimContentField'
|
import { default as default_b16af798585cda7cfed92f7b1dd0c707 } from '@/components/admin/shims/ShimContentField'
|
||||||
|
import { default as default_a6bf792526e9f56202161f190c57e1c6 } from '@/components/admin/tech-tree/TechTreeNav'
|
||||||
import { default as default_2f6c866ab4d4d51b3789f090b84b85c7 } from '@/components/static/VersionOverlay'
|
import { default as default_2f6c866ab4d4d51b3789f090b84b85c7 } from '@/components/static/VersionOverlay'
|
||||||
import { default as default_508743824696caae2894ebf34c2e68bb } from '@/components/graphics/AppIcon'
|
import { default as default_508743824696caae2894ebf34c2e68bb } from '@/components/graphics/AppIcon'
|
||||||
import { default as default_e752fee78378294ff03f91dba0a75a04 } from '@/components/graphics/AppLogo'
|
import { default as default_e752fee78378294ff03f91dba0a75a04 } from '@/components/graphics/AppLogo'
|
||||||
import { default as default_05548cb43495e62d80b80ec4d9f0f7e9 } from '@/components/static/BeforeDashboardText'
|
import { default as default_05548cb43495e62d80b80ec4d9f0f7e9 } from '@/components/static/BeforeDashboardText'
|
||||||
import { default as default_459d6b6f5ead73c736013da63a5ace3f } from '@/components/static/GoToFrontendLink'
|
import { default as default_459d6b6f5ead73c736013da63a5ace3f } from '@/components/static/GoToFrontendLink'
|
||||||
import { InstructionsProvider as InstructionsProvider_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
import { InstructionsProvider as InstructionsProvider_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
|
import { default as default_2b34d0df0cf91f4442ca2cd62ae7cd94 } from '@/components/admin/tech-tree/TechTreeView'
|
||||||
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||||
|
|
||||||
/** @type import('payload').ImportMap */
|
/** @type import('payload').ImportMap */
|
||||||
|
|
@ -84,11 +86,13 @@ export const importMap = {
|
||||||
"@ai-stack/payloadcms/client#SelectField": SelectField_4490b89d4413c1ffaecdacfe72efaf73,
|
"@ai-stack/payloadcms/client#SelectField": SelectField_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
"@ai-stack/payloadcms/client#PromptEditorField": PromptEditorField_4490b89d4413c1ffaecdacfe72efaf73,
|
"@ai-stack/payloadcms/client#PromptEditorField": PromptEditorField_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
"@/components/admin/shims/ShimContentField#default": default_b16af798585cda7cfed92f7b1dd0c707,
|
"@/components/admin/shims/ShimContentField#default": default_b16af798585cda7cfed92f7b1dd0c707,
|
||||||
|
"@/components/admin/tech-tree/TechTreeNav#default": default_a6bf792526e9f56202161f190c57e1c6,
|
||||||
"@/components/static/VersionOverlay#default": default_2f6c866ab4d4d51b3789f090b84b85c7,
|
"@/components/static/VersionOverlay#default": default_2f6c866ab4d4d51b3789f090b84b85c7,
|
||||||
"@/components/graphics/AppIcon#default": default_508743824696caae2894ebf34c2e68bb,
|
"@/components/graphics/AppIcon#default": default_508743824696caae2894ebf34c2e68bb,
|
||||||
"@/components/graphics/AppLogo#default": default_e752fee78378294ff03f91dba0a75a04,
|
"@/components/graphics/AppLogo#default": default_e752fee78378294ff03f91dba0a75a04,
|
||||||
"@/components/static/BeforeDashboardText#default": default_05548cb43495e62d80b80ec4d9f0f7e9,
|
"@/components/static/BeforeDashboardText#default": default_05548cb43495e62d80b80ec4d9f0f7e9,
|
||||||
"@/components/static/GoToFrontendLink#default": default_459d6b6f5ead73c736013da63a5ace3f,
|
"@/components/static/GoToFrontendLink#default": default_459d6b6f5ead73c736013da63a5ace3f,
|
||||||
"@ai-stack/payloadcms/client#InstructionsProvider": InstructionsProvider_4490b89d4413c1ffaecdacfe72efaf73,
|
"@ai-stack/payloadcms/client#InstructionsProvider": InstructionsProvider_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
|
"@/components/admin/tech-tree/TechTreeView#default": default_2b34d0df0cf91f4442ca2cd62ae7cd94,
|
||||||
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
58
src/collections/intelligence/TechCategories.ts
Normal file
58
src/collections/intelligence/TechCategories.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { CollectionConfig } from "payload";
|
||||||
|
|
||||||
|
import { requireIntelligencePermission } from "@/utils/access-control/divisionAccess";
|
||||||
|
|
||||||
|
/** Validates an optional hex color like the map feature styling (`src/lib/map/style.ts`). */
|
||||||
|
function validateHexColor(value: unknown): true | string {
|
||||||
|
if (!value) return true;
|
||||||
|
const str = String(value).trim();
|
||||||
|
if (!str) return true;
|
||||||
|
return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(str)
|
||||||
|
? true
|
||||||
|
: "Enter a hex color like #22d3ee, or leave empty for the default.";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TechCategories: CollectionConfig = {
|
||||||
|
slug: "tech-categories",
|
||||||
|
admin: {
|
||||||
|
group: "Intelligence",
|
||||||
|
useAsTitle: "name",
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
create: requireIntelligencePermission("tech-categories:create"),
|
||||||
|
update: requireIntelligencePermission("tech-categories:update"),
|
||||||
|
delete: requireIntelligencePermission("tech-categories:delete"),
|
||||||
|
read: requireIntelligencePermission("tech-categories:read"),
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
type: "text",
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Category label shown as a column band on the Tech Tree (e.g. Tier 1, Tier 2).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "position",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
min: 1,
|
||||||
|
defaultValue: 1,
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Timeline position: lower numbers sit further left on the Tech Tree. Categories with the same position are ordered alphabetically.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "color",
|
||||||
|
type: "text",
|
||||||
|
validate: validateHexColor,
|
||||||
|
admin: {
|
||||||
|
description: "Optional hex accent color for the band on the Tech Tree (e.g. #22d3ee).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -26,6 +26,34 @@ export const Technologies: CollectionConfig = {
|
||||||
type: "text",
|
type: "text",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "category",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "tech-categories",
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Tech Tree column this technology belongs to. Uncategorized technologies are auto-placed on the Tech Tree by prerequisite depth.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "treePosition",
|
||||||
|
type: "group",
|
||||||
|
label: "Tech Tree Position",
|
||||||
|
admin: {
|
||||||
|
description:
|
||||||
|
"Manual position on the Tech Tree canvas (set by dragging a node). Leave empty to use the automatic layout.",
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "x",
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "y",
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "approvalStatus",
|
name: "approvalStatus",
|
||||||
type: "select",
|
type: "select",
|
||||||
|
|
|
||||||
35267
src/migrations/20260930_230747_add_tech_categories.json
Normal file
35267
src/migrations/20260930_230747_add_tech_categories.json
Normal file
File diff suppressed because it is too large
Load diff
54
src/migrations/20260930_230747_add_tech_categories.ts
Normal file
54
src/migrations/20260930_230747_add_tech_categories.ts
Normal file
File diff suppressed because one or more lines are too long
35279
src/migrations/20261001_000320_add_tech_tree_position.json
Normal file
35279
src/migrations/20261001_000320_add_tech_tree_position.json
Normal file
File diff suppressed because it is too large
Load diff
13
src/migrations/20261001_000320_add_tech_tree_position.ts
Normal file
13
src/migrations/20261001_000320_add_tech_tree_position.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "technologies" ADD COLUMN "tree_position_x" numeric;
|
||||||
|
ALTER TABLE "technologies" ADD COLUMN "tree_position_y" numeric;`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "technologies" DROP COLUMN "tree_position_x";
|
||||||
|
ALTER TABLE "technologies" DROP COLUMN "tree_position_y";`)
|
||||||
|
}
|
||||||
|
|
@ -95,6 +95,8 @@ import * as migration_20260926_030859_add_mission_role_description from './20260
|
||||||
import * as migration_20260926_044319_add_mission_role_description_richtext from './20260926_044319_add_mission_role_description_richtext';
|
import * as migration_20260926_044319_add_mission_role_description_richtext from './20260926_044319_add_mission_role_description_richtext';
|
||||||
import * as migration_20260928_021138_change_ai_model from './20260928_021138_change_ai_model';
|
import * as migration_20260928_021138_change_ai_model from './20260928_021138_change_ai_model';
|
||||||
import * as migration_20260930_162200_add_documentation_wiki_category from './20260930_162200_add_documentation_wiki_category';
|
import * as migration_20260930_162200_add_documentation_wiki_category from './20260930_162200_add_documentation_wiki_category';
|
||||||
|
import * as migration_20260930_230747_add_tech_categories from './20260930_230747_add_tech_categories';
|
||||||
|
import * as migration_20261001_000320_add_tech_tree_position from './20261001_000320_add_tech_tree_position';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
{
|
{
|
||||||
|
|
@ -575,11 +577,21 @@ export const migrations = [
|
||||||
{
|
{
|
||||||
up: migration_20260928_021138_change_ai_model.up,
|
up: migration_20260928_021138_change_ai_model.up,
|
||||||
down: migration_20260928_021138_change_ai_model.down,
|
down: migration_20260928_021138_change_ai_model.down,
|
||||||
name: '20260928_021138_change_ai_model'
|
name: '20260928_021138_change_ai_model',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
up: migration_20260930_162200_add_documentation_wiki_category.up,
|
up: migration_20260930_162200_add_documentation_wiki_category.up,
|
||||||
down: migration_20260930_162200_add_documentation_wiki_category.down,
|
down: migration_20260930_162200_add_documentation_wiki_category.down,
|
||||||
name: '20260930_162200_add_documentation_wiki_category'
|
name: '20260930_162200_add_documentation_wiki_category',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260930_230747_add_tech_categories.up,
|
||||||
|
down: migration_20260930_230747_add_tech_categories.down,
|
||||||
|
name: '20260930_230747_add_tech_categories',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
up: migration_20261001_000320_add_tech_tree_position.up,
|
||||||
|
down: migration_20261001_000320_add_tech_tree_position.down,
|
||||||
|
name: '20261001_000320_add_tech_tree_position'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,11 @@ export const enum_roles_permissions = pgEnum("enum_roles_permissions", [
|
||||||
"technologies:update",
|
"technologies:update",
|
||||||
"technologies:delete",
|
"technologies:delete",
|
||||||
"admin:technologies:manage",
|
"admin:technologies:manage",
|
||||||
|
"tech-categories:create",
|
||||||
|
"tech-categories:read",
|
||||||
|
"tech-categories:update",
|
||||||
|
"tech-categories:delete",
|
||||||
|
"admin:tech-categories:manage",
|
||||||
"assets:create",
|
"assets:create",
|
||||||
"assets:read",
|
"assets:read",
|
||||||
"assets:update",
|
"assets:update",
|
||||||
|
|
@ -3317,6 +3322,11 @@ export const technologies = pgTable(
|
||||||
{
|
{
|
||||||
id: serial("id").primaryKey(),
|
id: serial("id").primaryKey(),
|
||||||
name: varchar("name").notNull(),
|
name: varchar("name").notNull(),
|
||||||
|
category: integer("category_id").references(() => tech_categories.id, {
|
||||||
|
onDelete: "set null",
|
||||||
|
}),
|
||||||
|
treePosition_x: numeric("tree_position_x", { mode: "number" }),
|
||||||
|
treePosition_y: numeric("tree_position_y", { mode: "number" }),
|
||||||
approvalStatus: enum_technologies_approval_status("approval_status")
|
approvalStatus: enum_technologies_approval_status("approval_status")
|
||||||
.notNull()
|
.notNull()
|
||||||
.default("in_progress"),
|
.default("in_progress"),
|
||||||
|
|
@ -3339,6 +3349,7 @@ export const technologies = pgTable(
|
||||||
.notNull(),
|
.notNull(),
|
||||||
},
|
},
|
||||||
(columns) => [
|
(columns) => [
|
||||||
|
index("technologies_category_idx").on(columns.category),
|
||||||
index("technologies_updated_at_idx").on(columns.updatedAt),
|
index("technologies_updated_at_idx").on(columns.updatedAt),
|
||||||
index("technologies_created_at_idx").on(columns.createdAt),
|
index("technologies_created_at_idx").on(columns.createdAt),
|
||||||
],
|
],
|
||||||
|
|
@ -3371,6 +3382,27 @@ export const technologies_rels = pgTable(
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const tech_categories = pgTable(
|
||||||
|
"tech_categories",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
position: numeric("position", { mode: "number" }).notNull().default(1),
|
||||||
|
color: varchar("color"),
|
||||||
|
updatedAt: timestamp("updated_at", { mode: "string", withTimezone: true, precision: 3 })
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", { mode: "string", withTimezone: true, precision: 3 })
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
uniqueIndex("tech_categories_name_idx").on(columns.name),
|
||||||
|
index("tech_categories_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("tech_categories_created_at_idx").on(columns.createdAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
export const assets_skin_configuration_applies_to_asset_types = pgTable(
|
export const assets_skin_configuration_applies_to_asset_types = pgTable(
|
||||||
"assets_skin_configuration_applies_to_asset_types",
|
"assets_skin_configuration_applies_to_asset_types",
|
||||||
{
|
{
|
||||||
|
|
@ -6856,6 +6888,10 @@ export const payload_mcp_api_keys = pgTable(
|
||||||
technologies_create: boolean("technologies_create").default(false),
|
technologies_create: boolean("technologies_create").default(false),
|
||||||
technologies_update: boolean("technologies_update").default(false),
|
technologies_update: boolean("technologies_update").default(false),
|
||||||
technologies_delete: boolean("technologies_delete").default(false),
|
technologies_delete: boolean("technologies_delete").default(false),
|
||||||
|
techCategories_find: boolean("tech_categories_find").default(false),
|
||||||
|
techCategories_create: boolean("tech_categories_create").default(false),
|
||||||
|
techCategories_update: boolean("tech_categories_update").default(false),
|
||||||
|
techCategories_delete: boolean("tech_categories_delete").default(false),
|
||||||
assets_find: boolean("assets_find").default(false),
|
assets_find: boolean("assets_find").default(false),
|
||||||
assets_create: boolean("assets_create").default(false),
|
assets_create: boolean("assets_create").default(false),
|
||||||
assets_update: boolean("assets_update").default(false),
|
assets_update: boolean("assets_update").default(false),
|
||||||
|
|
@ -7128,6 +7164,7 @@ export const payload_locked_documents_rels = pgTable(
|
||||||
campaignsID: integer("campaigns_id"),
|
campaignsID: integer("campaigns_id"),
|
||||||
factionsID: integer("factions_id"),
|
factionsID: integer("factions_id"),
|
||||||
technologiesID: integer("technologies_id"),
|
technologiesID: integer("technologies_id"),
|
||||||
|
"tech-categoriesID": integer("tech_categories_id"),
|
||||||
assetsID: integer("assets_id"),
|
assetsID: integer("assets_id"),
|
||||||
resourcesID: integer("resources_id"),
|
resourcesID: integer("resources_id"),
|
||||||
vehiclesID: integer("vehicles_id"),
|
vehiclesID: integer("vehicles_id"),
|
||||||
|
|
@ -7226,6 +7263,7 @@ export const payload_locked_documents_rels = pgTable(
|
||||||
index("payload_locked_documents_rels_campaigns_id_idx").on(columns.campaignsID),
|
index("payload_locked_documents_rels_campaigns_id_idx").on(columns.campaignsID),
|
||||||
index("payload_locked_documents_rels_factions_id_idx").on(columns.factionsID),
|
index("payload_locked_documents_rels_factions_id_idx").on(columns.factionsID),
|
||||||
index("payload_locked_documents_rels_technologies_id_idx").on(columns.technologiesID),
|
index("payload_locked_documents_rels_technologies_id_idx").on(columns.technologiesID),
|
||||||
|
index("payload_locked_documents_rels_tech_categories_id_idx").on(columns["tech-categoriesID"]),
|
||||||
index("payload_locked_documents_rels_assets_id_idx").on(columns.assetsID),
|
index("payload_locked_documents_rels_assets_id_idx").on(columns.assetsID),
|
||||||
index("payload_locked_documents_rels_resources_id_idx").on(columns.resourcesID),
|
index("payload_locked_documents_rels_resources_id_idx").on(columns.resourcesID),
|
||||||
index("payload_locked_documents_rels_vehicles_id_idx").on(columns.vehiclesID),
|
index("payload_locked_documents_rels_vehicles_id_idx").on(columns.vehiclesID),
|
||||||
|
|
@ -7454,6 +7492,11 @@ export const payload_locked_documents_rels = pgTable(
|
||||||
foreignColumns: [technologies.id],
|
foreignColumns: [technologies.id],
|
||||||
name: "payload_locked_documents_rels_technologies_fk",
|
name: "payload_locked_documents_rels_technologies_fk",
|
||||||
}).onDelete("cascade"),
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["tech-categoriesID"]],
|
||||||
|
foreignColumns: [tech_categories.id],
|
||||||
|
name: "payload_locked_documents_rels_tech_categories_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
foreignKey({
|
foreignKey({
|
||||||
columns: [columns["assetsID"]],
|
columns: [columns["assetsID"]],
|
||||||
foreignColumns: [assets.id],
|
foreignColumns: [assets.id],
|
||||||
|
|
@ -8870,7 +8913,12 @@ export const relations_technologies_rels = relations(technologies_rels, ({ one }
|
||||||
relationName: "technologies",
|
relationName: "technologies",
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
export const relations_technologies = relations(technologies, ({ many }) => ({
|
export const relations_technologies = relations(technologies, ({ one, many }) => ({
|
||||||
|
category: one(tech_categories, {
|
||||||
|
fields: [technologies.category],
|
||||||
|
references: [tech_categories.id],
|
||||||
|
relationName: "category",
|
||||||
|
}),
|
||||||
requirements_prerequisiteTechnologies: many(technologies_requirements_prerequisite_technologies, {
|
requirements_prerequisiteTechnologies: many(technologies_requirements_prerequisite_technologies, {
|
||||||
relationName: "requirements_prerequisiteTechnologies",
|
relationName: "requirements_prerequisiteTechnologies",
|
||||||
}),
|
}),
|
||||||
|
|
@ -8881,6 +8929,7 @@ export const relations_technologies = relations(technologies, ({ many }) => ({
|
||||||
relationName: "_rels",
|
relationName: "_rels",
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
export const relations_tech_categories = relations(tech_categories, () => ({}));
|
||||||
export const relations_assets_skin_configuration_applies_to_asset_types = relations(
|
export const relations_assets_skin_configuration_applies_to_asset_types = relations(
|
||||||
assets_skin_configuration_applies_to_asset_types,
|
assets_skin_configuration_applies_to_asset_types,
|
||||||
({ one }) => ({
|
({ one }) => ({
|
||||||
|
|
@ -10516,6 +10565,11 @@ export const relations_payload_locked_documents_rels = relations(
|
||||||
references: [technologies.id],
|
references: [technologies.id],
|
||||||
relationName: "technologies",
|
relationName: "technologies",
|
||||||
}),
|
}),
|
||||||
|
"tech-categoriesID": one(tech_categories, {
|
||||||
|
fields: [payload_locked_documents_rels["tech-categoriesID"]],
|
||||||
|
references: [tech_categories.id],
|
||||||
|
relationName: "tech-categories",
|
||||||
|
}),
|
||||||
assetsID: one(assets, {
|
assetsID: one(assets, {
|
||||||
fields: [payload_locked_documents_rels.assetsID],
|
fields: [payload_locked_documents_rels.assetsID],
|
||||||
references: [assets.id],
|
references: [assets.id],
|
||||||
|
|
@ -11075,6 +11129,7 @@ type DatabaseSchema = {
|
||||||
technologies_research_costs_resources: typeof technologies_research_costs_resources;
|
technologies_research_costs_resources: typeof technologies_research_costs_resources;
|
||||||
technologies: typeof technologies;
|
technologies: typeof technologies;
|
||||||
technologies_rels: typeof technologies_rels;
|
technologies_rels: typeof technologies_rels;
|
||||||
|
tech_categories: typeof tech_categories;
|
||||||
assets_skin_configuration_applies_to_asset_types: typeof assets_skin_configuration_applies_to_asset_types;
|
assets_skin_configuration_applies_to_asset_types: typeof assets_skin_configuration_applies_to_asset_types;
|
||||||
assets_crafting_crafting_data_inputs: typeof assets_crafting_crafting_data_inputs;
|
assets_crafting_crafting_data_inputs: typeof assets_crafting_crafting_data_inputs;
|
||||||
assets_crafting_crafting_data_byproducts: typeof assets_crafting_crafting_data_byproducts;
|
assets_crafting_crafting_data_byproducts: typeof assets_crafting_crafting_data_byproducts;
|
||||||
|
|
@ -11264,6 +11319,7 @@ type DatabaseSchema = {
|
||||||
relations_technologies_research_costs_resources: typeof relations_technologies_research_costs_resources;
|
relations_technologies_research_costs_resources: typeof relations_technologies_research_costs_resources;
|
||||||
relations_technologies_rels: typeof relations_technologies_rels;
|
relations_technologies_rels: typeof relations_technologies_rels;
|
||||||
relations_technologies: typeof relations_technologies;
|
relations_technologies: typeof relations_technologies;
|
||||||
|
relations_tech_categories: typeof relations_tech_categories;
|
||||||
relations_assets_skin_configuration_applies_to_asset_types: typeof relations_assets_skin_configuration_applies_to_asset_types;
|
relations_assets_skin_configuration_applies_to_asset_types: typeof relations_assets_skin_configuration_applies_to_asset_types;
|
||||||
relations_assets_crafting_crafting_data_inputs: typeof relations_assets_crafting_crafting_data_inputs;
|
relations_assets_crafting_crafting_data_inputs: typeof relations_assets_crafting_crafting_data_inputs;
|
||||||
relations_assets_crafting_crafting_data_byproducts: typeof relations_assets_crafting_crafting_data_byproducts;
|
relations_assets_crafting_crafting_data_byproducts: typeof relations_assets_crafting_crafting_data_byproducts;
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ export interface Config {
|
||||||
campaigns: Campaign;
|
campaigns: Campaign;
|
||||||
factions: Faction;
|
factions: Faction;
|
||||||
technologies: Technology;
|
technologies: Technology;
|
||||||
|
'tech-categories': TechCategory;
|
||||||
assets: Asset;
|
assets: Asset;
|
||||||
resources: Resource;
|
resources: Resource;
|
||||||
vehicles: Vehicle;
|
vehicles: Vehicle;
|
||||||
|
|
@ -188,6 +189,7 @@ export interface Config {
|
||||||
campaigns: CampaignsSelect<false> | CampaignsSelect<true>;
|
campaigns: CampaignsSelect<false> | CampaignsSelect<true>;
|
||||||
factions: FactionsSelect<false> | FactionsSelect<true>;
|
factions: FactionsSelect<false> | FactionsSelect<true>;
|
||||||
technologies: TechnologiesSelect<false> | TechnologiesSelect<true>;
|
technologies: TechnologiesSelect<false> | TechnologiesSelect<true>;
|
||||||
|
'tech-categories': TechCategoriesSelect<false> | TechCategoriesSelect<true>;
|
||||||
assets: AssetsSelect<false> | AssetsSelect<true>;
|
assets: AssetsSelect<false> | AssetsSelect<true>;
|
||||||
resources: ResourcesSelect<false> | ResourcesSelect<true>;
|
resources: ResourcesSelect<false> | ResourcesSelect<true>;
|
||||||
vehicles: VehiclesSelect<false> | VehiclesSelect<true>;
|
vehicles: VehiclesSelect<false> | VehiclesSelect<true>;
|
||||||
|
|
@ -656,6 +658,11 @@ export interface Role {
|
||||||
| 'technologies:update'
|
| 'technologies:update'
|
||||||
| 'technologies:delete'
|
| 'technologies:delete'
|
||||||
| 'admin:technologies:manage'
|
| 'admin:technologies:manage'
|
||||||
|
| 'tech-categories:create'
|
||||||
|
| 'tech-categories:read'
|
||||||
|
| 'tech-categories:update'
|
||||||
|
| 'tech-categories:delete'
|
||||||
|
| 'admin:tech-categories:manage'
|
||||||
| 'assets:create'
|
| 'assets:create'
|
||||||
| 'assets:read'
|
| 'assets:read'
|
||||||
| 'assets:update'
|
| 'assets:update'
|
||||||
|
|
@ -1538,6 +1545,17 @@ export interface Resource {
|
||||||
export interface Technology {
|
export interface Technology {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
/**
|
||||||
|
* Tech Tree column this technology belongs to. Uncategorized technologies are auto-placed on the Tech Tree by prerequisite depth.
|
||||||
|
*/
|
||||||
|
category?: (number | null) | TechCategory;
|
||||||
|
/**
|
||||||
|
* Manual position on the Tech Tree canvas (set by dragging a node). Leave empty to use the automatic layout.
|
||||||
|
*/
|
||||||
|
treePosition?: {
|
||||||
|
x?: number | null;
|
||||||
|
y?: number | null;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Mark whether this technology is ready for developer approval and implementation into the live environment.
|
* Mark whether this technology is ready for developer approval and implementation into the live environment.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1598,6 +1616,27 @@ export interface Technology {
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "tech-categories".
|
||||||
|
*/
|
||||||
|
export interface TechCategory {
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* Category label shown as a column band on the Tech Tree (e.g. Tier 1, Tier 2).
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Timeline position: lower numbers sit further left on the Tech Tree. Categories with the same position are ordered alphabetically.
|
||||||
|
*/
|
||||||
|
position: number;
|
||||||
|
/**
|
||||||
|
* Optional hex accent color for the band on the Tech Tree (e.g. #22d3ee).
|
||||||
|
*/
|
||||||
|
color?: string | null;
|
||||||
|
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` "labor-classifications".
|
* via the `definition` "labor-classifications".
|
||||||
|
|
@ -5732,6 +5771,24 @@ export interface PayloadMcpApiKey {
|
||||||
*/
|
*/
|
||||||
delete?: boolean | null;
|
delete?: boolean | null;
|
||||||
};
|
};
|
||||||
|
techCategories?: {
|
||||||
|
/**
|
||||||
|
* Allow clients to find tech-categories.
|
||||||
|
*/
|
||||||
|
find?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Allow clients to create tech-categories.
|
||||||
|
*/
|
||||||
|
create?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Allow clients to update tech-categories.
|
||||||
|
*/
|
||||||
|
update?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Allow clients to delete tech-categories.
|
||||||
|
*/
|
||||||
|
delete?: boolean | null;
|
||||||
|
};
|
||||||
assets?: {
|
assets?: {
|
||||||
/**
|
/**
|
||||||
* Allow clients to find assets.
|
* Allow clients to find assets.
|
||||||
|
|
@ -6735,6 +6792,10 @@ export interface PayloadLockedDocument {
|
||||||
relationTo: 'technologies';
|
relationTo: 'technologies';
|
||||||
value: number | Technology;
|
value: number | Technology;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'tech-categories';
|
||||||
|
value: number | TechCategory;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'assets';
|
relationTo: 'assets';
|
||||||
value: number | Asset;
|
value: number | Asset;
|
||||||
|
|
@ -7836,6 +7897,13 @@ export interface FactionsSelect<T extends boolean = true> {
|
||||||
*/
|
*/
|
||||||
export interface TechnologiesSelect<T extends boolean = true> {
|
export interface TechnologiesSelect<T extends boolean = true> {
|
||||||
name?: T;
|
name?: T;
|
||||||
|
category?: T;
|
||||||
|
treePosition?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
x?: T;
|
||||||
|
y?: T;
|
||||||
|
};
|
||||||
approvalStatus?: T;
|
approvalStatus?: T;
|
||||||
type?: T;
|
type?: T;
|
||||||
summary?: T;
|
summary?: T;
|
||||||
|
|
@ -7875,6 +7943,17 @@ export interface TechnologiesSelect<T extends boolean = true> {
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "tech-categories_select".
|
||||||
|
*/
|
||||||
|
export interface TechCategoriesSelect<T extends boolean = true> {
|
||||||
|
name?: T;
|
||||||
|
position?: T;
|
||||||
|
color?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "assets_select".
|
* via the `definition` "assets_select".
|
||||||
|
|
@ -9513,6 +9592,14 @@ export interface PayloadMcpApiKeysSelect<T extends boolean = true> {
|
||||||
update?: T;
|
update?: T;
|
||||||
delete?: T;
|
delete?: T;
|
||||||
};
|
};
|
||||||
|
techCategories?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
find?: T;
|
||||||
|
create?: T;
|
||||||
|
update?: T;
|
||||||
|
delete?: T;
|
||||||
|
};
|
||||||
assets?:
|
assets?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,16 @@ export const PERMISSION_GROUPS: PermissionGroup[] = [
|
||||||
{ value: "admin:technologies:manage", label: "Manage Admin Page" },
|
{ value: "admin:technologies:manage", label: "Manage Admin Page" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
group: "Tech Categories",
|
||||||
|
permissions: [
|
||||||
|
{ value: "tech-categories:create", label: "Create" },
|
||||||
|
{ value: "tech-categories:read", label: "Read" },
|
||||||
|
{ value: "tech-categories:update", label: "Update" },
|
||||||
|
{ value: "tech-categories:delete", label: "Delete" },
|
||||||
|
{ value: "admin:tech-categories:manage", label: "Manage Admin Page" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
// ---- Logistics -----------------------------------------------------------
|
// ---- Logistics -----------------------------------------------------------
|
||||||
{
|
{
|
||||||
|
|
@ -814,6 +824,11 @@ export const ALL_PERMISSION_VALUES = PERMISSION_GROUPS.flatMap((g) =>
|
||||||
"technologies:update",
|
"technologies:update",
|
||||||
"technologies:delete",
|
"technologies:delete",
|
||||||
"admin:technologies:manage",
|
"admin:technologies:manage",
|
||||||
|
"tech-categories:create",
|
||||||
|
"tech-categories:read",
|
||||||
|
"tech-categories:update",
|
||||||
|
"tech-categories:delete",
|
||||||
|
"admin:tech-categories:manage",
|
||||||
"assets:create",
|
"assets:create",
|
||||||
"assets:read",
|
"assets:read",
|
||||||
"assets:update",
|
"assets:update",
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ const DIVISION_ADMIN_QUALIFICATIONS: Record<
|
||||||
(payload: Payload, user: { id: number | string }) => Promise<boolean>
|
(payload: Payload, user: { id: number | string }) => Promise<boolean>
|
||||||
> = {
|
> = {
|
||||||
technologies: hasIntelligenceQualification,
|
technologies: hasIntelligenceQualification,
|
||||||
|
"tech-categories": hasIntelligenceQualification,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue