feat: member portal Sprint 2 — digest history page and restore Digest/DigestConfig schema models

- Restore Digest + DigestConfig models to schema.prisma (wiped by prior git reset)
- Add Tenant.digestConfig / Tenant.digests relations
- GET /my/digest API endpoint (last 20 digests, truncated summary)
- BFF route /api/my/digest
- /my/digest page: digest cards with date, message count, summary preview
- Add Digest nav item to MemberShell sidebar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 16:04:31 +05:30
parent a685f8b4d5
commit bc9fe7c145
6 changed files with 132 additions and 0 deletions
+33
View File
@@ -88,6 +88,8 @@ model Tenant {
auditEvents AuditEvent[]
rules TenantRule[]
groupAccesses GroupAccess[]
digestConfig DigestConfig?
digests Digest[]
}
enum AdminRole {
@@ -495,3 +497,34 @@ model TenantRule {
@@index([tenantId, isActive])
@@index([tenantId, matchType])
}
// ============================================================================
// Digest
// ============================================================================
model DigestConfig {
id String @id @default(cuid())
tenantId String @unique
tenant Tenant @relation(fields: [tenantId], references: [id])
targetGroupJid String
targetAccountId String
scheduleHour Int @default(20)
scheduleMinute Int @default(0)
isActive Boolean @default(true)
lastSentAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Digest {
id String @id @default(cuid())
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id])
content String
messageIds String[]
digestDate DateTime
sentAt DateTime @default(now())
@@unique([tenantId, digestDate])
@@index([tenantId])
}
+5
View File
@@ -29,6 +29,11 @@ export class MyController {
return this.service.getDashboard(member.sub, member.tenantId);
}
@Get('digest')
digests(@CurrentMember() member: MemberJwtPayload) {
return this.service.getDigests(member.tenantId);
}
@Get('profile')
profile(@CurrentMember() member: MemberJwtPayload) {
return this.service.getProfile(member.sub, member.tenantId);
+16
View File
@@ -26,6 +26,22 @@ export class MyService {
};
}
async getDigests(tenantId: string) {
const digests = await this.prisma.digest.findMany({
where: { tenantId },
orderBy: { digestDate: 'desc' },
take: 20,
select: { id: true, digestDate: true, content: true, messageIds: true, sentAt: true },
});
return digests.map((d) => ({
id: d.id,
digestDate: d.digestDate.toISOString(),
summary: d.content.slice(0, 300),
messageCount: (d.messageIds as string[]).length,
sentAt: d.sentAt.toISOString(),
}));
}
async getDashboard(userId: string, tenantId: string) {
const user = await this.prisma.towerUser.findFirst({ where: { id: userId, tenantId } });
if (!user) throw new NotFoundException('User not found');