Update with new components
@ -0,0 +1,97 @@
|
||||
// CourseApplicationController.java - REST Controller for Course Applications
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Course;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.CourseApplication;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.CourseApplicationDto;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.CourseRepository;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.CourseApplicationRepository;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/course-applications")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class CourseApplicationController {
|
||||
|
||||
@Autowired
|
||||
private CourseApplicationRepository courseApplicationRepository;
|
||||
|
||||
@Autowired
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
// Get all applications (for admin)
|
||||
@GetMapping
|
||||
public List<CourseApplication> getAllApplications() {
|
||||
return courseApplicationRepository.findAll();
|
||||
}
|
||||
|
||||
// Get applications by course ID
|
||||
@GetMapping("/course/{courseId}")
|
||||
public ResponseEntity<List<CourseApplication>> getApplicationsByCourseId(@PathVariable Long courseId) {
|
||||
try {
|
||||
List<CourseApplication> applications = courseApplicationRepository.findAllByCourseId(courseId);
|
||||
return ResponseEntity.ok(applications);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Get a single application by ID
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<CourseApplication> getApplicationById(@PathVariable Long id) {
|
||||
return courseApplicationRepository.findById(id)
|
||||
.map(application -> ResponseEntity.ok(application))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createApplication(@RequestBody CourseApplicationDto applicationDto) {
|
||||
Course course = courseRepository.findById(applicationDto.getCourseId())
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Course not found"));
|
||||
|
||||
CourseApplication application = new CourseApplication();
|
||||
application.setCourse(course);
|
||||
application.setFullName(applicationDto.getFullName());
|
||||
application.setEmail(applicationDto.getEmail());
|
||||
application.setPhone(applicationDto.getPhone());
|
||||
application.setQualification(applicationDto.getQualification());
|
||||
application.setExperience(applicationDto.getExperience());
|
||||
application.setCoverLetter(applicationDto.getCoverLetter());
|
||||
application.setResumeUrl(applicationDto.getResumeUrl());
|
||||
|
||||
courseApplicationRepository.save(application);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/status")
|
||||
public ResponseEntity<?> updateApplicationStatus(@PathVariable Long id, @RequestParam String status) {
|
||||
CourseApplication application = courseApplicationRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Application not found"));
|
||||
|
||||
try {
|
||||
application.setStatus(CourseApplication.ApplicationStatus.valueOf(status.toUpperCase()));
|
||||
courseApplicationRepository.save(application);
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.badRequest().body("Invalid status: " + status);
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteApplication(@PathVariable Long id) {
|
||||
return courseApplicationRepository.findById(id)
|
||||
.map(application -> {
|
||||
courseApplicationRepository.delete(application);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
// CourseController.java - REST Controller for Courses
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Course;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.CourseDto;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.CourseRepository;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/courses")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class CourseController {
|
||||
|
||||
@Autowired
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
// Get all active courses (for public display)
|
||||
@GetMapping("/active")
|
||||
public ResponseEntity<List<Course>> getActiveCourses() {
|
||||
try {
|
||||
List<Course> courses = courseRepository.findAllByIsActiveTrue();
|
||||
return ResponseEntity.ok(courses);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Get all courses (for admin)
|
||||
@GetMapping
|
||||
public List<Course> getAllCourses() {
|
||||
return courseRepository.findAll();
|
||||
}
|
||||
|
||||
// Get a single course by ID
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Course> getCourseById(@PathVariable Long id) {
|
||||
return courseRepository.findById(id)
|
||||
.map(course -> ResponseEntity.ok(course))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createCourse(@RequestBody CourseDto courseDto) {
|
||||
Course course = new Course();
|
||||
course.setTitle(courseDto.getTitle());
|
||||
course.setDescription(courseDto.getDescription());
|
||||
course.setDuration(courseDto.getDuration());
|
||||
course.setSeats(courseDto.getSeats());
|
||||
course.setCategory(courseDto.getCategory());
|
||||
course.setLevel(courseDto.getLevel());
|
||||
course.setInstructor(courseDto.getInstructor());
|
||||
course.setPrice(courseDto.getPrice());
|
||||
course.setStartDate(courseDto.getStartDate());
|
||||
course.setImageUrl(courseDto.getImageUrl());
|
||||
course.setEligibility(courseDto.getEligibility());
|
||||
course.setObjectives(courseDto.getObjectives());
|
||||
course.setActive(courseDto.isActive());
|
||||
|
||||
courseRepository.save(course);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> updateCourse(@PathVariable Long id, @RequestBody CourseDto courseDto) {
|
||||
Course course = courseRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Course not found"));
|
||||
|
||||
course.setTitle(courseDto.getTitle());
|
||||
course.setDescription(courseDto.getDescription());
|
||||
course.setDuration(courseDto.getDuration());
|
||||
course.setSeats(courseDto.getSeats());
|
||||
course.setCategory(courseDto.getCategory());
|
||||
course.setLevel(courseDto.getLevel());
|
||||
course.setInstructor(courseDto.getInstructor());
|
||||
course.setPrice(courseDto.getPrice());
|
||||
course.setStartDate(courseDto.getStartDate());
|
||||
course.setImageUrl(courseDto.getImageUrl());
|
||||
course.setEligibility(courseDto.getEligibility());
|
||||
course.setObjectives(courseDto.getObjectives());
|
||||
course.setActive(courseDto.isActive());
|
||||
|
||||
courseRepository.save(course);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteCourse(@PathVariable Long id) {
|
||||
return courseRepository.findById(id)
|
||||
.map(course -> {
|
||||
courseRepository.delete(course);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/events")
|
||||
@CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS})
|
||||
public class EventController {
|
||||
|
||||
@Autowired
|
||||
@ -33,8 +34,12 @@ public class EventController {
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Event> createEvent(@RequestBody Event event) {
|
||||
try {
|
||||
Event savedEvent = eventRepository.save(event);
|
||||
return new ResponseEntity<>(savedEvent, HttpStatus.CREATED);
|
||||
} catch (Exception e) {
|
||||
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@ -42,9 +47,13 @@ public class EventController {
|
||||
if (!eventRepository.existsById(id)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
try {
|
||||
event.setId(id);
|
||||
Event updatedEvent = eventRepository.save(event);
|
||||
return new ResponseEntity<>(updatedEvent, HttpStatus.OK);
|
||||
} catch (Exception e) {
|
||||
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@ -52,7 +61,25 @@ public class EventController {
|
||||
if (!eventRepository.existsById(id)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
try {
|
||||
eventRepository.deleteById(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
} catch (Exception e) {
|
||||
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional endpoint to get upcoming events
|
||||
@GetMapping("/upcoming")
|
||||
public ResponseEntity<List<Event>> getUpcomingEvents() {
|
||||
List<Event> events = eventRepository.findByIsActiveTrueOrderByDateAsc();
|
||||
return new ResponseEntity<>(events, HttpStatus.OK);
|
||||
}
|
||||
|
||||
// Additional endpoint to get past events
|
||||
@GetMapping("/past")
|
||||
public ResponseEntity<List<Event>> getPastEvents() {
|
||||
List<Event> events = eventRepository.findByIsActiveTrueOrderByDateDesc();
|
||||
return new ResponseEntity<>(events, HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/files")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class FileController { // Changed from FileUploadController to FileController
|
||||
|
||||
@Value("${file.upload.directory:uploads}")
|
||||
private String uploadDirectory;
|
||||
|
||||
@Value("${app.base-url:http://localhost:8080}")
|
||||
private String baseUrl;
|
||||
|
||||
@PostMapping("/upload")
|
||||
public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||
System.out.println("=== FILE UPLOAD DEBUG ===");
|
||||
System.out.println("File name: " + file.getOriginalFilename());
|
||||
System.out.println("Content type: " + file.getContentType());
|
||||
|
||||
try {
|
||||
if (file.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("File is empty");
|
||||
}
|
||||
|
||||
// Updated validation to accept both images and documents
|
||||
String contentType = file.getContentType();
|
||||
if (!isValidFileType(contentType)) {
|
||||
return ResponseEntity.badRequest().body("Invalid file type. Only images and documents (PDF, DOC, DOCX) are allowed.");
|
||||
}
|
||||
|
||||
// Create upload directory if it doesn't exist
|
||||
Path uploadPath = Paths.get(uploadDirectory);
|
||||
if (!Files.exists(uploadPath)) {
|
||||
Files.createDirectories(uploadPath);
|
||||
}
|
||||
|
||||
// Generate unique filename
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
|
||||
String uniqueFilename = UUID.randomUUID().toString() + fileExtension;
|
||||
|
||||
// Save file
|
||||
Path filePath = uploadPath.resolve(uniqueFilename);
|
||||
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
// Return file URL - use generic /files/ path for documents
|
||||
String fileUrl = "/uploads/" + uniqueFilename;
|
||||
Map<String, String> response = new HashMap<>();
|
||||
response.put("url", fileUrl);
|
||||
response.put("filename", uniqueFilename);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
|
||||
} catch (IOException e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Failed to upload file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Updated validation method
|
||||
private boolean isValidFileType(String contentType) {
|
||||
return contentType != null && (
|
||||
// Image types
|
||||
contentType.equals("image/jpeg") ||
|
||||
contentType.equals("image/jpg") ||
|
||||
contentType.equals("image/png") ||
|
||||
contentType.equals("image/gif") ||
|
||||
contentType.equals("image/webp") ||
|
||||
// Document types for resumes
|
||||
contentType.equals("application/pdf") ||
|
||||
contentType.equals("application/msword") ||
|
||||
contentType.equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document")
|
||||
);
|
||||
}
|
||||
|
||||
@GetMapping("/images/{filename}")
|
||||
public ResponseEntity<byte[]> getImage(@PathVariable String filename) {
|
||||
try {
|
||||
Path filePath = Paths.get(uploadDirectory).resolve(filename);
|
||||
if (!Files.exists(filePath)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
byte[] imageBytes = Files.readAllBytes(filePath);
|
||||
String contentType = Files.probeContentType(filePath);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header("Content-Type", contentType != null ? contentType : "application/octet-stream")
|
||||
.body(imageBytes);
|
||||
|
||||
} catch (IOException e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidImageType(String contentType) {
|
||||
return contentType != null && (
|
||||
contentType.equals("image/jpeg") ||
|
||||
contentType.equals("image/jpg") ||
|
||||
contentType.equals("image/png") ||
|
||||
contentType.equals("image/gif") ||
|
||||
contentType.equals("image/webp")
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.service.ProfileImageService;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@RequiredArgsConstructor
|
||||
@CrossOrigin(origins = "*")
|
||||
public class ImageController {
|
||||
|
||||
private final ProfileImageService profileImageService;
|
||||
|
||||
@GetMapping("/{professorId}/profile-image/{filename}")
|
||||
public ResponseEntity<byte[]> getProfileImage(
|
||||
@PathVariable UUID professorId,
|
||||
@PathVariable String filename) {
|
||||
try {
|
||||
log.debug("Fetching profile image for professor: {} with filename: {}", professorId, filename);
|
||||
|
||||
byte[] imageBytes = profileImageService.retrieveProfileImage(professorId, filename);
|
||||
|
||||
if (imageBytes == null || imageBytes.length == 0) {
|
||||
log.warn("No image data found for professor: {} with filename: {}", professorId, filename);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
// Determine content type based on filename
|
||||
String contentType = getContentTypeFromFilename(filename);
|
||||
headers.setContentType(MediaType.parseMediaType(contentType));
|
||||
|
||||
// Set cache control
|
||||
headers.setCacheControl(CacheControl.maxAge(Duration.ofHours(1)).cachePublic());
|
||||
|
||||
log.debug("Successfully retrieved image for professor: {}, size: {} bytes", professorId, imageBytes.length);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.headers(headers)
|
||||
.body(imageBytes);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error retrieving profile image for professor: {} with filename: {}", professorId, filename, e);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{professorId}/profile-image")
|
||||
public ResponseEntity<byte[]> getDefaultProfileImage(@PathVariable UUID professorId) {
|
||||
try {
|
||||
log.debug("Fetching default profile image for professor: {}", professorId);
|
||||
|
||||
// Try to get the default image (avatar.jpg or similar)
|
||||
byte[] imageBytes = profileImageService.retrieveProfileImage(professorId, "avatar.jpg");
|
||||
|
||||
if (imageBytes == null || imageBytes.length == 0) {
|
||||
log.warn("No default image found for professor: {}", professorId);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.IMAGE_JPEG);
|
||||
headers.setCacheControl(CacheControl.maxAge(Duration.ofHours(1)).cachePublic());
|
||||
|
||||
log.debug("Successfully retrieved default image for professor: {}, size: {} bytes", professorId, imageBytes.length);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.headers(headers)
|
||||
.body(imageBytes);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error retrieving default profile image for professor: {}", professorId, e);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint to check if a profile image exists
|
||||
*/
|
||||
@GetMapping("/{professorId}/profile-image/exists")
|
||||
public ResponseEntity<Boolean> checkProfileImageExists(@PathVariable UUID professorId) {
|
||||
try {
|
||||
byte[] imageBytes = profileImageService.retrieveProfileImage(professorId, "avatar.jpg");
|
||||
boolean exists = imageBytes != null && imageBytes.length > 0;
|
||||
|
||||
return ResponseEntity.ok(exists);
|
||||
} catch (Exception e) {
|
||||
log.error("Error checking if profile image exists for professor: {}", professorId, e);
|
||||
return ResponseEntity.ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine content type based on file extension
|
||||
*/
|
||||
private String getContentTypeFromFilename(String filename) {
|
||||
if (filename == null) {
|
||||
return MediaType.IMAGE_JPEG_VALUE;
|
||||
}
|
||||
|
||||
String lowerCaseFilename = filename.toLowerCase();
|
||||
|
||||
if (lowerCaseFilename.endsWith(".png")) {
|
||||
return MediaType.IMAGE_PNG_VALUE;
|
||||
} else if (lowerCaseFilename.endsWith(".gif")) {
|
||||
return MediaType.IMAGE_GIF_VALUE;
|
||||
} else if (lowerCaseFilename.endsWith(".webp")) {
|
||||
return "image/webp";
|
||||
} else if (lowerCaseFilename.endsWith(".bmp")) {
|
||||
return "image/bmp";
|
||||
} else {
|
||||
// Default to JPEG
|
||||
return MediaType.IMAGE_JPEG_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Job;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.JobApplication;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.JobApplicationDto;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.JobRepository;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.JobApplicationRepository;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/job-applications")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class JobApplicationController {
|
||||
|
||||
@Autowired
|
||||
private JobApplicationRepository jobApplicationRepository;
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
// Get all applications (for admin)
|
||||
@GetMapping
|
||||
public List<JobApplication> getAllApplications() {
|
||||
return jobApplicationRepository.findAll();
|
||||
}
|
||||
|
||||
// Get applications by job ID
|
||||
@GetMapping("/job/{jobId}")
|
||||
public ResponseEntity<List<JobApplication>> getApplicationsByJobId(@PathVariable Long jobId) {
|
||||
try {
|
||||
List<JobApplication> applications = jobApplicationRepository.findAllByJobId(jobId);
|
||||
return ResponseEntity.ok(applications);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Get a single application by ID
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<JobApplication> getApplicationById(@PathVariable Long id) {
|
||||
return jobApplicationRepository.findById(id)
|
||||
.map(application -> ResponseEntity.ok(application))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createApplication(@RequestBody JobApplicationDto applicationDto) {
|
||||
Job job = jobRepository.findById(applicationDto.getJobId())
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Job not found"));
|
||||
|
||||
JobApplication application = new JobApplication();
|
||||
application.setJob(job);
|
||||
application.setFullName(applicationDto.getFullName());
|
||||
application.setEmail(applicationDto.getEmail());
|
||||
application.setPhone(applicationDto.getPhone());
|
||||
application.setExperience(applicationDto.getExperience());
|
||||
application.setCoverLetter(applicationDto.getCoverLetter());
|
||||
application.setResumeUrl(applicationDto.getResumeUrl());
|
||||
|
||||
jobApplicationRepository.save(application);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/status")
|
||||
public ResponseEntity<?> updateApplicationStatus(@PathVariable Long id, @RequestParam String status) {
|
||||
JobApplication application = jobApplicationRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Application not found"));
|
||||
|
||||
try {
|
||||
application.setStatus(JobApplication.ApplicationStatus.valueOf(status.toUpperCase()));
|
||||
jobApplicationRepository.save(application);
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.badRequest().body("Invalid status: " + status);
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteApplication(@PathVariable Long id) {
|
||||
return jobApplicationRepository.findById(id)
|
||||
.map(application -> {
|
||||
jobApplicationRepository.delete(application);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
// JobController.java - REST Controller for Jobs
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Job;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.JobDto;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.JobRepository;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/jobs")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class JobController {
|
||||
|
||||
@Autowired
|
||||
private JobRepository jobRepository;
|
||||
|
||||
// Get all active jobs (for public display)
|
||||
@GetMapping("/active")
|
||||
public ResponseEntity<List<Job>> getActiveJobs() {
|
||||
try {
|
||||
List<Job> jobs = jobRepository.findAllByIsActiveTrue();
|
||||
return ResponseEntity.ok(jobs);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Get all jobs (for admin)
|
||||
@GetMapping
|
||||
public List<Job> getAllJobs() {
|
||||
return jobRepository.findAll();
|
||||
}
|
||||
|
||||
// Get a single job by ID
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Job> getJobById(@PathVariable Long id) {
|
||||
return jobRepository.findById(id)
|
||||
.map(job -> ResponseEntity.ok(job))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createJob(@RequestBody JobDto jobDto) {
|
||||
System.out.println("=== BACKEND DEBUG ===");
|
||||
System.out.println("Received JobDto: " + jobDto);
|
||||
System.out.println("isActive value: " + jobDto.isActive());
|
||||
// Remove the .getClass() line since boolean is a primitive
|
||||
|
||||
Job job = new Job();
|
||||
job.setTitle(jobDto.getTitle());
|
||||
job.setDepartment(jobDto.getDepartment());
|
||||
job.setLocation(jobDto.getLocation());
|
||||
job.setType(jobDto.getType());
|
||||
job.setExperience(jobDto.getExperience());
|
||||
job.setSalary(jobDto.getSalary());
|
||||
job.setDescription(jobDto.getDescription());
|
||||
job.setRequirements(jobDto.getRequirements());
|
||||
job.setResponsibilities(jobDto.getResponsibilities());
|
||||
job.setActive(jobDto.isActive());
|
||||
|
||||
System.out.println("Job before save - isActive: " + job.isActive());
|
||||
|
||||
Job savedJob = jobRepository.save(job);
|
||||
System.out.println("Job after save - isActive: " + savedJob.isActive());
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> updateJob(@PathVariable Long id, @RequestBody JobDto jobDto) {
|
||||
Job job = jobRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Job not found"));
|
||||
|
||||
job.setTitle(jobDto.getTitle());
|
||||
job.setDepartment(jobDto.getDepartment());
|
||||
job.setLocation(jobDto.getLocation());
|
||||
job.setType(jobDto.getType());
|
||||
job.setExperience(jobDto.getExperience());
|
||||
job.setSalary(jobDto.getSalary());
|
||||
job.setDescription(jobDto.getDescription());
|
||||
job.setRequirements(jobDto.getRequirements());
|
||||
job.setResponsibilities(jobDto.getResponsibilities());
|
||||
job.setActive(jobDto.isActive());
|
||||
|
||||
jobRepository.save(job);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteJob(@PathVariable Long id) {
|
||||
return jobRepository.findById(id)
|
||||
.map(job -> {
|
||||
jobRepository.delete(job);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Post;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.PostDto;
|
||||
@ -12,6 +11,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
|
||||
|
||||
@ -21,21 +21,28 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/posts")
|
||||
@CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS})
|
||||
public class PostController {
|
||||
|
||||
@Autowired
|
||||
private PostRepository postRepository;
|
||||
|
||||
@Autowired
|
||||
private ProfessorRepository professorRepository;
|
||||
|
||||
// Get all posts where isPosted is true
|
||||
@GetMapping("/posted")
|
||||
public ResponseEntity<List<Post>> getAllPostedPosts() {
|
||||
try {
|
||||
log.info("Fetching all posted posts");
|
||||
List<Post> posts = postRepository.findAllByIsPostedTrue();
|
||||
log.info("Retrieved {} posted posts", posts.size());
|
||||
return ResponseEntity.ok(posts);
|
||||
} catch (Exception e) {
|
||||
log.error("Error fetching posted posts: ", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
@ -44,13 +51,16 @@ public class PostController {
|
||||
@GetMapping("/tags/count")
|
||||
public ResponseEntity<Map<String, Long>> getTagsWithCount() {
|
||||
try {
|
||||
log.info("Fetching tag counts");
|
||||
List<Object[]> tagCounts = postRepository.findTagsWithCount();
|
||||
Map<String, Long> tagCountMap = new HashMap<>();
|
||||
for (Object[] tagCount : tagCounts) {
|
||||
tagCountMap.put((String) tagCount[0], (Long) tagCount[1]);
|
||||
}
|
||||
log.info("Retrieved {} unique tags", tagCountMap.size());
|
||||
return ResponseEntity.ok(tagCountMap);
|
||||
} catch (Exception e) {
|
||||
log.error("Error fetching tag counts: ", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
@ -59,37 +69,67 @@ public class PostController {
|
||||
@GetMapping("/tag/{tag}")
|
||||
public ResponseEntity<List<Post>> getPostsByTag(@PathVariable String tag) {
|
||||
try {
|
||||
log.info("Fetching posts by tag: {}", tag);
|
||||
List<Post> posts = postRepository.findAllByTagAndIsPostedTrue(tag);
|
||||
log.info("Retrieved {} posts for tag: {}", posts.size(), tag);
|
||||
return ResponseEntity.ok(posts);
|
||||
} catch (Exception e) {
|
||||
log.error("Error fetching posts by tag {}: ", tag, e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Get all posts
|
||||
@GetMapping
|
||||
public List<Post> getAllPosts() {
|
||||
return postRepository.findAll();
|
||||
public ResponseEntity<List<Post>> getAllPosts() {
|
||||
try {
|
||||
log.info("Fetching all posts");
|
||||
List<Post> posts = postRepository.findAll();
|
||||
log.info("Retrieved {} posts", posts.size());
|
||||
return ResponseEntity.ok(posts);
|
||||
} catch (Exception e) {
|
||||
log.error("Error fetching all posts: ", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Get a single post by ID
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<Post> getPostById(@PathVariable Long id) {
|
||||
try {
|
||||
log.info("Fetching post with id: {}", id);
|
||||
return postRepository.findById(id)
|
||||
.map(post -> ResponseEntity.ok(post))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.map(post -> {
|
||||
log.info("Found post with id: {}", id);
|
||||
return ResponseEntity.ok(post);
|
||||
})
|
||||
.orElseGet(() -> {
|
||||
log.warn("Post not found with id: {}", id);
|
||||
return ResponseEntity.notFound().build();
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("Error fetching post with id {}: ", id, e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private ProfessorRepository professorRepository;
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createPost(@RequestBody PostDto postDto) {
|
||||
try {
|
||||
// Debug logging to see what data is being received
|
||||
log.info("Creating new post with data:");
|
||||
log.info("Title: {}", postDto.getTitle());
|
||||
log.info("Content: {}", postDto.getContent());
|
||||
log.info("Posted: {}", postDto.isPosted());
|
||||
log.info("ImageUrl: {}", postDto.getImageUrl());
|
||||
log.info("Professors: {}", postDto.getProfessors());
|
||||
log.info("Tags: {}", postDto.getTags());
|
||||
|
||||
Post post = new Post();
|
||||
post.setTitle(postDto.getTitle());
|
||||
post.setContent(postDto.getContent());
|
||||
post.setPosted(postDto.isPosted());
|
||||
post.setImageUrl(postDto.getImageUrl());
|
||||
|
||||
// Fetch professors from IDs, filter out null IDs
|
||||
List<Long> validProfessorIds = postDto.getProfessors().stream()
|
||||
@ -102,17 +142,33 @@ public class PostController {
|
||||
post.setTags(postDto.getTags());
|
||||
|
||||
// Save the post
|
||||
postRepository.save(post);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
Post savedPost = postRepository.save(post);
|
||||
log.info("Successfully created post with id: {}", savedPost.getId());
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(savedPost);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error creating post: ", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Failed to create post: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> updatePost(@PathVariable Long id, @RequestBody PostDto postDto) {
|
||||
Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Post not found"));
|
||||
try {
|
||||
log.info("Updating post with id: {}", id);
|
||||
log.info("New Title: {}", postDto.getTitle());
|
||||
log.info("New Content length: {}", postDto.getContent() != null ? postDto.getContent().length() : 0);
|
||||
log.info("New Posted status: {}", postDto.isPosted());
|
||||
log.info("New ImageUrl: {}", postDto.getImageUrl());
|
||||
|
||||
Post post = postRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Post not found with id: " + id));
|
||||
|
||||
post.setTitle(postDto.getTitle());
|
||||
post.setContent(postDto.getContent());
|
||||
post.setPosted(postDto.isPosted());
|
||||
post.setImageUrl(postDto.getImageUrl());
|
||||
|
||||
// Fetch professors from IDs, filter out null IDs
|
||||
List<Long> validProfessorIds = postDto.getProfessors().stream()
|
||||
@ -125,18 +181,47 @@ public class PostController {
|
||||
post.setTags(postDto.getTags());
|
||||
|
||||
// Save the updated post
|
||||
postRepository.save(post);
|
||||
return ResponseEntity.ok().build();
|
||||
Post updatedPost = postRepository.save(post);
|
||||
log.info("Successfully updated post with id: {}", id);
|
||||
return ResponseEntity.ok(updatedPost);
|
||||
|
||||
} catch (ResourceNotFoundException e) {
|
||||
log.warn("Post not found for update: {}", e.getMessage());
|
||||
return ResponseEntity.notFound().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Error updating post with id {}: ", id, e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Failed to update post: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deletePost(@PathVariable Long id) {
|
||||
try {
|
||||
log.info("Deleting post with id: {}", id);
|
||||
return postRepository.findById(id)
|
||||
.map(post -> {
|
||||
postRepository.delete(post);
|
||||
return ResponseEntity.noContent().<Void>build(); // Explicitly specify the type parameter
|
||||
log.info("Successfully deleted post with id: {}", id);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
.orElseGet(() -> {
|
||||
log.warn("Post not found for deletion with id: {}", id);
|
||||
return ResponseEntity.notFound().build();
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("Error deleting post with id {}: ", id, e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle preflight OPTIONS requests
|
||||
@RequestMapping(method = RequestMethod.OPTIONS)
|
||||
public ResponseEntity<?> handleOptions() {
|
||||
return ResponseEntity.ok()
|
||||
.header("Access-Control-Allow-Origin", "*")
|
||||
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
.header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class PostDTO
|
||||
{
|
||||
|
||||
@NotNull
|
||||
private String title;
|
||||
|
||||
@NotNull
|
||||
private String content;
|
||||
|
||||
@NotEmpty(message = "At least one professor must be selected.")
|
||||
private List<Long> professors;
|
||||
|
||||
private List<String> tags;
|
||||
private boolean posted;
|
||||
|
||||
// Getters and setters
|
||||
// ...
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorCategory;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.service.ProfessorService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("public/professor")
|
||||
@RequiredArgsConstructor
|
||||
@CrossOrigin(origins = "*") // Configure this properly for production
|
||||
public class PublicProfessorController {
|
||||
|
||||
private final ProfessorService professorService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<Page<Professor>> getAllProfessors(Pageable pageable) {
|
||||
Page<Professor> professors = professorService.findAll(pageable);
|
||||
return ResponseEntity.ok(professors);
|
||||
}
|
||||
|
||||
@GetMapping("{professorId}")
|
||||
public ResponseEntity<Professor> getProfessorById(@PathVariable UUID professorId) {
|
||||
Professor professor = professorService.findByProfessorId(professorId);
|
||||
return ResponseEntity.ok(professor);
|
||||
}
|
||||
|
||||
@GetMapping("active")
|
||||
public ResponseEntity<Page<Professor>> getActiveProfessors(Pageable pageable) {
|
||||
Page<Professor> activeProfessors = professorService.findActiveProfessors(pageable);
|
||||
return ResponseEntity.ok(activeProfessors);
|
||||
}
|
||||
|
||||
// Add the missing endpoint that your Next.js frontend is calling
|
||||
@GetMapping("active/category/{category}")
|
||||
public ResponseEntity<Page<Professor>> getActiveProfessorsByCategory(
|
||||
@PathVariable String category,
|
||||
Pageable pageable) {
|
||||
try {
|
||||
ProfessorCategory professorCategory = ProfessorCategory.valueOf(category.toUpperCase());
|
||||
Page<Professor> professors = professorService.findActiveProfessorsByCategory(professorCategory, pageable);
|
||||
return ResponseEntity.ok(professors);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Invalid category provided: {}", category);
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
// Additional endpoint for all professors by category (active and inactive)
|
||||
@GetMapping("category/{category}")
|
||||
public ResponseEntity<Page<Professor>> getProfessorsByCategory(
|
||||
@PathVariable String category,
|
||||
Pageable pageable) {
|
||||
try {
|
||||
ProfessorCategory professorCategory = ProfessorCategory.valueOf(category.toUpperCase());
|
||||
Page<Professor> professors = professorService.findByCategory(professorCategory, pageable);
|
||||
return ResponseEntity.ok(professors);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Invalid category provided: {}", category);
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
// UpcomingEventController.java - REST Controller for Upcoming Events
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.controller;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.UpcomingEvent;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.UpcomingEventDto;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.repository.UpcomingEventRepository;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/upcoming-events")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class UpcomingEventController {
|
||||
|
||||
@Autowired
|
||||
private UpcomingEventRepository upcomingEventRepository;
|
||||
|
||||
// Get all active upcoming events (for public display)
|
||||
@GetMapping("/active")
|
||||
public ResponseEntity<List<UpcomingEvent>> getActiveUpcomingEvents() {
|
||||
try {
|
||||
List<UpcomingEvent> events = upcomingEventRepository.findAllByIsActiveTrue();
|
||||
return ResponseEntity.ok(events);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
// Get all upcoming events (for admin)
|
||||
@GetMapping
|
||||
public List<UpcomingEvent> getAllUpcomingEvents() {
|
||||
return upcomingEventRepository.findAll();
|
||||
}
|
||||
|
||||
// Get a single upcoming event by ID
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<UpcomingEvent> getUpcomingEventById(@PathVariable Long id) {
|
||||
return upcomingEventRepository.findById(id)
|
||||
.map(event -> ResponseEntity.ok(event))
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createUpcomingEvent(@RequestBody UpcomingEventDto eventDto) {
|
||||
UpcomingEvent event = new UpcomingEvent();
|
||||
event.setTitle(eventDto.getTitle());
|
||||
event.setDescription(eventDto.getDescription());
|
||||
event.setSchedule(eventDto.getSchedule());
|
||||
event.setEventDate(eventDto.getEventDate());
|
||||
event.setActive(eventDto.isActive());
|
||||
|
||||
upcomingEventRepository.save(event);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> updateUpcomingEvent(@PathVariable Long id, @RequestBody UpcomingEventDto eventDto) {
|
||||
UpcomingEvent event = upcomingEventRepository.findById(id)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Upcoming event not found"));
|
||||
|
||||
event.setTitle(eventDto.getTitle());
|
||||
event.setDescription(eventDto.getDescription());
|
||||
event.setSchedule(eventDto.getSchedule());
|
||||
event.setEventDate(eventDto.getEventDate());
|
||||
event.setActive(eventDto.isActive());
|
||||
|
||||
upcomingEventRepository.save(event);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> deleteUpcomingEvent(@PathVariable Long id) {
|
||||
return upcomingEventRepository.findById(id)
|
||||
.map(event -> {
|
||||
upcomingEventRepository.delete(event);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Course extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(columnDefinition = "TEXT", nullable = false)
|
||||
private String description;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String duration;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer seats;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String category;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String level;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String instructor;
|
||||
|
||||
private String price;
|
||||
|
||||
@Column(name = "start_date")
|
||||
private LocalDate startDate;
|
||||
|
||||
private String imageUrl;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "course_eligibility", joinColumns = @JoinColumn(name = "course_id"))
|
||||
@Column(name = "eligibility")
|
||||
private List<String> eligibility;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "course_objectives", joinColumns = @JoinColumn(name = "course_id"))
|
||||
@Column(name = "objective", columnDefinition = "TEXT")
|
||||
private List<String> objectives;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean isActive = true;
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
// CourseApplication.java - Entity for course applications
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import javax.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CourseApplication extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "course_id", nullable = false)
|
||||
private Course course;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String fullName;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String email;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String phone;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String qualification;
|
||||
|
||||
private String experience;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String coverLetter;
|
||||
|
||||
private String resumeUrl;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ApplicationStatus status = ApplicationStatus.PENDING;
|
||||
|
||||
public enum ApplicationStatus {
|
||||
PENDING, REVIEWED, SHORTLISTED, ACCEPTED, REJECTED, ENROLLED
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,18 @@ public class Event {
|
||||
|
||||
private String subTitle;
|
||||
|
||||
// New fields to match Next.js component
|
||||
private String description;
|
||||
|
||||
private String detail;
|
||||
|
||||
private String mainImage;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "event_gallery_images", joinColumns = @JoinColumn(name = "event_id"))
|
||||
@Column(name = "image_url")
|
||||
private List<String> galleryImages;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String date;
|
||||
|
||||
@ -48,9 +60,10 @@ public class Event {
|
||||
@CollectionTable(name = "organisers", joinColumns = @JoinColumn(name = "event_id"))
|
||||
private List<String> organisers;
|
||||
|
||||
// @ElementCollection
|
||||
// @CollectionTable(name = "fees", joinColumns = @JoinColumn(name = "event_id"))
|
||||
// private List<Fee> fee;
|
||||
// Fixed Fee mapping with proper column name
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "event_fees", joinColumns = @JoinColumn(name = "event_id"))
|
||||
private List<Fee> fee;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String phone;
|
||||
@ -61,6 +74,9 @@ public class Event {
|
||||
@Column(nullable = false)
|
||||
private Boolean isActive;
|
||||
|
||||
@Column(name = "is_deleted", nullable = false)
|
||||
private Boolean isDeleted = false;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "event_professors",
|
||||
@ -69,18 +85,59 @@ public class Event {
|
||||
)
|
||||
private List<Professor> professors;
|
||||
|
||||
// Assuming you have these classes defined as well
|
||||
// Embedded classes
|
||||
@Embeddable
|
||||
public static class Venue {
|
||||
private String title;
|
||||
private String date;
|
||||
private String address;
|
||||
private String info;
|
||||
|
||||
// Constructors, getters, setters
|
||||
public Venue() {}
|
||||
|
||||
public Venue(String title, String date, String address, String info) {
|
||||
this.title = title;
|
||||
this.date = date;
|
||||
this.address = address;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
// Getters and setters
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
|
||||
public String getDate() { return date; }
|
||||
public void setDate(String date) { this.date = date; }
|
||||
|
||||
public String getAddress() { return address; }
|
||||
public void setAddress(String address) { this.address = address; }
|
||||
|
||||
public String getInfo() { return info; }
|
||||
public void setInfo(String info) { this.info = info; }
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Fee {
|
||||
private String desc;
|
||||
@Column(name = "fee_description") // Explicit column mapping to avoid reserved word issues
|
||||
private String description;
|
||||
|
||||
@Column(name = "fee_cost")
|
||||
private Integer cost;
|
||||
|
||||
// Constructors, getters, setters
|
||||
public Fee() {}
|
||||
|
||||
public Fee(String description, Integer cost) {
|
||||
this.description = description;
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
// Getters and setters
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public Integer getCost() { return cost; }
|
||||
public void setCost(Integer cost) { this.cost = cost; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
// Job.java - Entity for job positions
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.List;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Job extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String department;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String location;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String type; // Full-time, Contract, Observership, etc.
|
||||
|
||||
@Column(nullable = false)
|
||||
private String experience;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String salary;
|
||||
|
||||
@Column(columnDefinition = "TEXT", nullable = false)
|
||||
private String description;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "job_requirements", joinColumns = @JoinColumn(name = "job_id"))
|
||||
@Column(name = "requirement")
|
||||
private List<String> requirements;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "job_responsibilities", joinColumns = @JoinColumn(name = "job_id"))
|
||||
@Column(name = "responsibility")
|
||||
private List<String> responsibilities;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean isActive = true;
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
// JobApplication.java - Entity for job applications
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import javax.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class JobApplication extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "job_id", nullable = false)
|
||||
private Job job;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String fullName;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String email;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String phone;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String experience;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String coverLetter;
|
||||
|
||||
private String resumeUrl; // Path to uploaded resume file
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ApplicationStatus status = ApplicationStatus.PENDING;
|
||||
|
||||
public enum ApplicationStatus {
|
||||
PENDING, REVIEWED, SHORTLISTED, INTERVIEWED, REJECTED, HIRED
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@ import lombok.*;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = false) // Changed from true since you're extending BaseEntity
|
||||
public class Post extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@ -38,4 +38,7 @@ public class Post extends BaseEntity {
|
||||
@CollectionTable(name = "post_tags", joinColumns = @JoinColumn(name = "post_id"))
|
||||
@Column(name = "tag")
|
||||
private List<String> tags;
|
||||
|
||||
@Column
|
||||
private String imageUrl; // Add this field
|
||||
}
|
||||
@ -1,18 +1,18 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@ -25,8 +25,6 @@ public class Professor implements Serializable {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
// @EqualsAndHashCode.Include
|
||||
// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
private Long id;
|
||||
|
||||
@Type(type = "org.hibernate.type.UUIDCharType")
|
||||
@ -43,11 +41,48 @@ public class Professor implements Serializable {
|
||||
private String profileImageUrl;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private WorkingStatus status; // Use enum to track detailed working status
|
||||
private WorkingStatus status;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ProfessorCategory category;
|
||||
|
||||
// Additional fields for Next.js integration
|
||||
private String phone;
|
||||
private String specialty;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String certification;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String training;
|
||||
|
||||
private String experience;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
private String designation;
|
||||
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@CollectionTable(name = "professor_work_days", joinColumns = @JoinColumn(name = "professor_id"))
|
||||
@Column(name = "work_day")
|
||||
private List<String> workDays;
|
||||
|
||||
// Use Set instead of List to avoid MultipleBagFetchException
|
||||
// Sets can be eagerly loaded together without issues
|
||||
@OneToMany(mappedBy = "professor", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private Set<ProfessorSkill> skills;
|
||||
|
||||
// Use Set instead of List to avoid MultipleBagFetchException
|
||||
@OneToMany(mappedBy = "professor", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private Set<ProfessorAward> awards;
|
||||
|
||||
@ManyToMany(mappedBy = "professors")
|
||||
@JsonIgnore
|
||||
@JsonIgnore // Keep this as @JsonIgnore to avoid circular references
|
||||
private List<Post> posts;
|
||||
|
||||
// Convenience method to get full name
|
||||
public String getName() {
|
||||
return firstName + " " + lastName;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ProfessorAward {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String title;
|
||||
private String year;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
private String imageUrl;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "professor_id")
|
||||
@JsonIgnore
|
||||
@ToString.Exclude
|
||||
@EqualsAndHashCode.Exclude
|
||||
private Professor professor;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
public enum ProfessorCategory {
|
||||
FACULTY,
|
||||
SUPPORT_TEAM,
|
||||
TRAINEE_FELLOW
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ProfessorSkill {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
private Integer level;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "professor_id")
|
||||
@JsonIgnore
|
||||
@ToString.Exclude
|
||||
@EqualsAndHashCode.Exclude
|
||||
private Professor professor;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
// UpcomingEvent.java - Entity for upcoming events
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.LocalDate;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UpcomingEvent extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(columnDefinition = "TEXT", nullable = false)
|
||||
private String description;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String schedule; // e.g., "Q3 2025", "Monthly Sessions", "Ongoing"
|
||||
|
||||
@Column(name = "event_date")
|
||||
private LocalDate eventDate;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean isActive = true;
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AwardDto {
|
||||
private String title;
|
||||
private String year;
|
||||
private String description;
|
||||
private String imageUrl;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
// CourseApplicationDto.java - DTO for Course Application
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Email;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CourseApplicationDto {
|
||||
@NotNull
|
||||
private Long courseId;
|
||||
|
||||
@NotNull
|
||||
private String fullName;
|
||||
|
||||
@NotNull
|
||||
@Email
|
||||
private String email;
|
||||
|
||||
@NotNull
|
||||
private String phone;
|
||||
|
||||
@NotNull
|
||||
private String qualification;
|
||||
|
||||
private String experience;
|
||||
private String coverLetter;
|
||||
private String resumeUrl;
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
// CourseDto.java - DTO for Course
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CourseDto {
|
||||
@NotNull
|
||||
private String title;
|
||||
|
||||
@NotNull
|
||||
private String description;
|
||||
|
||||
@NotNull
|
||||
private String duration;
|
||||
|
||||
@NotNull
|
||||
private Integer seats;
|
||||
|
||||
@NotNull
|
||||
private String category;
|
||||
|
||||
@NotNull
|
||||
private String level;
|
||||
|
||||
@NotNull
|
||||
private String instructor;
|
||||
|
||||
private String price;
|
||||
private LocalDate startDate;
|
||||
private String imageUrl;
|
||||
private List<String> eligibility;
|
||||
private List<String> objectives;
|
||||
private boolean active;
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public void setIsActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
// JobApplicationDto.java - DTO for Job Application
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Email;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class JobApplicationDto {
|
||||
@NotNull
|
||||
private Long jobId;
|
||||
|
||||
@NotNull
|
||||
private String fullName;
|
||||
|
||||
@NotNull
|
||||
@Email
|
||||
private String email;
|
||||
|
||||
@NotNull
|
||||
private String phone;
|
||||
|
||||
@NotNull
|
||||
private String experience;
|
||||
|
||||
private String coverLetter;
|
||||
private String resumeUrl;
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
// JobDto.java - DTO for Job
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class JobDto {
|
||||
@NotNull
|
||||
private String title;
|
||||
|
||||
@NotNull
|
||||
private String department;
|
||||
|
||||
@NotNull
|
||||
private String location;
|
||||
|
||||
@NotNull
|
||||
private String type;
|
||||
|
||||
@NotNull
|
||||
private String experience;
|
||||
|
||||
@NotNull
|
||||
private String salary;
|
||||
|
||||
@NotNull
|
||||
private String description;
|
||||
|
||||
private List<String> requirements;
|
||||
private List<String> responsibilities;
|
||||
|
||||
// Explicit boolean field handling (remove @Data for this field)
|
||||
private boolean active; // Change from isActive to active
|
||||
|
||||
// Explicit getters and setters for the boolean field
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
// For Jackson JSON deserialization
|
||||
public void setIsActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,17 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
|
||||
|
||||
public class PostDto {
|
||||
|
||||
@NotNull
|
||||
@ -31,7 +25,5 @@ public class PostDto {
|
||||
|
||||
private List<String> tags;
|
||||
private boolean posted;
|
||||
|
||||
// Getters and setters
|
||||
// ...
|
||||
private String imageUrl; // Add this field
|
||||
}
|
||||
@ -4,12 +4,16 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Role;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.WorkingStatus;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorCategory;
|
||||
// Add these imports at the top
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.SkillDto;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.AwardDto;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
@ -29,9 +33,19 @@ public class ProfessorDto {
|
||||
private String position;
|
||||
private String officeLocation;
|
||||
private WorkingStatus status;
|
||||
private ProfessorCategory category;
|
||||
private LocalDateTime joinDate;
|
||||
private MultipartFile profileImage; // Optional field for profile image URL
|
||||
private MultipartFile profileImage;
|
||||
|
||||
// Additional fields for Next.js integration
|
||||
private String phone;
|
||||
private String specialty;
|
||||
private String certification;
|
||||
private String training;
|
||||
private String experience;
|
||||
private String description;
|
||||
private String designation;
|
||||
private List<String> workDays;
|
||||
private List<SkillDto> skills;
|
||||
private List<AwardDto> awards;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.WorkingStatus;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorCategory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ProfessorResponseDto {
|
||||
private Long id;
|
||||
private UUID professorId;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private String department;
|
||||
private String position;
|
||||
private String officeLocation;
|
||||
private LocalDateTime joinDate;
|
||||
private String profileImageUrl;
|
||||
private WorkingStatus status;
|
||||
private ProfessorCategory category;
|
||||
|
||||
// Additional fields for Next.js integration
|
||||
private String phone;
|
||||
private String specialty;
|
||||
private String certification;
|
||||
private String training;
|
||||
private String experience;
|
||||
private String description;
|
||||
private String designation;
|
||||
private List<String> workDays;
|
||||
|
||||
// Nested DTOs for skills and awards
|
||||
private List<SkillDto> skills;
|
||||
private List<AwardDto> awards;
|
||||
|
||||
// Convenience method to get full name
|
||||
public String getName() {
|
||||
return firstName + " " + lastName;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class SkillDto {
|
||||
private String name;
|
||||
private Integer level;
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
// UpcomingEventDto.java - DTO for Upcoming Event
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.domain.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class UpcomingEventDto {
|
||||
@NotNull
|
||||
private String title;
|
||||
|
||||
@NotNull
|
||||
private String description;
|
||||
|
||||
@NotNull
|
||||
private String schedule;
|
||||
|
||||
private LocalDate eventDate;
|
||||
private boolean active;
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public void setIsActive(boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
@ -8,15 +8,12 @@ import org.mapstruct.Mapping;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
@Mapper(componentModel = "spring") // This is crucial for Spring integration
|
||||
// @Mapper(componentModel = "spring",imports = {LocalDateTime.class})
|
||||
@Mapper(componentModel = "spring", imports = {LocalDateTime.class})
|
||||
public interface UserMapper {
|
||||
|
||||
@Mapping(target = "isNotLocked", source = "notLocked")
|
||||
@Mapping(target = "isActive", source = "active")
|
||||
@Mapping(target = "joinDate", expression = "java(LocalDateTime.now())")
|
||||
@Mapping(target = "role", source = "role", resultType = String.class)
|
||||
@Mapping(target = "authorities", source = "role.authorities")
|
||||
User toEntity(UserDto userDto);
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
// CourseApplicationRepository.java
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.CourseApplication;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface CourseApplicationRepository extends JpaRepository<CourseApplication, Long> {
|
||||
List<CourseApplication> findAllByCourseId(Long courseId);
|
||||
List<CourseApplication> findAllByStatus(CourseApplication.ApplicationStatus status);
|
||||
List<CourseApplication> findAllByEmail(String email);
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
// CourseRepository.java
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Course;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface CourseRepository extends JpaRepository<Course, Long> {
|
||||
List<Course> findAllByIsActiveTrue();
|
||||
List<Course> findAllByCategory(String category);
|
||||
List<Course> findAllByLevel(String level);
|
||||
}
|
||||
@ -1,11 +1,39 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Event;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface EventRepository extends JpaRepository<Event, Long> {
|
||||
// Custom query methods can be added here if needed
|
||||
|
||||
// Find active events ordered by date ascending (for upcoming events)
|
||||
List<Event> findByIsActiveTrueOrderByDateAsc();
|
||||
|
||||
// Find active events ordered by date descending (for past events)
|
||||
List<Event> findByIsActiveTrueOrderByDateDesc();
|
||||
|
||||
// Find events by year
|
||||
List<Event> findByYearAndIsActiveTrue(String year);
|
||||
|
||||
// Find events by subject
|
||||
List<Event> findBySubjectContainingIgnoreCaseAndIsActiveTrue(String subject);
|
||||
|
||||
// Find events by title containing keyword
|
||||
List<Event> findByTitleContainingIgnoreCaseAndIsActiveTrue(String title);
|
||||
|
||||
// Custom query to search events by multiple fields
|
||||
@Query("SELECT e FROM Event e WHERE e.isActive = true AND " +
|
||||
"(LOWER(e.title) LIKE LOWER(CONCAT('%', ?1, '%')) OR " +
|
||||
"LOWER(e.description) LIKE LOWER(CONCAT('%', ?1, '%')) OR " +
|
||||
"LOWER(e.subject) LIKE LOWER(CONCAT('%', ?1, '%')))")
|
||||
List<Event> searchActiveEvents(String searchTerm);
|
||||
|
||||
// Find events by date range (you might need to adjust based on your date format)
|
||||
@Query("SELECT e FROM Event e WHERE e.isActive = true AND e.date BETWEEN ?1 AND ?2 ORDER BY e.date ASC")
|
||||
List<Event> findEventsByDateRange(String startDate, String endDate);
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
// JobApplicationRepository.java
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.JobApplication;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface JobApplicationRepository extends JpaRepository<JobApplication, Long> {
|
||||
List<JobApplication> findAllByJobId(Long jobId);
|
||||
List<JobApplication> findAllByStatus(JobApplication.ApplicationStatus status);
|
||||
List<JobApplication> findAllByEmail(String email);
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
// JobRepository.java
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Job;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface JobRepository extends JpaRepository<Job, Long> {
|
||||
List<Job> findAllByIsActiveTrue();
|
||||
List<Job> findAllByDepartment(String department);
|
||||
List<Job> findAllByType(String type);
|
||||
}
|
||||
@ -1,17 +1,18 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||
|
||||
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 org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.WorkingStatus;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorCategory;
|
||||
|
||||
// @RepositoryRestResource(collectionResourceRel = "professors", path = "professors")
|
||||
public interface ProfessorRepository extends JpaRepository<Professor, Long> {
|
||||
|
||||
@Query("SELECT p FROM Professor p WHERE p.email = :email")
|
||||
@ -24,4 +25,15 @@ public interface ProfessorRepository extends JpaRepository<Professor, Long> {
|
||||
|
||||
@Query("SELECT p FROM Professor p WHERE p.professorId = :professorId")
|
||||
Optional<Professor> findByProfessorId(@Param("professorId") UUID professorId);
|
||||
|
||||
@Query("SELECT p FROM Professor p WHERE p.status = :status")
|
||||
Page<Professor> findByStatus(@Param("status") WorkingStatus status, Pageable pageable);
|
||||
|
||||
@Query("SELECT p FROM Professor p WHERE p.category = :category")
|
||||
Page<Professor> findByCategory(@Param("category") ProfessorCategory category, Pageable pageable);
|
||||
|
||||
@Query("SELECT p FROM Professor p WHERE p.status = :status AND p.category = :category")
|
||||
Page<Professor> findByStatusAndCategory(@Param("status") WorkingStatus status,
|
||||
@Param("category") ProfessorCategory category,
|
||||
Pageable pageable);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// UpcomingEventRepository.java
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.repository;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.UpcomingEvent;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface UpcomingEventRepository extends JpaRepository<UpcomingEvent, Long> {
|
||||
List<UpcomingEvent> findAllByIsActiveTrue();
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package net.shyshkin.study.fullstack.supportportal.backend.service;
|
||||
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorCategory;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.ProfessorDto;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@ -29,4 +30,15 @@ public interface ProfessorService {
|
||||
byte[] getImageByProfessorId(UUID professorId, String filename);
|
||||
|
||||
byte[] getDefaultProfileImage(UUID professorId);
|
||||
|
||||
// Existing method for active professors
|
||||
Page<Professor> findActiveProfessors(Pageable pageable);
|
||||
|
||||
// New methods for category-based filtering
|
||||
Page<Professor> findByCategory(ProfessorCategory category, Pageable pageable);
|
||||
|
||||
Page<Professor> findActiveProfessorsByCategory(ProfessorCategory category, Pageable pageable);
|
||||
|
||||
// Method to find professor with details
|
||||
Professor findProfessorWithDetailsById(UUID professorId);
|
||||
}
|
||||
@ -3,7 +3,11 @@ package net.shyshkin.study.fullstack.supportportal.backend.service;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.Professor;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorAward;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorCategory;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.ProfessorSkill;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.ProfessorDto;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.domain.WorkingStatus;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.EmailExistsException;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.NotAnImageFileException;
|
||||
import net.shyshkin.study.fullstack.supportportal.backend.exception.domain.ProfessorNotFoundException;
|
||||
@ -25,14 +29,14 @@ import javax.annotation.PostConstruct;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant.*;
|
||||
|
||||
|
||||
import static org.springframework.http.MediaType.*;
|
||||
|
||||
@Slf4j
|
||||
@ -51,10 +55,8 @@ public class ProfessorServiceImpl implements ProfessorService {
|
||||
private final ProfileImageService profileImageService;
|
||||
private final RestTemplateBuilder restTemplateBuilder;
|
||||
|
||||
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
restTemplate = restTemplateBuilder
|
||||
@ -108,7 +110,26 @@ public class ProfessorServiceImpl implements ProfessorService {
|
||||
.orElseThrow(() -> new ProfessorNotFoundException(PROFESSOR_NOT_FOUND_MSG));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Professor> findActiveProfessors(Pageable pageable) {
|
||||
return professorRepository.findByStatus(WorkingStatus.ACTIVE, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Professor> findByCategory(ProfessorCategory category, Pageable pageable) {
|
||||
return professorRepository.findByCategory(category, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Professor> findActiveProfessorsByCategory(ProfessorCategory category, Pageable pageable) {
|
||||
return professorRepository.findByStatusAndCategory(WorkingStatus.ACTIVE, category, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Professor findProfessorWithDetailsById(UUID professorId) {
|
||||
return professorRepository.findByProfessorId(professorId)
|
||||
.orElseThrow(() -> new ProfessorNotFoundException(PROFESSOR_NOT_FOUND_MSG));
|
||||
}
|
||||
|
||||
private void saveProfileImage(Professor professor, MultipartFile profileImage) {
|
||||
if (profileImage == null) return;
|
||||
@ -135,33 +156,68 @@ public class ProfessorServiceImpl implements ProfessorService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Professor addNewProfessor(ProfessorDto professorDto) {
|
||||
|
||||
validateNewEmail(professorDto.getEmail());
|
||||
|
||||
Professor professor = professorMapper.toEntity(professorDto);
|
||||
|
||||
// Set a unique identifier for the professor
|
||||
professor.setProfessorId(generateUuid());
|
||||
// professor.setProfessorId(UUID.randomUUID()); // Set a unique identifier for the professor
|
||||
professor.setJoinDate(LocalDateTime.now()); // Set join date if not provided
|
||||
professor.setJoinDate(LocalDateTime.now());
|
||||
professor.setProfileImageUrl(generateDefaultProfileImageUrl(professor.getProfessorId()));
|
||||
|
||||
professorRepository.save(professor);
|
||||
// Save the professor first to get the ID
|
||||
Professor savedProfessor = professorRepository.save(professor);
|
||||
|
||||
// saveProfileImage(professor, professorDto.getProfileImage());
|
||||
// Handle skills if provided
|
||||
if (professorDto.getSkills() != null && !professorDto.getSkills().isEmpty()) {
|
||||
Set<ProfessorSkill> skills = professorDto.getSkills().stream()
|
||||
.filter(skillDto -> skillDto.getName() != null && !skillDto.getName().trim().isEmpty())
|
||||
.map(skillDto -> ProfessorSkill.builder()
|
||||
.name(skillDto.getName().trim())
|
||||
.level(skillDto.getLevel())
|
||||
.professor(savedProfessor)
|
||||
.build())
|
||||
.collect(Collectors.toSet());
|
||||
savedProfessor.setSkills(skills);
|
||||
}
|
||||
|
||||
return professor;
|
||||
// Handle awards if provided
|
||||
if (professorDto.getAwards() != null && !professorDto.getAwards().isEmpty()) {
|
||||
Set<ProfessorAward> awards = professorDto.getAwards().stream()
|
||||
.filter(awardDto -> awardDto.getTitle() != null && !awardDto.getTitle().trim().isEmpty())
|
||||
.map(awardDto -> ProfessorAward.builder()
|
||||
.title(awardDto.getTitle().trim())
|
||||
.year(awardDto.getYear())
|
||||
.description(awardDto.getDescription())
|
||||
.imageUrl(awardDto.getImageUrl())
|
||||
.professor(savedProfessor)
|
||||
.build())
|
||||
.collect(Collectors.toSet());
|
||||
savedProfessor.setAwards(awards);
|
||||
}
|
||||
|
||||
// Save again to persist the relationships
|
||||
Professor finalProfessor = professorRepository.save(savedProfessor);
|
||||
|
||||
// Handle profile image if provided
|
||||
if (professorDto.getProfileImage() != null) {
|
||||
saveProfileImage(finalProfessor, professorDto.getProfileImage());
|
||||
}
|
||||
|
||||
return finalProfessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Professor updateProfessor(UUID professorId, ProfessorDto professorDto) {
|
||||
|
||||
Professor professor = professorRepository.findByProfessorId(professorId)
|
||||
.orElseThrow(() -> new RuntimeException("Professor not found with id: " + professorId));
|
||||
|
||||
validateUpdateEmail(professorId, professorDto.getEmail());
|
||||
|
||||
// Update basic fields
|
||||
professor.setFirstName(professorDto.getFirstName());
|
||||
professor.setLastName(professorDto.getLastName());
|
||||
professor.setEmail(professorDto.getEmail());
|
||||
@ -169,13 +225,73 @@ public class ProfessorServiceImpl implements ProfessorService {
|
||||
professor.setPosition(professorDto.getPosition());
|
||||
professor.setOfficeLocation(professorDto.getOfficeLocation());
|
||||
professor.setStatus(professorDto.getStatus());
|
||||
professor.setJoinDate(professorDto.getJoinDate()); // Update join date if provided
|
||||
professor.setCategory(professorDto.getCategory());
|
||||
|
||||
professorRepository.save(professor);
|
||||
// Update extended fields
|
||||
professor.setPhone(professorDto.getPhone());
|
||||
professor.setSpecialty(professorDto.getSpecialty());
|
||||
professor.setCertification(professorDto.getCertification());
|
||||
professor.setTraining(professorDto.getTraining());
|
||||
professor.setExperience(professorDto.getExperience());
|
||||
professor.setDescription(professorDto.getDescription());
|
||||
professor.setDesignation(professorDto.getDesignation());
|
||||
professor.setWorkDays(professorDto.getWorkDays());
|
||||
|
||||
saveProfileImage(professor, professorDto.getProfileImage());
|
||||
if (professorDto.getJoinDate() != null) {
|
||||
professor.setJoinDate(professorDto.getJoinDate());
|
||||
}
|
||||
|
||||
return professor;
|
||||
// Create a final reference for lambda expressions
|
||||
final Professor professorRef = professor;
|
||||
|
||||
// Update skills - clear existing and add new ones
|
||||
if (professor.getSkills() != null) {
|
||||
professor.getSkills().clear();
|
||||
}
|
||||
if (professorDto.getSkills() != null && !professorDto.getSkills().isEmpty()) {
|
||||
Set<ProfessorSkill> skills = professorDto.getSkills().stream()
|
||||
.filter(skillDto -> skillDto.getName() != null && !skillDto.getName().trim().isEmpty())
|
||||
.map(skillDto -> ProfessorSkill.builder()
|
||||
.name(skillDto.getName().trim())
|
||||
.level(skillDto.getLevel())
|
||||
.professor(professorRef)
|
||||
.build())
|
||||
.collect(Collectors.toSet());
|
||||
if (professor.getSkills() == null) {
|
||||
professor.setSkills(new HashSet<>());
|
||||
}
|
||||
professor.getSkills().addAll(skills);
|
||||
}
|
||||
|
||||
// Update awards - clear existing and add new ones
|
||||
if (professor.getAwards() != null) {
|
||||
professor.getAwards().clear();
|
||||
}
|
||||
if (professorDto.getAwards() != null && !professorDto.getAwards().isEmpty()) {
|
||||
Set<ProfessorAward> awards = professorDto.getAwards().stream()
|
||||
.filter(awardDto -> awardDto.getTitle() != null && !awardDto.getTitle().trim().isEmpty())
|
||||
.map(awardDto -> ProfessorAward.builder()
|
||||
.title(awardDto.getTitle().trim())
|
||||
.year(awardDto.getYear())
|
||||
.description(awardDto.getDescription())
|
||||
.imageUrl(awardDto.getImageUrl())
|
||||
.professor(professorRef)
|
||||
.build())
|
||||
.collect(Collectors.toSet());
|
||||
if (professor.getAwards() == null) {
|
||||
professor.setAwards(new HashSet<>());
|
||||
}
|
||||
professor.getAwards().addAll(awards);
|
||||
}
|
||||
|
||||
Professor savedProfessor = professorRepository.save(professor);
|
||||
|
||||
// Handle profile image if provided
|
||||
if (professorDto.getProfileImage() != null) {
|
||||
saveProfileImage(savedProfessor, professorDto.getProfileImage());
|
||||
}
|
||||
|
||||
return savedProfessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
server:
|
||||
error:
|
||||
path: /error
|
||||
# whitelabel:
|
||||
# enabled: false
|
||||
|
||||
|
||||
|
||||
spring:
|
||||
mail:
|
||||
@ -22,22 +18,20 @@ spring:
|
||||
enable: true
|
||||
ssl:
|
||||
enable: false
|
||||
|
||||
datasource:
|
||||
# url: jdbc:mysql://mysql:3306/demo
|
||||
url: jdbc:mysql://210.18.189.94:8098/demo
|
||||
# url: jdbc:mysql://${MYSQL_HOST:db}:8098/demo
|
||||
username: youruser
|
||||
password: youruserpassword
|
||||
# url: ${SPRING_DATASOURCE_URL}
|
||||
# username: ${SPRING_DATASOURCE_USERNAME}
|
||||
# password: ${SPRING_DATASOURCE_PASSWORD}
|
||||
url: jdbc:mysql://localhost:3306/support_portal?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
|
||||
username: support_portal_user
|
||||
password: Supp0rt_Porta!_P@ssword
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.MySQL8Dialect
|
||||
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
@ -49,137 +43,40 @@ spring:
|
||||
resources:
|
||||
add-mappings: false
|
||||
|
||||
|
||||
|
||||
|
||||
# File upload configuration
|
||||
file:
|
||||
upload:
|
||||
directory: uploads
|
||||
|
||||
app:
|
||||
public-urls: /user/login,/user/register,/user/*/profile-image/**,/professors,/professors/**,/api/posts,/api/posts/*,/professor,/professor/*,/api/events,/api/events/*
|
||||
base-url: ${APP_BASE_URL:http://localhost:8080}
|
||||
# Updated public URLs to include image endpoints
|
||||
public-urls: /user/login,/user/register,/user/*/profile-image,/user/*/profile-image/**,/professors,/professors/**,/api/posts,/api/posts/*,/api/posts/posted,/api/posts/tag/*,/api/posts/tags/count,/api/files/**,/professor,/professor/*,/api/events,/api/events/*,/api/public/**,/api/jobs/active,/api/job-applications,/api/courses/active,/api/courses/*,/api/course-applications,/api/upcoming-events/active
|
||||
cors:
|
||||
allowed-origins: http://localhost:4200,https://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://portal.shyshkin.net,*
|
||||
allowed-origins: http://localhost:4200,https://localhost:4200,http://localhost:3000,https://localhost:3000,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://portal.shyshkin.net,*
|
||||
jwt:
|
||||
secret: custom_text
|
||||
|
||||
|
||||
# secret: ${random.value} #Does not work - every time generates new value
|
||||
# jasypt:
|
||||
# encryptor:
|
||||
# # password: ${JASYPT_PASSWORD}
|
||||
# password: custom_text
|
||||
# algorithm: PBEWITHHMACSHA512ANDAES_256
|
||||
# iv-generator-classname: org.jasypt.iv.RandomIvGenerator
|
||||
---
|
||||
# Production file upload configuration
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile: production
|
||||
file:
|
||||
upload:
|
||||
directory: /var/uploads/blog-images
|
||||
app:
|
||||
base-url: https://yourproductiondomain.com
|
||||
cors:
|
||||
allowed-origins: https://yourfrontenddomain.com,https://youradmindomain.com
|
||||
|
||||
---
|
||||
# spring:
|
||||
# config:
|
||||
# activate:
|
||||
# on-profile: local
|
||||
# datasource:
|
||||
# url: jdbc:mysql://210.18.189.94:8098/demo
|
||||
# username: youruser
|
||||
# password: youruserpassword
|
||||
|
||||
# jpa:
|
||||
# show-sql: true
|
||||
# logging:
|
||||
# level:
|
||||
# net.shyshkin: debug
|
||||
|
||||
---
|
||||
# spring:
|
||||
# config:
|
||||
# activate:
|
||||
# on-profile: aws-local
|
||||
# datasource:
|
||||
# url: jdbc:mysql://210.18.189.94:8098/demo
|
||||
# username: youruser
|
||||
# password: youruserpassword
|
||||
|
||||
# mail:
|
||||
# host: email-smtp.eu-north-1.amazonaws.com
|
||||
# port: 587
|
||||
# username: AKIAVW7XGDOWFHHCELIH
|
||||
# password: BJyWOWS1xWYR35MRCFn3BuuQ6vY+k7DRsdAvOfqDs/Fk
|
||||
|
||||
# we want to test (1) from localhost, (2) from S3 bucket Static Web Site, (3) from our EC2 instance
|
||||
# app:
|
||||
# email:
|
||||
# from: d.art.shishkin@gmail.com
|
||||
# carbon-copy: d.art.shishkin@gmail.com
|
||||
# cors:
|
||||
# allowed-origins: http://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://support-portal.shyshkin.net,http://portal.shyshkin.net
|
||||
# server:
|
||||
# port: 5000
|
||||
# logging:
|
||||
# level:
|
||||
# net.shyshkin: debug
|
||||
|
||||
---
|
||||
# spring:
|
||||
# config:
|
||||
# activate:
|
||||
# on-profile: aws-rds
|
||||
# datasource:
|
||||
# url: jdbc:mysql://210.18.189.94:8098/demo
|
||||
# username: youruser
|
||||
# password: youruserpassword
|
||||
|
||||
# mail:
|
||||
# host: email-smtp.eu-north-1.amazonaws.com
|
||||
# port: 587
|
||||
# username: custom_text
|
||||
# password: custom_text
|
||||
|
||||
# we want to test (1) from localhost, (2) from S3 bucket Static Web Site, (3) from our EC2 instance
|
||||
# app:
|
||||
# email:
|
||||
# from: d.art.shishkin@gmail.com
|
||||
# carbon-copy: d.art.shishkin@gmail.com
|
||||
# cors:
|
||||
# allowed-origins: http://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://support-portal.shyshkin.net,http://portal.shyshkin.net
|
||||
# server:
|
||||
# port: 5000
|
||||
# logging:
|
||||
# level:
|
||||
# net.shyshkin: debug
|
||||
|
||||
#####
|
||||
#
|
||||
# HTTPS configuration
|
||||
#
|
||||
#####
|
||||
|
||||
# server.ssl:
|
||||
# enabled: true # Enable HTTPS support (only accept HTTPS requests)
|
||||
# key-alias: securedPortal # Alias that identifies the key in the key store
|
||||
# key-store: classpath:securedPortal-keystore.p12 # Keystore location
|
||||
# key-store-password: custom_text
|
||||
# key-store-type: PKCS12 # Keystore format
|
||||
|
||||
# ---
|
||||
# spring:
|
||||
# config:
|
||||
# activate:
|
||||
# on-profile: image-s3
|
||||
# app:
|
||||
# amazon-s3:
|
||||
# bucket-name: portal-user-profile-images
|
||||
|
||||
# ---
|
||||
# spring:
|
||||
# config:
|
||||
# activate:
|
||||
# on-profile: image-s3-localstack
|
||||
# app:
|
||||
# amazon-s3:
|
||||
# bucket-name: portal-user-profile-images
|
||||
# config:
|
||||
# aws:
|
||||
# region: eu-north-1
|
||||
# s3:
|
||||
# url: http://127.0.0.1:4566
|
||||
# bucket-name: portal-user-profile-images
|
||||
# access-key: localstack
|
||||
# secret-key: localstack
|
||||
|
||||
|
||||
# Development file upload configuration with custom directory
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile: dev-custom-upload
|
||||
file:
|
||||
upload:
|
||||
directory: ${user.home}/blog-uploads
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.9 MiB |
314
support-portal-frontend/package-lock.json
generated
@ -18,10 +18,8 @@
|
||||
"@angular/router": "~12.2.0",
|
||||
"@auth0/angular-jwt": "^3.0.1",
|
||||
"@josipv/angular-editor-k2": "^2.20.0",
|
||||
"@nicky-lenaers/ngx-scroll-to": "^9.0.0",
|
||||
"angular-notifier": "^9.1.0",
|
||||
"angular-notifier": "^10.0.0",
|
||||
"ng-particles": "^2.1.11",
|
||||
"ngx-owl-carousel-o": "^5.0.0",
|
||||
"ngx-typed-js": "^2.0.2",
|
||||
"rxjs": "~6.6.0",
|
||||
"subsink": "^1.0.2",
|
||||
@ -330,6 +328,30 @@
|
||||
"yarn": ">= 1.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/core/node_modules/ajv": {
|
||||
"version": "8.6.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz",
|
||||
"integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/core/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@angular-devkit/schematics": {
|
||||
"version": "12.2.18",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.2.18.tgz",
|
||||
@ -2643,20 +2665,6 @@
|
||||
"webpack": "^5.30.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nicky-lenaers/ngx-scroll-to": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nicky-lenaers/ngx-scroll-to/-/ngx-scroll-to-9.0.0.tgz",
|
||||
"integrity": "sha512-eS0vyx8qX4UTMluRYc+sQF/vJHCnAKiufWrwQRme0VURwp+RdOoZDZpYrOPTxPfx6CVj72arTeV9auDYa0WKtA==",
|
||||
"engines": {
|
||||
"node": ">=8.0.0",
|
||||
"npm": ">=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^8.0.0 || ^9.0.0",
|
||||
"@angular/core": "^8.0.0 || ^9.0.0",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@ -3215,14 +3223,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.6.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz",
|
||||
"integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
@ -3256,6 +3265,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
@ -3266,22 +3299,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/angular-notifier": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-notifier/-/angular-notifier-9.1.0.tgz",
|
||||
"integrity": "sha512-K8D8UljdC4P4TfjYB0v39Zs3WjgvPR7Vvp3yzGhcW4I8gXIqkz4xQSbqJbIZABCGEWdKqPyAN48wl7yg8Q3Urg==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-notifier/-/angular-notifier-10.0.0.tgz",
|
||||
"integrity": "sha512-hVzFd41ZCT0O6EBlwN1cygl3qjXU4C41DVcaDrHK3CK5Y0JFbpFrJVuIAczKPjaul13rzJ/L7qzwobQHaTUwLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "2.0.x"
|
||||
"tslib": "2.3.x"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">= 11.0.0 < 12.0.0",
|
||||
"@angular/core": ">= 11.0.0 < 12.0.0",
|
||||
"rxjs": ">= 6.0.0 < 7.0.0"
|
||||
"@angular/common": ">= 12.0.0 < 13.0.0",
|
||||
"@angular/core": ">= 12.0.0 < 13.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-notifier/node_modules/tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/ansi-colors": {
|
||||
"version": "4.1.1",
|
||||
@ -4637,28 +4671,6 @@
|
||||
"webpack": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/copy-webpack-plugin/node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@ -5104,28 +5116,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/css-minimizer-webpack-plugin/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/css-minimizer-webpack-plugin/node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@ -6653,6 +6643,23 @@
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||
"integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||
@ -8472,10 +8479,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
@ -9402,28 +9410,6 @@
|
||||
"webpack": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-css-extract-plugin/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
@ -9763,19 +9749,6 @@
|
||||
"tsparticles": "^1.43.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-owl-carousel-o": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ngx-owl-carousel-o/-/ngx-owl-carousel-o-5.1.1.tgz",
|
||||
"integrity": "sha512-AmaU02UzONGrBSj4K28ZWEZxLYySyiC7Vefq4VacjAhYxUR+HA9jdQghG4zM+QlVPkmgFdZeepeqZ3/h9fU3ug==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": " ^11.0.0-rc.0 || ^11.0.0",
|
||||
"@angular/core": "^11.0.0-rc.0 || ^11.0.0",
|
||||
"rxjs": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-typed-js": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ngx-typed-js/-/ngx-typed-js-2.1.1.tgz",
|
||||
@ -13120,6 +13093,7 @@
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -13470,28 +13444,6 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@ -14625,28 +14577,6 @@
|
||||
"webpack": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@ -15303,28 +15233,6 @@
|
||||
"webpack": "^4.0.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/webpack-dev-middleware/node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
@ -15398,22 +15306,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
@ -15613,12 +15505,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/micromatch": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
|
||||
@ -15875,28 +15761,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/webpack/node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
|
||||
@ -20,10 +20,8 @@
|
||||
"@angular/router": "~12.2.0",
|
||||
"@auth0/angular-jwt": "^3.0.1",
|
||||
"@josipv/angular-editor-k2": "^2.20.0",
|
||||
"@nicky-lenaers/ngx-scroll-to": "^9.0.0",
|
||||
"angular-notifier": "^9.1.0",
|
||||
"angular-notifier": "^10.0.0",
|
||||
"ng-particles": "^2.1.11",
|
||||
"ngx-owl-carousel-o": "^5.0.0",
|
||||
"ngx-typed-js": "^2.0.2",
|
||||
"rxjs": "~6.6.0",
|
||||
"subsink": "^1.0.2",
|
||||
|
||||