diff --git a/src/pages/master/plantSection/component/ListPlantSection.jsx b/src/pages/master/plantSection/component/ListPlantSection.jsx
index a6a812c..e8c14f8 100644
--- a/src/pages/master/plantSection/component/ListPlantSection.jsx
+++ b/src/pages/master/plantSection/component/ListPlantSection.jsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import { Button, Col, Row, Space, Input, ConfigProvider, Card } from 'antd';
import {
PlusOutlined,
@@ -21,6 +21,11 @@ const ListPlantSection = ({
const [formDataFilter, setFormDataFilter] = useState({ criteria: '' });
const [trigerFilter, setTrigerFilter] = useState(false);
+ // Sync refreshList from parent to trigger table refresh
+ useEffect(() => {
+ setTrigerFilter((prev) => !prev);
+ }, [refreshList]);
+
const columns = [
{
title: 'No',
diff --git a/src/pages/user/component/ChangePasswordModal.jsx b/src/pages/user/component/ChangePasswordModal.jsx
index 5e83330..826359b 100644
--- a/src/pages/user/component/ChangePasswordModal.jsx
+++ b/src/pages/user/component/ChangePasswordModal.jsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { Modal, Input, Typography, Button, ConfigProvider } from 'antd';
+import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif';
import { changePassword } from '../../../api/user';
@@ -13,6 +14,15 @@ const ChangePasswordModal = (props) => {
});
const [errors, setErrors] = useState({});
+ // Password requirements state
+ const [passwordRequirements, setPasswordRequirements] = useState({
+ minLength: false,
+ hasUppercase: false,
+ hasLowercase: false,
+ hasNumber: false,
+ hasSpecialChar: false,
+ });
+
const validatePassword = (password) => {
if (!password) return 'Password wajib diisi';
@@ -70,6 +80,24 @@ const ChangePasswordModal = (props) => {
confirmPassword: '',
});
setErrors({});
+ setPasswordRequirements({
+ minLength: false,
+ hasUppercase: false,
+ hasLowercase: false,
+ hasNumber: false,
+ hasSpecialChar: false,
+ });
+ };
+
+ // Check password requirements
+ const checkPasswordRequirements = (password) => {
+ setPasswordRequirements({
+ minLength: password.length >= 8,
+ hasUppercase: /[A-Z]/.test(password),
+ hasLowercase: /[a-z]/.test(password),
+ hasNumber: /\d/.test(password),
+ hasSpecialChar: /[!@#$%^&*(),.?":{}|<>]/.test(password),
+ });
};
const handleSave = async () => {
@@ -122,6 +150,12 @@ const ChangePasswordModal = (props) => {
...formData,
[name]: value,
});
+
+ // Check password requirements on password change
+ if (name === 'newPassword') {
+ checkPasswordRequirements(value);
+ }
+
// Clear error for this field
if (errors[name]) {
setErrors({
@@ -211,6 +245,63 @@ const ChangePasswordModal = (props) => {
{errors.newPassword && (
{errors.newPassword}
)}
+
+ {/* Password Requirements Indicator */}
+
+
Password harus memenuhi:
+
+
+ {passwordRequirements.minLength ? (
+
+ ) : (
+
+ )}
+
+ Minimal 8 karakter
+
+
+
+ {passwordRequirements.hasUppercase ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 huruf besar (A-Z)
+
+
+
+ {passwordRequirements.hasLowercase ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 huruf kecil (a-z)
+
+
+
+ {passwordRequirements.hasNumber ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 angka (0-9)
+
+
+
+ {passwordRequirements.hasSpecialChar ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 karakter spesial (!@#$%^&*(),.?":{}|<>)
+
+
+
+
@@ -229,27 +320,6 @@ const ChangePasswordModal = (props) => {
)}
-
-
-
- Persyaratan password:
-
- - Minimal 8 karakter
- - Minimal 1 huruf besar (A-Z)
- - Minimal 1 huruf kecil (a-z)
- - Minimal 1 angka (0-9)
- - Minimal 1 karakter spesial (!@#$%^&*)
-
-
-
);
diff --git a/src/pages/user/component/DetailUser.jsx b/src/pages/user/component/DetailUser.jsx
index d251b5d..9403a99 100644
--- a/src/pages/user/component/DetailUser.jsx
+++ b/src/pages/user/component/DetailUser.jsx
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import { Modal, Input, Divider, Typography, Switch, Button, ConfigProvider, Select } from 'antd';
+import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif';
import { createUser, updateUser } from '../../../api/user';
import { getAllRole } from '../../../api/role';
@@ -25,13 +26,41 @@ const DetailUser = (props) => {
};
const [FormData, setFormData] = useState(defaultData);
+ const [originalEmail, setOriginalEmail] = useState(''); // Track original email
const [errors, setErrors] = useState({});
+ // Password requirements state
+ const [passwordRequirements, setPasswordRequirements] = useState({
+ minLength: false,
+ hasUppercase: false,
+ hasLowercase: false,
+ hasNumber: false,
+ hasSpecialChar: false,
+ });
+
const handleCancel = () => {
props.setSelectedData(null);
props.setActionMode('list');
setFormData(defaultData);
setErrors({});
+ setPasswordRequirements({
+ minLength: false,
+ hasUppercase: false,
+ hasLowercase: false,
+ hasNumber: false,
+ hasSpecialChar: false,
+ });
+ };
+
+ // Check password requirements
+ const checkPasswordRequirements = (password) => {
+ setPasswordRequirements({
+ minLength: password.length >= 8,
+ hasUppercase: /[A-Z]/.test(password),
+ hasLowercase: /[a-z]/.test(password),
+ hasNumber: /\d/.test(password),
+ hasSpecialChar: /[!@#$%^&*(),.?":{}|<>]/.test(password),
+ });
};
const validatePhone = (phone) => {
@@ -145,10 +174,20 @@ const DetailUser = (props) => {
// Backend expects field names with 'user_' prefix
const payload = {
user_fullname: FormData.user_fullname,
- user_email: FormData.user_email,
user_phone: phone,
};
+ // For update mode: only send email if it has changed
+ if (FormData.user_id) {
+ // Only include email if it has changed from original
+ if (FormData.user_email !== originalEmail) {
+ payload.user_email = FormData.user_email;
+ }
+ } else {
+ // For create mode: always send email
+ payload.user_email = FormData.user_email;
+ }
+
// Only add role_id if it exists (backend requires number >= 1, no null)
if (FormData.role_id) {
payload.role_id = FormData.role_id;
@@ -163,6 +202,7 @@ const DetailUser = (props) => {
// For update mode:
// - Don't send 'user_name' (username is immutable)
// - Don't send 'is_active' (backend validation schema doesn't allow it)
+ // - Only send email if it has changed
try {
console.log('Payload being sent:', payload);
@@ -214,6 +254,12 @@ const DetailUser = (props) => {
...FormData,
[name]: value,
});
+
+ // Check password requirements on password change
+ if (name === 'password') {
+ checkPasswordRequirements(value);
+ }
+
// Clear error for this field
if (errors[name]) {
setErrors({
@@ -288,8 +334,11 @@ const DetailUser = (props) => {
password: '',
confirmPassword: '',
});
+ // Store original email for comparison
+ setOriginalEmail(props.selectedData.user_email || '');
} else {
setFormData(defaultData);
+ setOriginalEmail('');
}
setErrors({});
@@ -451,6 +500,63 @@ const DetailUser = (props) => {
{errors.password}
)}
+
+ {/* Password Requirements Indicator */}
+
+
Password harus memenuhi:
+
+
+ {passwordRequirements.minLength ? (
+
+ ) : (
+
+ )}
+
+ Minimal 8 karakter
+
+
+
+ {passwordRequirements.hasUppercase ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 huruf besar (A-Z)
+
+
+
+ {passwordRequirements.hasLowercase ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 huruf kecil (a-z)
+
+
+
+ {passwordRequirements.hasNumber ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 angka (0-9)
+
+
+
+ {passwordRequirements.hasSpecialChar ? (
+
+ ) : (
+
+ )}
+
+ Minimal 1 karakter spesial (!@#$%^&*(),.?":{}|<>)
+
+
+
+
diff --git a/src/pages/user/component/ListUser.jsx b/src/pages/user/component/ListUser.jsx
index a8cab68..e160d08 100644
--- a/src/pages/user/component/ListUser.jsx
+++ b/src/pages/user/component/ListUser.jsx
@@ -50,7 +50,13 @@ const getRoleColor = (role_name, role_level) => {
return 'default';
};
-const columns = (showPreviewModal, showEditModal, showDeleteDialog, showApproveDialog, showChangePasswordModal) => [
+const columns = (
+ showPreviewModal,
+ showEditModal,
+ showDeleteDialog,
+ showApproveDialog,
+ showChangePasswordModal
+) => [
{
title: 'ID',
dataIndex: 'user_id',
@@ -257,7 +263,7 @@ const ListUser = memo(function ListUser(props) {
icon: 'question',
title: 'Konfirmasi',
message: 'Apakah anda yakin hapus user "' + param.user_fullname + '" ?',
- onConfirm: () => handleDelete(param.user_id),
+ onConfirm: () => handleDelete(param.user_id, param.user_fullname),
onCancel: () => props.setSelectedData(null),
});
};
@@ -285,14 +291,14 @@ const ListUser = memo(function ListUser(props) {
}
};
- const handleDelete = async (user_id) => {
+ const handleDelete = async (user_id, user_fullname) => {
const response = await deleteUser(user_id);
if (response.statusCode == 200) {
NotifAlert({
icon: 'success',
title: 'Berhasil',
- message: 'User "' + response.data.user_fullname + '" berhasil dihapus.',
+ message: 'User "' + user_fullname + '" berhasil dihapus.',
});
doFilter();
} else {