feat: Add Preistabelle for admin to manage monthly system price

- Add SystemPricing domain model to store monthly price in MongoDB
- Add SystemPricingRepository for database access
- Add SystemPricingService with get/save functionality
- Add SystemPricingController with GET/POST endpoints (admin only)
- Add PricingPage component for frontend
- Add navigation menu item for Preistabelle (above Rechnung)
- Add route /admin/preistabelle for the new page
This commit is contained in:
2026-03-17 21:18:27 +01:00
parent 571019d34b
commit 49b1a3b363
7 changed files with 269 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
package de.svencarstensen.muh.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.time.LocalDateTime;
@Document("systemPricing")
public record SystemPricing(
@Id String id,
Double monthlyPrice,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
}

View File

@@ -0,0 +1,7 @@
package de.svencarstensen.muh.repository;
import de.svencarstensen.muh.domain.SystemPricing;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface SystemPricingRepository extends MongoRepository<SystemPricing, String> {
}

View File

@@ -0,0 +1,38 @@
package de.svencarstensen.muh.service;
import de.svencarstensen.muh.domain.SystemPricing;
import de.svencarstensen.muh.repository.SystemPricingRepository;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Optional;
@Service
public class SystemPricingService {
private static final String PRICING_ID = "default";
private final SystemPricingRepository systemPricingRepository;
public SystemPricingService(SystemPricingRepository systemPricingRepository) {
this.systemPricingRepository = systemPricingRepository;
}
public Optional<SystemPricing> getCurrentPricing() {
return systemPricingRepository.findById(PRICING_ID);
}
public SystemPricing savePricing(Double monthlyPrice) {
LocalDateTime now = LocalDateTime.now();
Optional<SystemPricing> existing = systemPricingRepository.findById(PRICING_ID);
SystemPricing pricing = new SystemPricing(
PRICING_ID,
monthlyPrice,
existing.map(SystemPricing::createdAt).orElse(now),
now
);
return systemPricingRepository.save(pricing);
}
}

View File

@@ -0,0 +1,39 @@
package de.svencarstensen.muh.web;
import de.svencarstensen.muh.domain.SystemPricing;
import de.svencarstensen.muh.service.SystemPricingService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@RestController
@RequestMapping("/api/admin/pricing")
@PreAuthorize("hasRole('ADMIN')")
public class SystemPricingController {
private final SystemPricingService systemPricingService;
public SystemPricingController(SystemPricingService systemPricingService) {
this.systemPricingService = systemPricingService;
}
@GetMapping
public PricingResponse getPricing() {
Optional<SystemPricing> pricing = systemPricingService.getCurrentPricing();
return pricing.map(p -> new PricingResponse(p.monthlyPrice(), p.updatedAt().toString()))
.orElseGet(() -> new PricingResponse(null, null));
}
@PostMapping
public PricingResponse savePricing(@RequestBody PricingRequest request) {
SystemPricing saved = systemPricingService.savePricing(request.monthlyPrice());
return new PricingResponse(saved.monthlyPrice(), saved.updatedAt().toString());
}
public record PricingRequest(Double monthlyPrice) {
}
public record PricingResponse(Double monthlyPrice, String updatedAt) {
}
}