diff --git a/frontend/src/components/ConfigurationView.jsx b/frontend/src/components/ConfigurationView.jsx
index 34f7271..dd2ef4b 100644
--- a/frontend/src/components/ConfigurationView.jsx
+++ b/frontend/src/components/ConfigurationView.jsx
@@ -44,6 +44,8 @@ function ConfigurationView({
libraryResetError,
libraryResetMessage,
isResettingLibrary,
+ isLibraryResetConfirmationOpen,
+ waitingValidationResetCount,
platformCatalogSyncError,
platformCatalogSyncMessage,
isSyncingPlatformCatalog,
@@ -63,6 +65,8 @@ function ConfigurationView({
onOpenCollectionShares,
onDownloadOds,
onResetLibrary,
+ onCancelLibraryReset,
+ onConfirmLibraryReset,
onSyncPlatformCatalog,
onReinitializeCollection,
onLogout,
@@ -256,8 +260,81 @@ function ConfigurationView({
) : null}
+
+
);
}
+/**
+ * Affiche la confirmation du reset Bibliotheque.
+ *
+ * @param {Object} props - Etat de la pop-up et callbacks de confirmation.
+ * @returns {import("react").JSX.Element|null} Pop-up de confirmation ou rien.
+ * @throws {void} Ne leve pas d'exception pendant le rendu React.
+ */
+function LibraryResetConfirmationDialog({
+ isOpen,
+ isResettingLibrary,
+ waitingValidationCount,
+ onCancel,
+ onConfirm,
+}) {
+ if (!isOpen) {
+ return null;
+ }
+
+ return (
+
+
+
+ Confirmation
+ Confirmer le reset Bibliotheque
+
+
+ ATTENTION : ce reset supprime et reconstruit toute la Bibliotheque globale a partir des
+ imports utilisateur.
+
+ {Number(waitingValidationCount) > 0 ? (
+
+ {waitingValidationCount} jeu(x)
+ {" "}
+ marque(s) comme en attente de validation seront automatiquement acceptes si le reset est
+ lance.
+
+ ) : null}
+
+
+
+
+
+
+ );
+}
+
export default ConfigurationView;
diff --git a/frontend/src/components/appViewSwitchConfigurationRenderer.jsx b/frontend/src/components/appViewSwitchConfigurationRenderer.jsx
index 52a1da7..5e75c3b 100644
--- a/frontend/src/components/appViewSwitchConfigurationRenderer.jsx
+++ b/frontend/src/components/appViewSwitchConfigurationRenderer.jsx
@@ -33,6 +33,8 @@ function renderConfigurationView(props, pageLayoutProps) {
libraryResetError={props.libraryResetError}
libraryResetMessage={props.libraryResetMessage}
isResettingLibrary={props.isResettingLibrary}
+ isLibraryResetConfirmationOpen={props.isLibraryResetConfirmationOpen}
+ waitingValidationResetCount={props.waitingValidationResetCount}
platformCatalogSyncError={props.platformCatalogSyncError}
platformCatalogSyncMessage={props.platformCatalogSyncMessage}
isSyncingPlatformCatalog={props.isSyncingPlatformCatalog}
@@ -45,6 +47,8 @@ function renderConfigurationView(props, pageLayoutProps) {
onOpenCollectionShares={props.openCollectionShares}
onDownloadOds={props.downloadOdsFile}
onResetLibrary={props.resetLibrary}
+ onCancelLibraryReset={props.cancelLibraryReset}
+ onConfirmLibraryReset={props.confirmLibraryReset}
onSyncPlatformCatalog={props.syncPlatformCatalog}
onReinitializeCollection={props.reinitializeCollection}
/>
diff --git a/frontend/src/hooks/app/useCloudCollectionViewModel.js b/frontend/src/hooks/app/useCloudCollectionViewModel.js
index 7b42afa..84b7f6b 100644
--- a/frontend/src/hooks/app/useCloudCollectionViewModel.js
+++ b/frontend/src/hooks/app/useCloudCollectionViewModel.js
@@ -280,6 +280,8 @@ function useCloudCollectionViewModel() {
libraryResetError: libraryResetAction.libraryResetError,
libraryResetMessage: libraryResetAction.libraryResetMessage,
isResettingLibrary: libraryResetAction.isResettingLibrary,
+ isLibraryResetConfirmationOpen: libraryResetAction.isLibraryResetConfirmationOpen,
+ waitingValidationResetCount: libraryResetAction.waitingValidationCount,
adminLibraryImportError: adminLibraryCsvImportAction.adminLibraryImportError,
adminLibraryImportResult: adminLibraryCsvImportAction.adminLibraryImportResult,
isImportingAdminLibrary: adminLibraryCsvImportAction.isImportingAdminLibrary,
@@ -326,6 +328,8 @@ function useCloudCollectionViewModel() {
closeHomeSearch: homePage.closeHomeSearch,
downloadOdsFile: odsDownload.downloadOdsFile,
resetLibrary: libraryResetAction.resetLibrary,
+ cancelLibraryReset: libraryResetAction.cancelLibraryReset,
+ confirmLibraryReset: libraryResetAction.confirmLibraryReset,
importAdminLibraryCsv: adminLibraryCsvImportAction.importAdminLibraryCsv,
selectAdminLibraryImportFile: adminLibraryCsvImportAction.selectAdminLibraryImportFile,
syncPlatformCatalog: platformCatalogSyncAction.syncPlatformCatalog,
diff --git a/frontend/src/hooks/library/useLibraryResetAction.js b/frontend/src/hooks/library/useLibraryResetAction.js
index 7edc9be..92a3cb7 100644
--- a/frontend/src/hooks/library/useLibraryResetAction.js
+++ b/frontend/src/hooks/library/useLibraryResetAction.js
@@ -30,13 +30,14 @@ function useLibraryResetAction(options = {}) {
const [libraryResetMessage, setLibraryResetMessage] = useState("");
const [libraryResetError, setLibraryResetError] = useState("");
const [isResettingLibrary, setIsResettingLibrary] = useState(false);
+ const [isLibraryResetConfirmationOpen, setIsLibraryResetConfirmationOpen] = useState(false);
const waitingValidationCount = Math.max(
0,
Number.parseInt(options.waitingValidationCount, 10) || 0
);
/**
- * Demande confirmation puis appelle le backend de reset Bibliotheque.
+ * Ouvre la confirmation du reset Bibliotheque.
*
* @returns {Promise} Met a jour les messages de resultat.
* @throws {void} Les erreurs sont converties en message lisible.
@@ -44,11 +45,37 @@ function useLibraryResetAction(options = {}) {
const resetLibrary = async () => {
setLibraryResetMessage("");
setLibraryResetError("");
- const confirmed = window.confirm(buildLibraryResetConfirmationMessage(waitingValidationCount));
- if (!confirmed) {
- return;
- }
+ setIsLibraryResetConfirmationOpen(true);
+ };
+
+ /**
+ * Confirme le reset apres affichage de la pop-up de confirmation.
+ *
+ * @returns {Promise} Lance le reset apres fermeture de la pop-up.
+ * @throws {void} Les erreurs sont converties en message lisible.
+ */
+ const confirmLibraryReset = async () => {
+ setIsLibraryResetConfirmationOpen(false);
+ await launchLibraryReset();
+ };
+
+ /**
+ * Annule la demande de reset depuis la pop-up de confirmation.
+ *
+ * @returns {void} Ferme la pop-up sans appeler le backend.
+ * @throws {void} Ne leve pas d'exception.
+ */
+ const cancelLibraryReset = () => {
+ setIsLibraryResetConfirmationOpen(false);
+ };
+ /**
+ * Appelle le backend de reset Bibliotheque apres confirmation.
+ *
+ * @returns {Promise} Met a jour les messages de resultat.
+ * @throws {void} Les erreurs sont converties en message lisible.
+ */
+ const launchLibraryReset = async () => {
try {
setIsResettingLibrary(true);
await LibraryAdminApi.resetLibrary();
@@ -67,29 +94,15 @@ function useLibraryResetAction(options = {}) {
};
return {
+ cancelLibraryReset,
+ confirmLibraryReset,
+ isLibraryResetConfirmationOpen,
isResettingLibrary,
libraryResetError,
libraryResetMessage,
resetLibrary,
+ waitingValidationCount,
};
}
-/**
- * Construit le message de confirmation du reset Bibliotheque.
- *
- * @param {number} waitingValidationCount - Nombre de jeux actuellement en attente.
- * @returns {string} Message de confirmation affiche a l'administrateur.
- * @throws {void} Ne leve pas d'exception.
- */
-function buildLibraryResetConfirmationMessage(waitingValidationCount = 0) {
- const baseMessage = (
- "ATTENTION : ce reset supprime et reconstruit toute la Bibliotheque globale a partir des imports utilisateur."
- );
- const waitingMessage = Number(waitingValidationCount) > 0
- ? ` ${waitingValidationCount} jeu(x) en attente seront valides automatiquement par le reset.`
- : "";
- return `${baseMessage}${waitingMessage} Confirmer le lancement ?`;
-}
-
-export { buildLibraryResetConfirmationMessage };
export default useLibraryResetAction;
diff --git a/frontend/src/styles/admin.css b/frontend/src/styles/admin.css
index 13108f2..9dfb1b8 100644
--- a/frontend/src/styles/admin.css
+++ b/frontend/src/styles/admin.css
@@ -77,6 +77,33 @@
border-color: #991b1b;
}
+.adminResetConfirmationDialog {
+ background: #ffffff;
+ border: 1px solid #fecaca;
+ border-radius: 8px;
+ box-shadow: 0 24px 60px rgba(15, 23, 42, 0.28);
+ display: grid;
+ gap: 1rem;
+ padding: 1.25rem;
+ width: min(100%, 34rem);
+}
+
+.adminResetConfirmationDialog h2 {
+ color: #111827;
+ font-size: 1.25rem;
+ margin: 0;
+}
+
+.adminResetConfirmationDialog p {
+ color: #334155;
+ margin: 0;
+}
+
+.adminResetValidationWarning strong {
+ color: #b91c1c;
+ font-weight: 900;
+}
+
.usersTableSection {
margin-top: 1.5rem;
}
diff --git a/frontend/tests/gameValidationWorkflow.test.js b/frontend/tests/gameValidationWorkflow.test.js
index 569bbbc..0bd999f 100644
--- a/frontend/tests/gameValidationWorkflow.test.js
+++ b/frontend/tests/gameValidationWorkflow.test.js
@@ -14,7 +14,6 @@ import BackendRouteAccessService from "../src/services/BackendRouteAccessService
import LibraryAdminApi from "../src/services/LibraryAdminApi.js";
import LibraryApi from "../src/services/LibraryApi.js";
import { getLibraryGameColumns } from "../src/hooks/library/libraryGameColumns.js";
-import { buildLibraryResetConfirmationMessage } from "../src/hooks/library/useLibraryResetAction.js";
class MemoryStorage {
/** Initialise un stockage navigateur en memoire. */
@@ -107,11 +106,38 @@ test("derive la permission de gestion validation jeux depuis les routes admin",
assert.equal(userPermissions.canManageGameValidation, false);
});
-test("ajoute l'avertissement de validation automatique dans la confirmation reset", () => {
- const message = buildLibraryResetConfirmationMessage(4);
+test("declare la pop-up de confirmation reset permanente avec compteur colore", () => {
+ const hookSource = readFileSync(
+ new URL("../src/hooks/library/useLibraryResetAction.js", import.meta.url),
+ "utf8"
+ );
+ const configurationSource = readFileSync(
+ new URL("../src/components/ConfigurationView.jsx", import.meta.url),
+ "utf8"
+ );
+ const rendererSource = readFileSync(
+ new URL("../src/components/appViewSwitchConfigurationRenderer.jsx", import.meta.url),
+ "utf8"
+ );
+ const adminStyleSource = readFileSync(
+ new URL("../src/styles/admin.css", import.meta.url),
+ "utf8"
+ );
- assert.equal(message.includes("4 jeu(x) en attente"), true);
- assert.equal(message.includes("valides automatiquement"), true);
+ assert.equal(hookSource.includes("isLibraryResetConfirmationOpen"), true);
+ assert.equal(hookSource.includes("setIsLibraryResetConfirmationOpen(true)"), true);
+ assert.equal(hookSource.includes("window.confirm"), false);
+ assert.equal(configurationSource.includes("LibraryResetConfirmationDialog"), true);
+ assert.equal(configurationSource.includes("ATTENTION : ce reset supprime"), true);
+ assert.equal(configurationSource.includes("Number(waitingValidationCount) > 0"), true);
+ assert.equal(configurationSource.includes("{waitingValidationCount} jeu(x)"), true);
+ assert.equal(configurationSource.includes("seront automatiquement acceptes"), true);
+ assert.equal(configurationSource.includes("Annuler"), true);
+ assert.equal(configurationSource.includes("Confirmer le reset"), true);
+ assert.equal(rendererSource.includes("onCancelLibraryReset"), true);
+ assert.equal(rendererSource.includes("onConfirmLibraryReset"), true);
+ assert.equal(adminStyleSource.includes(".adminResetValidationWarning strong"), true);
+ assert.equal(adminStyleSource.includes("font-weight: 900"), true);
});
test("declare les controles admin de filtre, selection et badge Bibliotheque", () => {