feat: implement administrative audit allocation and management dashboard pages with supporting API services

This commit is contained in:
2026-07-12 18:19:50 +05:30
parent b667fe1082
commit e5c2b047d7
8 changed files with 551 additions and 248 deletions

View File

@@ -3,6 +3,7 @@ import { Box, Button, TextField, Container, Alert, Paper } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { useAuthStore } from '../../store/authStore';
import { accountService } from '../../api/accountService';
import type { Organization } from '../../api/accountService';
import { DbSelectionDialog } from './DbSelectionDialog';
import { useToastStore } from '../../store/toastStore';
@@ -12,6 +13,7 @@ export const Login: React.FC = () => {
const [isInvalid, setIsInvalid] = useState(false);
const [loading, setLoading] = useState(false);
const [dialogOpen, setDialogOpen] = useState(false);
const [dialogOrgs, setDialogOrgs] = useState<Organization[]>([]);
const [tempLoginResult, setTempLoginResult] = useState<Awaited<ReturnType<typeof accountService.login>> | null>(null);
const { login, isAuthenticated } = useAuthStore();
@@ -24,6 +26,23 @@ export const Login: React.FC = () => {
}
}, [isAuthenticated, navigate]);
const commitLogin = (
result: NonNullable<typeof tempLoginResult>,
org: { org_id: number | string; db_name: string },
) => {
login({
access_token: result.access_token,
refresh_token: result.refresh_token,
user_id: result.user.user_id,
username: result.user.username,
email: result.user.email,
role: result.user.role_name,
org_id: String(org.org_id),
db_name: org.db_name,
});
navigate('/activity-feeds', { replace: true });
};
const handleLogin = async () => {
setIsInvalid(false);
setLoading(true);
@@ -38,9 +57,27 @@ export const Login: React.FC = () => {
return;
}
// Every user picks which org/DB to work in after login.
setTempLoginResult(result);
setDialogOpen(true);
// Org entry is driven by where the user actually has work: supervisors
// get the full list, everyone else only orgs with records assigned to
// (or audited by) them. One org -> straight in; several -> pick;
// none -> land in their profile org with an empty feed.
let orgs: Organization[] = [];
try {
orgs = await accountService.getMyOrganizations(result.access_token);
} catch {
orgs = [];
}
if (orgs.length === 1) {
commitLogin(result, orgs[0]);
} else if (orgs.length === 0) {
useToastStore.getState().showToast('No work assigned to you yet.', 'info');
commitLogin(result, { org_id: result.user.org_id, db_name: result.user.db_name });
} else {
setTempLoginResult(result);
setDialogOrgs(orgs);
setDialogOpen(true);
}
} catch (error) {
setIsInvalid(true);
useToastStore.getState().showToast('Invalid username or password.', 'error');
@@ -49,20 +86,10 @@ export const Login: React.FC = () => {
}
};
const handleDbSelection = (selectedOrg?: { org_id: string; db_name: string; org_name?: string }) => {
const handleDbSelection = (selectedOrg?: Organization) => {
setDialogOpen(false);
if (selectedOrg && tempLoginResult) {
login({
access_token: tempLoginResult.access_token,
refresh_token: tempLoginResult.refresh_token,
user_id: tempLoginResult.user.user_id,
username: tempLoginResult.user.username,
email: tempLoginResult.user.email,
role: tempLoginResult.user.role_name,
org_id: selectedOrg.org_id,
db_name: selectedOrg.db_name,
});
navigate('/activity-feeds', { replace: true });
commitLogin(tempLoginResult, selectedOrg);
}
setTempLoginResult(null);
};
@@ -198,7 +225,7 @@ export const Login: React.FC = () => {
</Paper>
</Container>
<DbSelectionDialog open={dialogOpen} onClose={handleDbSelection} />
<DbSelectionDialog open={dialogOpen} organizations={dialogOrgs} onClose={handleDbSelection} />
</Box>
);
};