Newer
Older
job-tracker / src / main / java / com / kpaudel / controller / ApiController.java
package com.kpaudel.controller;

import com.kpaudel.model.ApplicationStatus;
import com.kpaudel.model.Company;
import com.kpaudel.model.JobApplication;
import com.kpaudel.model.User;
import com.kpaudel.repository.CompanyRepository;
import com.kpaudel.repository.JobApplicationRepository;
import com.kpaudel.repository.UserRepository;
import com.kpaudel.service.JobService;
import com.opencsv.CSVWriter;
import org.springframework.boot.autoconfigure.batch.BatchProperties;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ContentDisposition;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.UUID;

@RestController
@RequestMapping("/api")
@CrossOrigin(origins = {"http://localhost:3000", "https://jobs.kpaudel.com.np"})  // allow prod
public class ApiController {
    private final JobService service;
    private final CompanyRepository companyRepo;
    private final JobApplicationRepository jobRepo;
    private final UserRepository userRepo;

    public ApiController(JobService service, CompanyRepository companyRepo, JobApplicationRepository jobRepo, UserRepository userRepo) {
        this.service = service;
        this.companyRepo = companyRepo;
        this.jobRepo = jobRepo;
        this.userRepo = userRepo;
    }

    private User getCurrentUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();
        return userRepo.findByUsername(username).orElseThrow(() -> new RuntimeException("User not found"));
    }

    private UUID getCurrentUserId() {
        return getCurrentUser().getId();
    }

    @GetMapping("/jobs")
    public List<JobApplication> listJobs(@RequestParam Optional<ApplicationStatus> status) {
        UUID userId = getCurrentUserId();
        if (status.isPresent()) {
            return service.listByUserAndStatus(userId, status.get());
        }
        return service.listAllByUser(userId);
    }

    @GetMapping("/jobs/{id}")
    public ResponseEntity<JobApplication> getJob(@PathVariable UUID id) {
        UUID userId = getCurrentUserId();
        JobApplication job = this.jobRepo.findById(id).orElse(null);
        if (job == null || !job.getUser().getId().equals(userId)) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(job);
    }

    @PostMapping("/jobs")
    public ResponseEntity<JobApplication> createJob(@RequestBody JobApplication job) {
        UUID userId = getCurrentUserId();
        JobApplication jobApplication= this.service.create(job, userId);
        return ResponseEntity.status(HttpStatus.CREATED).body(jobApplication);
    }

    @PutMapping("/jobs/{id}")
    public ResponseEntity<JobApplication> updateJob(@PathVariable UUID id, @RequestBody JobApplication updated) {
        UUID userId = getCurrentUserId();
        JobApplication existing = jobRepo.findById(id).orElse(null);
        if (existing == null || !existing.getUser().getId().equals(userId)) {
            return ResponseEntity.notFound().build();
        }
        updated.setId(id);
        updated.setUser(existing.getUser()); // Preserve user
        return ResponseEntity.ok(jobRepo.save(updated));
    }

    @PatchMapping("/jobs/{id}/status")
    public ResponseEntity<JobApplication> updateStatus(@PathVariable UUID id, @RequestBody Map<String, String> body) {
        UUID userId = getCurrentUserId();
        ApplicationStatus status = ApplicationStatus.valueOf(body.get("status"));
        JobApplication jobApplication = service.updateStatus(id, status, userId);
        return ResponseEntity.ok(jobApplication);
    }

    @DeleteMapping("/jobs/{id}")
    public ResponseEntity<Void> deleteJob(@PathVariable UUID id) {
        UUID userId = getCurrentUserId();
        JobApplication job = jobRepo.findById(id).orElse(null);
        if (job == null || !job.getUser().getId().equals(userId)) {
            return ResponseEntity.notFound().build();
        }
        jobRepo.deleteById(id);
        return ResponseEntity.noContent().build();
    }

    @GetMapping("/companies")
    public List<Company> listCompanies() {
        return this.companyRepo.findAll();
    }

    @GetMapping("/companies/{id}")
    public ResponseEntity<Company> getCompany(@PathVariable UUID id) {
        return companyRepo.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @GetMapping("/companies/{id}/jobs")
    public List<JobApplication> listJobsByCompany(@PathVariable UUID id) {
        UUID userId = getCurrentUserId();
        return service.listByCompanyAndUser(id, userId);
    }

    @PostMapping("/companies")
    public ResponseEntity<Company> createCompany(@RequestBody Company c) {
        Company company=  this.companyRepo.save(c);
        return ResponseEntity.status(HttpStatus.CREATED).body(company);
    }


    @PutMapping("/companies/{id}")
    public ResponseEntity<Company> updateCompany(@PathVariable UUID id, @RequestBody Company updated) {
        return companyRepo.findById(id)
                .map(existing -> {
                    updated.setId(id);
                    return ResponseEntity.ok(companyRepo.save(updated));
                })
                .orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/companies/{id}")
    public ResponseEntity<Void> deleteCompany(@PathVariable UUID id) {
        if (companyRepo.existsById(id)) {
            companyRepo.deleteById(id);
            return ResponseEntity.noContent().build();
        }
        return ResponseEntity.notFound().build();
    }

    @GetMapping("/jobs/export")
    public ResponseEntity<byte[]> exportJobs() throws IOException {
        UUID userId = getCurrentUserId();
        List<JobApplication> jobs = service.listAllByUser(userId);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        CSVWriter writer = new CSVWriter(new java.io.OutputStreamWriter(out));
        //Add headers
        writer.writeNext(new String[]{"ID", "Role", "Company","JobUrl", "Applied Date", "Source", "Salary Expectation","Status", "Location", "Last Updated", "Notes"});
        //Add Data
        for (JobApplication job : jobs) {
            writer.writeNext(new String[]{
                    job.getId().toString(),
                    job.getRole(),
                    job.getCompany().getName(),
                    job.getJobUrl(),
                    job.getAppliedDate().toString(),
                    job.getSource(),
                    job.getSalaryExpectation(),
                    job.getStatus().name(),
                    job.getLocation(),
                    job.getLastUpdated().toString(),
                    job.getNotes()
            });
        }
        writer.close();
        byte[] data = out.toByteArray();
        String timestamp = java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("text/csv"));
        headers.setContentDisposition(ContentDisposition.attachment().filename("jobs_" + timestamp + ".csv").build());

        return new ResponseEntity<>(data, headers, HttpStatus.OK);
    }

    // Add to service if needed
    public List<JobApplication> listByUserAndStatus(UUID userId, ApplicationStatus status) {
        return this.jobRepo.findByUserIdAndStatus(userId, status);
    }

    public List<JobApplication> listByCompanyAndUser(UUID userId, UUID companyId) {
        return this.jobRepo.findByUserIdAndCompanyId(userId, companyId);
    }

    @GetMapping("/user/profile")
    public ResponseEntity<Map<String, Object>> getProfile() {
        User user = getCurrentUser();
        Map<String, Object> profile = new HashMap<>();
        profile.put("id", user.getId());
        profile.put("username", user.getUsername());
        profile.put("email", user.getEmail());
        profile.put("firstName", user.getFirstName());
        profile.put("lastName", user.getLastName());
        profile.put("status", user.getStatus().name());
        return ResponseEntity.ok(profile);
    }

    @GetMapping("/companies/export")
    public ResponseEntity<byte[]> exportCompanies() throws IOException {
        List<Company> companies = this.companyRepo.findAll();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        CSVWriter writer = new CSVWriter(new java.io.OutputStreamWriter(out));
        //Add headers
        writer.writeNext(new String[]{"ID", "Name", "Website", "Notes"});
        //Add Data
        for (Company company : companies) {
            writer.writeNext(new String[]{
                    company.getId().toString(),
                    company.getName(),
                    company.getWebsite(),
                    company.getNotes()
            });
        }
        writer.close();
        byte[] data = out.toByteArray();
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("text/csv"));
        headers.setContentDisposition(ContentDisposition.attachment().filename("companies_" + timestamp + ".csv").build());

        return new ResponseEntity<>(data, headers, HttpStatus.OK);
    }
}