237 lines
7.3 KiB
TypeScript
237 lines
7.3 KiB
TypeScript
/**
|
|
* @ai-summary Admin desktop page for managing community station submissions
|
|
*/
|
|
|
|
import React, { useState, useCallback } from 'react';
|
|
import {
|
|
Box,
|
|
Paper,
|
|
Tabs,
|
|
Tab,
|
|
Select,
|
|
MenuItem,
|
|
FormControl,
|
|
InputLabel,
|
|
useMediaQuery,
|
|
useTheme,
|
|
Grid,
|
|
Typography,
|
|
} from '@mui/material';
|
|
import { AdminSectionHeader } from '../components/AdminSectionHeader';
|
|
import { CommunityStationReviewQueue } from '../components/CommunityStationReviewQueue';
|
|
import { CommunityStationsList } from '../../stations/components/CommunityStationsList';
|
|
import {
|
|
usePendingSubmissions,
|
|
useAllCommunitySubmissions,
|
|
useReviewStation,
|
|
} from '../../stations/hooks/useCommunityStations';
|
|
import toast from 'react-hot-toast';
|
|
|
|
interface TabPanelProps {
|
|
children?: React.ReactNode;
|
|
index: number;
|
|
value: number;
|
|
}
|
|
|
|
const TabPanel: React.FC<TabPanelProps> = ({ children, value, index }) => {
|
|
if (value !== index) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div role="tabpanel">
|
|
<Box sx={{ padding: 2 }}>{children}</Box>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Admin page for reviewing and managing community station submissions
|
|
* Desktop layout with tab navigation and status filtering
|
|
*/
|
|
export const AdminCommunityStationsPage: React.FC = () => {
|
|
const theme = useTheme();
|
|
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
|
|
|
const [tabValue, setTabValue] = useState(0);
|
|
const [statusFilter, setStatusFilter] = useState<string>('');
|
|
const [page, setPage] = useState(0);
|
|
|
|
// Hooks
|
|
const pendingSubmissions = usePendingSubmissions(page, 50);
|
|
const allSubmissions = useAllCommunitySubmissions(statusFilter || undefined, page, 50);
|
|
const reviewMutation = useReviewStation();
|
|
|
|
// Determine which data to display
|
|
const displayData = tabValue === 0 ? pendingSubmissions : allSubmissions;
|
|
const stations = displayData.data?.stations || [];
|
|
const totalPages = displayData.data?.total
|
|
? Math.ceil(displayData.data.total / 50)
|
|
: 1;
|
|
|
|
// Handle approval
|
|
const handleApprove = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
await reviewMutation.mutateAsync({
|
|
id,
|
|
decision: { status: 'approved' },
|
|
});
|
|
toast.success('Station approved');
|
|
} catch (error: any) {
|
|
toast.error(error?.response?.data?.message || 'Failed to approve station');
|
|
}
|
|
},
|
|
[reviewMutation]
|
|
);
|
|
|
|
// Handle rejection
|
|
const handleReject = useCallback(
|
|
async (id: string, reason: string) => {
|
|
try {
|
|
await reviewMutation.mutateAsync({
|
|
id,
|
|
decision: { status: 'rejected', rejectionReason: reason },
|
|
});
|
|
toast.success('Station rejected');
|
|
} catch (error: any) {
|
|
toast.error(error?.response?.data?.message || 'Failed to reject station');
|
|
}
|
|
},
|
|
[reviewMutation]
|
|
);
|
|
|
|
return (
|
|
<Box>
|
|
{/* Header */}
|
|
<AdminSectionHeader
|
|
title="Community Gas Station Reviews"
|
|
stats={[
|
|
{ label: 'Pending', value: pendingSubmissions.data?.total || 0 },
|
|
{ label: 'Total', value: allSubmissions.data?.total || 0 }
|
|
]}
|
|
/>
|
|
|
|
{/* Main content */}
|
|
<Paper sx={{ m: 2 }}>
|
|
<Tabs
|
|
value={tabValue}
|
|
onChange={(_, newValue) => setTabValue(newValue)}
|
|
indicatorColor="primary"
|
|
textColor="primary"
|
|
aria-label="admin community stations tabs"
|
|
variant={isMobile ? 'scrollable' : 'standard'}
|
|
>
|
|
<Tab label={`Pending (${pendingSubmissions.data?.total || 0})`} id="tab-0" />
|
|
<Tab label="All Submissions" id="tab-1" />
|
|
</Tabs>
|
|
|
|
{/* Pending tab */}
|
|
<TabPanel value={tabValue} index={0}>
|
|
<CommunityStationReviewQueue
|
|
stations={stations}
|
|
loading={displayData.isLoading}
|
|
error={displayData.error ? 'Failed to load submissions' : null}
|
|
onApprove={handleApprove}
|
|
onReject={handleReject}
|
|
page={page}
|
|
onPageChange={setPage}
|
|
totalPages={totalPages}
|
|
isReviewLoading={reviewMutation.isPending}
|
|
/>
|
|
</TabPanel>
|
|
|
|
{/* All submissions tab */}
|
|
<TabPanel value={tabValue} index={1}>
|
|
<Grid container spacing={2} sx={{ mb: 2 }}>
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<FormControl fullWidth size="small">
|
|
<InputLabel>Filter by Status</InputLabel>
|
|
<Select
|
|
value={statusFilter}
|
|
label="Filter by Status"
|
|
onChange={(e) => {
|
|
setStatusFilter(e.target.value);
|
|
setPage(0);
|
|
}}
|
|
>
|
|
<MenuItem value="">All</MenuItem>
|
|
<MenuItem value="pending">Pending</MenuItem>
|
|
<MenuItem value="approved">Approved</MenuItem>
|
|
<MenuItem value="rejected">Rejected</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
<CommunityStationsList
|
|
stations={stations}
|
|
loading={displayData.isLoading}
|
|
error={displayData.error ? 'Failed to load submissions' : null}
|
|
isAdmin={true}
|
|
onApprove={handleApprove}
|
|
onReject={handleReject}
|
|
page={page}
|
|
onPageChange={setPage}
|
|
totalPages={totalPages}
|
|
/>
|
|
</TabPanel>
|
|
</Paper>
|
|
|
|
{/* Stats card */}
|
|
{pendingSubmissions.data && (
|
|
<Paper sx={{ m: 2, p: 2 }}>
|
|
<Grid container spacing={2}>
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<Box>
|
|
<Typography variant="h4" color="primary">
|
|
{pendingSubmissions.data.total}
|
|
</Typography>
|
|
<Typography variant="body2" color="textSecondary">
|
|
Pending Review
|
|
</Typography>
|
|
</Box>
|
|
</Grid>
|
|
{allSubmissions.data && (
|
|
<>
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<Box>
|
|
<Typography variant="h4" color="success.main">
|
|
{allSubmissions.data.stations.filter((s) => s.status === 'approved').length}
|
|
</Typography>
|
|
<Typography variant="body2" color="textSecondary">
|
|
Approved
|
|
</Typography>
|
|
</Box>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<Box>
|
|
<Typography variant="h4" color="error.main">
|
|
{allSubmissions.data.stations.filter((s) => s.status === 'rejected').length}
|
|
</Typography>
|
|
<Typography variant="body2" color="textSecondary">
|
|
Rejected
|
|
</Typography>
|
|
</Box>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<Box>
|
|
<Typography variant="h4">
|
|
{allSubmissions.data.total}
|
|
</Typography>
|
|
<Typography variant="body2" color="textSecondary">
|
|
Total Submitted
|
|
</Typography>
|
|
</Box>
|
|
</Grid>
|
|
</>
|
|
)}
|
|
</Grid>
|
|
</Paper>
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default AdminCommunityStationsPage;
|