feat(browse): modale de suppression + crayon d'édition sur le titre
- Remplace le confirm() natif par une vraie modale de confirmation avec gestion d'erreur inline - Ajoute un bouton crayon dans le <h1> pour éditer la catégorie courante (visible uniquement dans /browse/:id) - Signature openEdit() assouplie pour accepter Category (au lieu de CategoryWithCounts) 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:
@@ -45,7 +45,17 @@ import { forkJoin } from 'rxjs';
|
||||
<!-- En-tête -->
|
||||
<div class="fr-grid-row fr-grid-row--middle fr-mb-3w">
|
||||
<div class="fr-col">
|
||||
<h1 class="fr-h2 fr-mb-0">{{ browseData()?.category?.name ?? 'Catalogue' }}</h1>
|
||||
<h1 class="fr-h2 fr-mb-0">
|
||||
{{ browseData()?.category?.name ?? 'Catalogue' }}
|
||||
@if (browseData()?.category) {
|
||||
<button
|
||||
title="Modifier ce dossier"
|
||||
style="background:none;border:none;cursor:pointer;padding:0 0.25rem;vertical-align:middle;opacity:0.6"
|
||||
(click)="openEdit(browseData()!.category!)">
|
||||
<span class="fr-icon-edit-line" aria-hidden="true" style="font-size:0.875rem"></span>
|
||||
</button>
|
||||
}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="fr-col-auto" style="display:flex;gap:0.5rem">
|
||||
<a class="fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-upload-2-line"
|
||||
@@ -99,7 +109,7 @@ import { forkJoin } from 'rxjs';
|
||||
@if (cat.apiCount === 0 && cat.subcategoryCount === 0) {
|
||||
<button title="Supprimer"
|
||||
style="position:relative;z-index:1;background:none;border:none;cursor:pointer;padding:0 0.25rem;vertical-align:middle;opacity:0.6"
|
||||
(click)="deleteCategory(cat); $event.stopPropagation()">
|
||||
(click)="openDelete(cat); $event.stopPropagation()">
|
||||
<span class="fr-icon-delete-bin-line" aria-hidden="true" style="font-size:0.75rem"></span>
|
||||
</button>
|
||||
}
|
||||
@@ -229,6 +239,46 @@ import { forkJoin } from 'rxjs';
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Modale de suppression -->
|
||||
@if (showDeleteModal()) {
|
||||
<div style="position:fixed;inset:0;z-index:9999;background:rgba(22,22,22,.64);display:flex;align-items:flex-start;justify-content:center;padding-top:5vh;overflow-y:auto"
|
||||
role="dialog" aria-modal="true" aria-labelledby="delete-modal-title">
|
||||
<div style="background:white;width:calc(100% - 2rem);max-width:480px;margin:0 1rem 2rem">
|
||||
<div class="fr-p-4w">
|
||||
<div class="fr-grid-row fr-grid-row--middle fr-mb-3w">
|
||||
<div class="fr-col">
|
||||
<h2 id="delete-modal-title" class="fr-h3 fr-mb-0">Supprimer le dossier</h2>
|
||||
</div>
|
||||
<div class="fr-col-auto">
|
||||
<button class="fr-btn fr-btn--tertiary-no-outline fr-btn--sm"
|
||||
(click)="showDeleteModal.set(false)">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="fr-text--lead fr-mb-1w">
|
||||
Êtes-vous sûr de vouloir supprimer le dossier
|
||||
<strong>« {{ deletingCategory()?.name }} »</strong> ?
|
||||
</p>
|
||||
<p class="fr-text--sm fr-text--mention-grey fr-mb-3w">
|
||||
Cette action est irréversible.
|
||||
</p>
|
||||
@if (deleteError()) {
|
||||
<div class="fr-alert fr-alert--error fr-mb-2w">
|
||||
<p>{{ deleteError() }}</p>
|
||||
</div>
|
||||
}
|
||||
<div class="fr-btns-group fr-btns-group--right fr-btns-group--inline">
|
||||
<button class="fr-btn fr-btn--secondary" (click)="showDeleteModal.set(false)"
|
||||
[disabled]="deleteLoading()">Annuler</button>
|
||||
<button class="fr-btn fr-btn--error" (click)="confirmDelete()"
|
||||
[disabled]="deleteLoading()">
|
||||
{{ deleteLoading() ? 'Suppression...' : 'Supprimer' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Modale création / édition (overlay Angular, sans fr-modal pour éviter l'interférence du JS DSFR) -->
|
||||
@if (showFormModal()) {
|
||||
<div style="position:fixed;inset:0;z-index:9999;background:rgba(22,22,22,.64);display:flex;align-items:flex-start;justify-content:center;padding-top:5vh;overflow-y:auto"
|
||||
@@ -319,7 +369,7 @@ export class BrowseComponent implements OnInit {
|
||||
|
||||
private searchDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
/* Signaux pour la modale */
|
||||
/* Signaux pour la modale création / édition */
|
||||
showFormModal = signal(false);
|
||||
formMode = signal<'create' | 'edit'>('create');
|
||||
editingCategoryId = signal<string | null>(null);
|
||||
@@ -331,6 +381,12 @@ export class BrowseComponent implements OnInit {
|
||||
formLoading = signal(false);
|
||||
allCategories = signal<Category[]>([]);
|
||||
|
||||
/* Signaux pour la modale de suppression */
|
||||
showDeleteModal = signal(false);
|
||||
deletingCategory = signal<CategoryWithCounts | null>(null);
|
||||
deleteLoading = signal(false);
|
||||
deleteError = signal<string | null>(null);
|
||||
|
||||
/* Ancêtres = breadcrumb sans le nœud courant (déjà dans le h1) */
|
||||
ancestors = computed<Category[]>(() => {
|
||||
const d = this.browseData();
|
||||
@@ -416,7 +472,7 @@ export class BrowseComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
openEdit(cat: CategoryWithCounts) {
|
||||
openEdit(cat: Category) {
|
||||
this.formMode.set('edit');
|
||||
this.editingCategoryId.set(cat.id);
|
||||
this.formName.set(cat.name);
|
||||
@@ -462,13 +518,26 @@ export class BrowseComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
deleteCategory(cat: CategoryWithCounts) {
|
||||
if (cat.apiCount > 0 || cat.subcategoryCount > 0) return;
|
||||
if (!confirm(`Supprimer le dossier "${cat.name}" ?`)) return;
|
||||
openDelete(cat: CategoryWithCounts) {
|
||||
this.deletingCategory.set(cat);
|
||||
this.deleteError.set(null);
|
||||
this.showDeleteModal.set(true);
|
||||
}
|
||||
|
||||
confirmDelete() {
|
||||
const cat = this.deletingCategory();
|
||||
if (!cat) return;
|
||||
this.deleteLoading.set(true);
|
||||
this.categoryService.delete(cat.id).subscribe({
|
||||
next: () => this.loadBrowse(),
|
||||
error: (err: { error?: { message?: string } }) =>
|
||||
alert(err?.error?.message ?? 'Erreur lors de la suppression.'),
|
||||
next: () => {
|
||||
this.deleteLoading.set(false);
|
||||
this.showDeleteModal.set(false);
|
||||
this.loadBrowse();
|
||||
},
|
||||
error: (err: { error?: { message?: string } }) => {
|
||||
this.deleteLoading.set(false);
|
||||
this.deleteError.set(err?.error?.message ?? 'Erreur lors de la suppression.');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user