simplified user registration process
This commit is contained in:
parent
59a0b6b9b7
commit
2e9790b3b2
@ -22,12 +22,9 @@ public class AuthController {
|
||||
|
||||
@PostMapping("/register")
|
||||
@Operation(summary = "Registriert einen neuen User inkl. Zählpunkt",
|
||||
description = "Setzt den User auf Status PENDING und triggert den asynchronen EDA-Check.")
|
||||
description = "Setzt den User auf Status PENDING und versendet eine Verifizierungs-E-Mail.")
|
||||
public ResponseEntity<Void> register(@Valid @RequestBody RegistrationRequest request) {
|
||||
authService.register(request);
|
||||
// Logik: MeteringPoint anlegen
|
||||
// Event feuern: EdaTopologyCheckEvent
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package at.mueller.eeg.backend.iam.api.dto;
|
||||
|
||||
import at.mueller.eeg.backend.iam.domain.ParticipantType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.util.UUID;
|
||||
@ -12,15 +13,19 @@ public interface IamDtos {
|
||||
|
||||
@Schema(description = "Payload für die Registrierung eines neuen Users")
|
||||
record RegistrationRequest(
|
||||
@NotBlank @Schema(example = "Max") String firstName,
|
||||
@NotBlank @Schema(example = "Mustermann") String lastName,
|
||||
@Email @NotBlank @Schema(example = "max@example.com") String email,
|
||||
@NotBlank @Size(min = 8) @Schema(example = "SecurePass123!") String password,
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "Privatperson oder juristische Person (Firma/Verein/Gemeinde)", example = "PRIVATE")
|
||||
ParticipantType participantType,
|
||||
@NotBlank
|
||||
@Pattern(regexp = "^AT[0-9]{31}$", message = "Muss eine gültige österreichische 33-stellige Zählpunktnummer sein")
|
||||
@Schema(example = "AT0010000000000000000000001234567")
|
||||
String atNumber
|
||||
@Schema(description = "Vorname (bei juristischer Person: Vorname der Ansprechperson)", example = "Max")
|
||||
String firstName,
|
||||
@NotBlank
|
||||
@Schema(description = "Nachname (bei juristischer Person: Nachname der Ansprechperson)", example = "Mustermann")
|
||||
String lastName,
|
||||
@Schema(description = "Nur bei juristischer Person: Firmen-, Vereins- oder Gemeindename", example = "Musterverein")
|
||||
String organizationName,
|
||||
@Email @NotBlank @Schema(example = "max@example.com") String email,
|
||||
@NotBlank @Size(min = 8) @Schema(example = "SecurePass123!") String password
|
||||
) {}
|
||||
|
||||
@Schema(description = "Payload für den Login")
|
||||
@ -39,13 +44,13 @@ public interface IamDtos {
|
||||
@Schema(description = "Repräsentation eines Users für das Admin-Dashboard")
|
||||
record UserProfileResponse(
|
||||
UUID id,
|
||||
ParticipantType participantType,
|
||||
String firstName,
|
||||
String lastName,
|
||||
String organizationName,
|
||||
String email,
|
||||
String status,
|
||||
String primaryAtNumber
|
||||
String status
|
||||
) {}
|
||||
|
||||
record ErrorResponse(String message, int status) {}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package at.mueller.eeg.backend.iam.domain;
|
||||
|
||||
public enum ParticipantType {
|
||||
PRIVATE,
|
||||
COMPANY
|
||||
}
|
||||
@ -29,12 +29,16 @@ public class User implements UserDetails {
|
||||
@Column(nullable = false)
|
||||
private String passwordHash;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
private ParticipantType participantType = ParticipantType.PRIVATE;
|
||||
|
||||
private String organizationName;
|
||||
|
||||
private String firstName;
|
||||
|
||||
private String lastName;
|
||||
|
||||
private String iban;
|
||||
|
||||
@Column(nullable = false)
|
||||
private boolean emailVerified = false;
|
||||
|
||||
|
||||
@ -15,14 +15,12 @@ public interface UserMapper {
|
||||
|
||||
List<IamDtos.UserProfileResponse> mapToDto(List<User> users);
|
||||
|
||||
@Mapping(target = "primaryAtNumber", ignore = true)
|
||||
IamDtos.UserProfileResponse mapToDto(User user);
|
||||
|
||||
@Mapping(target = "status", constant = "PENDING")
|
||||
@Mapping(target = "role", constant = "MEMBER")
|
||||
@Mapping(target = "passwordHash", source = "password", qualifiedBy = UserMapperAnnotations.PasswordHash.class)
|
||||
@Mapping(target = "id", ignore = true)
|
||||
@Mapping(target = "iban", ignore = true)
|
||||
@Mapping(target = "enabled", constant = "false")
|
||||
@Mapping(target = "authorities", ignore = true)
|
||||
@Mapping(target = "emailVerified", ignore = true)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package at.mueller.eeg.backend.iam.service;
|
||||
|
||||
import at.mueller.eeg.backend.iam.domain.ParticipantType;
|
||||
import at.mueller.eeg.backend.iam.domain.User;
|
||||
import at.mueller.eeg.backend.iam.mapper.UserMapper;
|
||||
import at.mueller.eeg.backend.iam.repository.UserRepository;
|
||||
@ -62,6 +63,12 @@ public class AuthService {
|
||||
throw new RuntimeException("E-Mail already exists!");
|
||||
});
|
||||
|
||||
if (request.participantType() == ParticipantType.COMPANY
|
||||
&& (request.organizationName() == null || request.organizationName().isBlank())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Für juristische Personen ist ein Firmen-/Vereins-/Gemeindename erforderlich.");
|
||||
}
|
||||
|
||||
User newUser = userMapper.mapToEntity(request, new User());
|
||||
String token = UUID.randomUUID().toString();
|
||||
newUser.setVerificationToken(token);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package at.mueller.eeg.backend.backend;
|
||||
package at.mueller.eeg.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -0,0 +1,11 @@
|
||||
package at.mueller.eeg.backend.iam.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FakeMailService implements MailService{
|
||||
@Override
|
||||
public void sendVerificationEmail(String toEmail, String token) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -122,8 +122,7 @@ paths:
|
||||
tags:
|
||||
- Authentication
|
||||
summary: Registriert einen neuen User inkl. Zählpunkt
|
||||
description: Setzt den User auf Status PENDING und triggert den asynchronen
|
||||
EDA-Check.
|
||||
description: Setzt den User auf Status PENDING und versendet eine Verifizierungs-E-Mail.
|
||||
operationId: register
|
||||
requestBody:
|
||||
content:
|
||||
@ -220,6 +219,30 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EnergyCommunityDto"
|
||||
/api/dashboard/user:
|
||||
get:
|
||||
tags:
|
||||
- dashboard-controller
|
||||
operationId: getUserDashboard
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserStats"
|
||||
/api/dashboard/admin:
|
||||
get:
|
||||
tags:
|
||||
- dashboard-controller
|
||||
operationId: getAdminDashboard
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
'*/*':
|
||||
schema:
|
||||
$ref: "#/components/schemas/AdminStats"
|
||||
/api/community/metering-points/{id}/eligible-communities:
|
||||
get:
|
||||
tags:
|
||||
@ -241,6 +264,20 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/EligibleCommunityResponse"
|
||||
/api/auth/verify-email:
|
||||
get:
|
||||
tags:
|
||||
- Authentication
|
||||
operationId: verifyEmail
|
||||
parameters:
|
||||
- name: token
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
/api/admin/users/pending:
|
||||
get:
|
||||
tags:
|
||||
@ -306,14 +343,27 @@ components:
|
||||
type: object
|
||||
description: Payload für die Registrierung eines neuen Users
|
||||
properties:
|
||||
participantType:
|
||||
type: string
|
||||
description: Privatperson oder juristische Person (Firma/Verein/Gemeinde)
|
||||
enum:
|
||||
- PRIVATE
|
||||
- COMPANY
|
||||
example: PRIVATE
|
||||
firstName:
|
||||
type: string
|
||||
description: "Vorname (bei juristischer Person: Vorname der Ansprechperson)"
|
||||
example: Max
|
||||
minLength: 1
|
||||
lastName:
|
||||
type: string
|
||||
description: "Nachname (bei juristischer Person: Nachname der Ansprechperson)"
|
||||
example: Mustermann
|
||||
minLength: 1
|
||||
organizationName:
|
||||
type: string
|
||||
description: "Nur bei juristischer Person: Firmen-, Vereins- oder Gemeindename"
|
||||
example: Musterverein
|
||||
email:
|
||||
type: string
|
||||
format: email
|
||||
@ -324,16 +374,11 @@ components:
|
||||
example: SecurePass123!
|
||||
maxLength: 2147483647
|
||||
minLength: 8
|
||||
atNumber:
|
||||
type: string
|
||||
example: AT0010000000000000000000001234567
|
||||
minLength: 1
|
||||
pattern: "^AT[0-9]{31}$"
|
||||
required:
|
||||
- atNumber
|
||||
- email
|
||||
- firstName
|
||||
- lastName
|
||||
- participantType
|
||||
- password
|
||||
LoginRequest:
|
||||
type: object
|
||||
@ -366,6 +411,30 @@ components:
|
||||
type: string
|
||||
description: Rolle des Users
|
||||
example: USER
|
||||
UserStats:
|
||||
type: object
|
||||
properties:
|
||||
totalMeteringPoints:
|
||||
type: integer
|
||||
format: int64
|
||||
activeMemberships:
|
||||
type: integer
|
||||
format: int64
|
||||
pendingMemberships:
|
||||
type: integer
|
||||
format: int64
|
||||
AdminStats:
|
||||
type: object
|
||||
properties:
|
||||
totalCommunities:
|
||||
type: integer
|
||||
format: int64
|
||||
pendingUsers:
|
||||
type: integer
|
||||
format: int64
|
||||
totalActiveMemberships:
|
||||
type: integer
|
||||
format: int64
|
||||
EligibleCommunityResponse:
|
||||
type: object
|
||||
properties:
|
||||
@ -389,13 +458,18 @@ components:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
participantType:
|
||||
type: string
|
||||
enum:
|
||||
- PRIVATE
|
||||
- COMPANY
|
||||
firstName:
|
||||
type: string
|
||||
lastName:
|
||||
type: string
|
||||
organizationName:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
primaryAtNumber:
|
||||
type: string
|
||||
|
||||
@ -31,12 +31,14 @@
|
||||
{{ authService.currentUserRole() === 'ADMIN' ? 'Verwaltung' : 'Mein Bereich' }}
|
||||
</h1>
|
||||
|
||||
<div class="flex items-center gap-2 text-sm text-slate-600 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-200">
|
||||
<span class="relative flex h-2.5 w-2.5">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-emerald-500"></span>
|
||||
</span>
|
||||
{{ authService.currentUserRole() }}
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="text-sm text-gray-500">
|
||||
Rolle: <span class="font-semibold text-gray-700">{{ authService.currentUserRole() }}</span>
|
||||
</span>
|
||||
<button (click)="onLogout()"
|
||||
class="text-sm font-medium text-red-600 hover:text-red-700 border border-red-200 hover:border-red-300 px-3 py-1.5 rounded-md transition duration-150">
|
||||
Abmelden
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {Component, computed, inject} from '@angular/core';
|
||||
import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
|
||||
import {Router, RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';
|
||||
import {AuthService} from '../../services/auth';
|
||||
import {NAV_ITEMS} from './dashboard-nav-items';
|
||||
|
||||
@ -12,9 +12,15 @@ import {NAV_ITEMS} from './dashboard-nav-items';
|
||||
})
|
||||
export class DashboardLayout {
|
||||
authService = inject(AuthService);
|
||||
private router = inject(Router);
|
||||
|
||||
visibleNavItems = computed(() => {
|
||||
const role = this.authService.currentUserRole();
|
||||
return NAV_ITEMS.filter(item => role !== null && item.roles.includes(role));
|
||||
});
|
||||
|
||||
onLogout() {
|
||||
this.authService.logout();
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,6 @@ export class HeaderComponent {
|
||||
|
||||
onLogout() {
|
||||
this.authService.logout();
|
||||
this.router.navigate(['/login']);
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,12 +43,6 @@
|
||||
<div class="text-sm text-gray-500">{{ user.email }}</div>
|
||||
</td>
|
||||
|
||||
<td class="p-4">
|
||||
<div class="text-gray-900 font-mono text-sm">
|
||||
{{ user.primaryAtNumber || 'Kein Zählpunkt hinterlegt' }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="p-4">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
|
||||
{{ user.status }}
|
||||
|
||||
@ -7,104 +7,65 @@
|
||||
<h2 class="mt-2 text-xl font-semibold text-slate-600">Erstelle dein Mitgliedskonto</h2>
|
||||
</div>
|
||||
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-3xl bg-white shadow-md rounded-2xl border border-slate-100 overflow-hidden">
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-md bg-white shadow-md rounded-2xl border border-slate-100 overflow-hidden">
|
||||
|
||||
<mat-stepper linear #stepper class="!bg-transparent">
|
||||
@if (!registeredSuccessfully) {
|
||||
<form [formGroup]="accountForm" (ngSubmit)="onSubmit()" class="p-6 flex flex-col gap-4">
|
||||
<p class="text-sm text-slate-500 mb-2">
|
||||
Für die Registrierung reichen deine Kontaktdaten. Zählpunktnummer und IBAN kannst du nach der
|
||||
Freischaltung deines Kontos in deinem Mitgliederbereich hinterlegen.
|
||||
</p>
|
||||
|
||||
<mat-step [stepControl]="accountForm">
|
||||
<ng-template matStepLabel>Konto</ng-template>
|
||||
<form [formGroup]="accountForm" class="p-6 flex flex-col gap-4">
|
||||
<p class="text-sm text-slate-500 mb-2">Trage deine persönlichen Daten für den Portalzugang ein.</p>
|
||||
<mat-button-toggle-group formControlName="participantType" class="w-full mb-2">
|
||||
<mat-button-toggle value="PRIVATE" class="flex-1">Privatperson</mat-button-toggle>
|
||||
<mat-button-toggle value="COMPANY" class="flex-1">Firma / Verein / Gemeinde</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
|
||||
@if (isCompany) {
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Vollständiger Name / Vereinsname</mat-label>
|
||||
<input matInput formControlName="name" placeholder="Max Mustermann">
|
||||
<mat-label>Firmen-, Vereins- oder Gemeindename</mat-label>
|
||||
<input matInput formControlName="organizationName" placeholder="Musterverein">
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>{{ isCompany ? 'Vorname (Ansprechperson)' : 'Vorname' }}</mat-label>
|
||||
<input matInput formControlName="firstName" placeholder="Max">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>E-Mail-Adresse</mat-label>
|
||||
<input matInput type="email" formControlName="email" placeholder="max@beispiel.at">
|
||||
<mat-label>{{ isCompany ? 'Nachname (Ansprechperson)' : 'Nachname' }}</mat-label>
|
||||
<input matInput formControlName="lastName" placeholder="Mustermann">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Passwort</mat-label>
|
||||
<input matInput type="password" formControlName="password">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>E-Mail-Adresse</mat-label>
|
||||
<input matInput type="email" formControlName="email" placeholder="max@beispiel.at">
|
||||
</mat-form-field>
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<button mat-flat-button color="primary" matStepperNext type="button">Weiter</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Passwort</mat-label>
|
||||
<input matInput type="password" formControlName="password">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-step [stepControl]="energyForm">
|
||||
<ng-template matStepLabel>Anlagendaten</ng-template>
|
||||
<form [formGroup]="energyForm" class="p-6 flex flex-col gap-4">
|
||||
<p class="text-sm text-slate-500 mb-2">Diese Daten benötigen wir für die Zuordnung beim Netzbetreiber.</p>
|
||||
<div class="flex justify-end mt-4">
|
||||
<button mat-flat-button color="accent" type="submit" [disabled]="accountForm.invalid">
|
||||
Registrierung abschließen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Zählpunktnummer (33-stellig)</mat-label>
|
||||
<input matInput formControlName="accountingPointId" placeholder="AT0010000000000000000000000000000" maxlength="35">
|
||||
<mat-hint>Beginnt mit AT und findest du auf deiner Stromrechnung.</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-2">
|
||||
<mat-form-field appearance="outline" class="md:col-span-2">
|
||||
<mat-label>Straße & Hausnummer</mat-label>
|
||||
<input matInput formControlName="street">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>PLZ</mat-label>
|
||||
<input matInput formControlName="zip">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>Ort</mat-label>
|
||||
<input matInput formControlName="city">
|
||||
</mat-form-field>
|
||||
|
||||
<div class="flex justify-between mt-4">
|
||||
<button mat-button matStepperPrevious type="button">Zurück</button>
|
||||
<button mat-flat-button color="primary" matStepperNext type="button">Weiter</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="paymentForm">
|
||||
<ng-template matStepLabel>Abrechnung</ng-template>
|
||||
<form [formGroup]="paymentForm" (ngSubmit)="onSubmit()" class="p-6 flex flex-col gap-4">
|
||||
<p class="text-sm text-slate-500 mb-2">Für den Einzug der Netzkosten-Ersparnisse/Beiträge sowie Gutschriften für Einspeisung.</p>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>IBAN</mat-label>
|
||||
<input matInput formControlName="iban" placeholder="AT.. .... .... .... ....">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="w-full">
|
||||
<mat-label>BIC (Optional)</mat-label>
|
||||
<input matInput formControlName="bic">
|
||||
</mat-form-field>
|
||||
|
||||
<div class="bg-slate-50 p-4 rounded-xl border border-slate-200 text-xs text-slate-600 my-2 space-y-2">
|
||||
<p class="font-bold">SEPA-Lastschriftmandat (Creditor ID: ATXXXXXXXXXXXXXXXX)</p>
|
||||
<p>Ich ermächtige die Energiegemeinschaft, Zahlungen von meinem Konto mittels Lastschrift einzuziehen. Zugleich weise ich mein Kreditinstitut an, die eingelösten Lastschriften zu bezahlen.</p>
|
||||
<p class="italic text-slate-400">Hinweis: Ich kann innerhalb von 8 Wochen, beginnend mit dem Belastungsdatum, die Erstattung des belasteten Betrages verlangen.</p>
|
||||
</div>
|
||||
|
||||
<mat-checkbox formControlName="acceptMandate" color="primary" class="text-sm">
|
||||
Ich erteile das SEPA-Lastschriftmandat elektronisch.
|
||||
</mat-checkbox>
|
||||
|
||||
<div class="flex justify-between mt-4">
|
||||
<button mat-button matStepperPrevious type="button">Zurück</button>
|
||||
<button mat-flat-button color="accent" type="submit" [disabled]="accountForm.invalid || energyForm.invalid || paymentForm.invalid">
|
||||
Registrierung abschließen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
</mat-stepper>
|
||||
@if (registeredSuccessfully) {
|
||||
<div class="p-8 text-center">
|
||||
<h3 class="text-lg font-semibold text-slate-800 mb-2">Fast geschafft!</h3>
|
||||
<p class="text-sm text-slate-500">
|
||||
Wir haben dir eine Bestätigungs-E-Mail geschickt. Bitte verifiziere deine E-Mail-Adresse.
|
||||
Anschließend prüft ein Administrator dein Konto und schaltet es frei.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
import {Component, inject} from '@angular/core';
|
||||
import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {MatStepperModule} from '@angular/material/stepper';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {Router} from '@angular/router';
|
||||
import {AuthenticationService, RegistrationRequest} from '../../api';
|
||||
import {MatButtonToggleModule} from '@angular/material/button-toggle';
|
||||
import {AuthenticationService, RegistrationRequest, UserProfileResponse} from '../../api';
|
||||
import {RouterLink} from '@angular/router';
|
||||
import ParticipantTypeEnum = UserProfileResponse.ParticipantTypeEnum;
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
standalone: true,
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
MatStepperModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatCheckboxModule
|
||||
MatButtonToggleModule,
|
||||
RouterLink
|
||||
],
|
||||
templateUrl: './register.html',
|
||||
styleUrls: ['./register.scss']
|
||||
@ -27,52 +27,62 @@ import {AuthenticationService, RegistrationRequest} from '../../api';
|
||||
export class Register {
|
||||
private fb = inject(FormBuilder);
|
||||
private authService = inject(AuthenticationService);
|
||||
private router = inject(Router);
|
||||
|
||||
// Schritt 1: Login & Stammdaten
|
||||
// Für die Toggle-Buttons im Template
|
||||
readonly ParticipantType = RegistrationRequest.ParticipantTypeEnum;
|
||||
|
||||
registeredSuccessfully = false;
|
||||
|
||||
accountForm = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
participantType: this.fb.control<ParticipantTypeEnum>(
|
||||
RegistrationRequest.ParticipantTypeEnum.Private,
|
||||
{nonNullable: true, validators: Validators.required}
|
||||
),
|
||||
organizationName: [''],
|
||||
firstName: ['', Validators.required],
|
||||
lastName: ['', Validators.required],
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
password: ['', [Validators.required, Validators.minLength(8)]]
|
||||
});
|
||||
|
||||
// Schritt 2: Energiegemeinschaft-Spezifische Daten
|
||||
energyForm = this.fb.group({
|
||||
accountingPointId: ['', [Validators.required, Validators.pattern(/^AT\d{31}$/)]], // AT + 33 Ziffern für Zählpunkt
|
||||
street: ['', Validators.required],
|
||||
zip: ['', Validators.required],
|
||||
city: ['', Validators.required]
|
||||
});
|
||||
constructor() {
|
||||
this.accountForm.get('participantType')!.valueChanges.subscribe(type => {
|
||||
const orgControl = this.accountForm.get('organizationName')!;
|
||||
if (type === RegistrationRequest.ParticipantTypeEnum.Company) {
|
||||
orgControl.setValidators([Validators.required]);
|
||||
} else {
|
||||
orgControl.clearValidators();
|
||||
}
|
||||
orgControl.updateValueAndValidity();
|
||||
});
|
||||
}
|
||||
|
||||
// Schritt 3: SEPA Lastschrift / Mandat
|
||||
paymentForm = this.fb.group({
|
||||
iban: ['', [Validators.required, Validators.pattern(/^[A-Z]{2}[0-9]{2}[A-Z0-9]{12,30}$/)]],
|
||||
bic: [''],
|
||||
acceptMandate: [false, Validators.requiredTrue]
|
||||
});
|
||||
get isCompany(): boolean {
|
||||
return this.accountForm.value.participantType === RegistrationRequest.ParticipantTypeEnum.Company;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.accountForm.valid && this.energyForm.valid && this.paymentForm.valid) {
|
||||
const registrationData: RegistrationRequest = {
|
||||
firstName: this.accountForm.value.name!,
|
||||
lastName: this.accountForm.value.name!,
|
||||
email: this.accountForm.value.email!,
|
||||
password: this.accountForm.value.password!,
|
||||
atNumber: this.energyForm.value.accountingPointId!
|
||||
};
|
||||
console.log('Registrierungsdaten für Backend:', registrationData);
|
||||
|
||||
this.authService.register(registrationData).subscribe({
|
||||
next: (response) => {
|
||||
console.log('Erfolgreich registriert!', response);
|
||||
// Bei Erfolg z.B. auf eine Bestätigungsseite oder zum Login leiten
|
||||
// this.router.navigate(['/login']);
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Fehler bei der Registrierung', err);
|
||||
// Hier könntest du später einen Angular Material SnackBar einblenden
|
||||
}
|
||||
});
|
||||
if (this.accountForm.invalid) {
|
||||
this.accountForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
const registrationData: RegistrationRequest = {
|
||||
participantType: this.accountForm.value.participantType!,
|
||||
firstName: this.accountForm.value.firstName!,
|
||||
lastName: this.accountForm.value.lastName!,
|
||||
organizationName: this.accountForm.value.organizationName || undefined,
|
||||
email: this.accountForm.value.email!,
|
||||
password: this.accountForm.value.password!
|
||||
};
|
||||
|
||||
this.authService.register(registrationData).subscribe({
|
||||
next: () => {
|
||||
this.registeredSuccessfully = true;
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('Fehler bei der Registrierung', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user