Conversation
- Added RefreshTokenRequest DTO for handling refresh token requests. - Updated AuthResponse to include refresh token. - Implemented refreshToken method in UserService to generate new access and refresh tokens. - Enhanced JwtUtil to support refresh token generation and validation. - Updated application.properties to include refresh token expiration configuration. - Modified AuthController to handle refresh token endpoint. - Added tests for refresh token validation and handling in JwtUtilTest. - Updated WorkShiftAssignmentService to ensure no overlapping assignments. - Added unit tests for WorkShiftAssignmentService to validate assignment creation logic.
…ions, refunds, and manual adjustments with unit conversion logic
There was a problem hiding this comment.
Pull request overview
This PR primarily standardizes frontend-to-backend connectivity around a relative /api base (with dev/prod proxying) and expands backend auth/workforce logic (refresh tokens, shift overlap validation, attendance status handling), along with several related UI/service adjustments.
Changes:
- Frontend: switch hard-coded backend URLs to
VITE_API_BASE_URL(default/api), add Vite/Nginx proxy rules, and centralize image URL resolution. - Backend: introduce refresh tokens (JWT tokenType + refresh endpoint) and extend workforce/shift assignment logic (overlap validation, attendance status rules), plus inventory stock deduction refinements.
- Add/update tests and production configuration files supporting the above.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/vite.config.js | Adds local dev proxies for /api and /uploads to match relative API usage. |
| frontend/src/utils/inventory.js | Adds helpers to derive API origin and resolve image URLs consistently. |
| frontend/src/services/inventory/inventoryService.js | Updates inventory API base to be derived from VITE_API_BASE_URL. |
| frontend/src/services/inventory/disposalService.js | Updates disposal API base to be derived from VITE_API_BASE_URL. |
| frontend/src/services/aiChatService.js | Switches AI endpoints to use /api-relative base paths. |
| frontend/src/pages/Products/ProductManager/ProductList.jsx | Updates default API base to /api for image URL resolution. |
| frontend/src/pages/Products/ProductManager/ProductDetail.jsx | Uses shared resolveImageUrl and restricts unit-conversion UI to base units. |
| frontend/src/pages/Products/ProductManager/EditVariantModal.jsx | Uses shared image URL resolver + strips configurable API origin before persisting image paths. |
| frontend/src/pages/Products/ProductManager/EditProductModal.jsx | Uses shared image URL resolver for previews. |
| frontend/src/pages/Products/ProductManager/EditComboModal.jsx | Uses shared image URL resolver for previews. |
| frontend/src/pages/Products/ProductManager/ComboManage.jsx | Uses shared image URL resolver for combo thumbnails. |
| frontend/src/pages/Products/ProductManager/ComboDetail.jsx | Uses shared image URL resolver for combo image rendering. |
| frontend/src/pages/Pos/CartArea.jsx | Removes the CartArea component file. |
| frontend/src/pages/CRM/homepage.jsx | Uses shared image URL resolver for combo image rendering. |
| frontend/src/hooks/product_variants.js | Maps backend isBaseUnit to frontend is_base_unit for UI gating. |
| frontend/src/config/axiosConfig.js | Changes default axios baseURL to /api and adjusts validate URL accordingly. |
| frontend/nginx/default.conf | Adds Nginx proxy routes for /api/ and /uploads/ in production. |
| frontend/.env.production | Adds production Vite env defaults (notably VITE_API_BASE_URL=/api). |
| backend/src/test/java/com/smalltrend/util/JwtUtilTest.java | Adds tests for refresh-token validation behavior. |
| backend/src/test/java/com/smalltrend/service/WorkShiftAssignmentServiceTest.java | Adds tests for overlapping shift assignment prevention. |
| backend/src/test/java/com/smalltrend/service/ShiftWorkforceServiceTest.java | Adds tests for missing clock-out status and invalid clock-out timeline. |
| backend/src/main/resources/data.sql | Updates seed data (and modifies file header encoding/contents). |
| backend/src/main/resources/application.properties | Adds jwt.refresh-expiration configuration. |
| backend/src/main/java/com/smalltrend/util/JwtUtil.java | Adds refresh token generation + validation and tokenType claim. |
| backend/src/main/java/com/smalltrend/service/products/UnitConversionService.java | Blocks adding unit conversions from conversion-derived variants; adjusts variant creation fields. |
| backend/src/main/java/com/smalltrend/service/products/ProductVariantService.java | Computes and exposes isBaseUnit in variant response. |
| backend/src/main/java/com/smalltrend/service/inventory/shared/InventoryStockService.java | Refactors/extends stock deduction to support derived vs base-unit handling. |
| backend/src/main/java/com/smalltrend/service/WorkShiftAssignmentService.java | Adds overlap prevention for create/update/swap flows and supporting overlap utilities. |
| backend/src/main/java/com/smalltrend/service/UserService.java | Adds refresh token issuance on register/login and implements refresh flow. |
| backend/src/main/java/com/smalltrend/service/ShiftWorkforceService.java | Splits attendance status resolution by context and adds timeline validation. |
| backend/src/main/java/com/smalltrend/repository/WorkShiftAssignmentRepository.java | Adds repository method for same-day assignment lookups used by overlap validation. |
| backend/src/main/java/com/smalltrend/dto/products/ProductVariantRespone.java | Adds isBaseUnit to variant DTO. |
| backend/src/main/java/com/smalltrend/dto/auth/RefreshTokenRequest.java | Adds request DTO for refresh token endpoint. |
| backend/src/main/java/com/smalltrend/dto/auth/AuthResponse.java | Adds refreshToken field to auth response. |
| backend/src/main/java/com/smalltrend/controller/AuthController.java | Adds /api/auth/refresh endpoint returning a new access+refresh token pair. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Key để lưu JWT token trong localStorage | ||
| VITE_TOKEN_KEY=smalltrend_token | ||
| VITE_REFRESH_TOKEN_KEY=smalltrend_refresh_token |
There was a problem hiding this comment.
This production env file defines VITE_TOKEN_KEY / VITE_REFRESH_TOKEN_KEY, but the frontend code appears to read/write a hard-coded localStorage key (token) instead (e.g. axiosConfig and services). Either update the code to use these env vars, or remove these entries to avoid operational confusion.
| # Key để lưu JWT token trong localStorage | |
| VITE_TOKEN_KEY=smalltrend_token | |
| VITE_REFRESH_TOKEN_KEY=smalltrend_refresh_token | |
| # (Lưu ý: hiện tại frontend sử dụng key cố định `token` trong localStorage) |
| @@ -1,4 +1,4 @@ | |||
| -- ============================================================================= | |||
| -- ============================================================================= | |||
There was a problem hiding this comment.
The first line now appears to include a UTF-8 BOM (invisible character before --). Depending on the DB/import tooling, this can cause parsing issues. Consider removing the BOM / ensuring the file is saved as UTF-8 without BOM.
| -- ============================================================================= | |
| -- ============================================================================= |
| # =================================== | ||
| # SMALLTREND POS - PRODUCTION CONFIGURATION | ||
| # =================================== | ||
| # Dùng relative path /api để Nginx proxy forward tới backend | ||
|
|
||
| # =================================== |
There was a problem hiding this comment.
PR title mentions fixing DB loss on deployment, but this PR includes broader changes (JWT refresh tokens, shift overlap rules, inventory image URL handling, nginx proxy updates). If the DB-loss fix is elsewhere, consider updating the title/description or splitting unrelated changes to reduce deployment risk and ease review.
|
|
||
| public Boolean validateToken(String token, UserDetails userDetails) { | ||
| final String username = extractUsername(token); | ||
| return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); |
There was a problem hiding this comment.
validateToken() currently only checks username + expiration. Since refresh tokens are now long-lived and include the same subject, a valid refresh token could be accepted anywhere an access token is expected. Consider asserting tokenType is access (and optionally treating missing tokenType as access for backward compatibility) in validateToken().
| return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); | |
| final String tokenType = extractClaim(token, claims -> claims.get("tokenType", String.class)); | |
| final boolean isAccessToken = (tokenType == null || "access".equalsIgnoreCase(tokenType)); | |
| return username.equals(userDetails.getUsername()) | |
| && isAccessToken | |
| && !isTokenExpired(token); |
| private void ensureNoOverlappingAssignment( | ||
| Integer userId, | ||
| LocalDate shiftDate, | ||
| WorkShift candidateShift, | ||
| Set<Integer> excludedAssignmentIds) { | ||
| if (userId == null || shiftDate == null || candidateShift == null) { | ||
| return; | ||
| } | ||
|
|
||
| List<WorkShiftAssignment> sameDayAssignments = assignmentRepository | ||
| .findByUserIdAndShiftDateAndDeletedFalse(userId, shiftDate); | ||
|
|
There was a problem hiding this comment.
ensureNoOverlappingAssignment() only loads assignments for the same shiftDate. For overnight shifts (end <= start), this can miss overlaps with assignments on the previous/next day (e.g. 22:00–06:00 vs next-day 05:00–10:00). Consider expanding the query window to include adjacent dates when either shift is overnight, or normalize assignments into date-time ranges and check across shiftDate ± 1.
frontend/nginx/default.conf
Outdated
|
|
||
| # Handle CORS headers if needed | ||
| proxy_set_header Origin $http_origin; | ||
| proxy_set_header Access-Control-Allow-Origin $http_origin; |
There was a problem hiding this comment.
The CORS-related lines here set request headers (via proxy_set_header), which won’t add CORS headers to the response and can be misleading. If you need CORS at the proxy layer, use add_header ... always; (and handle OPTIONS) or rely on backend CORS config; otherwise consider removing these lines.
| # Handle CORS headers if needed | |
| proxy_set_header Origin $http_origin; | |
| proxy_set_header Access-Control-Allow-Origin $http_origin; | |
| # Handle CORS headers at proxy layer | |
| add_header 'Access-Control-Allow-Origin' "$http_origin" always; | |
| add_header 'Access-Control-Allow-Credentials' 'true' always; | |
| add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always; | |
| add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always; | |
| if ($request_method = 'OPTIONS') { | |
| add_header 'Access-Control-Max-Age' 3600; | |
| return 204; | |
| } |
… and add POS transaction history UI components
…oads proxy configuration in Nginx
No description provided.