feat: Admin SynthQuest level management + user dropdown with admin access
SynthQuest admin: - New "🎮 SynthQuest" section in admin sidebar - List custom levels with world, ID, title, patch status - Create new level: world selector, title, subtitle, description, concept (hint), available modules (tag input), boss flag, sort order - Edit existing levels inline - Import patch base from sandbox JSON export (📥 button per level) - Delete levels with confirmation Server: - custom_levels table (PostgreSQL) - CRUD API at /api/v1/admin/levels - POST /:id/import-patch to import sandbox JSON as preplaced modules Admin access: - User badge is now a hover dropdown with "🛠 Admin" + "Cerrar sesion" - Admin visible in Sandbox toolbar, Workshop nav, and user dropdown - onSwitchToAdmin passed through navigation chain Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,23 @@ export const likes = pgTable('likes', {
|
||||
pk: primaryKey({ columns: [table.userId, table.patchId] }),
|
||||
}));
|
||||
|
||||
export const customLevels = pgTable('custom_levels', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
worldId: varchar('world_id', { length: 20 }).notNull(),
|
||||
levelId: varchar('level_id', { length: 50 }).unique().notNull(),
|
||||
title: varchar('title', { length: 200 }).notNull(),
|
||||
subtitle: varchar('subtitle', { length: 200 }),
|
||||
description: text('description'),
|
||||
concept: text('concept'),
|
||||
availableModules: text('available_modules').array(),
|
||||
preplacedData: jsonb('preplaced_data'), // imported from sandbox export
|
||||
targetData: jsonb('target_data'),
|
||||
sortOrder: integer('sort_order').default(0),
|
||||
isBoss: boolean('is_boss').default(false),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const refreshTokens = pgTable('refresh_tokens', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
||||
|
||||
Reference in New Issue
Block a user