feat(browse): gestion CRUD des dossiers depuis la page Browse

- Ajout boutons Modifier/Supprimer sur chaque tuile de dossier
- Supprimer visible uniquement si dossier vide (0 APIs, 0 sous-dossiers)
- Modale Angular (sans fr-modal DSFR) pour créer/éditer un dossier
- Backend: remove() lève BadRequestException si dossier non vide
- Ajout fichiers YAML de démo (OpenAPI Petstore, AsyncAPI User Events)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
z3n
2026-05-22 13:59:16 +00:00
parent 921a6e652b
commit 93fadb43f9
4 changed files with 376 additions and 31 deletions

View File

@@ -1,4 +1,4 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull } from 'typeorm';
import { CategoryEntity } from './category.entity';
@@ -61,20 +61,23 @@ export class CategoriesService {
async remove(id: string): Promise<void> {
await this.findOneOrThrow(id);
/* Désassocier les APIs enfants */
await this.apiRepo
.createQueryBuilder()
.update(ApiEntryEntity)
.set({ categoryId: null })
.where('category_id = :id', { id })
.execute();
/* Détacher les sous-catégories enfants */
await this.categoryRepo
.createQueryBuilder()
.update(CategoryEntity)
.set({ parentId: null })
.where('parent_id = :id', { id })
.execute();
/* Vérifier qu'aucune API n'est rattachée */
const apiCount = await this.apiRepo.count({ where: { categoryId: id } });
if (apiCount > 0) {
throw new BadRequestException(
`Ce dossier contient ${apiCount} API(s). Déplacez-les avant de le supprimer.`,
);
}
/* Vérifier qu'aucun sous-dossier n'existe */
const subCount = await this.categoryRepo.count({ where: { parentId: id } });
if (subCount > 0) {
throw new BadRequestException(
`Ce dossier contient ${subCount} sous-dossier(s). Supprimez-les d'abord.`,
);
}
await this.categoryRepo.delete(id);
}