feat: add admin ideas management page with private notes

Adds a dedicated super admin page for managing idea submissions across
all tenants. Includes status summary cards, filterable/searchable table,
detail modal with status updates, and private admin notes for internal
tracking (sprint refs, thoughts, follow-ups). Notes are not visible to
tenant users.

- Database: admin_note column on shared.ideas (019 migration)
- Backend: PUT /admin/ideas/:id/note endpoint
- Frontend: AdminIdeasPage with table, filters, detail modal
- Sidebar: "Idea Submissions" nav link in admin sections
- Routing: /admin/ideas route under SuperAdminRoute guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
JoeBot
2026-04-02 17:35:30 -04:00
parent 140cd7acb7
commit d430b96b51
7 changed files with 352 additions and 0 deletions

View File

@@ -218,6 +218,17 @@ export class AdminController {
return { success: true, idea };
}
@Put('ideas/:id/note')
async updateIdeaNote(
@Req() req: any,
@Param('id') id: string,
@Body() body: { adminNote: string },
) {
await this.requireSuperadmin(req);
const idea = await this.ideasService.updateNote(id, body.adminNote);
return { success: true, idea };
}
@Put('organizations/:id/settings')
async updateOrgSettings(
@Req() req: any,

View File

@@ -30,6 +30,9 @@ export class Idea {
@Column({ length: 20, default: 'new' })
status: string;
@Column({ name: 'admin_note', type: 'text', nullable: true })
adminNote: string;
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
createdAt: Date;

View File

@@ -50,6 +50,7 @@ export class IdeasService {
'idea.description AS description',
'idea.status AS status',
'idea.createdAt AS "createdAt"',
'idea.adminNote AS "adminNote"',
'org.id AS "orgId"',
'org.name AS "orgName"',
'user.id AS "userId"',
@@ -75,4 +76,14 @@ export class IdeasService {
idea.status = status;
return this.ideasRepository.save(idea);
}
async updateNote(id: string, adminNote: string): Promise<Idea> {
const idea = await this.ideasRepository.findOne({ where: { id } });
if (!idea) {
throw new NotFoundException('Idea not found');
}
idea.adminNote = adminNote;
return this.ideasRepository.save(idea);
}
}