This commit is contained in:
@@ -326,31 +326,49 @@ exports.getExternalEmployeesList = async (req, res) => {
|
||||
timeout: 15000,
|
||||
httpsAgent: HR_INSECURE_HTTPS_AGENT
|
||||
};
|
||||
const currentSite = splitSite.findSiteNumber(req);
|
||||
const requestBodies = [
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD,
|
||||
site: currentSite,
|
||||
status: "active"
|
||||
},
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD,
|
||||
site: currentSite
|
||||
},
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD,
|
||||
status: "active"
|
||||
},
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD
|
||||
}
|
||||
];
|
||||
const requestedSite = Number(req.query?.site || req.body?.site);
|
||||
const hasRequestedSite = Number.isInteger(requestedSite) && requestedSite > 0;
|
||||
const requestBodies = hasRequestedSite
|
||||
? [
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD,
|
||||
site: requestedSite,
|
||||
status: "active"
|
||||
},
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD,
|
||||
site: requestedSite
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD,
|
||||
status: "active"
|
||||
},
|
||||
{
|
||||
username: HR_INTEGRATION_USERNAME,
|
||||
password: HR_INTEGRATION_PASSWORD
|
||||
}
|
||||
];
|
||||
const normalizeEmployeesList = (raw) => {
|
||||
if (Array.isArray(raw)) return raw;
|
||||
if (typeof raw === "string") {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return [];
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed);
|
||||
if (Array.isArray(parsed)) return parsed;
|
||||
if (Array.isArray(parsed?.data)) return parsed.data;
|
||||
if (Array.isArray(parsed?.employees)) return parsed.employees;
|
||||
if (Array.isArray(parsed?.result)) return parsed.result;
|
||||
if (Array.isArray(parsed?.results)) return parsed.results;
|
||||
} catch (_err) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
if (Array.isArray(raw?.data)) return raw.data;
|
||||
if (Array.isArray(raw?.employees)) return raw.employees;
|
||||
if (Array.isArray(raw?.result)) return raw.result;
|
||||
@@ -362,35 +380,19 @@ exports.getExternalEmployeesList = async (req, res) => {
|
||||
let bestResponseData = null;
|
||||
for (const requestBody of requestBodies) {
|
||||
try {
|
||||
console.log("[HR Integration] Requesting employee list from:", HR_EMPLOYEE_LIST_ENDPOINT, "payload:", requestBody);
|
||||
const response = await axios.post(HR_EMPLOYEE_LIST_ENDPOINT, requestBody, requestOptions);
|
||||
const list = normalizeEmployeesList(response?.data);
|
||||
console.log(
|
||||
"[HR Integration] employee list response meta:",
|
||||
{ endpoint: HR_EMPLOYEE_LIST_ENDPOINT, count: list.length, responseType: typeof response?.data }
|
||||
);
|
||||
console.log("[HR Integration] raw employee list response:", response?.data);
|
||||
bestResponseData = response?.data;
|
||||
if (list.length > 0) {
|
||||
return res.send(list);
|
||||
}
|
||||
} catch (attemptError) {
|
||||
console.log("[HR Integration] employee list attempt failed:", {
|
||||
endpoint: HR_EMPLOYEE_LIST_ENDPOINT,
|
||||
payload: requestBody,
|
||||
status: attemptError?.response?.status,
|
||||
data: attemptError?.response?.data,
|
||||
message: attemptError?.message
|
||||
});
|
||||
} catch (_attemptError) {
|
||||
// Try next payload variant.
|
||||
}
|
||||
}
|
||||
const emptyList = normalizeEmployeesList(bestResponseData);
|
||||
console.log("[HR Integration] all attempts completed; returning list length:", emptyList.length);
|
||||
return res.send(emptyList);
|
||||
} catch (err) {
|
||||
console.log("[HR Integration] /employees/list error status:", err?.response?.status);
|
||||
console.log("[HR Integration] /employees/list error data:", err?.response?.data);
|
||||
console.log("[HR Integration] /employees/list error message:", err?.message);
|
||||
res.status(500).send({
|
||||
message: err?.response?.data?.message || err.message || "Failed to fetch employees from HR system."
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ const SYSTEM_ACCESS_PERMISSION = "System Access";
|
||||
|
||||
const EmployeeList = () => {
|
||||
const navigate = useNavigate();
|
||||
const currentSite = EventsService.site || 3;
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [showInactive, setShowInactive] = useState(false);
|
||||
@@ -15,7 +16,6 @@ const EmployeeList = () => {
|
||||
const [isHrLoading, setIsHrLoading] = useState(false);
|
||||
const [isSavingHrPermission, setIsSavingHrPermission] = useState(false);
|
||||
const [hrKeyword, setHrKeyword] = useState('');
|
||||
const [hrSiteFilter, setHrSiteFilter] = useState(EventsService.site || 3);
|
||||
const [hrPermissionMap, setHrPermissionMap] = useState({});
|
||||
const [editingHrUser, setEditingHrUser] = useState(undefined);
|
||||
const [showHrPermissionModal, setShowHrPermissionModal] = useState(false);
|
||||
@@ -31,25 +31,16 @@ const EmployeeList = () => {
|
||||
setEmployees(data.data)
|
||||
);
|
||||
loadHrUsers();
|
||||
loadHrPermissionsBySite(currentSite);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadHrPermissionsBySite(hrSiteFilter);
|
||||
}, [hrSiteFilter]);
|
||||
|
||||
const loadHrUsers = () => {
|
||||
setIsHrLoading(true);
|
||||
EmployeeService.getExternalEmployeesList()
|
||||
.then((response) => {
|
||||
console.log('[HR Debug][EmployeeList] backend /employees/external/list response:', response?.data);
|
||||
setHrUsers(Array.isArray(response?.data) ? response.data : []);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('[HR Debug][EmployeeList] /employees/external/list error:', {
|
||||
status: error?.response?.status,
|
||||
data: error?.response?.data,
|
||||
message: error?.message
|
||||
});
|
||||
window.alert(error?.response?.data?.message || 'Failed to load HR users.');
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -95,9 +86,12 @@ const EmployeeList = () => {
|
||||
}
|
||||
|
||||
const filteredHrUsers = useMemo(() => {
|
||||
const site = Number(hrSiteFilter);
|
||||
return (hrUsers || [])
|
||||
.filter((item) => Number(item?.site) === site)
|
||||
.filter((item) => Number(item?.site) === Number(currentSite))
|
||||
.filter((item) => {
|
||||
const configuredPermissions = hrPermissionMap?.[item?.employee_id] || [];
|
||||
return configuredPermissions.length > 0;
|
||||
})
|
||||
.filter((item) => {
|
||||
if (!hrKeyword) return true;
|
||||
const key = hrKeyword.toLowerCase();
|
||||
@@ -107,7 +101,7 @@ const EmployeeList = () => {
|
||||
(item?.title || '').toLowerCase().includes(key)
|
||||
);
|
||||
});
|
||||
}, [hrUsers, hrKeyword, hrSiteFilter]);
|
||||
}, [hrUsers, hrKeyword, hrPermissionMap, currentSite]);
|
||||
|
||||
const openHrPermissionModal = (hrUser) => {
|
||||
setEditingHrUser(hrUser);
|
||||
@@ -141,7 +135,7 @@ const EmployeeList = () => {
|
||||
username: editingHrUser.username || '',
|
||||
name: editingHrUser.name || '',
|
||||
email: editingHrUser.email || '',
|
||||
allow_site: Number(hrSiteFilter),
|
||||
allow_site: Number(currentSite),
|
||||
permissions: selectedHrPermissions
|
||||
})
|
||||
.then(() => {
|
||||
@@ -161,10 +155,10 @@ const EmployeeList = () => {
|
||||
|
||||
const revokeHrPermissions = (hrUser) => {
|
||||
if (!hrUser?.employee_id) return;
|
||||
if (!window.confirm(`Revoke all permissions for ${hrUser?.username || 'this HR user'} on Site ${hrSiteFilter}?`)) {
|
||||
if (!window.confirm(`Revoke all permissions for ${hrUser?.username || 'this HR user'} on Site ${currentSite}?`)) {
|
||||
return;
|
||||
}
|
||||
EmployeeService.revokeExternalUserPermission(hrUser.employee_id, Number(hrSiteFilter))
|
||||
EmployeeService.revokeExternalUserPermission(hrUser.employee_id, Number(currentSite))
|
||||
.then(() => {
|
||||
setHrPermissionMap((prev) => {
|
||||
const next = { ...prev };
|
||||
@@ -202,18 +196,13 @@ const EmployeeList = () => {
|
||||
<h6 className="text-primary">HR System Users</h6>
|
||||
<div className="mb-3 d-flex align-items-center" style={{ gap: '12px', flexWrap: 'wrap' }}>
|
||||
<label>
|
||||
Site:
|
||||
<select className="ms-2" value={hrSiteFilter} onChange={(e) => setHrSiteFilter(Number(e.currentTarget.value))}>
|
||||
<option value={1}>Site 1</option>
|
||||
<option value={2}>Site 2</option>
|
||||
<option value={3}>Site 3</option>
|
||||
</select>
|
||||
Site: <strong className="ms-2">{currentSite}</strong>
|
||||
</label>
|
||||
<label>
|
||||
Filter:
|
||||
<input className="ms-2" type="text" value={hrKeyword} onChange={(e) => setHrKeyword(e.currentTarget.value)} />
|
||||
</label>
|
||||
<button className="btn btn-primary btn-sm" onClick={() => {loadHrUsers(); loadHrPermissionsBySite(hrSiteFilter);}} disabled={isHrLoading}>
|
||||
<button className="btn btn-primary btn-sm" onClick={() => {loadHrUsers(); loadHrPermissionsBySite(currentSite);}} disabled={isHrLoading}>
|
||||
{isHrLoading ? 'Loading...' : 'Refresh HR Users'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -299,7 +288,7 @@ const EmployeeList = () => {
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div className="mb-3">
|
||||
<strong>Allow Site:</strong> {hrSiteFilter}
|
||||
<strong>Allow Site:</strong> {currentSite}
|
||||
</div>
|
||||
{Object.entries(EMPLOYEE_PERMISSION_GROUPS).map(([groupName, permissionItems]) => (
|
||||
<div key={groupName} className="mb-3">
|
||||
|
||||
@@ -11,7 +11,7 @@ const ExternalEmployeesImport = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const [siteFilter, setSiteFilter] = useState('all');
|
||||
const [siteFilter, setSiteFilter] = useState(EventsService.site || 3);
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [showPermissionModal, setShowPermissionModal] = useState(false);
|
||||
const [selectedEmployee, setSelectedEmployee] = useState(undefined);
|
||||
@@ -32,15 +32,9 @@ const ExternalEmployeesImport = () => {
|
||||
setLoading(true);
|
||||
EmployeeService.getExternalEmployeesList()
|
||||
.then((response) => {
|
||||
console.log("[HR Debug][ExternalEmployeesImport] backend /employees/external/list response:", response?.data);
|
||||
setEmployees(Array.isArray(response?.data) ? response.data : []);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("[HR Debug][ExternalEmployeesImport] /employees/external/list error:", {
|
||||
status: error?.response?.status,
|
||||
data: error?.response?.data,
|
||||
message: error?.message
|
||||
});
|
||||
window.alert(error?.response?.data?.message || "Failed to load employees from HR system.");
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -146,6 +140,8 @@ const ExternalEmployeesImport = () => {
|
||||
<option value={1}>Site 1</option>
|
||||
<option value={2}>Site 2</option>
|
||||
<option value={3}>Site 3</option>
|
||||
<option value={4}>Site 4</option>
|
||||
<option value={5}>Site 5</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
|
||||
Reference in New Issue
Block a user