eeg_portal/docs/compose/plans/implementiere-die-erweiterung-der-admin-bersichten-im-eeg-po.md
Bernhard Müller cfc32b3a7d chore: cleanup test data and add missing test files
- Add TariffInviteServiceTest, GlobalExceptionHandlerTest, setup-test.ts
- Add compose plans/specs docs
- Extend .gitignore with *.xlsx, *.ps1, *.http, *.py, *.docx
- Remove stale test data files and scripts from working tree
2026-07-24 11:48:56 +02:00

12 KiB

Plan: Admin-Übersichten erweitern — Alle Mitgliedschaften & Alle Benutzer

Task-Übersicht

ID Beschreibung Abhängigkeit Typ
T1 User.java: createdAt-Feld hinzufügen Backend
T2 AuthService.register(): createdAt setzen T1 Backend
T3 UserRepository: findAllByOrderByCreatedAtDesc() T1 Backend
T4 MembershipRepository: findAllWithDetails() Backend
T5 MembershipService: getAllMemberships() T4 Backend
T6 MembershipController: GET /admin/memberships T5 Backend
T7 AdminIamService: getAllUsers() T3 Backend
T8 AdminIamController: GET /api/admin/users T7 Backend
T9 PendingMembershipResponse: validFrom/validTo erweitern Backend
T10 Backend-Tests schreiben T6, T8, T9 Test
T11 OpenAPI-Spec generieren + Frontend-API-Client regenerieren T6, T8 Build
T12 Frontend MembershipService: getAllMemberships() T11 Frontend
T13 AdminMembershipsComponent: Tab-Navigation + Tabelle T12, T9 Frontend
T14 AdminUserApprovalComponent: Tab-Navigation + Tabelle T11 Frontend
T15 Integrations-Test: UI manuell prüfen T13, T14 Test

T1: User.java — createdAt-Feld hinzufügen

Beschreibung: Füge der User-Entity ein createdAt-Feld vom Typ LocalDateTime hinzu.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/iam/domain/User.java

Akzeptanz:

  • createdAt Feld mit @Column(updatable = false) vorhanden
  • Kein @GeneratedValue — wird manuell gesetzt
  • Kompiliert ohne Fehler
  • ddl-auto: update erstellt die Spalte automatisch

Details:

@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;

T2: AuthService.register() — createdAt setzen

Beschreibung: Setze createdAt auf LocalDateTime.now() in der register()-Methode.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/iam/service/AuthService.java

Akzeptanz:

  • newUser.setCreatedAt(LocalDateTime.now()) vor userRepository.save(newUser)
  • Import für LocalDateTime vorhanden

Details: Zeile ~77, nach newUser.setVerificationToken(token):

newUser.setCreatedAt(LocalDateTime.now());

T3: UserRepository — findAllByOrderByCreatedAtDesc()

Beschreibung: Füge eine Spring Data Query-Methode hinzu, die alle Users absteigend nach Erstellungszeitpunkt sortiert.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/iam/repository/UserRepository.java

Akzeptanz:

  • Methode List<User> findAllByOrderByCreatedAtDesc() vorhanden
  • Kompiliert ohne Fehler

Details:

List<User> findAllByOrderByCreatedAtDesc();

T4: MembershipRepository — findAllWithDetails()

Beschreibung: Füge eine JPQL-Query hinzu, die alle Mitgliedschaften mit JOIN FETCH für MeteringPoint und EnergyCommunity zurückgibt.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java

Akzeptanz:

  • @Query mit JOIN FETCH auf meteringPoint und energyCommunity
  • Kein WHERE-Clause (alle Status)
  • Kompiliert ohne Fehler

Details:

@Query("SELECT m FROM Membership m JOIN FETCH m.meteringPoint mp JOIN FETCH m.energyCommunity ec")
List<Membership> findAllWithDetails();

T5: MembershipService — getAllMemberships()

Beschreibung: Neue Methode, die alle Mitgliedschaften lädt und als PendingMembershipResponse-Liste zurückgibt (mit User-Lookup).

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MembershipService.java

Akzeptanz:

  • @Transactional(readOnly = true) Annotation
  • Nutzt findAllWithDetails()
  • Gleiche User-Lookup-Logik wie getPendingMemberships() (Batch-Fetch aller User-IDs)
  • Gibt List<PendingMembershipResponse> zurück

Details: Kopiere die User-Lookup-Logik aus getPendingMemberships(), aber entferne den WHERE-Clause auf Status. Das PendingMembershipResponse-DTO muss um validFrom/validTo erweitert werden (siehe T9).


T6: MembershipController — GET /admin/memberships

Beschreibung: Neuer Admin-Endpoint für alle Mitgliedschaften.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/MembershipController.java

Akzeptanz:

  • @GetMapping("/admin/memberships") mit @PreAuthorize("hasRole('ADMIN')")
  • Gibt ResponseEntity<List<PendingMembershipResponse>> zurück
  • Swagger-Annotationen (@Operation, @ApiResponse)

Details:

@GetMapping("/admin/memberships")
@PreAuthorize("hasRole('ADMIN')")
@Operation(summary = "Lädt alle Mitgliedschaften", description = "Zeigt alle Mitgliedschaften aller Status.")
public ResponseEntity<List<PendingMembershipResponse>> getAllMemberships() {
    return ResponseEntity.ok(membershipService.getAllMemberships());
}

T7: AdminIamService — getAllUsers()

Beschreibung: Neue Methode, die alle Users sortiert nach Erstellungszeitpunkt zurückgibt.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/iam/service/AdminIamService.java

Akzeptanz:

  • @Transactional(readOnly = true) Annotation
  • Nutzt userRepository.findAllByOrderByCreatedAtDesc()
  • Gibt List<User> zurück

Details:

@Transactional(readOnly = true)
public List<User> getAllUsers() {
    return userRepository.findAllByOrderByCreatedAtDesc();
}

T8: AdminIamController — GET /api/admin/users

Beschreibung: Neuer Admin-Endpoint für alle Benutzer.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/iam/api/AdminIamController.java

Akzeptanz:

  • @GetMapping auf Root-Pfad /api/admin/users
  • Nutzt adminIamService.getAllUsers() + userMapper.mapToDto()
  • Gibt ResponseEntity<List<UserProfileResponse>> zurück
  • Swagger-Annotationen

Details:

@GetMapping
@Operation(summary = "Lädt alle User", description = "Zeigt alle registrierten Benutzer.")
public ResponseEntity<List<UserProfileResponse>> getAllUsers() {
    return ResponseEntity.ok(userMapper.mapToDto(adminIamService.getAllUsers()));
}

T9: PendingMembershipResponse — validFrom/validTo erweitern

Beschreibung: Erweitere das DTO-Record um validFrom und validTo für alle Mitgliedschaften.

Dateien:

  • eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/dto/PendingMembershipResponse.java

Akzeptanz:

  • Record hat neue Felder LocalDate validFrom, LocalDate validTo
  • getPendingMemberships() in MembershipService setzt beide Felder (null für PENDING)
  • getAllMemberships() setzt beide Felder aus Entity

Details:

record PendingMembershipResponse(
    UUID id,
    String userEmail,
    String firstName,
    String lastName,
    String atNumber,
    String communityName,
    Integer priorityLevel,
    String status,
    LocalDate validFrom,
    LocalDate validTo
) {}

Achtung: Da PendingMembershipResponse ein Java record ist, müssen ALLE Aufrufstellen aktualisiert werden, die dieses DTO erstellen. Insbesondere getPendingMemberships() in MembershipService.


T10: Backend-Tests schreiben

Beschreibung: Unit-Tests für die neuen Service-Methoden und Integrationstests für die neuen Endpoints.

Dateien:

  • eeg_backend/src/test/java/.../community/service/MembershipServiceTest.java (neu oder erweitern)
  • eeg_backend/src/test/java/.../iam/service/AdminIamServiceTest.java (neu oder erweitern)

Akzeptanz:

  • Test für getAllMemberships(): gibt alle Status zurück
  • Test für getAllUsers(): gibt alle Users sortiert zurück
  • Bestehende Tests weiterhin grün
  • .\mvnw test -pl eeg_backend erfolgreich

T11: OpenAPI-Spec generieren + Frontend-API-Client regenerieren

Beschreibung: Generiere die OpenAPI-Spec aus dem Backend und regeneriere den Frontend-API-Client.

Befehle:

.\mvnw test -pl eeg_backend -Dtest=OpenApiGeneratorTest
cd eeg_frontend && npm run generate-api

Akzeptanz:

  • eeg_frontend/openapi.yaml enthält neuen Endpoint /api/community/admin/memberships
  • eeg_frontend/openapi.yaml enthält neuen Endpoint /api/admin/users (Root)
  • Generierter AdminIAMService enthält getAllUsers()
  • Keine Type-Errors im Frontend

T12: Frontend MembershipService — getAllMemberships()

Beschreibung: Neue Methode im Frontend MembershipService.

Dateien:

  • eeg_frontend/src/app/services/membership.ts

Akzeptanz:

  • getAllMemberships(): Observable<PendingMembershipResponse[]> vorhanden
  • Nutzt GET /api/community/admin/memberships

Details:

getAllMemberships(): Observable<PendingMembershipResponse[]> {
  return this.http.get<PendingMembershipResponse[]>(`${this.apiUrl}/admin/memberships`);
}

T13: AdminMembershipsComponent — Tab-Navigation + Tabelle

Beschreibung: Erweitere die Admin-Mitgliedschafts-Seite mit Tab-Navigation und Tabellen-Ansicht.

Dateien:

  • eeg_frontend/src/app/pages/admin-memberships/admin-memberships.ts
  • eeg_frontend/src/app/pages/admin-memberships/admin-memberships.html

Akzeptanz:

  • Tab-Leiste: "Ausstehende Anträge" | "Aktive Mitgliedschaften" | "Alle"
  • Signal activeTab: signal<'pending' | 'active' | 'all'>('pending')
  • Signal allMemberships: signal<PendingMembershipResponse[]>([])
  • loadAllMemberships() lädt alle Daten beim Wechsel auf Tab "Aktive" oder "Alle"
  • Tab "Ausstehende": Bestehende Karten-Ansicht mit Genehmigen/Ablehnen
  • Tab "Aktive" + "Alle": Tabellen-Ansicht (Name, Email, Community, Zählpunkt, Priorität, Status)
  • Nur PENDING zeigt Aktions-Buttons
  • Hilfsfunktion getStatusLabel(status: string): string für deutsche Anzeige
  • Loading-Zustand für Tab-Wechsel

Details:

  • Client-seitige Filterung: Beim Laden werden alle Mitgliedschaften in allMemberships gespeichert
  • Tab "Ausstehende" filtert nach status === 'PENDING'
  • Tab "Aktive" filtert nach status === 'ACTIVE'
  • Tab "Alle" zeigt alle
  • Tabellen-Header: Benutzer | Email | Gemeinschaft | Zählpunkt | Priorität | Status

T14: AdminUserApprovalComponent — Tab-Navigation + Tabelle

Beschreibung: Erweitere die Admin-Benutzerverwaltung mit Tab-Navigation und Tabelle für alle Users.

Dateien:

  • eeg_frontend/src/app/pages/admin-user-approval/admin-user-approval.ts
  • eeg_frontend/src/app/pages/admin-user-approval/admin-user-approval.html

Akzeptanz:

  • Tab-Leiste: "Ausstehende Anträge" | "Alle Benutzer"
  • Signal activeTab: signal<'pending' | 'all'>('pending')
  • Signal allUsers: signal<UserProfileResponse[]>([])
  • loadAllUsers() nutzt generierten AdminIAMService.getAllUsers()
  • Tab "Ausstehende": Bestehende Tabelle mit Freigeben/Ablehnen
  • Tab "Alle": Tabelle mit Name, Email, Status, Rolle (keine Aktions-Buttons)
  • Status-Badge: PENDING=gelb, APPROVED=grün, REJECTED=rot
  • Hilfsfunktion getStatusLabel(status: string): string
  • Bug fix: Doppelte "Status"-Spalten-Überschrift beheben (Zeile 32-33 in aktuellem Template)

Details:

  • Tab "Ausstehende" lädt weiterhin getPendingUsers() (nur verifizierte PENDING-User)
  • Tab "Alle" lädt getAllUsers() beim ersten Wechsel
  • Status-Badges: bg-yellow-100 text-yellow-800 (PENDING), bg-green-100 text-green-800 (APPROVED), bg-red-100 text-red-800 (REJECTED)

T15: Integrations-Test — UI manuell prüfen

Beschreibung: Manuelle Überprüfung der Admin-Seiten im Browser.

Akzeptanz:

  • Backend startet ohne Fehler
  • GET /api/community/admin/memberships gibt alle Mitgliedschaften zurück
  • GET /api/admin/users gibt alle Benutzer zurück
  • Admin öffnet Mitgliedschaften → sieht Tabs "Ausstehende | Aktive | Alle"
  • Admin öffnet Benutzerverwaltung → sieht Tabs "Ausstehende | Alle"
  • Tab-Wechsel funktioniert sofort (kein Ladevorgang nötig außer beim ersten Laden)
  • Genehmigen/Ablehnen funktioniert weiterhin auf dem "Ausstehende"-Tab
  • Status-Badges werden korrekt angezeigt
  • .\mvnw test -pl eeg_backend alle Tests grün