DEV Community

prem Prema
prem Prema

Posted on

RoughNote

we are creating the Donation management app using springboot+postgres+react

This is Spring boot code

1_________________________________________________________
package com.maariyathaa.temple;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MaariyathaaApplication {
public static void main(String[] args) {
SpringApplication.run(MaariyathaaApplication.class, args);
}
}

2________________________________________________________

package com.maariyathaa.temple.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {

@Bean public OpenAPI customOpenAPI() { return new OpenAPI() .info(new Info() .title("Maariyathaa Temple Donation Management API") .version("1.0") .description("API for managing donations, families, and volunteers for the Maariyathaa Temple") .contact(new Contact() .name("Maariyathaa Temple Support") .email("support@maariyathaatemple.com")) .license(new License() .name("Apache 2.0") .url("https://www.apache.org/licenses/LICENSE-2.0.html"))); } 
Enter fullscreen mode Exit fullscreen mode

}

3___________________________________________

package com.maariyathaa.temple.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig {
@bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/*")
.allowedOrigins("http://localhost:3000", "http://localhost:8080")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("
")
.allowCredentials(true);
}
};
}
}


package com.maariyathaa.temple.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;

@data
@Entity
@Table(name = "donations")
public class Donation {
@id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "serial_no", unique = true) private String serialNo; @NotNull(message = "Donation date is mandatory") @Column(name = "donation_date", nullable = false) private LocalDate donationDate; @NotNull(message = "Family is mandatory") @ManyToOne @JoinColumn(name = "family_id", nullable = false) private Family family; @ManyToOne @JoinColumn(name = "volunteer_id") private Volunteer volunteer; @NotNull(message = "Amount is mandatory") @Positive(message = "Amount must be positive") @Column(nullable = false) private BigDecimal amount; @Column(name = "payment_type") private String paymentType; // "CASH" or "TRANSFER" @Column(name = "installment_number") private Integer installmentNumber; @Column(name = "total_installments") private Integer totalInstallments; @Column(name = "photo_url") private String photoUrl; @Column(name = "giver_sign_url") private String giverSignUrl; @Column(name = "volunteer_sign_url") private String volunteerSignUrl; @Column(name = "receiver_sign_url") private String receiverSignUrl; private String notes; private String status; @Column(name = "created_at") private LocalDateTime createdAt; @Column(name = "updated_at") private LocalDateTime updatedAt; // ... rest of the class remains the same @PrePersist protected void onCreate() { createdAt = LocalDateTime.now(); updatedAt = LocalDateTime.now(); if (serialNo == null) { serialNo = "DON" + System.currentTimeMillis(); } if (status == null) { status = "PENDING"; } if (installmentNumber == null) { installmentNumber = 1; } if (totalInstallments == null) { totalInstallments = 1; } } public Family getFamily() { return family; } public void setFamily(Family family) { this.family = family; } public Volunteer getVolunteer() { return volunteer; } public void setVolunteer(Volunteer volunteer) { this.volunteer = volunteer; } @PreUpdate protected void onUpdate() { updatedAt = LocalDateTime.now(); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;

@Entity
@Table(name = "families")
public class Family {
@id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotBlank(message = "Family name is mandatory") @Column(nullable = false) private String name; @Column(name = "house_no") private String houseNo; @Column(name = "street_name") private String streetName; private String address; @Pattern(regexp = "^[\\+]?[1-9][\\d]{0,15}$", message = "Phone number format is invalid") private String phone; // Default constructor public Family() { } // Old constructor (backward compatibility) public Family(String name, String address, String phone) { this.name = name; this.address = address; this.phone = phone; } // New constructor (all fields except id) public Family(String name, String houseNo, String streetName, String address, String phone) { this.name = name; this.houseNo = houseNo; this.streetName = streetName; this.address = address; this.phone = phone; } // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHouseNo() { return houseNo; } public void setHouseNo(String houseNo) { this.houseNo = houseNo; } public String getStreetName() { return streetName; } public void setStreetName(String streetName) { this.streetName = streetName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDateTime;

@data
@Entity
@Table(name = "notifications")
public class Notification {
@id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull(message = "Family is mandatory") @ManyToOne @JoinColumn(name = "family_id", nullable = false) private Family family; @NotBlank(message = "Message is mandatory") private String message; @Column(name = "message_type") private String messageType; private String status; @Column(name = "sent_at") private LocalDateTime sentAt; @Column(name = "created_at") private LocalDateTime createdAt; @PrePersist protected void onCreate() { createdAt = LocalDateTime.now(); if (status == null) { status = "PENDING"; } } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;

@Entity
@Table(name = "volunteers")
public class Volunteer {
@id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotBlank(message = "Volunteer name is mandatory") @Column(nullable = false) private String name; @Pattern(regexp = "^[\\+]?[1-9][\\d]{0,15}$", message = "Phone number format is invalid") private String phone; // Default constructor public Volunteer() { } // Parameterized constructor public Volunteer(String name, String phone) { this.name = name; this.phone = phone; } // Getters and setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ControllerAdvice
public class GlobalExceptionHandler {

private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) { logger.warn("Resource not found: {}", ex.getMessage()); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); } @ExceptionHandler(ValidationException.class) public ResponseEntity<String> handleValidationException(ValidationException ex) { logger.warn("Validation error: {}", ex.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); } @ExceptionHandler(Exception.class) public ResponseEntity<String> handleGeneralException(Exception ex) { logger.error("Unexpected error occurred: ", ex); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("An unexpected error occurred. Please try again later."); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.exception;

public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}


package com.maariyathaa.temple.exception;

public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message);
}
}


package com.maariyathaa.temple.repository;

import com.maariyathaa.temple.domain.Donation;
import com.maariyathaa.temple.domain.Family;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface DonationRepository extends JpaRepository {
List findByFamily(Family family);
List findByFamilyId(Long familyId);

// Pagination methods Page<Donation> findByFamilyId(Long familyId, Pageable pageable); Page<Donation> findAll(Pageable pageable); // Additional pagination methods you might find useful Page<Donation> findByStatus(String status, Pageable pageable); @Query("SELECT d FROM Donation d WHERE d.family.id = :familyId AND d.status = :status") Page<Donation> findByFamilyIdAndStatus(@Param("familyId") Long familyId, @Param("status") String status, Pageable pageable); 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.repository;

import com.maariyathaa.temple.domain.Family;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface FamilyRepository extends JpaRepository {
// Basic pagination
Page findAll(Pageable pageable);

// Search with pagination Page<Family> findByNameContainingIgnoreCase(String name, Pageable pageable); @Query("SELECT f FROM Family f WHERE f.name LIKE %:searchTerm% OR f.address LIKE %:searchTerm%") Page<Family> searchFamilies(@Param("searchTerm") String searchTerm, Pageable pageable); 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.repository;

import com.maariyathaa.temple.domain.Notification;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface NotificationRepository extends JpaRepository {
// Basic pagination
Page findAll(Pageable pageable);

// Filter by status with pagination Page<Notification> findByStatus(String status, Pageable pageable); // Filter by family with pagination Page<Notification> findByFamilyId(Long familyId, Pageable pageable); @Query("SELECT n FROM Notification n WHERE n.family.id = :familyId AND n.status = :status") Page<Notification> findByFamilyIdAndStatus(@Param("familyId") Long familyId, @Param("status") String status, Pageable pageable); 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.repository;

import com.maariyathaa.temple.domain.Volunteer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface VolunteerRepository extends JpaRepository {
// Basic pagination
Page findAll(Pageable pageable);

// Search with pagination Page<Volunteer> findByNameContainingIgnoreCase(String name, Pageable pageable); @Query("SELECT v FROM Volunteer v WHERE v.name LIKE %:searchTerm% OR v.phone LIKE %:searchTerm%") Page<Volunteer> searchVolunteers(@Param("searchTerm") String searchTerm, Pageable pageable); 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.service;

import com.maariyathaa.temple.domain.Donation;
import com.maariyathaa.temple.domain.Family;
import com.maariyathaa.temple.domain.Volunteer;
import com.maariyathaa.temple.exception.ResourceNotFoundException;
import com.maariyathaa.temple.repository.DonationRepository;
import com.maariyathaa.temple.repository.FamilyRepository;
import com.maariyathaa.temple.repository.VolunteerRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.HashMap;

@Service
public class DonationService {
private final DonationRepository donationRepository;
private final FamilyRepository familyRepository;
private final VolunteerRepository volunteerRepository;

private static final BigDecimal TARGET_AMOUNT = new BigDecimal("3000"); public DonationService(DonationRepository donationRepository, FamilyRepository familyRepository, VolunteerRepository volunteerRepository) { this.donationRepository = donationRepository; this.familyRepository = familyRepository; this.volunteerRepository = volunteerRepository; } // Existing methods... // Add these pagination methods: public Page<Donation> getAllDonations(Pageable pageable) { return donationRepository.findAll(pageable); } public Page<Donation> getDonationsByFamilyId(Long familyId, Pageable pageable) { return donationRepository.findByFamilyId(familyId, pageable); } public Page<Donation> getDonationsByStatus(String status, Pageable pageable) { return donationRepository.findByStatus(status, pageable); } // ... existing methods ... public Donation saveDonation(Donation donation) { // Validate family exists - USE ResourceNotFoundException Family family = familyRepository.findById(donation.getFamily().getId()) .orElseThrow(() -> new ResourceNotFoundException("Family not found with id: " + donation.getFamily().getId())); donation.setFamily(family); // This line stays the same // Validate volunteer exists if provided - USE ResourceNotFoundException if (donation.getVolunteer() != null && donation.getVolunteer().getId() != null) { Volunteer volunteer = volunteerRepository.findById(donation.getVolunteer().getId()) .orElseThrow(() -> new ResourceNotFoundException("Volunteer not found with id: " + donation.getVolunteer().getId())); donation.setVolunteer(volunteer); } else { donation.setVolunteer(null); } // Rest of the method remains exactly the same // Get existing donations BEFORE saving the current one List<Donation> familyDonations = donationRepository.findByFamilyId(family.getId()); // Calculate total paid by this family (excluding current donation) BigDecimal totalPaid = familyDonations.stream() .map(Donation::getAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); // Add current donation amount to total totalPaid = totalPaid.add(donation.getAmount()); // Determine payment status if (totalPaid.compareTo(BigDecimal.ZERO) == 0) { donation.setStatus("NOT_PAID"); } else if (totalPaid.compareTo(TARGET_AMOUNT) < 0) { donation.setStatus("PARTIAL"); } else { donation.setStatus("COMPLETED"); } return donationRepository.save(donation); } public Map<String, Object> getFamilyPaymentStatus(Long familyId) { Family family = familyRepository.findById(familyId) .orElseThrow(() -> new RuntimeException("Family not found with id: " + familyId)); List<Donation> donations = donationRepository.findByFamilyId(familyId); BigDecimal totalPaid = donations.stream() .map(Donation::getAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal remainingAmount = TARGET_AMOUNT.subtract(totalPaid); if (remainingAmount.compareTo(BigDecimal.ZERO) < 0) { remainingAmount = BigDecimal.ZERO; } String status; if (totalPaid.compareTo(BigDecimal.ZERO) == 0) { status = "NOT_PAID"; } else if (totalPaid.compareTo(TARGET_AMOUNT) < 0) { status = "PARTIAL"; } else { status = "COMPLETED"; } Map<String, Object> statusInfo = new HashMap<>(); statusInfo.put("familyId", familyId); statusInfo.put("familyName", family.getName()); statusInfo.put("totalPaid", totalPaid); statusInfo.put("targetAmount", TARGET_AMOUNT); statusInfo.put("remainingAmount", remainingAmount); statusInfo.put("status", status); statusInfo.put("installments", donations.size()); statusInfo.put("donations", donations); return statusInfo; } public Map<Long, Map<String, Object>> getAllFamiliesPaymentStatus() { List<Family> families = familyRepository.findAll(); Map<Long, Map<String, Object>> statusMap = new HashMap<>(); for (Family family : families) { statusMap.put(family.getId(), getFamilyPaymentStatus(family.getId())); } return statusMap; } public List<Donation> getPendingPayments() { List<Donation> allDonations = donationRepository.findAll(); // Filter families that haven't completed payment return allDonations.stream() .filter(donation -> { List<Donation> familyDonations = donationRepository.findByFamilyId(donation.getFamily().getId()); BigDecimal totalPaid = familyDonations.stream() .map(Donation::getAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); return totalPaid.compareTo(TARGET_AMOUNT) < 0; }) .collect(Collectors.toList()); } 
Enter fullscreen mode Exit fullscreen mode

// Add these to DonationService
public List getAllDonations() {
return donationRepository.findAll();
}

public Donation getDonationById(Long id) { return donationRepository.findById(id) .orElseThrow(() -> new RuntimeException("Donation not found with id: " + id)); } public List<Donation> getDonationsByFamilyId(Long familyId) { return donationRepository.findByFamilyId(familyId); } public void deleteDonation(Long id) { donationRepository.deleteById(id); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.service;

import com.maariyathaa.temple.domain.Family;
import com.maariyathaa.temple.repository.FamilyRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class FamilyService {
private final FamilyRepository familyRepository;

public FamilyService(FamilyRepository familyRepository) { this.familyRepository = familyRepository; } // Add pagination methods: public Page<Family> getAllFamilies(Pageable pageable) { return familyRepository.findAll(pageable); } public Page<Family> searchFamilies(String searchTerm, Pageable pageable) { return familyRepository.searchFamilies(searchTerm, pageable); } // Keep existing methods: public List<Family> getAllFamilies() { return familyRepository.findAll(); } public Family getFamilyById(Long id) { return familyRepository.findById(id) .orElseThrow(() -> new RuntimeException("Family not found with id: " + id)); } public Family saveFamily(Family family) { return familyRepository.save(family); } public void deleteFamily(Long id) { familyRepository.deleteById(id); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.service;

import com.maariyathaa.temple.domain.Notification;
import com.maariyathaa.temple.repository.NotificationRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class NotificationService {
private final NotificationRepository notificationRepository;

public NotificationService(NotificationRepository notificationRepository) { this.notificationRepository = notificationRepository; } // Pagination methods: public Page<Notification> getAllNotifications(Pageable pageable) { return notificationRepository.findAll(pageable); } public Page<Notification> getNotificationsByStatus(String status, Pageable pageable) { return notificationRepository.findByStatus(status, pageable); } public Page<Notification> getNotificationsByFamilyId(Long familyId, Pageable pageable) { return notificationRepository.findByFamilyId(familyId, pageable); } // Regular methods: public List<Notification> getAllNotifications() { return notificationRepository.findAll(); } public Notification getNotificationById(Long id) { return notificationRepository.findById(id) .orElseThrow(() -> new RuntimeException("Notification not found with id: " + id)); } public Notification saveNotification(Notification notification) { return notificationRepository.save(notification); } public void deleteNotification(Long id) { notificationRepository.deleteById(id); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.service;

import com.maariyathaa.temple.domain.Volunteer;
import com.maariyathaa.temple.repository.VolunteerRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class VolunteerService {
private final VolunteerRepository volunteerRepository;

public VolunteerService(VolunteerRepository volunteerRepository) { this.volunteerRepository = volunteerRepository; } // Add pagination methods: public Page<Volunteer> getAllVolunteers(Pageable pageable) { return volunteerRepository.findAll(pageable); } public Page<Volunteer> searchVolunteers(String searchTerm, Pageable pageable) { return volunteerRepository.searchVolunteers(searchTerm, pageable); } // Keep existing methods: public List<Volunteer> getAllVolunteers() { return volunteerRepository.findAll(); } public Volunteer getVolunteerById(Long id) { return volunteerRepository.findById(id) .orElseThrow(() -> new RuntimeException("Volunteer not found with id: " + id)); } public Volunteer saveVolunteer(Volunteer volunteer) { return volunteerRepository.save(volunteer); } public void deleteVolunteer(Long id) { volunteerRepository.deleteById(id); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.web;

import com.maariyathaa.temple.domain.Donation;
import com.maariyathaa.temple.service.DonationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/donations")
@RequiredArgsConstructor
public class DonationController {
private final DonationService donationService;

@Operation(summary = "Get all donations with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Donations retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping public ResponseEntity<Page<Donation>> getAllDonations( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "donationDate") String sortBy, @RequestParam(defaultValue = "desc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(donationService.getAllDonations(pageable)); } @Operation(summary = "Get donation by ID") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Donation retrieved successfully"), @ApiResponse(responseCode = "404", description = "Donation not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/{id}") public ResponseEntity<Donation> getDonationById(@PathVariable Long id) { return ResponseEntity.ok(donationService.getDonationById(id)); } @Operation(summary = "Create a new donation") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Donation created successfully"), @ApiResponse(responseCode = "400", description = "Invalid input"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @PostMapping public ResponseEntity<Donation> createDonation(@Valid @RequestBody Donation donation) { return new ResponseEntity<>(donationService.saveDonation(donation), HttpStatus.CREATED); } @Operation(summary = "Delete a donation") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Donation deleted successfully"), @ApiResponse(responseCode = "404", description = "Donation not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @DeleteMapping("/{id}") public ResponseEntity<Void> deleteDonation(@PathVariable Long id) { donationService.deleteDonation(id); return ResponseEntity.noContent().build(); } @Operation(summary = "Get donations by family ID with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Donations retrieved successfully"), @ApiResponse(responseCode = "404", description = "Family not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/family/{familyId}") public ResponseEntity<Page<Donation>> getDonationsByFamily( @PathVariable Long familyId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "donationDate") String sortBy, @RequestParam(defaultValue = "desc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(donationService.getDonationsByFamilyId(familyId, pageable)); } @Operation(summary = "Get donations by status with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Donations retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/status/{status}") public ResponseEntity<Page<Donation>> getDonationsByStatus( @PathVariable String status, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "donationDate") String sortBy, @RequestParam(defaultValue = "desc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(donationService.getDonationsByStatus(status, pageable)); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.web;

import com.maariyathaa.temple.domain.Family;
import com.maariyathaa.temple.service.FamilyService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/families")
@RequiredArgsConstructor
public class FamilyController {
private final FamilyService familyService;

@Operation(summary = "Get all families with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Families retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping public ResponseEntity<Page<Family>> getAllFamilies( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "name") String sortBy, @RequestParam(defaultValue = "asc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(familyService.getAllFamilies(pageable)); } @Operation(summary = "Get family by ID") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Family retrieved successfully"), @ApiResponse(responseCode = "404", description = "Family not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/{id}") public ResponseEntity<Family> getFamilyById(@PathVariable Long id) { return ResponseEntity.ok(familyService.getFamilyById(id)); } @Operation(summary = "Create a new family") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Family created successfully"), @ApiResponse(responseCode = "400", description = "Invalid input"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @PostMapping public ResponseEntity<Family> createFamily(@Valid @RequestBody Family family) { return new ResponseEntity<>(familyService.saveFamily(family), HttpStatus.CREATED); } @Operation(summary = "Delete a family") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Family deleted successfully"), @ApiResponse(responseCode = "404", description = "Family not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @DeleteMapping("/{id}") public ResponseEntity<Void> deleteFamily(@PathVariable Long id) { familyService.deleteFamily(id); return ResponseEntity.noContent().build(); } @Operation(summary = "Search families with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Families retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/search") public ResponseEntity<Page<Family>> searchFamilies( @RequestParam String searchTerm, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "name") String sortBy, @RequestParam(defaultValue = "asc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(familyService.searchFamilies(searchTerm, pageable)); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.web;

import com.maariyathaa.temple.domain.Notification;
import com.maariyathaa.temple.service.NotificationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/notifications")
@RequiredArgsConstructor
public class NotificationController {
private final NotificationService notificationService;

@Operation(summary = "Get all notifications with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Notifications retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping public ResponseEntity<Page<Notification>> getAllNotifications( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "createdAt") String sortBy, @RequestParam(defaultValue = "desc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(notificationService.getAllNotifications(pageable)); } @Operation(summary = "Get notifications by status with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Notifications retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/status/{status}") public ResponseEntity<Page<Notification>> getNotificationsByStatus( @PathVariable String status, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "createdAt") String sortBy, @RequestParam(defaultValue = "desc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(notificationService.getNotificationsByStatus(status, pageable)); } @Operation(summary = "Get notifications by family ID with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Notifications retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/family/{familyId}") public ResponseEntity<Page<Notification>> getNotificationsByFamilyId( @PathVariable Long familyId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "createdAt") String sortBy, @RequestParam(defaultValue = "desc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(notificationService.getNotificationsByFamilyId(familyId, pageable)); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.web;

import com.maariyathaa.temple.service.DonationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/payments")
@RequiredArgsConstructor
public class PaymentController {
private final DonationService donationService;

@Operation(summary = "Get payment status for a family") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Payment status retrieved successfully"), @ApiResponse(responseCode = "404", description = "Family not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/status/family/{familyId}") public ResponseEntity<Map<String, Object>> getFamilyPaymentStatus(@PathVariable Long familyId) { return ResponseEntity.ok(donationService.getFamilyPaymentStatus(familyId)); } @Operation(summary = "Get payment status for all families") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Payment statuses retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/status/all") public ResponseEntity<Map<Long, Map<String, Object>>> getAllFamiliesPaymentStatus() { return ResponseEntity.ok(donationService.getAllFamiliesPaymentStatus()); } @Operation(summary = "Get pending payments") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Pending payments retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/pending") public ResponseEntity<?> getPendingPayments() { return ResponseEntity.ok(donationService.getPendingPayments()); } 
Enter fullscreen mode Exit fullscreen mode

}


package com.maariyathaa.temple.web;

import com.maariyathaa.temple.domain.Volunteer;
import com.maariyathaa.temple.service.VolunteerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/volunteers")
@RequiredArgsConstructor
public class VolunteerController {
private final VolunteerService volunteerService;

@Operation(summary = "Get all volunteers with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Volunteers retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping public ResponseEntity<Page<Volunteer>> getAllVolunteers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "name") String sortBy, @RequestParam(defaultValue = "asc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(volunteerService.getAllVolunteers(pageable)); } @Operation(summary = "Get volunteer by ID") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Volunteer retrieved successfully"), @ApiResponse(responseCode = "404", description = "Volunteer not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/{id}") public ResponseEntity<Volunteer> getVolunteerById(@PathVariable Long id) { return ResponseEntity.ok(volunteerService.getVolunteerById(id)); } @Operation(summary = "Create a new volunteer") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Volunteer created successfully"), @ApiResponse(responseCode = "400", description = "Invalid input"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @PostMapping public ResponseEntity<Volunteer> createVolunteer(@Valid @RequestBody Volunteer volunteer) { return new ResponseEntity<>(volunteerService.saveVolunteer(volunteer), HttpStatus.CREATED); } @Operation(summary = "Delete a volunteer") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Volunteer deleted successfully"), @ApiResponse(responseCode = "404", description = "Volunteer not found"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @DeleteMapping("/{id}") public ResponseEntity<Void> deleteVolunteer(@PathVariable Long id) { volunteerService.deleteVolunteer(id); return ResponseEntity.noContent().build(); } @Operation(summary = "Search volunteers with pagination") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Volunteers retrieved successfully"), @ApiResponse(responseCode = "500", description = "Internal server error") }) @GetMapping("/search") public ResponseEntity<Page<Volunteer>> searchVolunteers( @RequestParam String searchTerm, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "name") String sortBy, @RequestParam(defaultValue = "asc") String direction) { Sort.Direction sortDirection = "desc".equalsIgnoreCase(direction) ? Sort.Direction.DESC : Sort.Direction.ASC; Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); return ResponseEntity.ok(volunteerService.searchVolunteers(searchTerm, pageable)); } 
Enter fullscreen mode Exit fullscreen mode

}

___________________________________________________________________# Application name
spring.application.name=Maariyathaa

Database configuration

spring.datasource.url=jdbc:postgresql://localhost:5432/maariyathaa
spring.datasource.username=maariyathaa
spring.datasource.password=maariyathaa

JPA configuration

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

Server port

server.port=8080

Better JSON output

spring.jackson.serialization.indent-output=true


<?xml version="1.0" encoding="UTF-8"?>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0

org.springframework.boot
spring-boot-starter-parent
3.5.5
<!-- lookup parent from repository -->

com.maariyathaa
maariyathaa
0.0.1-SNAPSHOT
Maariyathaa
Temple donation management system














17



org.springframework.boot
spring-boot-starter-data-jpa


org.springframework.boot
spring-boot-starter-web

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 
Enter fullscreen mode Exit fullscreen mode


org.springdoc
springdoc-openapi-starter-webmvc-ui
2.8.5

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> 
Enter fullscreen mode Exit fullscreen mode

FrontEnd Section

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';

function App() {
const [families, setFamilies] = useState([]);
const [volunteers, setVolunteers] = useState([]);
const [activeTab, setActiveTab] = useState('families');
const [newFamily, setNewFamily] = useState({ name: '', address: '', phone: '' });
const [newVolunteer, setNewVolunteer] = useState({ name: '', phone: '' });
const [editingFamily, setEditingFamily] = useState(null);
const [editingVolunteer, setEditingVolunteer] = useState(null);
const [editFamilyData, setEditFamilyData] = useState({ name: '', address: '', phone: '' });
const [editVolunteerData, setEditVolunteerData] = useState({ name: '', phone: '' });

// Fetch data from backend
useEffect(() => {
fetchFamilies();
fetchVolunteers();
}, []);

const fetchFamilies = async () => {
try {
const response = await axios.get('/api/families');
setFamilies(response.data);
} catch (error) {
console.error('Error fetching families:', error);
}
};

const fetchVolunteers = async () => {
try {
const response = await axios.get('/api/volunteers');
setVolunteers(response.data);
} catch (error) {
console.error('Error fetching volunteers:', error);
}
};

const handleAddFamily = async (e) => {
e.preventDefault();
try {
await axios.post('/api/families', newFamily);
setNewFamily({ name: '', address: '', phone: '' });
fetchFamilies();
} catch (error) {
console.error('Error adding family:', error);
}
};

const handleAddVolunteer = async (e) => {
e.preventDefault();
try {
await axios.post('/api/volunteers', newVolunteer);
setNewVolunteer({ name: '', phone: '' });
fetchVolunteers();
} catch (error) {
console.error('Error adding volunteer:', error);
}
};

// ===== ADD EDIT AND DELETE FUNCTIONALITY HERE =====

// Family functions
const handleEditFamily = (family) => {
setEditingFamily(family.id);
setEditFamilyData({
name: family.name,
address: family.address,
phone: family.phone
});
};

const handleUpdateFamily = async (id) => {
try {
await axios.put(/api/families/${id}, editFamilyData);
setEditingFamily(null);
fetchFamilies();
} catch (error) {
console.error('Error updating family:', error);
}
};

const handleDeleteFamily = async (id) => {
try {
await axios.delete(/api/families/${id});
fetchFamilies();
} catch (error) {
console.error('Error deleting family:', error);
}
};

// Volunteer functions
const handleEditVolunteer = (volunteer) => {
setEditingVolunteer(volunteer.id);
setEditVolunteerData({
name: volunteer.name,
phone: volunteer.phone
});
};

const handleUpdateVolunteer = async (id) => {
try {
await axios.put(/api/volunteers/${id}, editVolunteerData);
setEditingVolunteer(null);
fetchVolunteers();
} catch (error) {
console.error('Error updating volunteer:', error);
}
};

const handleDeleteVolunteer = async (id) => {
try {
await axios.delete(/api/volunteers/${id});
fetchVolunteers();
} catch (error) {
console.error('Error deleting volunteer:', error);
}
};

// ===== END OF EDIT AND DELETE FUNCTIONALITY =====

return (



Maariyathaa Temple Management System


 <div className="tabs"> <button className={activeTab === 'families' ? 'active' : ''} onClick={() => setActiveTab('families')} > Families </button> <button className={activeTab === 'volunteers' ? 'active' : ''} onClick={() => setActiveTab('volunteers')} > Volunteers </button> </div> <div className="content"> {activeTab === 'families' && ( <div> <h2>Families</h2> <form onSubmit={handleAddFamily} className="form"> <h3>Add New Family</h3> <input type="text" placeholder="Family Name" value={newFamily.name} onChange={(e) => setNewFamily({...newFamily, name: e.target.value})} required /> <input type="text" placeholder="Address" value={newFamily.address} onChange={(e) => setNewFamily({...newFamily, address: e.target.value})} /> <input type="text" placeholder="Phone" value={newFamily.phone} onChange={(e) => setNewFamily({...newFamily, phone: e.target.value})} /> <button type="submit">Add Family</button> </form> <div className="list"> <h3>Family List</h3> {families.length === 0 ? ( <p>No families found</p> ) : ( <ul> {families.map(family => ( <li key={family.id}> {editingFamily === family.id ? ( <div className="edit-form"> <input type="text" value={editFamilyData.name} onChange={(e) => setEditFamilyData({...editFamilyData, name: e.target.value})} /> <input type="text" value={editFamilyData.address} onChange={(e) => setEditFamilyData({...editFamilyData, address: e.target.value})} /> <input type="text" value={editFamilyData.phone} onChange={(e) => setEditFamilyData({...editFamilyData, phone: e.target.value})} /> <button onClick={() => handleUpdateFamily(family.id)}>Save</button> <button onClick={() => setEditingFamily(null)}>Cancel</button> </div> ) : ( <div> <strong>{family.name}</strong><br /> {family.address && <span>Address: {family.address}<br /></span>} {family.phone && <span>Phone: {family.phone}</span>} <div className="item-actions"> <button onClick={() => handleEditFamily(family)}>Edit</button> <button onClick={() => handleDeleteFamily(family.id)}>Delete</button> </div> </div> )} </li> ))} </ul> )} </div> </div> )} {activeTab === 'volunteers' && ( <div> <h2>Volunteers</h2> <form onSubmit={handleAddVolunteer} className="form"> <h3>Add New Volunteer</h3> <input type="text" placeholder="Volunteer Name" value={newVolunteer.name} onChange={(e) => setNewVolunteer({...newVolunteer, name: e.target.value})} required /> <input type="text" placeholder="Phone" value={newVolunteer.phone} onChange={(e) => setNewVolunteer({...newVolunteer, phone: e.target.value})} /> <button type="submit">Add Volunteer</button> </form> <div className="list"> <h3>Volunteer List</h3> {volunteers.length === 0 ? ( <p>No volunteers found</p> ) : ( <ul> {volunteers.map(volunteer => ( <li key={volunteer.id}> {editingVolunteer === volunteer.id ? ( <div className="edit-form"> <input type="text" value={editVolunteerData.name} onChange={(e) => setEditVolunteerData({...editVolunteerData, name: e.target.value})} /> <input type="text" value={editVolunteerData.phone} onChange={(e) => setEditVolunteerData({...editVolunteerData, phone: e.target.value})} /> <button onClick={() => handleUpdateVolunteer(volunteer.id)}>Save</button> <button onClick={() => setEditingVolunteer(null)}>Cancel</button> </div> ) : ( <div> <strong>{volunteer.name}</strong><br /> {volunteer.phone && <span>Phone: {volunteer.phone}</span>} <div className="item-actions"> <button onClick={() => handleEditVolunteer(volunteer)}>Edit</button> <button onClick={() => handleDeleteVolunteer(volunteer.id)}>Delete</button> </div> </div> )} </li> ))} </ul> )} </div> </div> )} </div> </div> 

);
}

export default App;


/* Donation Management Styles */
.donation-management {
padding: 20px;
}

.donation-stats {
display: flex;
gap: 20px;
margin-bottom: 30px;
}

.stat-card {
background-color: #f5f5f5;
padding: 20px;
border-radius: 8px;
text-align: center;
flex: 1;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.stat-card h3 {
margin: 0 0 10px 0;
color: #666;
font-size: 14px;
}

.stat-card p {
margin: 0;
font-size: 24px;
font-weight: bold;
color: #333;
}

.donation-form {
background-color: #f9f9f9;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}

.form-row {
display: flex;
gap: 20px;
margin-bottom: 15px;
}

.form-group {
flex: 1;
margin-bottom: 15px;
}

.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}

.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}

.installment-fields {
display: flex;
align-items: center;
gap: 10px;
}

.installment-fields input {
width: 60px;
}

.installment-fields span {
color: #666;
}

.btn-primary {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}

.btn-primary:hover {
background-color: #45a049;
}

.donation-list table,
.payment-status table {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
}

.donation-list th,
.donation-list td,
.payment-status th,
.payment-status td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}

.donation-list th,
.payment-status th {
background-color: #f2f2f2;
}

.status-pending {
color: #ff9800;
font-weight: bold;
}

.status-partial {
color: #2196f3;
font-weight: bold;
}

.status-completed {
color: #4CAF50;
font-weight: bold;
}

.status-not-paid {
color: #f44336;
font-weight: bold;
}

.status-fully-paid {
color: #4CAF50;
font-weight: bold;
}

.action-buttons {
display: flex;
gap: 5px;
}

.btn-edit {
background-color: #2196F3;
color: white;
padding: 5px 10px;
border: none;
border-radius: 3px;
cursor: pointer;
}

.btn-delete {
background-color: #f44336;
color: white;
padding: 5px 10px;
border: none;
border-radius: 3px;
cursor: pointer;
}

/* Payment Status Styles */
.payment-overview {
margin-bottom: 30px;
}

.status-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}

.status-card {
background-color: #f8f9fa;
border-radius: 8px;
padding: 20px;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.status-title {
font-size: 14px;
color: #6c757d;
margin-bottom: 10px;
}

.status-value {
font-size: 24px;
font-weight: bold;
color: #343a40;
}

.status-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}

.status-not-paid {
background-color: #ffeaea;
color: #dc3545;
}

.status-partial {
background-color: #fff3cd;
color: #856404;
}

.status-completed {
background-color: #d4edda;
color: #155724;
}

.status-pending {
background-color: #cce5ff;
color: #004085;
}

.payment-status-table {
margin-bottom: 30px;
}

.payment-status-table table {
width: 100%;
border-collapse: collapse;
}

.payment-status-table th,
.payment-status-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #dee2e6;
}

.payment-status-table th {
background-color: #f8f9fa;
font-weight: bold;
}

.btn-reminder {
background-color: #17a2b8;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}

.btn-reminder:hover:not(:disabled) {
background-color: #138496;
}

.btn-reminder:disabled {
background-color: #6c757d;
cursor: not-allowed;
}

.installment-history {
margin-bottom: 30px;
}

.installment-history table {
width: 100%;
border-collapse: collapse;
}

.installment-history th,
.installment-history td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #dee2e6;
}

.installment-history th {
background-color: #f8f9fa;
font-weight: bold;
}
/* Add these styles to your existing App.css */

.item-actions {
margin-top: 10px;
}

.item-actions button {
margin-right: 5px;
padding: 5px 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
}

.item-actions button:last-child {
background-color: #f44336;
}

.edit-form {
display: flex;
flex-direction: column;
gap: 5px;
}

.edit-form input {
padding: 5px;
border: 1px solid #ddd;
border-radius: 3px;
}

.edit-form button {
padding: 5px 10px;
margin-right: 5px;
border: none;
border-radius: 3px;
cursor: pointer;
}

.edit-form button:first-of-type {
background-color: #4CAF50;
color: white;
}

.edit-form button:last-of-type {
background-color: #f44336;
color: white;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 50vh;
}

.loading-container p {
margin-top: 20px;
font-size: 18px;
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 50vh;
}

.loading-container p {
margin-top: 20px;
font-size: 18px;
}


const fetchData = async () => {
try {
const familyRes = await axios.get("/api/families");
console.log("📦 Families raw data:", familyRes.data);

// ✅ If API returns { content: [...] } const familyData = Array.isArray(familyRes.data) ? familyRes.data : familyRes.data.content || []; setFamilies(familyData); const donationRes = await axios.get("/api/donations"); console.log("📦 Donations raw data:", donationRes.data); const donationData = Array.isArray(donationRes.data) ? donationRes.data : donationRes.data.content || []; setDonations(donationData); const volunteerRes = await axios.get("/api/volunteers"); console.log("📦 Volunteers raw data:", volunteerRes.data); const volunteerData = Array.isArray(volunteerRes.data) ? volunteerRes.data : volunteerRes.data.content || []; setVolunteers(volunteerData); const paymentRes = await axios.get("/api/payments"); console.log("📦 Payments raw data:", paymentRes.data); const paymentData = Array.isArray(paymentRes.data) ? paymentRes.data : paymentRes.data.content || []; setPayments(paymentData); 

} catch (error) {
console.error("❌ Error fetching data:", error);
}
};


*The spring boot run result *

. ____ _ __ _ _
/\ / ' __ _ ()_ __ __ _ \ \ \ \
( ( )_
| '_ | '| | ' \/ ` | \ \ \ \
\/ _
)| |)| | | | | || (| | ) ) ) )
' |
__| .|| ||| |_, | / / / /
=========||==============|_/=////

[32m :: Spring Boot :: [39m [2m (v3.5.5)[0;39m

[2m2025-08-25T18:52:37.694+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mc.m.temple.MaariyathaaApplication [0;39m [2m:[0;39m Starting MaariyathaaApplication using Java 17.0.16 with PID 27963 (/home/prem/Developer/sts/Maariyathaa/target/classes started by prem in /home/prem/Developer/sts/Maariyathaa)
[2m2025-08-25T18:52:37.696+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mc.m.temple.MaariyathaaApplication [0;39m [2m:[0;39m No active profile set, falling back to 1 default profile: "default"
[2m2025-08-25T18:52:37.751+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36m.e.DevToolsPropertyDefaultsPostProcessor[0;39m [2m:[0;39m Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
[2m2025-08-25T18:52:37.751+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36m.e.DevToolsPropertyDefaultsPostProcessor[0;39m [2m:[0;39m For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
[2m2025-08-25T18:52:38.737+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Bootstrapping Spring Data JPA repositories in DEFAULT mode.
[2m2025-08-25T18:52:38.797+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Finished Spring Data repository scanning in 48 ms. Found 4 JPA repository interfaces.
[2m2025-08-25T18:52:39.327+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.s.b.w.embedded.tomcat.TomcatWebServer [0;39m [2m:[0;39m Tomcat initialized with port 8080 (http)
[2m2025-08-25T18:52:39.342+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.apache.catalina.core.StandardService [0;39m [2m:[0;39m Starting service [Tomcat]
[2m2025-08-25T18:52:39.342+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.apache.catalina.core.StandardEngine [0;39m [2m:[0;39m Starting Servlet engine: [Apache Tomcat/10.1.44]
[2m2025-08-25T18:52:39.382+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.a.c.c.C.[Tomcat].[localhost].[/] [0;39m [2m:[0;39m Initializing Spring embedded WebApplicationContext
[2m2025-08-25T18:52:39.383+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mw.s.c.ServletWebServerApplicationContext[0;39m [2m:[0;39m Root WebApplicationContext: initialization completed in 1631 ms
[2m2025-08-25T18:52:39.509+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.hibernate.jpa.internal.util.LogHelper [0;39m [2m:[0;39m HHH000204: Processing PersistenceUnitInfo [name: default]
[2m2025-08-25T18:52:39.567+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36morg.hibernate.Version [0;39m [2m:[0;39m HHH000412: Hibernate ORM core version 6.6.26.Final
[2m2025-08-25T18:52:39.603+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.h.c.internal.RegionFactoryInitiator [0;39m [2m:[0;39m HHH000026: Second-level cache disabled
[2m2025-08-25T18:52:39.829+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.s.o.j.p.SpringPersistenceUnitInfo [0;39m [2m:[0;39m No LoadTimeWeaver setup: ignoring JPA class transformer
[2m2025-08-25T18:52:39.862+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Starting...
[2m2025-08-25T18:52:40.138+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mcom.zaxxer.hikari.pool.HikariPool [0;39m [2m:[0;39m HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@1c42865c
[2m2025-08-25T18:52:40.139+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mcom.zaxxer.hikari.HikariDataSource [0;39m [2m:[0;39m HikariPool-1 - Start completed.
[2m2025-08-25T18:52:40.175+05:30[0;39m [33m WARN[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36morg.hibernate.orm.deprecation [0;39m [2m:[0;39m HHH90000025: PostgreSQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
[2m2025-08-25T18:52:40.192+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36morg.hibernate.orm.connections.pooling [0;39m [2m:[0;39m HHH10001005: Database info:
Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
Database driver: undefined/unknown
Database version: 14.18
Autocommit mode: undefined/unknown
Isolation level: undefined/unknown
Minimum pool size: undefined/unknown
Maximum pool size: undefined/unknown
[2m2025-08-25T18:52:40.946+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.h.e.t.j.p.i.JtaPlatformInitiator [0;39m [2m:[0;39m HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
[2m2025-08-25T18:52:41.177+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mj.LocalContainerEntityManagerFactoryBean[0;39m [2m:[0;39m Initialized JPA EntityManagerFactory for persistence unit 'default'
[2m2025-08-25T18:52:41.417+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.s.d.j.r.query.QueryEnhancerFactory [0;39m [2m:[0;39m Hibernate is in classpath; If applicable, HQL parser will be used.
[2m2025-08-25T18:52:41.944+05:30[0;39m [33m WARN[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mJpaBaseConfiguration$JpaWebConfiguration[0;39m [2m:[0;39m spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
[2m2025-08-25T18:52:42.288+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.s.b.d.a.OptionalLiveReloadServer [0;39m [2m:[0;39m LiveReload server is running on port 35729
[2m2025-08-25T18:52:42.328+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mo.s.b.w.embedded.tomcat.TomcatWebServer [0;39m [2m:[0;39m Tomcat started on port 8080 (http) with context path '/'
[2m2025-08-25T18:52:42.339+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [ restartedMain] [0;39m[36mc.m.temple.MaariyathaaApplication [0;39m [2m:[0;39m Started MaariyathaaApplication in 5.131 seconds (process running for 5.803)
[2m2025-08-25T18:52:54.225+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [nio-8080-exec-1] [0;39m[36mo.a.c.c.C.[Tomcat].[localhost].[/] [0;39m [2m:[0;39m Initializing Spring DispatcherServlet 'dispatcherServlet'
[2m2025-08-25T18:52:54.226+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [nio-8080-exec-1] [0;39m[36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Initializing Servlet 'dispatcherServlet'
[2m2025-08-25T18:52:54.229+05:30[0;39m [32m INFO[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [nio-8080-exec-1] [0;39m[36mo.s.web.servlet.DispatcherServlet [0;39m [2m:[0;39m Completed initialization in 3 ms
Hibernate:
select
v1_0.id,
v1_0.name,
v1_0.phone
from
volunteers v1_0
order by
v1_0.name
offset
? rows
fetch
first ? rows only
Hibernate:
select
f1_0.id,
f1_0.address,
f1_0.house_no,
f1_0.name,
f1_0.phone,
f1_0.street_name
from
families f1_0
order by
f1_0.name
offset
? rows
fetch
first ? rows only
[2m2025-08-25T18:52:54.512+05:30[0;39m [33m WARN[0;39m [35m27963[0;39m [2m--- [Maariyathaa] [nio-8080-exec-1] [0;39m[36mration$PageModule$WarningLoggingModifier[0;39m [2m:[0;39m Serializing PageImpl instances as-is is not supported, meaning that there is no guarantee about the stability of the resulting JSON structure!
For a stable JSON structure, please use Spring Data's PagedModel (globally via @EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO))
or Spring HATEOAS and Spring Data's PagedResourcesAssembler as documented in https://docs.spring.io/spring-data/commons/reference/repositories/core-extensions.html#core.web.pageables.

Hibernate:
select
f1_0.id,
f1_0.address,
f1_0.house_no,
f1_0.name,
f1_0.phone,
f1_0.street_name
from
families f1_0
order by
f1_0.name
offset
? rows
fetch
first ? rows only
Hibernate:
select
v1_0.id,
v1_0.name,
v1_0.phone
from
volunteers v1_0
order by
v1_0.name
offset
? rows
fetch
first ? rows only


Top comments (0)