feat(15-07): Aktivierungsdialog mit drei Aktionen in /admin/modules
- Neue ActivateModuleDialog.tsx: Abbrechen / "Spaeter konfigurieren" (nur POST /modules/:id/activate) / "Sofort freigeben" (POST .../activate gefolgt von POST /module-grants fuer die als Standard markierte Gruppe) -- zwei getrennte Aufrufe, kein neuer kombinierter Endpoint (D-10) - Laedt GET /groups beim Oeffnen; ohne markierte Standardgruppe ist "Sofort freigeben" disabled mit Hinweistext (D-13) - admin/modules/page.tsx: toggleModule-Klick verzweigt -- Deaktivierung bleibt direkt, Aktivierung oeffnet den Dialog statt sofort zu aktivieren; Erfolg aktualisiert Aktivierungs-Map + Sidebar-Refresh wie bisher - grants-matrix.test.tsx erweitert um 3 Tests fuer den Dialog (alle drei Buttons vorhanden, Sofort-freigeben deaktiviert ohne Standardgruppe, Aufrufreihenfolge activate->grant)
This commit is contained in:
@@ -5,6 +5,7 @@ import Link from 'next/link';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useAuthStore } from '@/lib/stores/auth-store';
|
||||
import { useMarketplaceStore } from '@/lib/stores/marketplace-store';
|
||||
import { ActivateModuleDialog } from './components/ActivateModuleDialog';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
||||
|
||||
@@ -40,6 +41,10 @@ export default function AdminModulesPage() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [toggling, setToggling] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
// Deactivation stays on the direct toggleModule path below. Activation
|
||||
// instead opens this dialog (D-10) -- the module named here is the one
|
||||
// whose "Aktivieren"-switch was just clicked.
|
||||
const [activateDialogModule, setActivateDialogModule] = useState<Module | null>(null);
|
||||
|
||||
const isAdmin = currentUser?.role === 'ADMIN' || currentUser?.role === 'SUPER_ADMIN';
|
||||
|
||||
@@ -192,7 +197,16 @@ export default function AdminModulesPage() {
|
||||
{isActive ? t('activated') : t('deactivated')}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => toggleModule(mod.id, isActive)}
|
||||
onClick={() => {
|
||||
if (isActive) {
|
||||
// Deactivation is unchanged -- direct toggle, no dialog.
|
||||
toggleModule(mod.id, true);
|
||||
} else {
|
||||
// Activation opens the confirmation dialog instead of
|
||||
// activating directly (D-10).
|
||||
setActivateDialogModule(mod);
|
||||
}
|
||||
}}
|
||||
disabled={isToggling}
|
||||
className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-wait disabled:opacity-50 ${
|
||||
isActive ? 'bg-primary' : 'bg-input'
|
||||
@@ -212,6 +226,25 @@ export default function AdminModulesPage() {
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activateDialogModule && (
|
||||
<ActivateModuleDialog
|
||||
open
|
||||
moduleId={activateDialogModule.id}
|
||||
moduleName={activateDialogModule.name}
|
||||
onCancel={() => setActivateDialogModule(null)}
|
||||
onSuccess={(moduleId) => {
|
||||
setActivations((prev) => {
|
||||
const next = new Map(prev);
|
||||
next.set(moduleId, true);
|
||||
return next;
|
||||
});
|
||||
bumpSidebarRefresh();
|
||||
setActivateDialogModule(null);
|
||||
}}
|
||||
onError={(message) => setError(message)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user