Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,73 +1,149 @@
package com.techtorque.project_service.controller;

import com.techtorque.project_service.dto.*;
import com.techtorque.project_service.entity.Project;
import com.techtorque.project_service.service.ProjectService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/projects")
@Tag(name = "Custom Projects (Modifications)", description = "Endpoints for managing custom vehicle modifications.")
@SecurityRequirement(name = "bearerAuth")
@RequiredArgsConstructor
public class ProjectController {

// @Autowired
// private ProjectService projectService;
private final ProjectService projectService;

@Operation(summary = "Request a new modification project (customer only)")
@PostMapping
@PreAuthorize("hasRole('CUSTOMER')")
public ResponseEntity<?> requestModification(
/* @RequestBody ProjectRequestDto dto, */
public ResponseEntity<ApiResponse> requestModification(
@Valid @RequestBody ProjectRequestDto dto,
@RequestHeader("X-User-Subject") String customerId) {
// TODO: Delegate to projectService.requestNewProject(...);
return ResponseEntity.ok().build();

Project project = projectService.requestNewProject(dto, customerId);
ProjectResponseDto response = mapToResponseDto(project);

return ResponseEntity
.status(HttpStatus.CREATED)
.body(ApiResponse.success("Project request submitted successfully", response));
}

@Operation(summary = "List projects for the current customer")
@GetMapping
@PreAuthorize("hasRole('CUSTOMER')")
public ResponseEntity<?> listCustomerProjects(@RequestHeader("X-User-Subject") String customerId) {
// TODO: Delegate to projectService.getProjectsForCustomer(customerId);
return ResponseEntity.ok().build();
public ResponseEntity<ApiResponse> listCustomerProjects(@RequestHeader("X-User-Subject") String customerId) {
List<Project> projects = projectService.getProjectsForCustomer(customerId);
List<ProjectResponseDto> response = projects.stream()
.map(this::mapToResponseDto)
.collect(Collectors.toList());

return ResponseEntity.ok(ApiResponse.success("Projects retrieved successfully", response));
}

@Operation(summary = "Get details for a specific project")
@GetMapping("/{projectId}")
@PreAuthorize("hasAnyRole('CUSTOMER', 'EMPLOYEE')")
public ResponseEntity<?> getProjectDetails(@PathVariable String projectId) {
// TODO: Delegate to projectService, ensuring access rights
return ResponseEntity.ok().build();
@PreAuthorize("hasAnyRole('CUSTOMER', 'EMPLOYEE', 'ADMIN')")
public ResponseEntity<ApiResponse> getProjectDetails(
@PathVariable String projectId,
@RequestHeader("X-User-Subject") String userId,
@RequestHeader("X-User-Roles") String userRoles) {

Project project = projectService.getProjectDetails(projectId, userId, userRoles)
.orElseThrow(() -> new RuntimeException("Project not found or access denied"));

ProjectResponseDto response = mapToResponseDto(project);
return ResponseEntity.ok(ApiResponse.success("Project retrieved successfully", response));
}

@Operation(summary = "Submit a quote for a project (employee/admin only)")
@PutMapping("/{projectId}/quote")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN')")
public ResponseEntity<?> submitQuote(@PathVariable String projectId /*, @RequestBody QuoteDto dto */) {
// TODO: Delegate to projectService.submitQuoteForProject(...);
return ResponseEntity.ok().build();
public ResponseEntity<ApiResponse> submitQuote(
@PathVariable String projectId,
@Valid @RequestBody QuoteDto dto) {

Project project = projectService.submitQuoteForProject(projectId, dto);
ProjectResponseDto response = mapToResponseDto(project);

return ResponseEntity.ok(ApiResponse.success("Quote submitted successfully", response));
}

@Operation(summary = "Accept a quote for a project (customer only)")
@PostMapping("/{projectId}/accept")
@PreAuthorize("hasRole('CUSTOMER')")
public ResponseEntity<?> acceptQuote(
public ResponseEntity<ApiResponse> acceptQuote(
@PathVariable String projectId,
@RequestHeader("X-User-Subject") String customerId) {
// TODO: Delegate to projectService.acceptQuote(projectId, customerId);
return ResponseEntity.ok().build();

Project project = projectService.acceptQuote(projectId, customerId);
ProjectResponseDto response = mapToResponseDto(project);

return ResponseEntity.ok(ApiResponse.success("Quote accepted successfully", response));
}

@Operation(summary = "Reject a quote for a project (customer only)")
@PostMapping("/{projectId}/reject")
@PreAuthorize("hasRole('CUSTOMER')")
public ResponseEntity<?> rejectQuote(
public ResponseEntity<ApiResponse> rejectQuote(
@PathVariable String projectId,
// @RequestBody RejectionDto dto,
@Valid @RequestBody RejectionDto dto,
@RequestHeader("X-User-Subject") String customerId) {
// TODO: Delegate to projectService.rejectQuote(projectId, dto, customerId);
return ResponseEntity.ok().build();

Project project = projectService.rejectQuote(projectId, dto, customerId);
ProjectResponseDto response = mapToResponseDto(project);

return ResponseEntity.ok(ApiResponse.success("Quote rejected successfully", response));
}

@Operation(summary = "Update project progress (employee/admin only)")
@PutMapping("/{projectId}/progress")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN')")
public ResponseEntity<ApiResponse> updateProgress(
@PathVariable String projectId,
@Valid @RequestBody ProgressUpdateDto dto) {

Project project = projectService.updateProgress(projectId, dto);
ProjectResponseDto response = mapToResponseDto(project);

return ResponseEntity.ok(ApiResponse.success("Progress updated successfully", response));
}

@Operation(summary = "List all projects (admin/employee only)")
@GetMapping("/all")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN')")
public ResponseEntity<ApiResponse> listAllProjects() {
List<Project> projects = projectService.getAllProjects();
List<ProjectResponseDto> response = projects.stream()
.map(this::mapToResponseDto)
.collect(Collectors.toList());

return ResponseEntity.ok(ApiResponse.success("All projects retrieved successfully", response));
}

// Helper method to map Entity to DTO
private ProjectResponseDto mapToResponseDto(Project project) {
return ProjectResponseDto.builder()
.id(project.getId())
.customerId(project.getCustomerId())
.vehicleId(project.getVehicleId())
.description(project.getDescription())
.budget(project.getBudget())
.status(project.getStatus())
.progress(project.getProgress())
.createdAt(project.getCreatedAt())
.updatedAt(project.getUpdatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,48 @@
package com.techtorque.project_service.controller;

import com.techtorque.project_service.dto.ApiResponse;
import com.techtorque.project_service.entity.StandardService;
import com.techtorque.project_service.service.StandardServiceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@RestController
@RequestMapping("/services")
@Tag(name = "Standard Services", description = "Endpoints for managing appointment-based services.")
@SecurityRequirement(name = "bearerAuth")
@RequiredArgsConstructor
public class ServiceController {

// @Autowired
// private StandardService serviceLayer; // Renamed to avoid confusion
private final StandardServiceService standardServiceService;

@Operation(summary = "List services for the current customer")
@GetMapping
@PreAuthorize("hasRole('CUSTOMER')")
public ResponseEntity<?> listCustomerServices(
public ResponseEntity<ApiResponse> listCustomerServices(
@RequestHeader("X-User-Subject") String customerId,
@RequestParam(required = false) String status) {
// TODO: Delegate to serviceLayer.getServicesForCustomer(customerId, status);
return ResponseEntity.ok().build();
List<StandardService> services = standardServiceService.getServicesForCustomer(customerId, status);
return ResponseEntity.ok(ApiResponse.success("Services retrieved successfully", services));
}

@Operation(summary = "Get details for a specific service")
@GetMapping("/{serviceId}")
@PreAuthorize("hasAnyRole('CUSTOMER', 'EMPLOYEE')")
public ResponseEntity<?> getServiceDetails(@PathVariable String serviceId) {
// TODO: Delegate to serviceLayer, ensuring customer/employee has access
return ResponseEntity.ok().build();
public ResponseEntity<ApiResponse> getServiceDetails(
@PathVariable String serviceId,
@RequestHeader("X-User-Subject") String userId,
@RequestHeader("X-User-Roles") String userRole) {
return standardServiceService.getServiceDetails(serviceId, userId, userRole)
.map(service -> ResponseEntity.ok(ApiResponse.success("Service retrieved successfully", service)))
.orElse(ResponseEntity.notFound().build());
}

@Operation(summary = "Update service status, notes, or completion estimate (employee only)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.techtorque.project_service.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse {
private boolean success;
private String message;
private Object data;

public static ApiResponse success(String message) {
return ApiResponse.builder()
.success(true)
.message(message)
.build();
}

public static ApiResponse success(String message, Object data) {
return ApiResponse.builder()
.success(true)
.message(message)
.data(data)
.build();
}

public static ApiResponse error(String message) {
return ApiResponse.builder()
.success(false)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.techtorque.project_service.dto;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProgressUpdateDto {

@NotNull(message = "Progress percentage is required")
@Min(value = 0, message = "Progress must be between 0 and 100")
@Max(value = 100, message = "Progress must be between 0 and 100")
private Integer progress;

private String notes;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.techtorque.project_service.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProjectRequestDto {

@NotBlank(message = "Vehicle ID is required")
private String vehicleId;

@NotBlank(message = "Description is required")
@Size(min = 10, max = 2000, message = "Description must be between 10 and 2000 characters")
private String description;

private BigDecimal budget;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.techtorque.project_service.dto;

import com.techtorque.project_service.entity.ProjectStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProjectResponseDto {
private String id;
private String customerId;
private String vehicleId;
private String description;
private BigDecimal budget;
private ProjectStatus status;
private int progress;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.techtorque.project_service.dto;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QuoteDto {

@NotNull(message = "Quote amount is required")
@Min(value = 0, message = "Quote amount must be positive")
private BigDecimal quoteAmount;

private String notes;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.techtorque.project_service.dto;

import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RejectionDto {

@Size(max = 500, message = "Reason cannot exceed 500 characters")
private String reason;
}
Loading