Hello team,
I am working on querying user data within DHIS2 using React. I was able to successfully fetch the user’s First Name, Username, Last Login, and Email. However, when it comes to fetching the Organization Unit, I am receiving empty values (i.e., ,,
).
Could you help me identify what might be causing this issue? Below is the relevant code:
import React, { useState } from ‘react’;
import ‘./UserTable.css’;
import { useDataQuery } from ‘@dhis2/app-runtime’;
// Define the query to fetch necessary user data
const myQuery = {
results: {
resource: ‘users’,
params: {
pageSize: 2500, // Fetch 2500 users per request (adjust according to your system’s limit)
fields: [
‘id’,
‘firstName’,
‘email’,
‘username’,
‘displayName’,
‘lastLogin’,
‘organisationUnits’
],
},
},
};
const UserTable = () => {
const { loading, error, data } = useDataQuery(myQuery);
const [currentPage, setCurrentPage] = useState(1);
const [usersPerPage] = useState(20); // Number of users per page
const [searchQuery, setSearchQuery] = useState('');
const [selectedUser, setSelectedUser] = useState(null); // State for selected user
const [isEditing, setIsEditing] = useState(false); // State to toggle edit mode
const [editedUser, setEditedUser] = useState(null); // State for editing user
if (error) {
return <span>ERROR: {error.message}</span>;
}
if (loading) {
return <span>Loading...</span>;
}
// Extract users from the API response
const users = data.results.users || [];
// Filter users based on search query
const filteredUsers = users.filter((user) => {
const query = searchQuery.toLowerCase();
return (
(user.displayName && user.displayName.toLowerCase().includes(query)) ||
(user.firstName && user.firstName.toLowerCase().includes(query)) ||
(user.email && user.email.toLowerCase().includes(query)) ||
(user.username && user.username.toLowerCase().includes(query))
);
});
// Pagination calculation
const totalUsers = filteredUsers.length;
const totalPages = Math.ceil(totalUsers / usersPerPage);
const indexOfLastUser = currentPage * usersPerPage;
const indexOfFirstUser = indexOfLastUser - usersPerPage;
const currentUsers = filteredUsers.slice(indexOfFirstUser, indexOfLastUser);
// Handle page change
const handlePageChange = (pageNumber) => {
setCurrentPage(pageNumber);
};
// Handle search input change
const handleSearchChange = (event) => {
setSearchQuery(event.target.value);
};
// Handle user selection
const handleSelectUser = (user) => {
setSelectedUser(user);
setEditedUser(user); // Set the selected user as the one to be edited
setIsEditing(false); // By default, don't start editing
};
// Handle edit toggle
const handleEditToggle = () => {
setIsEditing(!isEditing);
};
// Handle change in edited fields
const handleInputChange = (event) => {
const { name, value } = event.target;
setEditedUser((prev) => ({
...prev,
[name]: value,
}));
};
// Convert data to CSV
const convertToCSV = (data) => {
const header = ['Display Name', 'First Name', 'Username', 'Email', 'Last Login','orgUnit','Organisation Units'];
const rows = data.map((user) => [
user.displayName || 'N/A',
user.firstName || 'N/A',
user.username || 'N/A',
user.email || 'N/A',
user.lastLogin || 'N/A',
user.organisationUnits?.map(unit => unit.name).join(', ') || 'N/A',
]);
const csvContent = [
header.join(','), // Add header row
...rows.map(row => row.join(',')), // Add data rows
].join('\n');
return csvContent;
};
// Handle CSV download
const downloadCSV = () => {
const csvContent = convertToCSV(filteredUsers);
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'users.csv';
link.click();
};
return (
<div>
<h1>User List</h1>
<div className="search-download-container">
<input
type="text"
placeholder="Search users (name, email)"
value={searchQuery}
onChange={handleSearchChange}
className="search-input"
/>
<button onClick={downloadCSV} className="download-button">
Download as CSV
</button>
</div>
<table className="user-table">
<thead>
<tr>
<th>Display Name</th>
<th>First Name</th>
<th>Username</th>
<th>Last Login</th>
<th>Email</th>
<th>Organisation Units</th>
</tr>
</thead>
<tbody>
{currentUsers.map(user => (
<tr key={user.id} onClick={() => handleSelectUser(user)}>
<td>{user.displayName || 'N/A'}</td>
<td>{user.firstName || 'N/A'}</td>
<td>{user.username || 'N/A'}</td>
<td>{user.lastLogin || 'N/A'}</td>
<td>{user.email || 'N/A'}</td>
<td>
{user.organisationUnits?.map(unit => unit.name).join(', ') || 'N/A'}
</td>
</tr>
))}
</tbody>
</table>
{selectedUser && (
<div className="user-details">
<h2>User Details</h2>
{isEditing ? (
<div className="edit-form">
<label>
Display Name:
<input
type="text"
name="displayName"
value={editedUser.displayName}
onChange={handleInputChange}
/>
</label>
<label>
First Name:
<input
type="text"
name="firstName"
value={editedUser.firstName}
onChange={handleInputChange}
/>
</label>
<label>
Email:
<input
type="email"
name="email"
value={editedUser.email}
onChange={handleInputChange}
/>
</label>
<label>
Username:
<input
type="text"
name="username"
value={editedUser.username}
onChange={handleInputChange}
/>
</label>
<button onClick={handleEditToggle}>Save Changes</button>
</div>
) : (
<div>
<p><strong>Display Name:</strong> {selectedUser.displayName}</p>
<p><strong>First Name:</strong> {selectedUser.firstName}</p>
<p><strong>Email:</strong> {selectedUser.email}</p>
<p><strong>Username:</strong> {selectedUser.username}</p>
<p><strong>Last Login:</strong> {selectedUser.lastLogin}</p>
<p><strong>Organisation Units:</strong> {selectedUser.organisationUnits?.map(unit => unit.name).join(', ')}</p>
<button onClick={handleEditToggle}>Edit User</button>
</div>
)}
</div>
)}
<div className="pagination">
<button onClick={() => handlePageChange(1)} disabled={currentPage === 1}>First</button>
<button onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1}>Previous</button>
<span>Page {currentPage} of {totalPages}</span>
<button onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages}>Next</button>
<button onClick={() => handlePageChange(totalPages)} disabled={currentPage === totalPages}>Last</button>
</div>
</div>
);
};
export default UserTable;
the output is
Thank you for your assistance!
Best regards,