Commit 7ee67ed9 authored by Jordan Grossemy's avatar Jordan Grossemy
Browse files

API injects : rejouer et modifier en cours d'exercice

- POST /injects/:id/replay : re-diffuse un inject déjà envoyé.
- PATCH /injects/:id : modifie un inject non envoyé (contenu, canal, horaire).

Co-Authored-By: Claude (RCA)
parent ca1cc06d
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ import {
  HttpCode,
  HttpStatus,
  Param,
  Patch,
  Post,
  UseGuards,
} from '@nestjs/common';
@@ -59,6 +60,18 @@ export class InjectsController {
    return this.injects.triggerManual(me.tenantId, id, me.userId);
  }

  @Post('injects/:id/replay')
  @Roles('TENANT_ADMIN', 'DESIGNER', 'ANIMATOR')
  replay(@CurrentUser() me: AuthUser, @Param('id') id: string) {
    return this.injects.replay(me.tenantId, id, me.userId);
  }

  @Patch('injects/:id')
  @Roles('TENANT_ADMIN', 'DESIGNER', 'ANIMATOR')
  update(@CurrentUser() me: AuthUser, @Param('id') id: string, @Body() dto: CreateInjectDto) {
    return this.injects.update(me.tenantId, id, dto);
  }

  @Post('injects/:id/reschedule')
  @Roles('TENANT_ADMIN', 'DESIGNER', 'ANIMATOR')
  reschedule(@CurrentUser() me: AuthUser, @Param('id') id: string, @Body() dto: RescheduleDto) {
+35 −3
Original line number Diff line number Diff line
@@ -182,15 +182,47 @@ export class InjectsService {
    return this.send(tenantId, id, userId);
  }

  /** Rejoue un inject deja envoye : re-diffuse le contenu (nouveaux messages/posts). */
  async replay(tenantId: string, id: string, userId: string) {
    return this.send(tenantId, id, userId, { replay: true });
  }

  /** Modifie un inject non encore envoye (contenu, canal, horaire) ; le repasse en attente. */
  async update(tenantId: string, id: string, dto: CreateInjectDto) {
    const inject = await this.prisma.forTenant(tenantId).inject.findFirst({ where: { id } });
    if (!inject) throw new NotFoundException('Inject introuvable');
    if (inject.status === 'SENT') {
      throw new BadRequestException('Impossible de modifier un inject deja envoye (utilisez « Rejouer »)');
    }
    if (dto.trigger === 'SCHEDULED' && dto.offsetSeconds == null) {
      throw new BadRequestException('Un inject planifie requiert un horaire T+ (offsetSeconds)');
    }
    const { targeting, payload } = this.buildPayload(dto);
    await this.prisma.forTenant(tenantId).inject.updateMany({
      where: { id },
      data: {
        channel: dto.channel ?? 'EMAIL',
        trigger: dto.trigger,
        status: 'PENDING',
        title: dto.title,
        offsetSeconds: dto.trigger === 'SCHEDULED' ? dto.offsetSeconds : null,
        senderCharId: dto.senderCharId ?? null,
        targeting,
        payload,
      },
    });
    return this.prisma.forTenant(tenantId).inject.findFirst({ where: { id } });
  }

  /** Envoi effectif d'un inject (commun au declenchement manuel et a l'ordonnanceur). */
  async send(tenantId: string, injectId: string, userId?: string) {
  async send(tenantId: string, injectId: string, userId?: string, opts?: { replay?: boolean }) {
    const db = this.prisma.forTenant(tenantId);
    const inject = await db.inject.findFirst({
      where: { id: injectId },
      include: { exercise: true },
    });
    if (!inject) throw new NotFoundException('Inject introuvable');
    if (inject.status === 'SENT') throw new ConflictException('Inject deja envoye');
    if (inject.status === 'SENT' && !opts?.replay) throw new ConflictException('Inject deja envoye');

    const atSec = computeElapsedSeconds(inject.exercise, Date.now());
    let senderName = 'Animation';
@@ -247,7 +279,7 @@ export class InjectsService {
    const logMessage =
      inject.channel === 'CALL'
        ? `☎ Appel à passer : ${inject.title}`
        : `Inject envoyé : ${inject.title}`;
        : `${opts?.replay ? 'Inject rejoué' : 'Inject envoyé'} : ${inject.title}`;
    await db.eventLog.create({
      data: {
        tenantId,