Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/main/java/com/jobtracker/config/FakeDataSeeder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.jobtracker.entity.JobApplication;
import com.jobtracker.entity.User;
import com.jobtracker.entity.enums.ApplicationStatus;
import com.jobtracker.repository.ApplicationRepository;
import com.jobtracker.repository.UserRepository;
import net.datafaker.Faker;
Expand Down Expand Up @@ -95,8 +94,13 @@ private JobApplication buildFakeApplication(User user) {
return application;
}

private ApplicationStatus randomStatus() {
ApplicationStatus[] statuses = ApplicationStatus.values();
return statuses[ThreadLocalRandom.current().nextInt(statuses.length)];
private static final String[] STATUSES = {
"RH", "Pending HR Response", "Pending Hiring Manager Response",
"Technical Test", "Pending Technical Test Response",
"Offer Negotiation", "Ghosting", "Rejected", "Approved"
};

private String randomStatus() {
return STATUSES[ThreadLocalRandom.current().nextInt(STATUSES.length)];
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/jobtracker/controller/ApplicationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,19 @@ public ResponseEntity<List<ApplicationResponse>> getOverdue() {
return ResponseEntity.ok(applicationService.getOverdue());
}

@Operation(
summary = "List valid application statuses",
description = "Returns all valid status values ordered by display order",
responses = {
@ApiResponse(responseCode = "200", description = "List of valid status values")
}
)
@PreAuthorize("hasRole('USER') or hasAuthority('SCOPE_read:applications')")
@GetMapping("/statuses")
public ResponseEntity<List<String>> getStatuses() {
return ResponseEntity.ok(applicationService.listStatuses());
}

@Operation(
summary = "Extract link metadata",
description = "Extracts rich preview metadata (title, description, image, domain) from a URL",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public record ApplicationResponse(
@Schema(description = "Timestamp when the latest Google Docs resume was generated", example = "2024-06-11T14:45:00")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
LocalDateTime driveResumeGeneratedAt,
@Schema(description = "Whether this application is queued to be sent later (draft mode)", example = "false")
boolean toSendLater,
@Schema(description = "Number of interviews held for this application", example = "2")
int interviewCount,
@Schema(description = "Record creation timestamp", example = "2024-06-01T10:00:00")
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/com/jobtracker/dto/auth/RegisterRequest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.jobtracker.dto.auth;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
Expand All @@ -25,5 +26,9 @@ public record RegisterRequest(

@Schema(description = "Confirm password (must match password)", example = "secureP@ss1")
@NotBlank(message = "Confirm password is required")
String confirmPassword
String confirmPassword,

@Schema(description = "User must accept the Privacy Policy to register", example = "true")
@AssertTrue(message = "You must accept the Privacy Policy to create an account")
boolean acceptedPrivacyPolicy
) {}
4 changes: 3 additions & 1 deletion src/main/java/com/jobtracker/dto/auth/UserResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ public record UserResponse(
@Schema(description = "Granted application roles", example = "[\"USER\", \"BETA\"]")
Set<String> roles,
@Schema(description = "Whether the user can access Google integration features", example = "true")
boolean canUseGoogleIntegration
boolean canUseGoogleIntegration,
@Schema(description = "Whether the user has accepted the Privacy Policy", example = "true")
boolean privacyPolicyAccepted
) {}
22 changes: 22 additions & 0 deletions src/main/java/com/jobtracker/entity/ApplicationStatusEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.jobtracker.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "application_statuses")
public class ApplicationStatusEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true, nullable = false, length = 100)
private String name;

@Column(name = "display_order", nullable = false)
private int displayOrder;

public Long getId() { return id; }
public String getName() { return name; }
public int getDisplayOrder() { return displayOrder; }
}
17 changes: 6 additions & 11 deletions src/main/java/com/jobtracker/entity/InterviewEvent.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.jobtracker.entity;

import com.jobtracker.entity.enums.ApplicationStatus;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
Expand Down Expand Up @@ -37,13 +34,11 @@ public class InterviewEvent {
@JoinColumn(name = "application_id", nullable = false)
private JobApplication application;

@Enumerated(EnumType.STRING)
@Column(name = "old_status", length = 100, columnDefinition = "varchar(100)")
private ApplicationStatus oldStatus;
private String oldStatus;

@Enumerated(EnumType.STRING)
@Column(name = "new_status", nullable = false, length = 100, columnDefinition = "varchar(100)")
private ApplicationStatus newStatus;
private String newStatus;

@Column(name = "occurred_at", nullable = false)
private LocalDateTime occurredAt;
Expand Down Expand Up @@ -79,19 +74,19 @@ public void setApplication(JobApplication application) {
this.application = application;
}

public ApplicationStatus getOldStatus() {
public String getOldStatus() {
return oldStatus;
}

public void setOldStatus(ApplicationStatus oldStatus) {
public void setOldStatus(String oldStatus) {
this.oldStatus = oldStatus;
}

public ApplicationStatus getNewStatus() {
public String getNewStatus() {
return newStatus;
}

public void setNewStatus(ApplicationStatus newStatus) {
public void setNewStatus(String newStatus) {
this.newStatus = newStatus;
}

Expand Down
21 changes: 12 additions & 9 deletions src/main/java/com/jobtracker/entity/JobApplication.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.jobtracker.entity;

import com.jobtracker.entity.enums.ApplicationStatus;
import jakarta.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -49,13 +48,14 @@ public class JobApplication {
@Column(name = "next_step_date_time")
private LocalDateTime nextStepDateTime;

@Enumerated(EnumType.STRING)
@Column(nullable = true, length = 100, columnDefinition = "varchar(100)")
private ApplicationStatus status;
private String status;

@Enumerated(EnumType.STRING)
@Column(name = "previous_status", nullable = true, length = 100, columnDefinition = "varchar(100)")
private ApplicationStatus previousStatus;
private String previousStatus;

@Column(name = "to_send_later", nullable = false)
private boolean toSendLater;

@Column(name = "recruiter_dm_reminder_enabled", nullable = false)
private boolean recruiterDmReminderEnabled;
Expand Down Expand Up @@ -156,11 +156,14 @@ protected void onUpdate() {
public LocalDateTime getNextStepDateTime() { return nextStepDateTime; }
public void setNextStepDateTime(LocalDateTime nextStepDateTime) { this.nextStepDateTime = nextStepDateTime; }

public ApplicationStatus getStatus() { return status; }
public void setStatus(ApplicationStatus status) { this.status = status; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }

public String getPreviousStatus() { return previousStatus; }
public void setPreviousStatus(String previousStatus) { this.previousStatus = previousStatus; }

public ApplicationStatus getPreviousStatus() { return previousStatus; }
public void setPreviousStatus(ApplicationStatus previousStatus) { this.previousStatus = previousStatus; }
public boolean isToSendLater() { return toSendLater; }
public void setToSendLater(boolean toSendLater) { this.toSendLater = toSendLater; }

public boolean isRecruiterDmReminderEnabled() { return recruiterDmReminderEnabled; }
public void setRecruiterDmReminderEnabled(boolean recruiterDmReminderEnabled) { this.recruiterDmReminderEnabled = recruiterDmReminderEnabled; }
Expand Down
74 changes: 68 additions & 6 deletions src/main/java/com/jobtracker/entity/ToolExecutionMetric.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.jobtracker.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.UuidGenerator;

import java.time.LocalDateTime;
Expand All @@ -13,11 +12,6 @@
@Index(name = "idx_tool_metrics_created_at", columnList = "created_at"),
@Index(name = "idx_tool_metrics_expensive", columnList = "expensive")
})
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ToolExecutionMetric {

@Id
Expand Down Expand Up @@ -58,4 +52,72 @@ protected void onCreate() {
createdAt = LocalDateTime.now();
}
}

public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }

public String getToolName() { return toolName; }
public void setToolName(String toolName) { this.toolName = toolName; }

public long getExecutionTimeMs() { return executionTimeMs; }
public void setExecutionTimeMs(long executionTimeMs) { this.executionTimeMs = executionTimeMs; }

public int getRequestBytes() { return requestBytes; }
public void setRequestBytes(int requestBytes) { this.requestBytes = requestBytes; }

public int getResponseBytes() { return responseBytes; }
public void setResponseBytes(int responseBytes) { this.responseBytes = responseBytes; }

public int getRequestTokens() { return requestTokens; }
public void setRequestTokens(int requestTokens) { this.requestTokens = requestTokens; }

public int getResponseTokens() { return responseTokens; }
public void setResponseTokens(int responseTokens) { this.responseTokens = responseTokens; }

public int getTotalTokens() { return totalTokens; }
public void setTotalTokens(int totalTokens) { this.totalTokens = totalTokens; }

public boolean isExpensive() { return expensive; }
public void setExpensive(boolean expensive) { this.expensive = expensive; }

public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }

public static Builder builder() { return new Builder(); }

public static class Builder {
private String toolName;
private long executionTimeMs;
private int requestBytes;
private int responseBytes;
private int requestTokens;
private int responseTokens;
private int totalTokens;
private boolean expensive;
private LocalDateTime createdAt;

public Builder toolName(String toolName) { this.toolName = toolName; return this; }
public Builder executionTimeMs(long executionTimeMs) { this.executionTimeMs = executionTimeMs; return this; }
public Builder requestBytes(int requestBytes) { this.requestBytes = requestBytes; return this; }
public Builder responseBytes(int responseBytes) { this.responseBytes = responseBytes; return this; }
public Builder requestTokens(int requestTokens) { this.requestTokens = requestTokens; return this; }
public Builder responseTokens(int responseTokens) { this.responseTokens = responseTokens; return this; }
public Builder totalTokens(int totalTokens) { this.totalTokens = totalTokens; return this; }
public Builder expensive(boolean expensive) { this.expensive = expensive; return this; }
public Builder createdAt(LocalDateTime createdAt) { this.createdAt = createdAt; return this; }

public ToolExecutionMetric build() {
ToolExecutionMetric m = new ToolExecutionMetric();
m.toolName = toolName;
m.executionTimeMs = executionTimeMs;
m.requestBytes = requestBytes;
m.responseBytes = responseBytes;
m.requestTokens = requestTokens;
m.responseTokens = responseTokens;
m.totalTokens = totalTokens;
m.expensive = expensive;
m.createdAt = createdAt;
return m;
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/jobtracker/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public class User {
@Column(name = "reminder_time", nullable = false)
private LocalTime reminderTime;

@Column(name = "privacy_policy_accepted", nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0")
private boolean privacyPolicyAccepted;

@Column(name = "privacy_policy_accepted_at")
private LocalDateTime privacyPolicyAcceptedAt;

@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;

Expand Down Expand Up @@ -79,4 +85,10 @@ protected void onUpdate() {

public Set<Role> getRoles() { return roles; }
public void setRoles(Set<Role> roles) { this.roles = roles; }

public boolean isPrivacyPolicyAccepted() { return privacyPolicyAccepted; }
public void setPrivacyPolicyAccepted(boolean privacyPolicyAccepted) { this.privacyPolicyAccepted = privacyPolicyAccepted; }

public LocalDateTime getPrivacyPolicyAcceptedAt() { return privacyPolicyAcceptedAt; }
public void setPrivacyPolicyAcceptedAt(LocalDateTime privacyPolicyAcceptedAt) { this.privacyPolicyAcceptedAt = privacyPolicyAcceptedAt; }
}
5 changes: 3 additions & 2 deletions src/main/java/com/jobtracker/mapper/ApplicationMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public ApplicationResponse toResponse(JobApplication app) {
app.isRhAcceptedConnection(),
app.isInterviewScheduled(),
app.getNextStepDateTime(),
app.getStatus() != null ? app.getStatus().getDisplayName() : null,
app.getPreviousStatus() != null ? app.getPreviousStatus().getDisplayName() : null,
app.getStatus(),
app.getPreviousStatus(),
app.isRecruiterDmReminderEnabled(),
app.getRecruiterDmSentAt(),
app.getNote(),
Expand All @@ -31,6 +31,7 @@ public ApplicationResponse toResponse(JobApplication app) {
app.getDriveResumeFileName(),
app.getDriveResumeDocumentUrl(),
app.getDriveResumeGeneratedAt(),
app.isToSendLater(),
app.getInterviewCount(),
app.getCreatedAt(),
app.getUpdatedAt()
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/jobtracker/mapper/AuthMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public UserResponse toUserResponse(User user) {
user.getEmail(),
user.getReminderTime(),
roles,
roles.contains(RoleName.BETA.name()));
roles.contains(RoleName.BETA.name()),
user.isPrivacyPolicyAccepted());
}
}
1 change: 1 addition & 0 deletions src/main/java/com/jobtracker/mcp/McpPromptsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* Registers MCP prompt templates.
*/
@Service
@SuppressWarnings("unused")
public class McpPromptsConfig {

/**
Expand Down
Loading
Loading