fix: update password update messages and handle invalid current password error
This commit is contained in:
@@ -0,0 +1,107 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
NiLai-Clock is a worker attendance management system with role-based dashboards for workers and managers. It's built as a Vue.js SPA with an Express.js backend, featuring QR code attendance tracking, geofencing, and comprehensive reporting capabilities.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- `npm run dev` - Start frontend development server (Vite on port 5173)
|
||||||
|
- `npm run backend` - Start backend server (Express on port 3000)
|
||||||
|
- `npm run dev:all` - Run both frontend and backend concurrently
|
||||||
|
- `npm install` - Install dependencies
|
||||||
|
|
||||||
|
### Production
|
||||||
|
- `npm run build` - Build for production using Vite
|
||||||
|
- `npm run preview` - Preview production build locally
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
- `npm run lint` - Run ESLint with auto-fix
|
||||||
|
- `npm run format` - Format code using Prettier
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Frontend (Vue.js)
|
||||||
|
- **Framework**: Vue 3 with Composition API
|
||||||
|
- **Build Tool**: Vite with Vue plugin and TailwindCSS
|
||||||
|
- **Routing**: Vue Router with hash-based routing and role-based guards
|
||||||
|
- **State**: Session storage for authentication (userId, userRole, token)
|
||||||
|
- **Styling**: TailwindCSS v4 with responsive design
|
||||||
|
- **Internationalization**: Vue I18n with English (en) and Malay (ms) locales
|
||||||
|
|
||||||
|
### Backend (Express.js)
|
||||||
|
- **Server**: Express with CORS, supports both HTTP and HTTPS
|
||||||
|
- **Database**: MySQL with connection pooling (mysql2)
|
||||||
|
- **Authentication**: JWT tokens with bcrypt for password hashing
|
||||||
|
- **Routes**: Separated into manager (`/api/managers/*`) and worker (`/api/*`) routes
|
||||||
|
|
||||||
|
### Key Components Structure
|
||||||
|
|
||||||
|
#### Views (Role-Based)
|
||||||
|
- **Worker**: `WorkerDashboard`, `WorkerHistory`, `WorkerSettings`, `WorkerChangePassword`
|
||||||
|
- **Manager**: `ManagerDashboard`, `ManagerAttendanceRecord`
|
||||||
|
- **Auth**: `Login` (supports both roles)
|
||||||
|
|
||||||
|
#### Components
|
||||||
|
- **Management**: `PersonnelManagement`, `GeofenceManagement`, `QrCodeManagement`, `KillSwitchManagement`
|
||||||
|
- **UI**: `Toast` notifications with `useToast` composable
|
||||||
|
- **Reporting**: `WarningReporting` with CSV export capabilities
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
- Role-based routing with `meta: { requiresAuth: true, role: 'worker|manager' }`
|
||||||
|
- Navigation guards redirect users to appropriate dashboards
|
||||||
|
- Session storage manages authentication state
|
||||||
|
- API requests include JWT tokens via Authorization header
|
||||||
|
|
||||||
|
### Key Libraries
|
||||||
|
- **Maps**: Leaflet with drawing capabilities for geofencing
|
||||||
|
- **QR Codes**: html5-qrcode for scanning, qrcode for generation
|
||||||
|
- **Geospatial**: @turf/turf for geographical calculations
|
||||||
|
- **Data Export**: json2csv for attendance reports
|
||||||
|
- **UUID**: For device identification and worker login tracking
|
||||||
|
|
||||||
|
## Environment Configuration
|
||||||
|
|
||||||
|
Create `.env` file with:
|
||||||
|
```
|
||||||
|
DB_HOST=your_database_host
|
||||||
|
DB_USER=your_database_user
|
||||||
|
DB_PASSWORD=your_database_password
|
||||||
|
DB_NAME=your_database_name
|
||||||
|
DB_PORT=your_database_port
|
||||||
|
VITE_API_BASE_URL=your_api_base_url
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional SSL configuration:
|
||||||
|
- `SSL_ENABLED=true`
|
||||||
|
- `HTTP_PORT=3000`
|
||||||
|
- `HTTPS_PORT=3443`
|
||||||
|
|
||||||
|
## Development Notes
|
||||||
|
|
||||||
|
- Frontend uses hash-based routing for better deployment compatibility
|
||||||
|
- Backend supports both HTTP and HTTPS with configurable ports
|
||||||
|
- CORS configured for multiple origins including mobile app protocols
|
||||||
|
- Database connection is tested on server startup with proper error handling
|
||||||
|
- All API calls go through centralized `apiFetch` utility with error handling
|
||||||
|
- ESLint configured to ignore unused variables/parameters prefixed with underscore
|
||||||
|
- Prettier integrated with Vue ESLint config for consistent formatting
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
The application manages worker attendance with these core entities:
|
||||||
|
- **Workers**: Authentication, device UUID tracking, role assignment
|
||||||
|
- **Managers**: Administrative access and permissions
|
||||||
|
- **Attendance Records**: Clock in/out with location and timestamp data
|
||||||
|
- **Geofences**: Location boundaries for valid attendance tracking
|
||||||
|
- **QR Codes**: Dynamic codes for attendance verification
|
||||||
|
|
||||||
|
## Docker Support
|
||||||
|
|
||||||
|
Includes Docker configuration:
|
||||||
|
- `Dockerfile` - Node.js Alpine-based container
|
||||||
|
- `docker-compose.yml` - Multi-service setup with nginx reverse proxy
|
||||||
|
- Production deployment on port 18080 (nginx) and 18081 (app)
|
||||||
+2
-1
@@ -81,8 +81,9 @@
|
|||||||
"updatePassword": "Update Password",
|
"updatePassword": "Update Password",
|
||||||
"passwordsNoMatch": "New passwords do not match.",
|
"passwordsNoMatch": "New passwords do not match.",
|
||||||
"passwordTooShort": "New password must be at least 6 characters long.",
|
"passwordTooShort": "New password must be at least 6 characters long.",
|
||||||
"passwordUpdated": "Password updated successfully! You can now use your new password to log in.",
|
"passwordUpdated": "Password updated successfully!",
|
||||||
"passwordUpdateError": "An error occurred while updating the password.",
|
"passwordUpdateError": "An error occurred while updating the password.",
|
||||||
|
"invalidCurrentPassword": "The current password you entered is incorrect.",
|
||||||
|
|
||||||
"attendanceLogFor": "Attendance Log for",
|
"attendanceLogFor": "Attendance Log for",
|
||||||
"addManualClockOut": "Add Manual Clock-Out",
|
"addManualClockOut": "Add Manual Clock-Out",
|
||||||
|
|||||||
+2
-1
@@ -82,8 +82,9 @@
|
|||||||
"updatePassword": "Kemaskini Kata Laluan",
|
"updatePassword": "Kemaskini Kata Laluan",
|
||||||
"passwordsNoMatch": "Kata laluan baharu tidak sepadan.",
|
"passwordsNoMatch": "Kata laluan baharu tidak sepadan.",
|
||||||
"passwordTooShort": "Kata laluan baharu mesti sekurang-kurangnya 6 aksara.",
|
"passwordTooShort": "Kata laluan baharu mesti sekurang-kurangnya 6 aksara.",
|
||||||
"passwordUpdated": "Kata laluan berjaya dikemaskini! Anda boleh guna kata laluan baharu untuk log masuk.",
|
"passwordUpdated": "Kata laluan berjaya dikemaskini!",
|
||||||
"passwordUpdateError": "Ralat semasa mengemaskini kata laluan.",
|
"passwordUpdateError": "Ralat semasa mengemaskini kata laluan.",
|
||||||
|
"invalidCurrentPassword": "Kata laluan semasa yang anda masukkan tidak betul.",
|
||||||
|
|
||||||
"attendanceLogFor": "Log Kehadiran untuk",
|
"attendanceLogFor": "Log Kehadiran untuk",
|
||||||
"addManualClockOut": "Tambah Clock-Out Secara Manual",
|
"addManualClockOut": "Tambah Clock-Out Secara Manual",
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const handleChangePassword = async () => {
|
|||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch('/api/worker/change-password', {
|
await apiFetch('/api/worker/change-password', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
currentPassword: passwords.value.currentPassword,
|
currentPassword: passwords.value.currentPassword,
|
||||||
@@ -92,20 +92,20 @@ const handleChangePassword = async () => {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
// apiFetch already handles response parsing and throws errors for non-200 status
|
||||||
const errorData = await response.json()
|
// If we reach here, the request was successful
|
||||||
if (response.status === 401) {
|
|
||||||
errorMessage.value = 'invalidCurrentPassword'
|
|
||||||
} else {
|
|
||||||
errorMessage.value = errorData.message || 'passwordUpdateError'
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
successMessage.value = 'passwordUpdated'
|
successMessage.value = 'passwordUpdated'
|
||||||
passwords.value = { currentPassword: '', newPassword: '', confirmPassword: '' }
|
passwords.value = { currentPassword: '', newPassword: '', confirmPassword: '' }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorMessage.value = err.message || 'passwordUpdateError'
|
// Handle specific error cases based on the error message from server
|
||||||
|
if (err.message.includes('Incorrect current password') || err.message.includes('401')) {
|
||||||
|
errorMessage.value = 'invalidCurrentPassword'
|
||||||
|
} else if (err.message.includes('Invalid input')) {
|
||||||
|
errorMessage.value = 'passwordUpdateError'
|
||||||
|
} else {
|
||||||
|
// For any other errors, use the generic error message
|
||||||
|
errorMessage.value = 'passwordUpdateError'
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user