user avatar in edit dialogs (#312)

This commit is contained in:
2026-04-15 15:58:46 -06:00
committed by GitHub
parent df5510224b
commit 2077c1b10c
10 changed files with 295 additions and 0 deletions

View File

@@ -16,6 +16,7 @@ public class EmployeeResponse {
private String staffRole;
private Long primaryStoreId;
private Boolean active;
private String avatarUrl;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -45,6 +46,8 @@ public class EmployeeResponse {
public void setPrimaryStoreId(Long primaryStoreId) { this.primaryStoreId = primaryStoreId; }
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
public String getAvatarUrl() { return avatarUrl; }
public void setAvatarUrl(String avatarUrl) { this.avatarUrl = avatarUrl; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }

View File

@@ -11,6 +11,7 @@ public class UserResponse {
private String role;
private Boolean active;
private Integer loyaltyPoints;
private String avatarUrl;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -81,6 +82,14 @@ public class UserResponse {
this.loyaltyPoints = loyaltyPoints;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}

View File

@@ -8,6 +8,7 @@ import org.example.petshopdesktop.api.dto.user.UserResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
public class UserApi {
@@ -45,4 +46,12 @@ public class UserApi {
public UserResponse updateUser(Long id, UserRequest request) throws Exception {
return apiClient.put("/api/v1/users/" + id, request, UserResponse.class);
}
public void uploadUserAvatar(Long userId, Path filePath) throws Exception {
apiClient.postMultipart("/api/v1/users/" + userId + "/avatar", "avatar", filePath, Object.class);
}
public void deleteUserAvatar(Long userId) throws Exception {
apiClient.delete("/api/v1/users/" + userId + "/avatar");
}
}

View File

@@ -9,15 +9,21 @@ import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import org.example.petshopdesktop.Validator;
import org.example.petshopdesktop.api.dto.user.UserRequest;
import org.example.petshopdesktop.api.dto.user.UserResponse;
import org.example.petshopdesktop.api.endpoints.CustomerApi;
import org.example.petshopdesktop.api.endpoints.UserApi;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.util.ActivityLogger;
import org.example.petshopdesktop.util.DesktopImageSupport;
import org.example.petshopdesktop.util.FilePickerSupport;
import org.example.petshopdesktop.util.TextFieldFormatSupport;
import java.io.File;
public class CustomerEditDialogController {
@FXML private TextField txtFirstName;
@@ -32,7 +38,15 @@ public class CustomerEditDialogController {
@FXML private Label lblError;
@FXML private Button btnSave;
@FXML private ImageView imgAvatarPreview;
@FXML private Label lblAvatarStatus;
@FXML private Button btnChangeAvatar;
@FXML private Button btnRemoveAvatar;
private UserResponse customer;
private File selectedAvatarFile;
private String currentAvatarUrl;
private boolean removeAvatarRequested;
@FXML
void initialize() {
@@ -41,6 +55,9 @@ public class CustomerEditDialogController {
boolean isAdmin = UserSession.getInstance().isAdmin();
txtLoyaltyPoints.setDisable(!isAdmin);
btnChangeAvatar.setOnMouseClicked(e -> handleChangeAvatar());
btnRemoveAvatar.setOnMouseClicked(e -> handleRemoveAvatar());
}
public void setCustomer(UserResponse user) {
@@ -55,6 +72,65 @@ public class CustomerEditDialogController {
cbActive.setValue(Boolean.TRUE.equals(user.getActive()) ? "Active" : "Inactive");
int pts = user.getLoyaltyPoints() != null ? user.getLoyaltyPoints() : 0;
txtLoyaltyPoints.setText(String.valueOf(pts));
currentAvatarUrl = user.getAvatarUrl();
refreshAvatarPreview();
}
private void handleChangeAvatar() {
File file = FilePickerSupport.pickImageFile(btnSave.getScene().getWindow());
if (file == null) return;
selectedAvatarFile = file;
removeAvatarRequested = false;
lblAvatarStatus.setText("Selected: " + file.getName());
DesktopImageSupport.loadImageInto(imgAvatarPreview, file.toURI().toString(), 90, 90);
btnRemoveAvatar.setDisable(false);
}
private void handleRemoveAvatar() {
selectedAvatarFile = null;
removeAvatarRequested = true;
currentAvatarUrl = null;
refreshAvatarPreview();
}
private void applyAvatarChanges(Long userId) throws Exception {
String previousAvatarUrl = currentAvatarUrl;
if (removeAvatarRequested) {
try {
UserApi.getInstance().deleteUserAvatar(userId);
} catch (Exception ignored) {
}
}
if (selectedAvatarFile != null) {
UserApi.getInstance().uploadUserAvatar(userId, selectedAvatarFile.toPath());
currentAvatarUrl = "/api/v1/users/" + userId + "/avatar/file";
} else if (removeAvatarRequested) {
currentAvatarUrl = null;
}
DesktopImageSupport.evict(previousAvatarUrl);
DesktopImageSupport.evict(currentAvatarUrl);
selectedAvatarFile = null;
removeAvatarRequested = false;
}
private void refreshAvatarPreview() {
if (imgAvatarPreview == null || lblAvatarStatus == null || btnRemoveAvatar == null) return;
imgAvatarPreview.setImage(null);
if (selectedAvatarFile != null) {
lblAvatarStatus.setText("Selected: " + selectedAvatarFile.getName());
DesktopImageSupport.loadImageInto(imgAvatarPreview, selectedAvatarFile.toURI().toString(), 90, 90);
btnRemoveAvatar.setDisable(false);
return;
}
if (currentAvatarUrl != null && !currentAvatarUrl.isBlank()) {
lblAvatarStatus.setText("Current avatar loaded");
DesktopImageSupport.loadImageInto(imgAvatarPreview, currentAvatarUrl, 90, 90);
btnRemoveAvatar.setDisable(false);
return;
}
lblAvatarStatus.setText("No avatar");
btnRemoveAvatar.setDisable(true);
}
private String[] splitFullName(String fullName) {
@@ -148,6 +224,7 @@ public class CustomerEditDialogController {
if (finalLoyaltyPoints != null) request.setLoyaltyPoints(finalLoyaltyPoints);
CustomerApi.getInstance().updateCustomer(customer.getId(), request);
applyAvatarChanges(customer.getId());
Platform.runLater(this::close);
} catch (Exception e) {

View File

@@ -10,6 +10,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import org.example.petshopdesktop.Validator;
import org.example.petshopdesktop.api.dto.common.DropdownOption;
@@ -17,9 +18,13 @@ import org.example.petshopdesktop.api.dto.employee.EmployeeRequest;
import org.example.petshopdesktop.api.dto.employee.EmployeeResponse;
import org.example.petshopdesktop.api.endpoints.DropdownApi;
import org.example.petshopdesktop.api.endpoints.EmployeeApi;
import org.example.petshopdesktop.api.endpoints.UserApi;
import org.example.petshopdesktop.util.ActivityLogger;
import org.example.petshopdesktop.util.DesktopImageSupport;
import org.example.petshopdesktop.util.FilePickerSupport;
import org.example.petshopdesktop.util.TextFieldFormatSupport;
import java.io.File;
import java.util.List;
public class StaffEditDialogController {
@@ -37,8 +42,16 @@ public class StaffEditDialogController {
@FXML private Label lblError;
@FXML private Button btnSave;
@FXML private ImageView imgAvatarPreview;
@FXML private Label lblAvatarStatus;
@FXML private Button btnChangeAvatar;
@FXML private Button btnRemoveAvatar;
private EmployeeResponse employee;
private Long pendingStoreId = null;
private File selectedAvatarFile;
private String currentAvatarUrl;
private boolean removeAvatarRequested;
@FXML
void initialize() {
@@ -60,6 +73,9 @@ public class StaffEditDialogController {
}
});
btnChangeAvatar.setOnMouseClicked(e -> handleChangeAvatar());
btnRemoveAvatar.setOnMouseClicked(e -> handleRemoveAvatar());
loadStores();
}
@@ -110,6 +126,65 @@ public class StaffEditDialogController {
pendingStoreId = emp.getPrimaryStoreId();
applyPendingStore();
currentAvatarUrl = emp.getAvatarUrl();
refreshAvatarPreview();
}
private void handleChangeAvatar() {
File file = FilePickerSupport.pickImageFile(btnSave.getScene().getWindow());
if (file == null) return;
selectedAvatarFile = file;
removeAvatarRequested = false;
lblAvatarStatus.setText("Selected: " + file.getName());
DesktopImageSupport.loadImageInto(imgAvatarPreview, file.toURI().toString(), 90, 90);
btnRemoveAvatar.setDisable(false);
}
private void handleRemoveAvatar() {
selectedAvatarFile = null;
removeAvatarRequested = true;
currentAvatarUrl = null;
refreshAvatarPreview();
}
private void applyAvatarChanges(Long userId) throws Exception {
String previousAvatarUrl = currentAvatarUrl;
if (removeAvatarRequested) {
try {
UserApi.getInstance().deleteUserAvatar(userId);
} catch (Exception ignored) {
}
}
if (selectedAvatarFile != null) {
UserApi.getInstance().uploadUserAvatar(userId, selectedAvatarFile.toPath());
currentAvatarUrl = "/api/v1/users/" + userId + "/avatar/file";
} else if (removeAvatarRequested) {
currentAvatarUrl = null;
}
DesktopImageSupport.evict(previousAvatarUrl);
DesktopImageSupport.evict(currentAvatarUrl);
selectedAvatarFile = null;
removeAvatarRequested = false;
}
private void refreshAvatarPreview() {
if (imgAvatarPreview == null || lblAvatarStatus == null || btnRemoveAvatar == null) return;
imgAvatarPreview.setImage(null);
if (selectedAvatarFile != null) {
lblAvatarStatus.setText("Selected: " + selectedAvatarFile.getName());
DesktopImageSupport.loadImageInto(imgAvatarPreview, selectedAvatarFile.toURI().toString(), 90, 90);
btnRemoveAvatar.setDisable(false);
return;
}
if (currentAvatarUrl != null && !currentAvatarUrl.isBlank()) {
lblAvatarStatus.setText("Current avatar loaded");
DesktopImageSupport.loadImageInto(imgAvatarPreview, currentAvatarUrl, 90, 90);
btnRemoveAvatar.setDisable(false);
return;
}
lblAvatarStatus.setText("No avatar");
btnRemoveAvatar.setDisable(true);
}
private String[] splitFullName(String fullName) {
@@ -193,6 +268,7 @@ public class StaffEditDialogController {
request.setPrimaryStoreId(storeId);
EmployeeApi.getInstance().updateEmployee(employee.getId(), request);
applyAvatarChanges(employee.getUserId());
Platform.runLater(this::close);
} catch (Exception e) {