Add visual save confirmation to User Management page

Role and agency changes now show an inline green checkmark + "Saved" indicator
on the affected row that auto-clears after 2 seconds. Agency creation shows a
green success banner that auto-dismisses after 3 seconds. Successful actions
also clear any stale error banners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael 2026-02-22 08:15:17 -06:00
parent 8220955cd4
commit 51c4909ee7

View file

@ -22,6 +22,8 @@ export const UserManagement: React.FC = () => {
const [agencies, setAgencies] = useState<AgencyResponse[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [savedUserId, setSavedUserId] = useState<string | null>(null);
const [successMessage, setSuccessMessage] = useState<string | null>(null);
// New agency form
const [newAgencyName, setNewAgencyName] = useState('');
@ -48,10 +50,17 @@ export const UserManagement: React.FC = () => {
}
};
const showSavedIndicator = (userId: string) => {
setSavedUserId(userId);
setError(null);
setTimeout(() => setSavedUserId(prev => prev === userId ? null : prev), 2000);
};
const handleRoleChange = async (userId: string, newRole: string) => {
try {
const updated = await apiService.updateUser(userId, { role: newRole });
setUsers(prev => prev.map(u => u.id === userId ? updated : u));
showSavedIndicator(userId);
} catch (err) {
console.error('Failed to update user role:', err);
setError('Failed to update user role.');
@ -62,6 +71,7 @@ export const UserManagement: React.FC = () => {
try {
const updated = await apiService.updateUser(userId, { agency_id: agencyId });
setUsers(prev => prev.map(u => u.id === userId ? updated : u));
showSavedIndicator(userId);
} catch (err) {
console.error('Failed to update user agency:', err);
setError('Failed to update user agency.');
@ -77,6 +87,9 @@ export const UserManagement: React.FC = () => {
const newAgency = await apiService.createAgency(newAgencyName.trim());
setAgencies(prev => [...prev, newAgency].sort((a, b) => a.name.localeCompare(b.name)));
setNewAgencyName('');
setError(null);
setSuccessMessage('Agency created successfully');
setTimeout(() => setSuccessMessage(prev => prev === 'Agency created successfully' ? null : prev), 3000);
} catch (err) {
console.error('Failed to create agency:', err);
setError('Failed to create agency.');
@ -115,6 +128,13 @@ export const UserManagement: React.FC = () => {
</div>
)}
{successMessage && (
<div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-[10px] text-green-700 text-sm">
{successMessage}
<button onClick={() => setSuccessMessage(null)} className="ml-2 font-semibold hover:underline">Dismiss</button>
</div>
)}
{/* Users Table */}
<section className="mb-10">
<h2 className="text-xl font-semibold text-primary-blue mb-4">Users ({users.length})</h2>
@ -128,6 +148,7 @@ export const UserManagement: React.FC = () => {
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Role</th>
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Agency</th>
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider">Created</th>
<th className="px-6 py-3 text-left text-xs font-bold text-black-title uppercase tracking-wider w-20"></th>
</tr>
</thead>
<tbody className="divide-y divide-grey-300">
@ -178,11 +199,21 @@ export const UserManagement: React.FC = () => {
<td className="px-6 py-4 whitespace-nowrap text-sm text-grey-700">
{formatDate(user.created_at)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
{savedUserId === user.id && (
<span className="inline-flex items-center gap-1 text-green-700 font-medium">
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
</svg>
Saved
</span>
)}
</td>
</tr>
))}
{users.length === 0 && (
<tr>
<td colSpan={5} className="px-6 py-8 text-center text-grey-700">
<td colSpan={6} className="px-6 py-8 text-center text-grey-700">
No users found.
</td>
</tr>